haildb-2.3.2/0000755000175000017500000000000011513177437013642 5ustar00pcrewspcrews00000000000000haildb-2.3.2/ddl/0000755000175000017500000000000011513177437014405 5ustar00pcrewspcrews00000000000000haildb-2.3.2/ddl/ddl0ddl.c0000644000175000017500000015051711513177357016072 0ustar00pcrewspcrews00000000000000/****************************************************** Implemention of Innobase DDL operations. (c) 2008 Oracle Corpn/Innobase Oy Created 12 Oct 2008. *******************************************************/ #include "srv0srv.h" #include "api0misc.h" #include "trx0roll.h" #include "dict0crea.h" #include "dict0boot.h" #include "dict0load.h" #include "log0log.h" #include "lock0lock.h" #include "btr0pcur.h" #include "ddl0ddl.h" #include "pars0pars.h" #include "ut0lst.h" /* List of tables we should drop in background. ALTER TABLE requires that the table handler can drop the table in background when there are no queries to it any more. Protected by the kernel mutex. */ typedef struct ddl_drop_struct ddl_drop_t; struct ddl_drop_struct{ char* table_name; UT_LIST_NODE_T(ddl_drop_t) ddl_drop_list; }; static UT_LIST_BASE_NODE_T(ddl_drop_t) ddl_drop_list; static ibool ddl_drop_list_inited = FALSE; /* Magic table names for invoking various monitor threads */ static const char S_innodb_monitor[] = "innodb_monitor"; static const char S_innodb_lock_monitor[] = "innodb_lock_monitor"; static const char S_innodb_tablespace_monitor[] = "innodb_tablespace_monitor"; static const char S_innodb_table_monitor[] = "innodb_table_monitor"; static const char S_innodb_mem_validate[] = "innodb_mem_validate"; /************************************************************************* Drops a table as a background operation. On Unix in ALTER TABLE the table handler does not remove the table before all handles to it has been removed. Furhermore, the call to the drop table must be non-blocking. Therefore we do the drop table as a background operation, which is taken care of by the master thread in srv0srv.c. @return error code or DB_SUCCESS */ static ulint ddl_drop_table_in_background( /*=========================*/ const char* name) /*!< in: table name */ { trx_t* trx; ulint error; ibool started; trx = trx_allocate_for_background(); started = trx_start(trx, ULINT_UNDEFINED); ut_a(started); /* If the original transaction was dropping a table referenced by foreign keys, we must set the following to be able to drop the table: */ trx->check_foreigns = FALSE; #if 0 ib_logger(ib_stream, "InnoDB: Info: Dropping table "); ut_print_name(ib_stream, trx, TRUE, name); ib_logger(ib_stream, " from background drop list\n"); #endif /* Try to drop the table in InnoDB */ dict_lock_data_dictionary(trx); error = ddl_drop_table(name, trx, FALSE); trx_commit(trx); dict_unlock_data_dictionary(trx); log_buffer_flush_to_disk(); trx_free_for_background(trx); return(error); } /************************************************************************* The master thread in srv0srv.c calls this regularly to drop tables which we must drop in background after queries to them have ended. Such lazy dropping of tables is needed in ALTER TABLE on Unix. @return how many tables dropped + remaining tables in list */ UNIV_INTERN ulint ddl_drop_tables_in_background(void) /*===============================*/ { ddl_drop_t* drop; dict_table_t* table; ulint n_tables; ulint n_tables_dropped = 0; loop: mutex_enter(&kernel_mutex); if (!ddl_drop_list_inited) { UT_LIST_INIT(ddl_drop_list); ddl_drop_list_inited = TRUE; } drop = UT_LIST_GET_FIRST(ddl_drop_list); n_tables = UT_LIST_GET_LEN(ddl_drop_list); mutex_exit(&kernel_mutex); if (drop == NULL) { /* All tables dropped */ return(n_tables + n_tables_dropped); } mutex_enter(&(dict_sys->mutex)); table = dict_table_get_low(drop->table_name); mutex_exit(&(dict_sys->mutex)); if (table == NULL) { /* If for some reason the table has already been dropped through some other mechanism, do not try to drop it */ goto already_dropped; } if (DB_SUCCESS != ddl_drop_table_in_background(drop->table_name)) { /* If the DROP fails for some table, we return, and let the main thread retry later */ return(n_tables + n_tables_dropped); } n_tables_dropped++; already_dropped: mutex_enter(&kernel_mutex); UT_LIST_REMOVE(ddl_drop_list, ddl_drop_list, drop); ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Dropped table "); ut_print_name(ib_stream, NULL, TRUE, drop->table_name); ib_logger(ib_stream, " in background drop queue.\n"); mem_free(drop->table_name); mem_free(drop); mutex_exit(&kernel_mutex); goto loop; } /************************************************************************* Get the background drop list length. NOTE: the caller must own the kernel mutex! @return how many tables in list */ UNIV_INTERN ulint ddl_get_background_drop_list_len_low(void) /*======================================*/ { ut_ad(mutex_own(&kernel_mutex)); if (!ddl_drop_list_inited) { UT_LIST_INIT(ddl_drop_list); ddl_drop_list_inited = TRUE; } return(UT_LIST_GET_LEN(ddl_drop_list)); } /************************************************************************* If a table is not yet in the drop list, adds the table to the list of tables which the master thread drops in background. We need this on Unix because in ALTER TABLE may call drop table even if the table has running queries on it. Also, if there are running foreign key checks on the table, we drop the table lazily. @return TRUE if the table was not yet in the drop list, and was added there */ static ibool ddl_add_table_to_background_drop_list( /*==================================*/ const char* name) /*!< in: table name */ { ddl_drop_t* drop; mutex_enter(&kernel_mutex); if (!ddl_drop_list_inited) { UT_LIST_INIT(ddl_drop_list); ddl_drop_list_inited = TRUE; } /* Look if the table already is in the drop list */ drop = UT_LIST_GET_FIRST(ddl_drop_list); while (drop != NULL) { if (strcmp(drop->table_name, name) == 0) { /* Already in the list */ mutex_exit(&kernel_mutex); return(FALSE); } drop = UT_LIST_GET_NEXT(ddl_drop_list, drop); } drop = mem_alloc(sizeof(ddl_drop_t)); drop->table_name = mem_strdup(name); UT_LIST_ADD_LAST(ddl_drop_list, ddl_drop_list, drop); /* ib_logger(ib_stream, "InnoDB: Adding table "); ut_print_name(ib_stream, trx, TRUE, drop->table_name); ib_logger(ib_stream, " to background drop list\n"); */ mutex_exit(&kernel_mutex); return(TRUE); } /************************************************************************* Drops a table but does not commit the transaction. If the name of the dropped table ends in one of "innodb_monitor", "innodb_lock_monitor", "innodb_tablespace_monitor", "innodb_table_monitor", then this will also stop the printing of monitor output by the master thread. @return error code or DB_SUCCESS */ UNIV_INTERN ulint ddl_drop_table( /*===========*/ const char* name, /*!< in: table name */ trx_t* trx, /*!< in: transaction handle */ ibool drop_db)/*!< in: TRUE=dropping whole database */ { dict_foreign_t* foreign; dict_table_t* table; ulint space_id; enum db_err err; const char* table_name; ulint namelen; pars_info_t* info = NULL; ut_a(name != NULL); if (srv_created_new_raw) { ib_logger(ib_stream, "InnoDB: A new raw disk partition was initialized:\n" "InnoDB: we do not allow database modifications" " by the user.\n" "InnoDB: Shut down the server and edit your config file " "so that newraw is replaced with raw.\n"); return(DB_ERROR); } trx->op_info = "dropping table"; /* The table name is prefixed with the database name and a '/'. Certain table names starting with 'innodb_' have their special meaning regardless of the database name. Thus, we need to ignore the database name prefix in the comparisons. */ table_name = strchr(name, '/'); ut_a(table_name); table_name++; namelen = strlen(table_name) + 1; if (namelen == sizeof S_innodb_monitor && !memcmp(table_name, S_innodb_monitor, sizeof S_innodb_monitor)) { /* Table name equals "innodb_monitor": stop monitor prints */ srv_print_innodb_monitor = FALSE; srv_print_innodb_lock_monitor = FALSE; } else if (namelen == sizeof S_innodb_lock_monitor && !memcmp(table_name, S_innodb_lock_monitor, sizeof S_innodb_lock_monitor)) { srv_print_innodb_monitor = FALSE; srv_print_innodb_lock_monitor = FALSE; } else if (namelen == sizeof S_innodb_tablespace_monitor && !memcmp(table_name, S_innodb_tablespace_monitor, sizeof S_innodb_tablespace_monitor)) { srv_print_innodb_tablespace_monitor = FALSE; } else if (namelen == sizeof S_innodb_table_monitor && !memcmp(table_name, S_innodb_table_monitor, sizeof S_innodb_table_monitor)) { srv_print_innodb_table_monitor = FALSE; } /* Serialize data dictionary operations with dictionary mutex: no deadlocks can occur then in these operations */ if (trx->dict_operation_lock_mode != RW_X_LATCH) { return(DB_SCHEMA_NOT_LOCKED); } ut_ad(mutex_own(&(dict_sys->mutex))); #ifdef UNIV_SYNC_DEBUG ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_EX)); #endif /* UNIV_SYNC_DEBUG */ table = dict_table_get_low(name); if (!table) { err = DB_TABLE_NOT_FOUND; ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Error: table "); ut_print_name(ib_stream, trx, TRUE, name); ib_logger(ib_stream, " does not exist in the InnoDB internal\n" "InnoDB: data dictionary though the client is" " trying to drop it.\n" "InnoDB: You can look for further help on the\n" "InnoDB: InnoDB website. Check the site for details\n"); goto func_exit; } /* Check if the table is referenced by foreign key constraints from some other table (not the table itself) */ foreign = UT_LIST_GET_FIRST(table->referenced_list); while (foreign && foreign->foreign_table == table) { check_next_foreign: foreign = UT_LIST_GET_NEXT(referenced_list, foreign); } if (foreign && trx->check_foreigns && !(drop_db && dict_tables_have_same_db( name, foreign->foreign_table_name))) { /* We only allow dropping a referenced table if FOREIGN_KEY_CHECKS is set to 0 */ err = DB_CANNOT_DROP_CONSTRAINT; mutex_enter(&dict_foreign_err_mutex); ut_print_timestamp(ib_stream); ib_logger(ib_stream, " Cannot drop table "); ut_print_name(ib_stream, trx, TRUE, name); ib_logger(ib_stream, "\nbecause it is referenced by "); ut_print_name(ib_stream, trx, TRUE, foreign->foreign_table_name); ib_logger(ib_stream, "\n"); mutex_exit(&dict_foreign_err_mutex); goto func_exit; } if (foreign && trx->check_foreigns) { goto check_next_foreign; } if (table->n_handles_opened > 0) { ibool added; added = ddl_add_table_to_background_drop_list(table->name); if (added) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Warning: Client is" " trying to drop table (%lu) ", (ulint) table->id.low); ut_print_name(ib_stream, trx, TRUE, table->name); ib_logger(ib_stream, "\n" "InnoDB: though there are still" " open handles to it.\n" "InnoDB: Adding the table to the" " background drop queue.\n"); /* We return DB_SUCCESS though the drop will happen lazily later */ err = DB_SUCCESS; } else { /* The table is already in the background drop list */ err = DB_TABLESPACE_DELETED; } goto func_exit; } /* TODO: could we replace the counter n_foreign_key_checks_running with lock checks on the table? Acquire here an exclusive lock on the table, and rewrite lock0lock.c and the lock wait in srv0srv.c so that they can cope with the table having been dropped here? Foreign key checks take an IS or IX lock on the table. */ if (table->n_foreign_key_checks_running > 0) { const char* table_name = table->name; ibool added; added = ddl_add_table_to_background_drop_list(table_name); if (added) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: You are trying to drop table "); ut_print_name(ib_stream, trx, TRUE, table_name); ib_logger(ib_stream, "\n" "InnoDB: though there is a" " foreign key check running on it.\n" "InnoDB: Adding the table to" " the background drop queue.\n"); /* We return DB_SUCCESS though the drop will happen lazily later */ err = DB_SUCCESS; } else { /* The table is already in the background drop list */ err = DB_TABLESPACE_DELETED; } goto func_exit; } /* Remove any locks there are on the table or its records */ lock_remove_all_on_table(table, TRUE); trx_set_dict_operation(trx, TRX_DICT_OP_TABLE); trx->table_id = table->id; #if 0 ib_logger(ib_stream, "Dropping: %ld\n", (long) table->id.low); #endif /* We use the private SQL parser of Innobase to generate the query graphs needed in deleting the dictionary data from system tables in Innobase. Deleting a row from SYS_INDEXES table also frees the file segments of the B-tree associated with the index. */ info = pars_info_create(); pars_info_add_str_literal(info, "table_name", name); err = que_eval_sql(info, "PROCEDURE DROP_TABLE_PROC () IS\n" "sys_foreign_id CHAR;\n" "table_id CHAR;\n" "index_id CHAR;\n" "foreign_id CHAR;\n" "found INT;\n" "BEGIN\n" "SELECT ID INTO table_id\n" "FROM SYS_TABLES\n" "WHERE NAME = :table_name\n" "LOCK IN SHARE MODE;\n" "IF (SQL % NOTFOUND) THEN\n" " RETURN;\n" "END IF;\n" "found := 1;\n" "SELECT ID INTO sys_foreign_id\n" "FROM SYS_TABLES\n" "WHERE NAME = 'SYS_FOREIGN'\n" "LOCK IN SHARE MODE;\n" "IF (SQL % NOTFOUND) THEN\n" " found := 0;\n" "END IF;\n" "IF (:table_name = 'SYS_FOREIGN') THEN\n" " found := 0;\n" "END IF;\n" "IF (:table_name = 'SYS_FOREIGN_COLS') THEN\n" " found := 0;\n" "END IF;\n" "WHILE found = 1 LOOP\n" " SELECT ID INTO foreign_id\n" " FROM SYS_FOREIGN\n" " WHERE FOR_NAME = :table_name\n" " AND TO_BINARY(FOR_NAME)\n" " = TO_BINARY(:table_name)\n" " LOCK IN SHARE MODE;\n" " IF (SQL % NOTFOUND) THEN\n" " found := 0;\n" " ELSE\n" " DELETE FROM SYS_FOREIGN_COLS\n" " WHERE ID = foreign_id;\n" " DELETE FROM SYS_FOREIGN\n" " WHERE ID = foreign_id;\n" " END IF;\n" "END LOOP;\n" "found := 1;\n" "WHILE found = 1 LOOP\n" " SELECT ID INTO index_id\n" " FROM SYS_INDEXES\n" " WHERE TABLE_ID = table_id\n" " LOCK IN SHARE MODE;\n" " IF (SQL % NOTFOUND) THEN\n" " found := 0;\n" " ELSE\n" " DELETE FROM SYS_FIELDS\n" " WHERE INDEX_ID = index_id;\n" " DELETE FROM SYS_INDEXES\n" " WHERE ID = index_id\n" " AND TABLE_ID = table_id;\n" " END IF;\n" "END LOOP;\n" "DELETE FROM SYS_COLUMNS\n" "WHERE TABLE_ID = table_id;\n" "DELETE FROM SYS_TABLES\n" "WHERE ID = table_id;\n" "END;\n" , FALSE, trx); if (err != DB_SUCCESS) { if (err != DB_OUT_OF_FILE_SPACE) { ib_logger(ib_stream, "InnoDB: Error: unexpected err: %d", err); ut_error; } err = DB_MUST_GET_MORE_FILE_SPACE; ib_handle_errors(&err, trx, NULL, NULL); ut_error; } else { ibool is_path; const char* name_or_path; mem_heap_t* heap; heap = mem_heap_create(200); /* Clone the name, in case it has been allocated from table->heap, which will be freed by dict_table_remove_from_cache(table) below. */ name = mem_heap_strdup(heap, name); space_id = table->space; if (table->dir_path_of_temp_table != NULL) { is_path = TRUE; name_or_path = mem_heap_strdup( heap, table->dir_path_of_temp_table); } else { is_path = FALSE; name_or_path = name; } dict_table_remove_from_cache(table); // FIXME: srv_force_recovery should be passed in as an arg if (dict_load_table(srv_force_recovery, name) != NULL) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Error: not able to remove table "); ut_print_name(ib_stream, trx, TRUE, name); ib_logger(ib_stream, " from the dictionary cache!\n"); err = DB_ERROR; } /* Do not drop possible .ibd tablespace if something went wrong: we do not want to delete valuable data of the user */ if (err == DB_SUCCESS && space_id > 0) { if (!fil_space_for_table_exists_in_mem(space_id, name_or_path, is_path, FALSE, TRUE)) { err = DB_SUCCESS; ib_logger(ib_stream, "InnoDB: We removed now the InnoDB" " internal data dictionary entry\n" "InnoDB: of table "); ut_print_name(ib_stream, trx, TRUE, name); ib_logger(ib_stream, ".\n"); } else if (!fil_delete_tablespace(space_id)) { ib_logger(ib_stream, "InnoDB: We removed now the InnoDB" " internal data dictionary entry\n" "InnoDB: of table "); ut_print_name(ib_stream, trx, TRUE, name); ib_logger(ib_stream, ".\n"); ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Error: not able to" " delete tablespace %lu of table ", (ulong) space_id); ut_print_name(ib_stream, trx, TRUE, name); ib_logger(ib_stream, "!\n"); err = DB_ERROR; } } mem_heap_free(heap); } func_exit: trx->op_info = ""; #ifndef UNIV_HOTBACKUP srv_wake_master_thread(); #endif /* !UNIV_HOTBACKUP */ return((int) err); } /* Evaluates to true if str1 equals str2_onstack, used for comparing the above strings. */ #define STR_EQ(str1, str1_len, str2_onstack) \ ((str1_len) == sizeof(str2_onstack) \ && memcmp(str1, str2_onstack, sizeof(str2_onstack)) == 0) /************************************************************************* Creates a table, if the name of the table ends in one of "innodb_monitor", "innodb_lock_monitor", "innodb_tablespace_monitor", "innodb_table_monitor", then this will also start the printing of monitor output by the master thread. If the table name ends in "innodb_mem_validate", InnoDB will try to invoke mem_validate(). @return error code or DB_SUCCESS */ UNIV_INTERN ulint ddl_create_table( /*=============*/ dict_table_t* table, /*!< in: table definition */ trx_t* trx) /*!< in: transaction handle */ { tab_node_t* node; mem_heap_t* heap; que_thr_t* thr; const char* table_name; ulint table_name_len; ulint err; ulint i; ut_ad(trx->client_thread_id == os_thread_get_curr_id()); #ifdef UNIV_SYNC_DEBUG ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_EX)); #endif /* UNIV_SYNC_DEBUG */ ut_ad(mutex_own(&(dict_sys->mutex))); ut_ad(trx->dict_operation_lock_mode == RW_X_LATCH); if (srv_created_new_raw) { ib_logger(ib_stream, "InnoDB: A new raw disk partition was initialized:\n" "InnoDB: we do not allow database modifications" " by the user.\n" "InnoDB: Shut down the database and edit your config " "file so that newraw is replaced with raw.\n"); err_exit: dict_mem_table_free(table); return(DB_ERROR); /* The table name is prefixed with the database name and a '/'. Certain table names starting with 'innodb_' have their special meaning regardless of the database name. Thus, we need to ignore the database name prefix in the comparisons. */ } else if (strchr(table->name, '/') == NULL) { ib_logger(ib_stream, " InnoDB: Error: table "); ut_print_name(ib_stream, trx, TRUE, table->name); ib_logger(ib_stream, "not prefixed with a database name and '/'\n"); goto err_exit; } trx->op_info = "creating table"; /* Check that no reserved column names are used. */ for (i = 0; i < dict_table_get_n_user_cols(table); i++) { if (dict_col_name_is_reserved( dict_table_get_col_name(table, i))) { goto err_exit; } } table_name = strchr(table->name, '/'); table_name++; table_name_len = strlen(table_name) + 1; if (STR_EQ(table_name, table_name_len, S_innodb_monitor)) { /* Table equals "innodb_monitor": start monitor prints */ srv_print_innodb_monitor = TRUE; /* The lock timeout monitor thread also takes care of InnoDB monitor prints */ os_event_set(srv_lock_timeout_thread_event); } else if (STR_EQ(table_name, table_name_len, S_innodb_lock_monitor)) { srv_print_innodb_monitor = TRUE; srv_print_innodb_lock_monitor = TRUE; os_event_set(srv_lock_timeout_thread_event); } else if (STR_EQ(table_name, table_name_len, S_innodb_tablespace_monitor)) { srv_print_innodb_tablespace_monitor = TRUE; os_event_set(srv_lock_timeout_thread_event); } else if (STR_EQ(table_name, table_name_len, S_innodb_table_monitor)) { srv_print_innodb_table_monitor = TRUE; os_event_set(srv_lock_timeout_thread_event); } else if (STR_EQ(table_name, table_name_len, S_innodb_mem_validate)) { /* We define here a debugging feature intended for developers */ ib_logger(ib_stream, "Validating InnoDB memory:\n" "to use this feature you must compile InnoDB with\n" "UNIV_MEM_DEBUG defined in univ.i and" " the server must be\n" "quiet because allocation from a mem heap" " is not protected\n" "by any semaphore.\n"); #ifdef UNIV_MEM_DEBUG ut_a(mem_validate()); ib_logger(ib_stream, "Memory validated\n"); #else /* UNIV_MEM_DEBUG */ ib_logger(ib_stream, "Memory NOT validated (recompile with " "UNIV_MEM_DEBUG)\n"); #endif /* UNIV_MEM_DEBUG */ } heap = mem_heap_create(512); trx_set_dict_operation(trx, TRX_DICT_OP_TABLE); node = tab_create_graph_create(table, heap, FALSE); thr = pars_complete_graph_for_exec(node, trx, heap); ut_a(thr == que_fork_start_command(que_node_get_parent(thr))); que_run_threads(thr); err = trx->error_state; if (UNIV_UNLIKELY(err != DB_SUCCESS)) { trx->error_state = DB_SUCCESS; } switch (err) { case DB_OUT_OF_FILE_SPACE: ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Warning: cannot create table "); ut_print_name(ib_stream, trx, TRUE, table->name); ib_logger(ib_stream, " because tablespace full\n"); if (dict_table_get_low(table->name)) { ddl_drop_table(table->name, trx, FALSE); } break; case DB_DUPLICATE_KEY: ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Error: table "); ut_print_name(ib_stream, trx, TRUE, table->name); ib_logger(ib_stream, " already exists in InnoDB internal\n" "InnoDB: data dictionary.\n" "InnoDB: You can look for further help on\n" "InnoDB: the InnoDB website\n"); /* We may also get err == DB_ERROR if the .ibd file for the table already exists */ break; } que_graph_free((que_t*) que_node_get_parent(thr)); trx->op_info = ""; return((int) err); } /************************************************************************* Does an index creation operation. @return error number or DB_SUCCESS */ UNIV_INTERN ulint ddl_create_index( /*=============*/ dict_index_t* index, /*!< in: index definition */ trx_t* trx) /*!< in: transaction handle */ { ulint err; que_thr_t* thr; /* Query thread */ ind_node_t* node; /* Index creation node */ mem_heap_t* heap; /* Memory heap */ #ifdef UNIV_SYNC_DEBUG ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_EX)); #endif /* UNIV_SYNC_DEBUG */ ut_ad(mutex_own(&(dict_sys->mutex))); /* This heap is destroyed when the query graph is freed. */ heap = mem_heap_create(512); node = ind_create_graph_create(index, heap, FALSE); thr = pars_complete_graph_for_exec(node, trx, heap); ut_a(thr == que_fork_start_command(que_node_get_parent(thr))); que_run_threads(thr); err = trx->error_state; que_graph_free((que_t*) que_node_get_parent(thr)); return(err); } /************************************************************************* Truncates a table @return error code or DB_SUCCESS */ UNIV_INTERN enum db_err ddl_truncate_table( /*===============*/ dict_table_t* table, /*!< in: table handle */ trx_t* trx) /*!< in: transaction handle */ { dict_foreign_t* foreign; enum db_err err; mem_heap_t* heap; byte* buf; dtuple_t* tuple; dfield_t* dfield; dict_index_t* sys_index; btr_pcur_t pcur; mtr_t mtr; dulint new_id; ulint recreate_space = 0; pars_info_t* info = NULL; /* How do we prevent crashes caused by ongoing operations on the table? Old operations could try to access non-existent pages. 1) SQL queries, INSERT, SELECT, ...: we must get an exclusive table lock on the table before we can do TRUNCATE TABLE. Ensure there are no running queries on the table. This guarantee has to be provided by the SQL layer. 2) Purge and rollback: we assign a new table id for the table. Since purge and rollback look for the table based on the table id, they see the table as 'dropped' and discard their operations. 3) Insert buffer: TRUNCATE TABLE is analogous to DROP TABLE, so we do not have to remove insert buffer records, as the insert buffer works at a low level. If a freed page is later reallocated, the allocator will remove the ibuf entries for it. When we truncate *.ibd files by recreating them (analogous to DISCARD TABLESPACE), we remove all entries for the table in the insert buffer tree. This is not strictly necessary, because in 6) we will assign a new tablespace identifier, but we can free up some space in the system tablespace. 4) Linear readahead and random readahead: we use the same method as in 3) to discard ongoing operations. (This is only relevant for TRUNCATE TABLE by DISCARD TABLESPACE.) 5) FOREIGN KEY operations: if table->n_foreign_key_checks_running > 0, we do not allow the TRUNCATE. We also reserve the data dictionary latch. 6) Crash recovery: To prevent the application of pre-truncation redo log records on the truncated tablespace, we will assign a new tablespace identifier to the truncated tablespace. */ if (srv_created_new_raw) { ib_logger(ib_stream, "InnoDB: A new raw disk partition was initialized:\n" "InnoDB: we do not allow database modifications" " by the user.\n" "InnoDB: Shut down server and edit config file so " "that newraw is replaced with raw.\n"); return(DB_ERROR); } trx->op_info = "truncating table"; /* Serialize data dictionary operations with dictionary mutex: no deadlocks can occur then in these operations */ ut_a(trx->dict_operation_lock_mode != 0); /* Prevent foreign key checks etc. while we are truncating the table */ ut_ad(mutex_own(&(dict_sys->mutex))); #ifdef UNIV_SYNC_DEBUG ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_EX)); #endif /* UNIV_SYNC_DEBUG */ /* Check if the table is referenced by foreign key constraints from some other table (not the table itself) */ foreign = UT_LIST_GET_FIRST(table->referenced_list); while (foreign && foreign->foreign_table == table) { foreign = UT_LIST_GET_NEXT(referenced_list, foreign); } if (foreign && trx->check_foreigns) { /* We only allow truncating a referenced table if FOREIGN_KEY_CHECKS is set to 0 */ mutex_enter(&dict_foreign_err_mutex); ut_print_timestamp(ib_stream); ib_logger(ib_stream, " Cannot truncate table "); ut_print_name(ib_stream, trx, TRUE, table->name); ib_logger(ib_stream, " by DROP+CREATE\n" "InnoDB: because it is referenced by "); ut_print_name(ib_stream, trx, TRUE, foreign->foreign_table_name); ib_logger(ib_stream, "\n"); mutex_exit(&dict_foreign_err_mutex); err = DB_ERROR; goto func_exit; } /* TODO: could we replace the counter n_foreign_key_checks_running with lock checks on the table? Acquire here an exclusive lock on the table, and rewrite lock0lock.c and the lock wait in srv0srv.c so that they can cope with the table having been truncated here? Foreign key checks take an IS or IX lock on the table. */ if (table->n_foreign_key_checks_running > 0) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Cannot truncate table "); ut_print_name(ib_stream, trx, TRUE, table->name); ib_logger(ib_stream, " by DROP+CREATE\n" "InnoDB: because there is a foreign key check" " running on it.\n"); err = DB_ERROR; goto func_exit; } /* Remove all locks except the table-level S and X locks. */ lock_remove_all_on_table(table, FALSE); trx->table_id = table->id; if (table->space && !table->dir_path_of_temp_table) { /* Discard and create the single-table tablespace. */ ulint space = table->space; ulint flags = fil_space_get_flags(space); if (flags != ULINT_UNDEFINED && fil_discard_tablespace(space)) { dict_index_t* index; space = 0; if (fil_create_new_single_table_tablespace( &space, table->name, FALSE, flags, FIL_IBD_FILE_INITIAL_SIZE) != DB_SUCCESS) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: TRUNCATE TABLE %s failed to" " create a new tablespace\n", table->name); table->ibd_file_missing = 1; err = DB_ERROR; goto func_exit; } recreate_space = space; /* Replace the space_id in the data dictionary cache. The persisent data dictionary (SYS_TABLES.SPACE and SYS_INDEXES.SPACE) are updated later in this function. */ table->space = space; index = dict_table_get_first_index(table); do { index->space = space; index = dict_table_get_next_index(index); } while (index); mtr_start(&mtr); fsp_header_init(space, FIL_IBD_FILE_INITIAL_SIZE, &mtr); mtr_commit(&mtr); } } /* scan SYS_INDEXES for all indexes of the table */ heap = mem_heap_create(800); tuple = dtuple_create(heap, 1); dfield = dtuple_get_nth_field(tuple, 0); buf = mem_heap_alloc(heap, 8); mach_write_to_8(buf, table->id); dfield_set_data(dfield, buf, 8); sys_index = dict_table_get_first_index(dict_sys->sys_indexes); dict_index_copy_types(tuple, sys_index, 1); mtr_start(&mtr); btr_pcur_open_on_user_rec(sys_index, tuple, PAGE_CUR_GE, BTR_MODIFY_LEAF, &pcur, &mtr); for (;;) { rec_t* rec; const byte* field; ulint len; ulint root_page_no; if (!btr_pcur_is_on_user_rec(&pcur)) { /* The end of SYS_INDEXES has been reached. */ break; } rec = btr_pcur_get_rec(&pcur); field = rec_get_nth_field_old(rec, 0, &len); ut_ad(len == 8); if (memcmp(buf, field, len) != 0) { /* End of indexes for the table (TABLE_ID mismatch). */ break; } if (rec_get_deleted_flag(rec, FALSE)) { /* The index has been dropped. */ goto next_rec; } /* This call may commit and restart mtr and reposition pcur. */ root_page_no = dict_truncate_index_tree(table, recreate_space, &pcur, &mtr); rec = btr_pcur_get_rec(&pcur); if (root_page_no != FIL_NULL) { page_rec_write_index_page_no( rec, DICT_SYS_INDEXES_PAGE_NO_FIELD, root_page_no, &mtr); /* We will need to commit and restart the mini-transaction in order to avoid deadlocks. The dict_truncate_index_tree() call has allocated a page in this mini-transaction, and the rest of this loop could latch another index page. */ mtr_commit(&mtr); mtr_start(&mtr); btr_pcur_restore_position(BTR_MODIFY_LEAF, &pcur, &mtr); } next_rec: btr_pcur_move_to_next_user_rec(&pcur, &mtr); } btr_pcur_close(&pcur); mtr_commit(&mtr); mem_heap_free(heap); new_id = dict_hdr_get_new_id(DICT_HDR_TABLE_ID); info = pars_info_create(); pars_info_add_int4_literal(info, "space", (lint) table->space); pars_info_add_dulint_literal(info, "old_id", table->id); pars_info_add_dulint_literal(info, "new_id", new_id); err = que_eval_sql(info, "PROCEDURE RENUMBER_TABLESPACE_PROC () IS\n" "BEGIN\n" "UPDATE SYS_TABLES" " SET ID = :new_id, SPACE = :space\n" " WHERE ID = :old_id;\n" "UPDATE SYS_COLUMNS SET TABLE_ID = :new_id\n" " WHERE TABLE_ID = :old_id;\n" "UPDATE SYS_INDEXES" " SET TABLE_ID = :new_id, SPACE = :space\n" " WHERE TABLE_ID = :old_id;\n" "COMMIT WORK;\n" "END;\n" , FALSE, trx); if (err != DB_SUCCESS) { trx->error_state = DB_SUCCESS; trx_rollback(trx, FALSE, NULL); trx->error_state = DB_SUCCESS; ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Unable to assign a new identifier to table "); ut_print_name(ib_stream, trx, TRUE, table->name); ib_logger(ib_stream, "\n" "InnoDB: after truncating it. Background processes" " may corrupt the table!\n"); err = DB_ERROR; } else { dict_table_change_id_in_cache(table, new_id); } dict_update_statistics(table); func_exit: trx->op_info = ""; srv_wake_master_thread(); return(err); } /************************************************************************* Drops an index. @return error code or DB_SUCCESS */ UNIV_INTERN ulint ddl_drop_index( /*===========*/ dict_table_t* table, /*!< in: table instance */ dict_index_t* index, /*!< in: index to drop */ trx_t* trx) /*!< in: transaction handle */ { ulint err = DB_SUCCESS; pars_info_t* info = pars_info_create(); /* We use the private SQL parser of Innobase to generate the query graphs needed in deleting the dictionary data from system tables in Innobase. Deleting a row from SYS_INDEXES table also frees the file segments of the B-tree associated with the index. */ static const char str1[] = "PROCEDURE DROP_INDEX_PROC () IS\n" "BEGIN\n" /* Rename the index, so that it will be dropped by row_merge_drop_temp_indexes() at crash recovery if the server crashes before this trx is committed. */ "UPDATE SYS_INDEXES SET NAME=CONCAT('" TEMP_INDEX_PREFIX_STR "', NAME) WHERE ID = :indexid;\n" "COMMIT WORK;\n" /* Drop the field definitions of the index. */ "DELETE FROM SYS_FIELDS WHERE INDEX_ID = :indexid;\n" /* Drop the index definition and the B-tree. */ "DELETE FROM SYS_INDEXES WHERE ID = :indexid;\n" "END;\n"; ut_ad(index && table && trx); pars_info_add_dulint_literal(info, "indexid", index->id); trx_start_if_not_started(trx); trx->op_info = "dropping index"; ut_a(trx->dict_operation_lock_mode == RW_X_LATCH); err = que_eval_sql(info, str1, FALSE, trx); ut_a(err == DB_SUCCESS); /* Replace this index with another equivalent index for all foreign key constraints on this table where this index is used */ dict_table_replace_index_in_foreign_list(table, index); dict_index_remove_from_cache(table, index); trx->op_info = ""; return(err); } /******************************************************************** Delete a single constraint. @return error code or DB_SUCCESS */ static int ddl_delete_constraint_low( /*======================*/ const char* id, /*!< in: constraint id */ trx_t* trx) /*!< in: transaction handle */ { pars_info_t* info = pars_info_create(); pars_info_add_str_literal(info, "id", id); return((int) que_eval_sql( info, "PROCEDURE DELETE_CONSTRAINT () IS\n" "BEGIN\n" "DELETE FROM SYS_FOREIGN_COLS WHERE ID = :id;\n" "DELETE FROM SYS_FOREIGN WHERE ID = :id;\n" "END;\n" , FALSE, trx)); } /******************************************************************** Delete a single constraint. @return error code or DB_SUCCESS */ static int ddl_delete_constraint( /*==================*/ const char* id, /*!< in: constraint id */ const char* database_name, /*!< in: database name, with the trailing '/' */ mem_heap_t* heap, /*!< in: memory heap */ trx_t* trx) /*!< in: transaction handle */ { ulint err; /* New format constraints have ids /. */ err = ddl_delete_constraint_low( mem_heap_strcat(heap, database_name, id), trx); if (err == DB_SUCCESS && !strchr(id, '/')) { /* Old format < 4.0.18 constraints have constraint ids _. We only try deleting them if the constraint name does not contain a '/' character, otherwise deleting a new format constraint named 'foo/bar' from database 'baz' would remove constraint 'bar' from database 'foo', if it existed. */ err = ddl_delete_constraint_low(id, trx); } return((int) err); } /************************************************************************* Renames a table. @return error code or DB_SUCCESS */ UNIV_INTERN ulint ddl_rename_table( /*=============*/ const char* old_name, /*!< in: old table name */ const char* new_name, /*!< in: new table name */ trx_t* trx) /*!< in: transaction handle */ { dict_table_t* table; ulint err = DB_ERROR; mem_heap_t* heap = NULL; const char** constraints_to_drop = NULL; ulint n_constraints_to_drop = 0; pars_info_t* info = NULL; ut_a(old_name != NULL); ut_a(new_name != NULL); ut_ad(trx->client_thread_id == os_thread_get_curr_id()); if (srv_created_new_raw || srv_force_recovery != IB_RECOVERY_DEFAULT) { ib_logger(ib_stream, "InnoDB: A new raw disk partition was initialized or\n" "InnoDB: innodb_force_recovery is on: we do not allow\n" "InnoDB: database modifications by the user. Shut down\n" "InnoDB: the server and ensure that newraw is replaced\n" "InnoDB: with raw, and innodb_force_... is removed.\n"); goto func_exit; } trx->op_info = "renaming table"; table = dict_table_get_low(old_name); if (!table) { err = DB_TABLE_NOT_FOUND; goto func_exit; } else if (table->ibd_file_missing) { err = DB_TABLE_NOT_FOUND; goto func_exit; } /* We use the private SQL parser of Innobase to generate the query graphs needed in updating the dictionary data from system tables. */ info = pars_info_create(); pars_info_add_str_literal(info, "new_table_name", new_name); pars_info_add_str_literal(info, "old_table_name", old_name); err = que_eval_sql( info, "PROCEDURE RENAME_TABLE () IS\n" "BEGIN\n" "UPDATE SYS_TABLES SET NAME = :new_table_name\n" " WHERE NAME = :old_table_name;\n" "END;\n" , FALSE, trx); if (err == DB_SUCCESS) { /* Rename all constraints. */ info = pars_info_create(); pars_info_add_str_literal(info, "new_table_name", new_name); pars_info_add_str_literal(info, "old_table_name", old_name); err = que_eval_sql( info, "PROCEDURE RENAME_CONSTRAINT_IDS () IS\n" "gen_constr_prefix CHAR;\n" "new_db_name CHAR;\n" "foreign_id CHAR;\n" "new_foreign_id CHAR;\n" "old_db_name_len INT;\n" "old_t_name_len INT;\n" "new_db_name_len INT;\n" "id_len INT;\n" "found INT;\n" "BEGIN\n" "found := 1;\n" "old_db_name_len := INSTR(:old_table_name, '/')-1;\n" "new_db_name_len := INSTR(:new_table_name, '/')-1;\n" "new_db_name := SUBSTR(:new_table_name, 0,\n" " new_db_name_len);\n" "old_t_name_len := LENGTH(:old_table_name);\n" "gen_constr_prefix := CONCAT(:old_table_name,\n" " '_ibfk_');\n" "WHILE found = 1 LOOP\n" " SELECT ID INTO foreign_id\n" " FROM SYS_FOREIGN\n" " WHERE FOR_NAME = :old_table_name\n" " AND TO_BINARY(FOR_NAME)\n" " = TO_BINARY(:old_table_name)\n" " LOCK IN SHARE MODE;\n" " IF (SQL % NOTFOUND) THEN\n" " found := 0;\n" " ELSE\n" " UPDATE SYS_FOREIGN\n" " SET FOR_NAME = :new_table_name\n" " WHERE ID = foreign_id;\n" " id_len := LENGTH(foreign_id);\n" " IF (INSTR(foreign_id, '/') > 0) THEN\n" " IF (INSTR(foreign_id,\n" " gen_constr_prefix) > 0)\n" " THEN\n" " new_foreign_id :=\n" " CONCAT(:new_table_name,\n" " SUBSTR(foreign_id, old_t_name_len,\n" " id_len - old_t_name_len));\n" " ELSE\n" " new_foreign_id :=\n" " CONCAT(new_db_name,\n" " SUBSTR(foreign_id,\n" " old_db_name_len,\n" " id_len - old_db_name_len));\n" " END IF;\n" " UPDATE SYS_FOREIGN\n" " SET ID = new_foreign_id\n" " WHERE ID = foreign_id;\n" " UPDATE SYS_FOREIGN_COLS\n" " SET ID = new_foreign_id\n" " WHERE ID = foreign_id;\n" " END IF;\n" " END IF;\n" "END LOOP;\n" "UPDATE SYS_FOREIGN SET REF_NAME = :new_table_name\n" "WHERE REF_NAME = :old_table_name\n" " AND TO_BINARY(REF_NAME)\n" " = TO_BINARY(:old_table_name);\n" "END;\n" , FALSE, trx); } else if (n_constraints_to_drop > 0) { /* Drop some constraints of tmp tables. */ ulint i; char* db_name; ulint db_name_len; db_name_len = dict_get_db_name_len(old_name) + 1; db_name = mem_heap_strdupl(heap, old_name, db_name_len); for (i = 0; i < n_constraints_to_drop; i++) { err = ddl_delete_constraint( constraints_to_drop[i], db_name, heap, trx); if (err != DB_SUCCESS) { break; } } } if (err != DB_SUCCESS) { if (err == DB_DUPLICATE_KEY) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Error; possible reasons:\n" "InnoDB: 1) Table rename would cause" " two FOREIGN KEY constraints\n" "InnoDB: to have the same internal name" " in case-insensitive comparison.\n" " trying to rename table.\n" "InnoDB: If table "); ut_print_name(ib_stream, trx, TRUE, new_name); ib_logger(ib_stream, " is a temporary table, then it can be that\n" "InnoDB: there are still queries running" " on the table, and it will be\n" "InnoDB: dropped automatically when" " the queries end.\n"); } trx->error_state = DB_SUCCESS; trx_rollback(trx, FALSE, NULL); trx->error_state = DB_SUCCESS; } else { /* The following call will also rename the .ibd data file if the table is stored in a single-table tablespace */ if (!dict_table_rename_in_cache(table, new_name, TRUE)) { trx->error_state = DB_SUCCESS; trx_rollback(trx, FALSE, NULL); trx->error_state = DB_SUCCESS; goto func_exit; } /* We only want to switch off some of the type checking in an ALTER, not in a RENAME. */ err = dict_load_foreigns(new_name, trx->check_foreigns); if (err != DB_SUCCESS) { ibool ret; ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Error: in RENAME TABLE" " table "); ut_print_name(ib_stream, trx, TRUE, new_name); ib_logger(ib_stream, "\n" "InnoDB: is referenced in" " foreign key constraints\n" "InnoDB: which are not compatible" " with the new table definition.\n"); ret = dict_table_rename_in_cache(table,old_name, FALSE); ut_a(ret); trx->error_state = DB_SUCCESS; trx_rollback(trx, FALSE, NULL); trx->error_state = DB_SUCCESS; } } func_exit: if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } trx->op_info = ""; return(err); } /************************************************************************* Renames an index. @return error code or DB_SUCCESS */ UNIV_INTERN ulint ddl_rename_index( /*=============*/ const char* table_name, /*!< in: table that owns the index */ const char* old_name, /*!< in: old table name */ const char* new_name, /*!< in: new table name */ trx_t* trx) /*!< in: transaction handle */ { dict_table_t* table; pars_info_t* info = NULL; ulint err = DB_ERROR; ut_a(old_name != NULL); ut_a(old_name != NULL); ut_a(table_name != NULL); ut_ad(trx->client_thread_id == os_thread_get_curr_id()); if (srv_created_new_raw || srv_force_recovery != IB_RECOVERY_DEFAULT) { ib_logger(ib_stream, "InnoDB: A new raw disk partition was initialized or\n" "InnoDB: innodb_force_recovery is on: we do not allow\n" "InnoDB: database modifications by the user. Shut down\n" "InnoDB: the server and ensure that newraw is replaced\n" "InnoDB: with raw, and innodb_force_... is removed.\n"); goto func_exit; } trx->op_info = "renaming index"; table = dict_table_get_low(table_name); if (!table || table->ibd_file_missing) { err = DB_TABLE_NOT_FOUND; goto func_exit; } /* We use the private SQL parser of Innobase to generate the query graphs needed in updating the dictionary data from system tables. */ info = pars_info_create(); pars_info_add_str_literal(info, "table_name", table_name); pars_info_add_str_literal(info, "new_index_name", new_name); pars_info_add_str_literal(info, "old_index_name", old_name); err = que_eval_sql( info, "PROCEDURE RENAME_TABLE () IS\n" "table_id CHAR;\n" "BEGIN\n" "SELECT ID INTO table_id\n" " FROM SYS_TABLES\n" " WHERE NAME = :table_name\n" "LOCK IN SHARE MODE;\n" "IF (SQL % NOTFOUND) THEN\n" " RETURN;\n" "END IF;\n" "UPDATE SYS_INDEXES SET NAME = :new_index_name\n" " WHERE NAME = :old_index_name\n" " AND table_id = table_id;\n" "END;\n" , FALSE, trx); if (err == DB_SUCCESS) { dict_index_t* index; index = dict_table_get_first_index(table); do { /* FIXME: We are leaking memory here, well sort of, since the previous name allocation will not be freed till the index instance is destroyed. */ if (strcasecmp(index->name, old_name) == 0) { index->name = mem_heap_strdup( index->heap, new_name); break; } index = dict_table_get_next_index(index); } while (index); } else { trx->error_state = DB_SUCCESS; trx_rollback(trx, FALSE, NULL); } func_exit: trx->op_info = ""; return(err); } /*********************************************************************** Drop all foreign keys in a database, see Bug#18942. @return error code or DB_SUCCESS */ static enum db_err ddl_drop_all_foreign_keys_in_db( /*============================*/ const char* name, /*!< in: database name which ends to '/' */ trx_t* trx) /*!< in: transaction handle */ { enum db_err err; pars_info_t* pinfo; ut_a(name[strlen(name) - 1] == '/'); pinfo = pars_info_create(); pars_info_add_str_literal(pinfo, "dbname", name); /* true if for_name is not prefixed with dbname */ #define TABLE_NOT_IN_THIS_DB \ "SUBSTR(for_name, 0, LENGTH(:dbname)) <> :dbname" err = que_eval_sql(pinfo, "PROCEDURE DROP_ALL_FOREIGN_KEYS_PROC () IS\n" "foreign_id CHAR;\n" "for_name CHAR;\n" "found INT;\n" "DECLARE CURSOR cur IS\n" "SELECT ID, FOR_NAME FROM SYS_FOREIGN\n" "WHERE FOR_NAME >= :dbname\n" "LOCK IN SHARE MODE\n" "ORDER BY FOR_NAME;\n" "BEGIN\n" "found := 1;\n" "OPEN cur;\n" "WHILE found = 1 LOOP\n" " FETCH cur INTO foreign_id, for_name;\n" " IF (SQL % NOTFOUND) THEN\n" " found := 0;\n" " ELSIF (" TABLE_NOT_IN_THIS_DB ") THEN\n" " found := 0;\n" " ELSIF (1=1) THEN\n" " DELETE FROM SYS_FOREIGN_COLS\n" " WHERE ID = foreign_id;\n" " DELETE FROM SYS_FOREIGN\n" " WHERE ID = foreign_id;\n" " END IF;\n" "END LOOP;\n" "CLOSE cur;\n" "END;\n", FALSE, /* do not reserve dict mutex, we are already holding it */ trx); return(err); } /************************************************************************* Drops a database. @return error code or DB_SUCCESS */ UNIV_INTERN enum db_err ddl_drop_database( /*==============*/ const char* name, /*!< in: database name which ends in '/' */ trx_t* trx) /*!< in: transaction handle */ { char* table_name; enum db_err err = DB_SUCCESS; ulint namelen = ut_strlen(name); ut_a(name[namelen - 1] == '/'); ut_ad(trx->client_thread_id == os_thread_get_curr_id()); trx->op_info = "dropping database"; loop: dict_lock_data_dictionary(trx); while ((table_name = dict_get_first_table_name_in_db(name))) { dict_table_t* table; ut_a(memcmp(table_name, name, namelen) == 0); table = dict_table_get_low(table_name); ut_a(table); /* Wait until the user does not have any queries running on the table */ if (table->n_handles_opened > 0) { dict_unlock_data_dictionary(trx); ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Warning: The client is trying to" " drop database "); ut_print_name(ib_stream, trx, TRUE, name); ib_logger(ib_stream, "\n" "InnoDB: though there are still" " open handles to table "); ut_print_name(ib_stream, trx, TRUE, table_name); ib_logger(ib_stream, ".\n"); os_thread_sleep(1000000); mem_free(table_name); goto loop; } err = ddl_drop_table(table_name, trx, TRUE); if (err != DB_SUCCESS) { ib_logger(ib_stream, "InnoDB: DROP DATABASE "); ut_print_name(ib_stream, trx, TRUE, name); ib_logger(ib_stream, " failed with error %lu for table ", (ulint) err); ut_print_name(ib_stream, trx, TRUE, table_name); ib_logger(ib_stream, "\n"); mem_free(table_name); break; } mem_free(table_name); } if (err == DB_SUCCESS) { /* After dropping all tables try to drop all leftover foreign keys in case orphaned ones exist */ err = ddl_drop_all_foreign_keys_in_db(name, trx); if (err != DB_SUCCESS) { ib_logger(ib_stream, "InnoDB: DROP DATABASE "); ut_print_name(ib_stream, trx, TRUE, name); ib_logger(ib_stream, " failed with error %d while " "dropping all foreign keys", err); } } dict_unlock_data_dictionary(trx); trx->op_info = ""; return(err); } /*********************************************************************//** Drop all partially created indexes. */ UNIV_INTERN void ddl_drop_all_temp_indexes( /*======================*/ ib_recovery_t recovery) /*!< in: recovery level setting */ { trx_t* trx; btr_pcur_t pcur; mtr_t mtr; ibool started; /* Load the table definitions that contain partially defined indexes, so that the data dictionary information can be checked when accessing the tablename.ibd files. */ trx = trx_allocate_for_background(); started = trx_start(trx, ULINT_UNDEFINED); ut_a(started); trx->op_info = "dropping partially created indexes"; dict_lock_data_dictionary(trx); mtr_start(&mtr); btr_pcur_open_at_index_side( TRUE, dict_table_get_first_index(dict_sys->sys_indexes), BTR_SEARCH_LEAF, &pcur, TRUE, &mtr); for (;;) { const rec_t* rec; ulint len; const byte* field; dict_table_t* table; dulint table_id; btr_pcur_move_to_next_user_rec(&pcur, &mtr); if (!btr_pcur_is_on_user_rec(&pcur)) { break; } rec = btr_pcur_get_rec(&pcur); field = rec_get_nth_field_old(rec, DICT_SYS_INDEXES_NAME_FIELD, &len); if (len == UNIV_SQL_NULL || len == 0 || mach_read_from_1(field) != (ulint) TEMP_INDEX_PREFIX) { continue; } /* This is a temporary index. */ field = rec_get_nth_field_old(rec, 0/*TABLE_ID*/, &len); if (len != 8) { /* Corrupted TABLE_ID */ continue; } table_id = mach_read_from_8(field); btr_pcur_store_position(&pcur, &mtr); btr_pcur_commit_specify_mtr(&pcur, &mtr); table = dict_load_table_on_id(recovery, table_id); if (table) { dict_index_t* index; for (index = dict_table_get_first_index(table); index; index = dict_table_get_next_index(index)) { if (*index->name == TEMP_INDEX_PREFIX) { ddl_drop_index(table, index, trx); trx_commit(trx); } } } mtr_start(&mtr); btr_pcur_restore_position(BTR_SEARCH_LEAF, &pcur, &mtr); } btr_pcur_close(&pcur); mtr_commit(&mtr); dict_unlock_data_dictionary(trx); trx_commit(trx); trx_free_for_background(trx); } /*********************************************************************//** Drop all temporary tables. */ UNIV_INTERN void ddl_drop_all_temp_tables( /*=====================*/ ib_recovery_t recovery) /*!< in: recovery level setting*/ { trx_t* trx; btr_pcur_t pcur; mtr_t mtr; mem_heap_t* heap; ibool started; trx = trx_allocate_for_background(); started = trx_start(trx, ULINT_UNDEFINED); trx->op_info = "dropping temporary tables"; dict_lock_data_dictionary(trx); heap = mem_heap_create(200); mtr_start(&mtr); btr_pcur_open_at_index_side( TRUE, dict_table_get_first_index(dict_sys->sys_tables), BTR_SEARCH_LEAF, &pcur, TRUE, &mtr); for (;;) { const rec_t* rec; ulint len; const byte* field; dict_table_t* table; const char* table_name; btr_pcur_move_to_next_user_rec(&pcur, &mtr); if (!btr_pcur_is_on_user_rec(&pcur)) { break; } rec = btr_pcur_get_rec(&pcur); field = rec_get_nth_field_old(rec, 4/*N_COLS*/, &len); if (len != 4 || !(mach_read_from_4(field) & 0x80000000UL)) { continue; } /* Because this is not a ROW_FORMAT=REDUNDANT table, the is_temp flag is valid. Examine it. */ field = rec_get_nth_field_old(rec, 7/*MIX_LEN*/, &len); if (len != 4 || !(mach_read_from_4(field) & DICT_TF2_TEMPORARY)) { continue; } /* This is a temporary table. */ field = rec_get_nth_field_old(rec, 0/*NAME*/, &len); if (len == UNIV_SQL_NULL || len == 0) { /* Corrupted SYS_TABLES.NAME */ continue; } table_name = mem_heap_strdupl(heap, (const char*) field, len); btr_pcur_store_position(&pcur, &mtr); btr_pcur_commit_specify_mtr(&pcur, &mtr); table = dict_load_table(recovery, table_name); if (table) { ddl_drop_table(table_name, trx, FALSE); trx_commit(trx); } mtr_start(&mtr); btr_pcur_restore_position(BTR_SEARCH_LEAF, &pcur, &mtr); } btr_pcur_close(&pcur); mtr_commit(&mtr); mem_heap_free(heap); dict_unlock_data_dictionary(trx); trx_commit(trx); trx_free_for_background(trx); } haildb-2.3.2/usr/0000755000175000017500000000000011513177437014453 5ustar00pcrewspcrews00000000000000haildb-2.3.2/usr/usr0sess.c0000644000175000017500000000347211513177357016415 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file usr/usr0sess.c Sessions Created 6/25/1996 Heikki Tuuri *******************************************************/ #include "usr0sess.h" #ifdef UNIV_NONINL #include "usr0sess.ic" #endif #include "trx0trx.h" /*********************************************************************//** Opens a session. @return own: session object */ UNIV_INTERN sess_t* sess_open(void) /*===========*/ { sess_t* sess; ut_ad(mutex_own(&kernel_mutex)); sess = mem_alloc(sizeof(sess_t)); sess->state = SESS_ACTIVE; sess->trx = trx_create(sess); UT_LIST_INIT(sess->graphs); return(sess); } /*********************************************************************//** Closes a session, freeing the memory occupied by it. */ UNIV_INTERN void sess_close( /*=======*/ sess_t* sess) /*!< in, own: session object */ { ut_ad(!mutex_own(&kernel_mutex)); ut_a(UT_LIST_GET_LEN(sess->graphs) == 0); trx_free_for_background(sess->trx); mem_free(sess); } haildb-2.3.2/buf/0000755000175000017500000000000011513177437014416 5ustar00pcrewspcrews00000000000000haildb-2.3.2/buf/buf0lru.c0000644000175000017500000016622211513177357016153 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2010, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file buf/buf0lru.c The database buffer replacement algorithm Created 11/5/1995 Heikki Tuuri *******************************************************/ #include "buf0lru.h" #ifdef UNIV_NONINL #include "buf0lru.ic" #endif #include "ut0byte.h" #include "ut0lst.h" #include "ut0rnd.h" #include "sync0sync.h" #include "sync0rw.h" #include "hash0hash.h" #include "os0sync.h" #include "fil0fil.h" #include "btr0btr.h" #include "buf0buddy.h" #include "buf0buf.h" #include "buf0flu.h" #include "buf0rea.h" #include "btr0sea.h" #include "ibuf0ibuf.h" #include "os0file.h" #include "page0zip.h" #include "log0recv.h" #include "srv0srv.h" /** The number of blocks from the LRU_old pointer onward, including the block pointed to, must be buf_LRU_old_ratio/BUF_LRU_OLD_RATIO_DIV of the whole LRU list length, except that the tolerance defined below is allowed. Note that the tolerance must be small enough such that for even the BUF_LRU_OLD_MIN_LEN long LRU list, the LRU_old pointer is not allowed to point to either end of the LRU list. */ #define BUF_LRU_OLD_TOLERANCE 20 /** The minimum amount of non-old blocks when the LRU_old list exists (that is, when there are more than BUF_LRU_OLD_MIN_LEN blocks). @see buf_LRU_old_adjust_len */ #define BUF_LRU_NON_OLD_MIN_LEN 5 #if BUF_LRU_NON_OLD_MIN_LEN >= BUF_LRU_OLD_MIN_LEN # error "BUF_LRU_NON_OLD_MIN_LEN >= BUF_LRU_OLD_MIN_LEN" #endif /** When dropping the search hash index entries before deleting an ibd file, we build a local array of pages belonging to that tablespace in the buffer pool. Following is the size of that array. */ #define BUF_LRU_DROP_SEARCH_HASH_SIZE 1024 /** If we switch on the InnoDB monitor because there are too few available frames in the buffer pool, we set this to TRUE */ static ibool buf_lru_switched_on_innodb_mon = FALSE; /******************************************************************//** These statistics are not 'of' LRU but 'for' LRU. We keep count of I/O and page_zip_decompress() operations. Based on the statistics, buf_LRU_evict_from_unzip_LRU() decides if we want to evict from unzip_LRU or the regular LRU. From unzip_LRU, we will only evict the uncompressed frame (meaning we can evict dirty blocks as well). From the regular LRU, we will evict the entire block (i.e.: both the uncompressed and compressed data), which must be clean. */ /* @{ */ /** Number of intervals for which we keep the history of these stats. Each interval is 1 second, defined by the rate at which srv_error_monitor_thread() calls buf_LRU_stat_update(). */ #define BUF_LRU_STAT_N_INTERVAL 50 /** Co-efficient with which we multiply I/O operations to equate them with page_zip_decompress() operations. */ #define BUF_LRU_IO_TO_UNZIP_FACTOR 50 /** Sampled values buf_LRU_stat_cur. Protected by buf_pool_mutex. Updated by buf_LRU_stat_update(). */ static buf_LRU_stat_t buf_LRU_stat_arr[BUF_LRU_STAT_N_INTERVAL]; /** Cursor to buf_LRU_stat_arr[] that is updated in a round-robin fashion. */ static ulint buf_LRU_stat_arr_ind; /** Current operation counters. Not protected by any mutex. Cleared by buf_LRU_stat_update(). */ UNIV_INTERN buf_LRU_stat_t buf_LRU_stat_cur; /** Running sum of past values of buf_LRU_stat_cur. Updated by buf_LRU_stat_update(). Protected by buf_pool_mutex. */ UNIV_INTERN buf_LRU_stat_t buf_LRU_stat_sum; /* @} */ /** @name Heuristics for detecting index scan @{ */ /** Reserve this much/BUF_LRU_OLD_RATIO_DIV of the buffer pool for "old" blocks. Protected by buf_pool_mutex. */ UNIV_INTERN ulint buf_LRU_old_ratio; /** Move blocks to "new" LRU list only if the first access was at least this many milliseconds ago. Not protected by any mutex or latch. */ UNIV_INTERN ulint buf_LRU_old_threshold_ms; /* @} */ /******************************************************************//** Takes a block out of the LRU list and page hash table. If the block is compressed-only (BUF_BLOCK_ZIP_PAGE), the object will be freed and buf_pool_zip_mutex will be released. If a compressed page or a compressed-only block descriptor is freed, other compressed pages or compressed-only block descriptors may be relocated. @return the new state of the block (BUF_BLOCK_ZIP_FREE if the state was BUF_BLOCK_ZIP_PAGE, or BUF_BLOCK_REMOVE_HASH otherwise) */ static enum buf_page_state buf_LRU_block_remove_hashed_page( /*=============================*/ buf_page_t* bpage, /*!< in: block, must contain a file page and be in a state where it can be freed; there may or may not be a hash index to the page */ ibool zip); /*!< in: TRUE if should remove also the compressed page of an uncompressed page */ /******************************************************************//** Puts a file page whose has no hash index to the free list. */ static void buf_LRU_block_free_hashed_page( /*===========================*/ buf_block_t* block); /*!< in: block, must contain a file page and be in a state where it can be freed */ /******************************************************************//** Reset buffer LRU variables. */ UNIV_INTERN void buf_LRU_var_init(void) /*==================*/ { buf_lru_switched_on_innodb_mon = FALSE; memset(buf_LRU_stat_arr, 0x0, sizeof(buf_LRU_stat_arr)); buf_LRU_stat_arr_ind = 0; memset(&buf_LRU_stat_cur, 0x0, sizeof(buf_LRU_stat_cur)); memset(&buf_LRU_stat_sum, 0x0, sizeof(buf_LRU_stat_sum)); } /********************************************************************** Determines if the unzip_LRU list should be used for evicting a victim instead of the general LRU list. @return TRUE if should use unzip_LRU */ UNIV_INLINE ibool buf_LRU_evict_from_unzip_LRU(void) /*==============================*/ { ulint io_avg; ulint unzip_avg; ut_ad(buf_pool_mutex_own()); /* If the unzip_LRU list is empty, we can only use the LRU. */ if (UT_LIST_GET_LEN(buf_pool->unzip_LRU) == 0) { return(FALSE); } /* If unzip_LRU is at most 10% of the size of the LRU list, then use the LRU. This slack allows us to keep hot decompressed pages in the buffer pool. */ if (UT_LIST_GET_LEN(buf_pool->unzip_LRU) <= UT_LIST_GET_LEN(buf_pool->LRU) / 10) { return(FALSE); } /* If eviction hasn't started yet, we assume by default that a workload is disk bound. */ if (buf_pool->freed_page_clock == 0) { return(TRUE); } /* Calculate the average over past intervals, and add the values of the current interval. */ io_avg = buf_LRU_stat_sum.io / BUF_LRU_STAT_N_INTERVAL + buf_LRU_stat_cur.io; unzip_avg = buf_LRU_stat_sum.unzip / BUF_LRU_STAT_N_INTERVAL + buf_LRU_stat_cur.unzip; /* Decide based on our formula. If the load is I/O bound (unzip_avg is smaller than the weighted io_avg), evict an uncompressed frame from unzip_LRU. Otherwise we assume that the load is CPU bound and evict from the regular LRU. */ return(unzip_avg <= io_avg * BUF_LRU_IO_TO_UNZIP_FACTOR); } /******************************************************************//** Attempts to drop page hash index on a batch of pages belonging to a particular space id. */ static void buf_LRU_drop_page_hash_batch( /*=========================*/ ulint space_id, /*!< in: space id */ ulint zip_size, /*!< in: compressed page size in bytes or 0 for uncompressed pages */ const ulint* arr, /*!< in: array of page_no */ ulint count) /*!< in: number of entries in array */ { ulint i; ut_ad(arr != NULL); ut_ad(count <= BUF_LRU_DROP_SEARCH_HASH_SIZE); for (i = 0; i < count; ++i) { btr_search_drop_page_hash_when_freed(space_id, zip_size, arr[i]); } } /******************************************************************//** When doing a DROP TABLE/DISCARD TABLESPACE we have to drop all page hash index entries belonging to that table. This function tries to do that in batch. Note that this is a 'best effort' attempt and does not guarantee that ALL hash entries will be removed. */ static void buf_LRU_drop_page_hash_for_tablespace( /*==================================*/ ulint id) /*!< in: space id */ { buf_page_t* bpage; ulint* page_arr; ulint num_entries; ulint zip_size; zip_size = fil_space_get_zip_size(id); if (UNIV_UNLIKELY(zip_size == ULINT_UNDEFINED)) { /* Somehow, the tablespace does not exist. Nothing to drop. */ ut_ad(0); return; } page_arr = ut_malloc(sizeof(ulint) * BUF_LRU_DROP_SEARCH_HASH_SIZE); buf_pool_mutex_enter(); scan_again: num_entries = 0; bpage = UT_LIST_GET_LAST(buf_pool->LRU); while (bpage != NULL) { mutex_t* block_mutex = buf_page_get_mutex(bpage); buf_page_t* prev_bpage; mutex_enter(block_mutex); prev_bpage = UT_LIST_GET_PREV(LRU, bpage); ut_a(buf_page_in_file(bpage)); if (buf_page_get_state(bpage) != BUF_BLOCK_FILE_PAGE || bpage->space != id || bpage->buf_fix_count > 0 || bpage->io_fix != BUF_IO_NONE) { /* We leave the fixed pages as is in this scan. To be dealt with later in the final scan. */ mutex_exit(block_mutex); goto next_page; } if (((buf_block_t*) bpage)->is_hashed) { /* Store the offset(i.e.: page_no) in the array so that we can drop hash index in a batch later. */ page_arr[num_entries] = bpage->offset; mutex_exit(block_mutex); ut_a(num_entries < BUF_LRU_DROP_SEARCH_HASH_SIZE); ++num_entries; if (num_entries < BUF_LRU_DROP_SEARCH_HASH_SIZE) { goto next_page; } /* Array full. We release the buf_pool_mutex to obey the latching order. */ buf_pool_mutex_exit(); buf_LRU_drop_page_hash_batch(id, zip_size, page_arr, num_entries); num_entries = 0; buf_pool_mutex_enter(); } else { mutex_exit(block_mutex); } next_page: /* Note that we may have released the buf_pool mutex above after reading the prev_bpage during processing of a page_hash_batch (i.e.: when the array was full). This means that prev_bpage can change in LRU list. This is OK because this function is a 'best effort' to drop as many search hash entries as possible and it does not guarantee that ALL such entries will be dropped. */ bpage = prev_bpage; /* If, however, bpage has been removed from LRU list to the free list then we should restart the scan. bpage->state is protected by buf_pool mutex. */ if (bpage && !buf_page_in_file(bpage)) { ut_a(num_entries == 0); goto scan_again; } } buf_pool_mutex_exit(); /* Drop any remaining batch of search hashed pages. */ buf_LRU_drop_page_hash_batch(id, zip_size, page_arr, num_entries); ut_free(page_arr); } /******************************************************************//** Invalidates all pages belonging to a given tablespace when we are deleting the data file(s) of that tablespace. */ UNIV_INTERN void buf_LRU_invalidate_tablespace( /*==========================*/ ulint id) /*!< in: space id */ { buf_page_t* bpage; ibool all_freed; /* Before we attempt to drop pages one by one we first attempt to drop page hash index entries in batches to make it more efficient. The batching attempt is a best effort attempt and does not guarantee that all pages hash entries will be dropped. We get rid of remaining page hash entries one by one below. */ buf_LRU_drop_page_hash_for_tablespace(id); scan_again: buf_pool_mutex_enter(); all_freed = TRUE; bpage = UT_LIST_GET_LAST(buf_pool->LRU); while (bpage != NULL) { buf_page_t* prev_bpage; ibool prev_bpage_buf_fix = FALSE; ut_a(buf_page_in_file(bpage)); prev_bpage = UT_LIST_GET_PREV(LRU, bpage); /* bpage->space and bpage->io_fix are protected by buf_pool_mutex and block_mutex. It is safe to check them while holding buf_pool_mutex only. */ if (buf_page_get_space(bpage) != id) { /* Skip this block, as it does not belong to the space that is being invalidated. */ } else if (buf_page_get_io_fix(bpage) != BUF_IO_NONE) { /* We cannot remove this page during this scan yet; maybe the system is currently reading it in, or flushing the modifications to the file */ all_freed = FALSE; } else { mutex_t* block_mutex = buf_page_get_mutex(bpage); mutex_enter(block_mutex); if (bpage->buf_fix_count > 0) { /* We cannot remove this page during this scan yet; maybe the system is currently reading it in, or flushing the modifications to the file */ all_freed = FALSE; goto next_page; } #ifdef UNIV_DEBUG if (buf_debug_prints) { ib_logger(ib_stream, "Dropping space %lu page %lu\n", (ulong) buf_page_get_space(bpage), (ulong) buf_page_get_page_no(bpage)); } #endif if (buf_page_get_state(bpage) != BUF_BLOCK_FILE_PAGE) { /* This is a compressed-only block descriptor. Ensure that prev_bpage cannot be relocated when bpage is freed. */ if (UNIV_LIKELY(prev_bpage != NULL)) { switch (buf_page_get_state( prev_bpage)) { case BUF_BLOCK_FILE_PAGE: /* Descriptors of uncompressed blocks will not be relocated, because we are holding the buf_pool_mutex. */ break; case BUF_BLOCK_ZIP_PAGE: case BUF_BLOCK_ZIP_DIRTY: /* Descriptors of compressed- only blocks can be relocated, unless they are buffer-fixed. Because both bpage and prev_bpage are protected by buf_pool_zip_mutex, it is not necessary to acquire further mutexes. */ ut_ad(&buf_pool_zip_mutex == block_mutex); ut_ad(mutex_own(block_mutex)); prev_bpage_buf_fix = TRUE; prev_bpage->buf_fix_count++; break; default: ut_error; } } } else if (((buf_block_t*) bpage)->is_hashed) { ulint page_no; ulint zip_size; buf_pool_mutex_exit(); zip_size = buf_page_get_zip_size(bpage); page_no = buf_page_get_page_no(bpage); mutex_exit(block_mutex); /* Note that the following call will acquire an S-latch on the page */ btr_search_drop_page_hash_when_freed( id, zip_size, page_no); goto scan_again; } if (bpage->oldest_modification != 0) { buf_flush_remove(bpage); } /* Remove from the LRU list. */ if (buf_LRU_block_remove_hashed_page(bpage, TRUE) != BUF_BLOCK_ZIP_FREE) { buf_LRU_block_free_hashed_page((buf_block_t*) bpage); } else { /* The block_mutex should have been released by buf_LRU_block_remove_hashed_page() when it returns BUF_BLOCK_ZIP_FREE. */ ut_ad(block_mutex == &buf_pool_zip_mutex); ut_ad(!mutex_own(block_mutex)); if (prev_bpage_buf_fix) { /* We temporarily buffer-fixed prev_bpage, so that buf_buddy_free() could not relocate it, in case it was a compressed-only block descriptor. */ mutex_enter(block_mutex); ut_ad(prev_bpage->buf_fix_count > 0); prev_bpage->buf_fix_count--; mutex_exit(block_mutex); } goto next_page_no_mutex; } next_page: mutex_exit(block_mutex); } next_page_no_mutex: bpage = prev_bpage; } buf_pool_mutex_exit(); if (!all_freed) { os_thread_sleep(20000); goto scan_again; } } /********************************************************************//** Insert a compressed block into buf_pool->zip_clean in the LRU order. */ UNIV_INTERN void buf_LRU_insert_zip_clean( /*=====================*/ buf_page_t* bpage) /*!< in: pointer to the block in question */ { buf_page_t* b; ut_ad(buf_pool_mutex_own()); ut_ad(buf_page_get_state(bpage) == BUF_BLOCK_ZIP_PAGE); /* Find the first successor of bpage in the LRU list that is in the zip_clean list. */ b = bpage; do { b = UT_LIST_GET_NEXT(LRU, b); } while (b && buf_page_get_state(b) != BUF_BLOCK_ZIP_PAGE); /* Insert bpage before b, i.e., after the predecessor of b. */ if (b) { b = UT_LIST_GET_PREV(list, b); } if (b) { UT_LIST_INSERT_AFTER(list, buf_pool->zip_clean, b, bpage); } else { UT_LIST_ADD_FIRST(list, buf_pool->zip_clean, bpage); } } /******************************************************************//** Try to free an uncompressed page of a compressed block from the unzip LRU list. The compressed page is preserved, and it need not be clean. @return TRUE if freed */ UNIV_INLINE ibool buf_LRU_free_from_unzip_LRU_list( /*=============================*/ ulint n_iterations) /*!< in: how many times this has been called repeatedly without result: a high value means that we should search farther; we will search n_iterations / 5 of the unzip_LRU list, or nothing if n_iterations >= 5 */ { buf_block_t* block; ulint distance; ut_ad(buf_pool_mutex_own()); /* Theoratically it should be much easier to find a victim from unzip_LRU as we can choose even a dirty block (as we'll be evicting only the uncompressed frame). In a very unlikely eventuality that we are unable to find a victim from unzip_LRU, we fall back to the regular LRU list. We do this if we have done five iterations so far. */ if (UNIV_UNLIKELY(n_iterations >= 5) || !buf_LRU_evict_from_unzip_LRU()) { return(FALSE); } distance = 100 + (n_iterations * UT_LIST_GET_LEN(buf_pool->unzip_LRU)) / 5; for (block = UT_LIST_GET_LAST(buf_pool->unzip_LRU); UNIV_LIKELY(block != NULL) && UNIV_LIKELY(distance > 0); block = UT_LIST_GET_PREV(unzip_LRU, block), distance--) { enum buf_lru_free_block_status freed; ut_ad(buf_block_get_state(block) == BUF_BLOCK_FILE_PAGE); ut_ad(block->in_unzip_LRU_list); ut_ad(block->page.in_LRU_list); mutex_enter(&block->mutex); freed = buf_LRU_free_block(&block->page, FALSE, NULL); mutex_exit(&block->mutex); switch (freed) { case BUF_LRU_FREED: return(TRUE); case BUF_LRU_CANNOT_RELOCATE: /* If we failed to relocate, try regular LRU eviction. */ return(FALSE); case BUF_LRU_NOT_FREED: /* The block was buffer-fixed or I/O-fixed. Keep looking. */ continue; } /* inappropriate return value from buf_LRU_free_block() */ ut_error; } return(FALSE); } /******************************************************************//** Try to free a clean page from the common LRU list. @return TRUE if freed */ UNIV_INLINE ibool buf_LRU_free_from_common_LRU_list( /*==============================*/ ulint n_iterations) /*!< in: how many times this has been called repeatedly without result: a high value means that we should search farther; if n_iterations < 10, then we search n_iterations / 10 * buf_pool->curr_size pages from the end of the LRU list */ { buf_page_t* bpage; ulint distance; ut_ad(buf_pool_mutex_own()); distance = 100 + (n_iterations * buf_pool->curr_size) / 10; for (bpage = UT_LIST_GET_LAST(buf_pool->LRU); UNIV_LIKELY(bpage != NULL) && UNIV_LIKELY(distance > 0); bpage = UT_LIST_GET_PREV(LRU, bpage), distance--) { enum buf_lru_free_block_status freed; unsigned accessed; mutex_t* block_mutex = buf_page_get_mutex(bpage); ut_ad(buf_page_in_file(bpage)); ut_ad(bpage->in_LRU_list); mutex_enter(block_mutex); accessed = buf_page_is_accessed(bpage); freed = buf_LRU_free_block(bpage, TRUE, NULL); mutex_exit(block_mutex); switch (freed) { case BUF_LRU_FREED: /* Keep track of pages that are evicted without ever being accessed. This gives us a measure of the effectiveness of readahead */ if (!accessed) { ++buf_pool->stat.n_ra_pages_evicted; } return(TRUE); case BUF_LRU_NOT_FREED: /* The block was dirty, buffer-fixed, or I/O-fixed. Keep looking. */ continue; case BUF_LRU_CANNOT_RELOCATE: /* This should never occur, because we want to discard the compressed page too. */ break; } /* inappropriate return value from buf_LRU_free_block() */ ut_error; } return(FALSE); } /******************************************************************//** Try to free a replaceable block. @return TRUE if found and freed */ UNIV_INTERN ibool buf_LRU_search_and_free_block( /*==========================*/ ulint n_iterations) /*!< in: how many times this has been called repeatedly without result: a high value means that we should search farther; if n_iterations < 10, then we search n_iterations / 10 * buf_pool->curr_size pages from the end of the LRU list; if n_iterations < 5, then we will also search n_iterations / 5 of the unzip_LRU list. */ { ibool freed = FALSE; buf_pool_mutex_enter(); freed = buf_LRU_free_from_unzip_LRU_list(n_iterations); if (!freed) { freed = buf_LRU_free_from_common_LRU_list(n_iterations); } if (!freed) { buf_pool->LRU_flush_ended = 0; } else if (buf_pool->LRU_flush_ended > 0) { buf_pool->LRU_flush_ended--; } buf_pool_mutex_exit(); return(freed); } /******************************************************************//** Tries to remove LRU flushed blocks from the end of the LRU list and put them to the free list. This is beneficial for the efficiency of the insert buffer operation, as flushed pages from non-unique non-clustered indexes are here taken out of the buffer pool, and their inserts redirected to the insert buffer. Otherwise, the flushed blocks could get modified again before read operations need new buffer blocks, and the i/o work done in flushing would be wasted. */ UNIV_INTERN void buf_LRU_try_free_flushed_blocks(void) /*=================================*/ { buf_pool_mutex_enter(); while (buf_pool->LRU_flush_ended > 0) { buf_pool_mutex_exit(); buf_LRU_search_and_free_block(1); buf_pool_mutex_enter(); } buf_pool_mutex_exit(); } /******************************************************************//** Returns TRUE if less than 25 % of the buffer pool is available. This can be used in heuristics to prevent huge transactions eating up the whole buffer pool for their locks. @return TRUE if less than 25 % of buffer pool left */ UNIV_INTERN ibool buf_LRU_buf_pool_running_out(void) /*==============================*/ { ibool ret = FALSE; buf_pool_mutex_enter(); if (!recv_recovery_on && UT_LIST_GET_LEN(buf_pool->free) + UT_LIST_GET_LEN(buf_pool->LRU) < buf_pool->curr_size / 4) { ret = TRUE; } buf_pool_mutex_exit(); return(ret); } /******************************************************************//** Returns a free block from the buf_pool. The block is taken off the free list. If it is empty, returns NULL. @return a free control block, or NULL if the buf_block->free list is empty */ UNIV_INTERN buf_block_t* buf_LRU_get_free_only(void) /*=======================*/ { buf_block_t* block; ut_ad(buf_pool_mutex_own()); block = (buf_block_t*) UT_LIST_GET_FIRST(buf_pool->free); if (block) { ut_ad(block->page.in_free_list); ut_d(block->page.in_free_list = FALSE); ut_ad(!block->page.in_flush_list); ut_ad(!block->page.in_LRU_list); ut_a(!buf_page_in_file(&block->page)); UT_LIST_REMOVE(list, buf_pool->free, (&block->page)); mutex_enter(&block->mutex); buf_block_set_state(block, BUF_BLOCK_READY_FOR_USE); UNIV_MEM_ALLOC(block->frame, UNIV_PAGE_SIZE); mutex_exit(&block->mutex); } return(block); } /******************************************************************//** Returns a free block from the buf_pool. The block is taken off the free list. If it is empty, blocks are moved from the end of the LRU list to the free list. @return the free control block, in state BUF_BLOCK_READY_FOR_USE */ UNIV_INTERN buf_block_t* buf_LRU_get_free_block( /*===================*/ ulint zip_size) /*!< in: compressed page size in bytes, or 0 if uncompressed tablespace */ { buf_block_t* block = NULL; ibool freed; ulint n_iterations = 1; ibool mon_value_was = FALSE; ibool started_monitor = FALSE; loop: buf_pool_mutex_enter(); if (!recv_recovery_on && UT_LIST_GET_LEN(buf_pool->free) + UT_LIST_GET_LEN(buf_pool->LRU) < buf_pool->curr_size / 20) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: ERROR: over 95 percent of the buffer pool" " is occupied by\n" "InnoDB: lock heaps or the adaptive hash index!" " Check that your\n" "InnoDB: transactions do not set too many row locks.\n" "InnoDB: Your buffer pool size is %lu MB." " Maybe you should make\n" "InnoDB: the buffer pool bigger?\n" "InnoDB: We intentionally generate a seg fault" " to print a stack trace\n" "InnoDB: on Linux!\n", (ulong) (buf_pool->curr_size / (1024 * 1024 / UNIV_PAGE_SIZE))); ut_error; } else if (!recv_recovery_on && (UT_LIST_GET_LEN(buf_pool->free) + UT_LIST_GET_LEN(buf_pool->LRU)) < buf_pool->curr_size / 3) { if (!buf_lru_switched_on_innodb_mon) { /* Over 67 % of the buffer pool is occupied by lock heaps or the adaptive hash index. This may be a memory leak! */ ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: WARNING: over 67 percent of" " the buffer pool is occupied by\n" "InnoDB: lock heaps or the adaptive" " hash index! Check that your\n" "InnoDB: transactions do not set too many" " row locks.\n" "InnoDB: Your buffer pool size is %lu MB." " Maybe you should make\n" "InnoDB: the buffer pool bigger?\n" "InnoDB: Starting the InnoDB Monitor to print" " diagnostics, including\n" "InnoDB: lock heap and hash index sizes.\n", (ulong) (buf_pool->curr_size / (1024 * 1024 / UNIV_PAGE_SIZE))); buf_lru_switched_on_innodb_mon = TRUE; srv_print_innodb_monitor = TRUE; os_event_set(srv_lock_timeout_thread_event); } } else if (buf_lru_switched_on_innodb_mon) { /* Switch off the InnoDB Monitor; this is a simple way to stop the monitor if the situation becomes less urgent, but may also surprise users if the user also switched on the monitor! */ buf_lru_switched_on_innodb_mon = FALSE; srv_print_innodb_monitor = FALSE; } /* If there is a block in the free list, take it */ block = buf_LRU_get_free_only(); if (block) { #ifdef UNIV_DEBUG block->page.zip.m_start = #endif /* UNIV_DEBUG */ block->page.zip.m_end = block->page.zip.m_nonempty = block->page.zip.n_blobs = 0; if (UNIV_UNLIKELY(zip_size)) { ibool lru; page_zip_set_size(&block->page.zip, zip_size); block->page.zip.data = buf_buddy_alloc(zip_size, &lru); UNIV_MEM_DESC(block->page.zip.data, zip_size, block); } else { page_zip_set_size(&block->page.zip, 0); block->page.zip.data = NULL; } buf_pool_mutex_exit(); if (started_monitor) { srv_print_innodb_monitor = mon_value_was; } return(block); } /* If no block was in the free list, search from the end of the LRU list and try to free a block there */ buf_pool_mutex_exit(); freed = buf_LRU_search_and_free_block(n_iterations); if (freed > 0) { goto loop; } if (n_iterations > 30) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Warning: difficult to find free blocks in\n" "InnoDB: the buffer pool (%lu search iterations)!" " Consider\n" "InnoDB: increasing the buffer pool size.\n" "InnoDB: It is also possible that" " in your Unix version\n" "InnoDB: fsync is very slow, or" " completely frozen inside\n" "InnoDB: the OS kernel. Then upgrading to" " a newer version\n" "InnoDB: of your operating system may help." " Look at the\n" "InnoDB: number of fsyncs in diagnostic info below.\n" "InnoDB: Pending flushes (fsync) log: %lu;" " buffer pool: %lu\n" "InnoDB: %lu OS file reads, %lu OS file writes," " %lu OS fsyncs\n" "InnoDB: Starting InnoDB Monitor to print further\n" "InnoDB: diagnostics to the standard output.\n", (ulong) n_iterations, (ulong) fil_n_pending_log_flushes, (ulong) fil_n_pending_tablespace_flushes, (ulong) os_n_file_reads, (ulong) os_n_file_writes, (ulong) os_n_fsyncs); mon_value_was = srv_print_innodb_monitor; started_monitor = TRUE; srv_print_innodb_monitor = TRUE; os_event_set(srv_lock_timeout_thread_event); } /* No free block was found: try to flush the LRU list */ buf_flush_free_margin(); ++srv_buf_pool_wait_free; os_aio_simulated_wake_handler_threads(); buf_pool_mutex_enter(); if (buf_pool->LRU_flush_ended > 0) { /* We have written pages in an LRU flush. To make the insert buffer more efficient, we try to move these pages to the free list. */ buf_pool_mutex_exit(); buf_LRU_try_free_flushed_blocks(); } else { buf_pool_mutex_exit(); } if (n_iterations > 10) { os_thread_sleep(500000); } n_iterations++; goto loop; } /*******************************************************************//** Moves the LRU_old pointer so that the length of the old blocks list is inside the allowed limits. */ UNIV_INLINE void buf_LRU_old_adjust_len(void) /*========================*/ { ulint old_len; ulint new_len; ut_a(buf_pool->LRU_old); ut_ad(buf_pool_mutex_own()); ut_ad(buf_LRU_old_ratio >= BUF_LRU_OLD_RATIO_MIN); ut_ad(buf_LRU_old_ratio <= BUF_LRU_OLD_RATIO_MAX); #if BUF_LRU_OLD_RATIO_MIN * BUF_LRU_OLD_MIN_LEN <= BUF_LRU_OLD_RATIO_DIV * (BUF_LRU_OLD_TOLERANCE + 5) # error "BUF_LRU_OLD_RATIO_MIN * BUF_LRU_OLD_MIN_LEN <= BUF_LRU_OLD_RATIO_DIV * (BUF_LRU_OLD_TOLERANCE + 5)" #endif #ifdef UNIV_LRU_DEBUG /* buf_pool->LRU_old must be the first item in the LRU list whose "old" flag is set. */ ut_a(buf_pool->LRU_old->old); ut_a(!UT_LIST_GET_PREV(LRU, buf_pool->LRU_old) || !UT_LIST_GET_PREV(LRU, buf_pool->LRU_old)->old); ut_a(!UT_LIST_GET_NEXT(LRU, buf_pool->LRU_old) || UT_LIST_GET_NEXT(LRU, buf_pool->LRU_old)->old); #endif /* UNIV_LRU_DEBUG */ old_len = buf_pool->LRU_old_len; new_len = ut_min(UT_LIST_GET_LEN(buf_pool->LRU) * buf_LRU_old_ratio / BUF_LRU_OLD_RATIO_DIV, UT_LIST_GET_LEN(buf_pool->LRU) - (BUF_LRU_OLD_TOLERANCE + BUF_LRU_NON_OLD_MIN_LEN)); for (;;) { buf_page_t* LRU_old = buf_pool->LRU_old; ut_a(LRU_old); ut_ad(LRU_old->in_LRU_list); #ifdef UNIV_LRU_DEBUG ut_a(LRU_old->old); #endif /* UNIV_LRU_DEBUG */ /* Update the LRU_old pointer if necessary */ if (old_len + BUF_LRU_OLD_TOLERANCE < new_len) { buf_pool->LRU_old = LRU_old = UT_LIST_GET_PREV( LRU, LRU_old); #ifdef UNIV_LRU_DEBUG ut_a(!LRU_old->old); #endif /* UNIV_LRU_DEBUG */ old_len = ++buf_pool->LRU_old_len; buf_page_set_old(LRU_old, TRUE); } else if (old_len > new_len + BUF_LRU_OLD_TOLERANCE) { buf_pool->LRU_old = UT_LIST_GET_NEXT(LRU, LRU_old); old_len = --buf_pool->LRU_old_len; buf_page_set_old(LRU_old, FALSE); } else { return; } } } /*******************************************************************//** Initializes the old blocks pointer in the LRU list. This function should be called when the LRU list grows to BUF_LRU_OLD_MIN_LEN length. */ static void buf_LRU_old_init(void) /*==================*/ { buf_page_t* bpage; ut_ad(buf_pool_mutex_own()); ut_a(UT_LIST_GET_LEN(buf_pool->LRU) == BUF_LRU_OLD_MIN_LEN); /* We first initialize all blocks in the LRU list as old and then use the adjust function to move the LRU_old pointer to the right position */ for (bpage = UT_LIST_GET_LAST(buf_pool->LRU); bpage != NULL; bpage = UT_LIST_GET_PREV(LRU, bpage)) { ut_ad(bpage->in_LRU_list); ut_ad(buf_page_in_file(bpage)); /* This loop temporarily violates the assertions of buf_page_set_old(). */ bpage->old = TRUE; } buf_pool->LRU_old = UT_LIST_GET_FIRST(buf_pool->LRU); buf_pool->LRU_old_len = UT_LIST_GET_LEN(buf_pool->LRU); buf_LRU_old_adjust_len(); } /******************************************************************//** Remove a block from the unzip_LRU list if it belonged to the list. */ static void buf_unzip_LRU_remove_block_if_needed( /*=================================*/ buf_page_t* bpage) /*!< in/out: control block */ { ut_ad(buf_pool); ut_ad(bpage); ut_ad(buf_page_in_file(bpage)); ut_ad(buf_pool_mutex_own()); if (buf_page_belongs_to_unzip_LRU(bpage)) { buf_block_t* block = (buf_block_t*) bpage; ut_ad(block->in_unzip_LRU_list); ut_d(block->in_unzip_LRU_list = FALSE); UT_LIST_REMOVE(unzip_LRU, buf_pool->unzip_LRU, block); } } /******************************************************************//** Removes a block from the LRU list. */ UNIV_INLINE void buf_LRU_remove_block( /*=================*/ buf_page_t* bpage) /*!< in: control block */ { ut_ad(buf_pool); ut_ad(bpage); ut_ad(buf_pool_mutex_own()); ut_a(buf_page_in_file(bpage)); ut_ad(bpage->in_LRU_list); /* If the LRU_old pointer is defined and points to just this block, move it backward one step */ if (UNIV_UNLIKELY(bpage == buf_pool->LRU_old)) { /* Below: the previous block is guaranteed to exist, because the LRU_old pointer is only allowed to differ by BUF_LRU_OLD_TOLERANCE from strict buf_LRU_old_ratio/BUF_LRU_OLD_RATIO_DIV of the LRU list length. */ buf_page_t* prev_bpage = UT_LIST_GET_PREV(LRU, bpage); ut_a(prev_bpage); #ifdef UNIV_LRU_DEBUG ut_a(!prev_bpage->old); #endif /* UNIV_LRU_DEBUG */ buf_pool->LRU_old = prev_bpage; buf_page_set_old(prev_bpage, TRUE); buf_pool->LRU_old_len++; } /* Remove the block from the LRU list */ UT_LIST_REMOVE(LRU, buf_pool->LRU, bpage); ut_d(bpage->in_LRU_list = FALSE); buf_unzip_LRU_remove_block_if_needed(bpage); /* If the LRU list is so short that LRU_old is not defined, clear the "old" flags and return */ if (UT_LIST_GET_LEN(buf_pool->LRU) < BUF_LRU_OLD_MIN_LEN) { for (bpage = UT_LIST_GET_FIRST(buf_pool->LRU); bpage != NULL; bpage = UT_LIST_GET_NEXT(LRU, bpage)) { /* This loop temporarily violates the assertions of buf_page_set_old(). */ bpage->old = FALSE; } buf_pool->LRU_old = NULL; buf_pool->LRU_old_len = 0; return; } ut_ad(buf_pool->LRU_old); /* Update the LRU_old_len field if necessary */ if (buf_page_is_old(bpage)) { buf_pool->LRU_old_len--; } /* Adjust the length of the old block list if necessary */ buf_LRU_old_adjust_len(); } /******************************************************************//** Adds a block to the LRU list of decompressed zip pages. */ UNIV_INTERN void buf_unzip_LRU_add_block( /*====================*/ buf_block_t* block, /*!< in: control block */ ibool old) /*!< in: TRUE if should be put to the end of the list, else put to the start */ { ut_ad(buf_pool); ut_ad(block); ut_ad(buf_pool_mutex_own()); ut_a(buf_page_belongs_to_unzip_LRU(&block->page)); ut_ad(!block->in_unzip_LRU_list); ut_d(block->in_unzip_LRU_list = TRUE); if (old) { UT_LIST_ADD_LAST(unzip_LRU, buf_pool->unzip_LRU, block); } else { UT_LIST_ADD_FIRST(unzip_LRU, buf_pool->unzip_LRU, block); } } /******************************************************************//** Adds a block to the LRU list end. */ UNIV_INLINE void buf_LRU_add_block_to_end_low( /*=========================*/ buf_page_t* bpage) /*!< in: control block */ { ut_ad(buf_pool); ut_ad(bpage); ut_ad(buf_pool_mutex_own()); ut_a(buf_page_in_file(bpage)); ut_ad(!bpage->in_LRU_list); UT_LIST_ADD_LAST(LRU, buf_pool->LRU, bpage); ut_d(bpage->in_LRU_list = TRUE); if (UT_LIST_GET_LEN(buf_pool->LRU) > BUF_LRU_OLD_MIN_LEN) { ut_ad(buf_pool->LRU_old); /* Adjust the length of the old block list if necessary */ buf_page_set_old(bpage, TRUE); buf_pool->LRU_old_len++; buf_LRU_old_adjust_len(); } else if (UT_LIST_GET_LEN(buf_pool->LRU) == BUF_LRU_OLD_MIN_LEN) { /* The LRU list is now long enough for LRU_old to become defined: init it */ buf_LRU_old_init(); } else { buf_page_set_old(bpage, buf_pool->LRU_old != NULL); } /* If this is a zipped block with decompressed frame as well then put it on the unzip_LRU list */ if (buf_page_belongs_to_unzip_LRU(bpage)) { buf_unzip_LRU_add_block((buf_block_t*) bpage, TRUE); } } /******************************************************************//** Adds a block to the LRU list. */ UNIV_INLINE void buf_LRU_add_block_low( /*==================*/ buf_page_t* bpage, /*!< in: control block */ ibool old) /*!< in: TRUE if should be put to the old blocks in the LRU list, else put to the start; if the LRU list is very short, the block is added to the start, regardless of this parameter */ { ut_ad(buf_pool); ut_ad(bpage); ut_ad(buf_pool_mutex_own()); ut_a(buf_page_in_file(bpage)); ut_ad(!bpage->in_LRU_list); if (!old || (UT_LIST_GET_LEN(buf_pool->LRU) < BUF_LRU_OLD_MIN_LEN)) { UT_LIST_ADD_FIRST(LRU, buf_pool->LRU, bpage); bpage->freed_page_clock = buf_pool->freed_page_clock; } else { #ifdef UNIV_LRU_DEBUG /* buf_pool->LRU_old must be the first item in the LRU list whose "old" flag is set. */ ut_a(buf_pool->LRU_old->old); ut_a(!UT_LIST_GET_PREV(LRU, buf_pool->LRU_old) || !UT_LIST_GET_PREV(LRU, buf_pool->LRU_old)->old); ut_a(!UT_LIST_GET_NEXT(LRU, buf_pool->LRU_old) || UT_LIST_GET_NEXT(LRU, buf_pool->LRU_old)->old); #endif /* UNIV_LRU_DEBUG */ UT_LIST_INSERT_AFTER(LRU, buf_pool->LRU, buf_pool->LRU_old, bpage); buf_pool->LRU_old_len++; } ut_d(bpage->in_LRU_list = TRUE); if (UT_LIST_GET_LEN(buf_pool->LRU) > BUF_LRU_OLD_MIN_LEN) { ut_ad(buf_pool->LRU_old); /* Adjust the length of the old block list if necessary */ buf_page_set_old(bpage, old); buf_LRU_old_adjust_len(); } else if (UT_LIST_GET_LEN(buf_pool->LRU) == BUF_LRU_OLD_MIN_LEN) { /* The LRU list is now long enough for LRU_old to become defined: init it */ buf_LRU_old_init(); } else { buf_page_set_old(bpage, buf_pool->LRU_old != NULL); } /* If this is a zipped block with decompressed frame as well then put it on the unzip_LRU list */ if (buf_page_belongs_to_unzip_LRU(bpage)) { buf_unzip_LRU_add_block((buf_block_t*) bpage, old); } } /******************************************************************//** Adds a block to the LRU list. */ UNIV_INTERN void buf_LRU_add_block( /*==============*/ buf_page_t* bpage, /*!< in: control block */ ibool old) /*!< in: TRUE if should be put to the old blocks in the LRU list, else put to the start; if the LRU list is very short, the block is added to the start, regardless of this parameter */ { buf_LRU_add_block_low(bpage, old); } /******************************************************************//** Moves a block to the start of the LRU list. */ UNIV_INTERN void buf_LRU_make_block_young( /*=====================*/ buf_page_t* bpage) /*!< in: control block */ { ut_ad(buf_pool_mutex_own()); if (bpage->old) { buf_pool->stat.n_pages_made_young++; } buf_LRU_remove_block(bpage); buf_LRU_add_block_low(bpage, FALSE); } /******************************************************************//** Moves a block to the end of the LRU list. */ UNIV_INTERN void buf_LRU_make_block_old( /*===================*/ buf_page_t* bpage) /*!< in: control block */ { buf_LRU_remove_block(bpage); buf_LRU_add_block_to_end_low(bpage); } /******************************************************************//** Try to free a block. If bpage is a descriptor of a compressed-only page, the descriptor object will be freed as well. NOTE: If this function returns BUF_LRU_FREED, it will not temporarily release buf_pool_mutex. Furthermore, the page frame will no longer be accessible via bpage. The caller must hold buf_pool_mutex and buf_page_get_mutex(bpage) and release these two mutexes after the call. No other buf_page_get_mutex() may be held when calling this function. @return BUF_LRU_FREED if freed, BUF_LRU_CANNOT_RELOCATE or BUF_LRU_NOT_FREED otherwise. */ UNIV_INTERN enum buf_lru_free_block_status buf_LRU_free_block( /*===============*/ buf_page_t* bpage, /*!< in: block to be freed */ ibool zip, /*!< in: TRUE if should remove also the compressed page of an uncompressed page */ ibool* buf_pool_mutex_released) /*!< in: pointer to a variable that will be assigned TRUE if buf_pool_mutex was temporarily released, or NULL */ { buf_page_t* b = NULL; mutex_t* block_mutex = buf_page_get_mutex(bpage); ut_ad(buf_pool_mutex_own()); ut_ad(mutex_own(block_mutex)); ut_ad(buf_page_in_file(bpage)); ut_ad(bpage->in_LRU_list); ut_ad(!bpage->in_flush_list == !bpage->oldest_modification); UNIV_MEM_ASSERT_RW(bpage, sizeof *bpage); if (!buf_page_can_relocate(bpage)) { /* Do not free buffer-fixed or I/O-fixed blocks. */ return(BUF_LRU_NOT_FREED); } #ifdef UNIV_IBUF_COUNT_DEBUG ut_a(ibuf_count_get(bpage->space, bpage->offset) == 0); #endif /* UNIV_IBUF_COUNT_DEBUG */ if (zip || !bpage->zip.data) { /* This would completely free the block. */ /* Do not completely free dirty blocks. */ if (bpage->oldest_modification) { return(BUF_LRU_NOT_FREED); } } else if (bpage->oldest_modification) { /* Do not completely free dirty blocks. */ if (buf_page_get_state(bpage) != BUF_BLOCK_FILE_PAGE) { ut_ad(buf_page_get_state(bpage) == BUF_BLOCK_ZIP_DIRTY); return(BUF_LRU_NOT_FREED); } goto alloc; } else if (buf_page_get_state(bpage) == BUF_BLOCK_FILE_PAGE) { /* Allocate the control block for the compressed page. If it cannot be allocated (without freeing a block from the LRU list), refuse to free bpage. */ alloc: buf_pool_mutex_exit_forbid(); b = buf_buddy_alloc(sizeof *b, NULL); buf_pool_mutex_exit_allow(); if (UNIV_UNLIKELY(!b)) { return(BUF_LRU_CANNOT_RELOCATE); } memcpy(b, bpage, sizeof *b); } #ifdef UNIV_DEBUG if (buf_debug_prints) { ib_logger(ib_stream, "Putting space %lu page %lu to free list\n", (ulong) buf_page_get_space(bpage), (ulong) buf_page_get_page_no(bpage)); } #endif /* UNIV_DEBUG */ if (buf_LRU_block_remove_hashed_page(bpage, zip) == BUF_BLOCK_REMOVE_HASH) { ut_a(bpage->buf_fix_count == 0); if (b) { buf_page_t* prev_b = UT_LIST_GET_PREV(LRU, b); const ulint fold = buf_page_address_fold( bpage->space, bpage->offset); ut_a(!buf_page_hash_get(bpage->space, bpage->offset)); b->state = b->oldest_modification ? BUF_BLOCK_ZIP_DIRTY : BUF_BLOCK_ZIP_PAGE; UNIV_MEM_DESC(b->zip.data, page_zip_get_size(&b->zip), b); /* The fields in_page_hash and in_LRU_list of the to-be-freed block descriptor should have been cleared in buf_LRU_block_remove_hashed_page(), which invokes buf_LRU_remove_block(). */ ut_ad(!bpage->in_page_hash); ut_ad(!bpage->in_LRU_list); /* bpage->state was BUF_BLOCK_FILE_PAGE because b != NULL. The type cast below is thus valid. */ ut_ad(!((buf_block_t*) bpage)->in_unzip_LRU_list); /* The fields of bpage were copied to b before buf_LRU_block_remove_hashed_page() was invoked. */ ut_ad(!b->in_zip_hash); ut_ad(b->in_page_hash); ut_ad(b->in_LRU_list); HASH_INSERT(buf_page_t, hash, buf_pool->page_hash, fold, b); /* Insert b where bpage was in the LRU list. */ if (UNIV_LIKELY(prev_b != NULL)) { ulint lru_len; ut_ad(prev_b->in_LRU_list); ut_ad(buf_page_in_file(prev_b)); UNIV_MEM_ASSERT_RW(prev_b, sizeof *prev_b); UT_LIST_INSERT_AFTER(LRU, buf_pool->LRU, prev_b, b); if (buf_page_is_old(b)) { buf_pool->LRU_old_len++; if (UNIV_UNLIKELY (buf_pool->LRU_old == UT_LIST_GET_NEXT(LRU, b))) { buf_pool->LRU_old = b; } } lru_len = UT_LIST_GET_LEN(buf_pool->LRU); if (lru_len > BUF_LRU_OLD_MIN_LEN) { ut_ad(buf_pool->LRU_old); /* Adjust the length of the old block list if necessary */ buf_LRU_old_adjust_len(); } else if (lru_len == BUF_LRU_OLD_MIN_LEN) { /* The LRU list is now long enough for LRU_old to become defined: init it */ buf_LRU_old_init(); } #ifdef UNIV_LRU_DEBUG /* Check that the "old" flag is consistent in the block and its neighbours. */ buf_page_set_old(b, buf_page_is_old(b)); #endif /* UNIV_LRU_DEBUG */ } else { ut_d(b->in_LRU_list = FALSE); buf_LRU_add_block_low(b, buf_page_is_old(b)); } if (b->state == BUF_BLOCK_ZIP_PAGE) { buf_LRU_insert_zip_clean(b); } else { /* Relocate on buf_pool->flush_list. */ buf_flush_relocate_on_flush_list(bpage, b); } bpage->zip.data = NULL; page_zip_set_size(&bpage->zip, 0); /* Prevent buf_page_get_gen() from decompressing the block while we release buf_pool_mutex and block_mutex. */ b->buf_fix_count++; b->io_fix = BUF_IO_READ; } if (buf_pool_mutex_released) { *buf_pool_mutex_released = TRUE; } buf_pool_mutex_exit(); mutex_exit(block_mutex); /* Remove possible adaptive hash index on the page. The page was declared uninitialized by buf_LRU_block_remove_hashed_page(). We need to flag the contents of the page valid (which it still is) in order to avoid bogus Valgrind warnings.*/ UNIV_MEM_VALID(((buf_block_t*) bpage)->frame, UNIV_PAGE_SIZE); btr_search_drop_page_hash_index((buf_block_t*) bpage); UNIV_MEM_INVALID(((buf_block_t*) bpage)->frame, UNIV_PAGE_SIZE); if (b) { /* Compute and stamp the compressed page checksum while not holding any mutex. The block is already half-freed (BUF_BLOCK_REMOVE_HASH) and removed from buf_pool->page_hash, thus inaccessible by any other thread. */ mach_write_to_4( b->zip.data + FIL_PAGE_SPACE_OR_CHKSUM, UNIV_LIKELY(srv_use_checksums) ? page_zip_calc_checksum( b->zip.data, page_zip_get_size(&b->zip)) : BUF_NO_CHECKSUM_MAGIC); } buf_pool_mutex_enter(); mutex_enter(block_mutex); if (b) { mutex_enter(&buf_pool_zip_mutex); b->buf_fix_count--; buf_page_set_io_fix(b, BUF_IO_NONE); mutex_exit(&buf_pool_zip_mutex); } buf_LRU_block_free_hashed_page((buf_block_t*) bpage); } else { /* The block_mutex should have been released by buf_LRU_block_remove_hashed_page() when it returns BUF_BLOCK_ZIP_FREE. */ ut_ad(block_mutex == &buf_pool_zip_mutex); mutex_enter(block_mutex); } return(BUF_LRU_FREED); } /******************************************************************//** Puts a block back to the free list. */ UNIV_INTERN void buf_LRU_block_free_non_file_page( /*=============================*/ buf_block_t* block) /*!< in: block, must not contain a file page */ { void* data; ut_ad(block); ut_ad(buf_pool_mutex_own()); ut_ad(mutex_own(&block->mutex)); switch (buf_block_get_state(block)) { case BUF_BLOCK_MEMORY: case BUF_BLOCK_READY_FOR_USE: break; default: ut_error; } #if defined UNIV_AHI_DEBUG || defined UNIV_DEBUG ut_a(block->n_pointers == 0); #endif /* UNIV_AHI_DEBUG || UNIV_DEBUG */ ut_ad(!block->page.in_free_list); ut_ad(!block->page.in_flush_list); ut_ad(!block->page.in_LRU_list); buf_block_set_state(block, BUF_BLOCK_NOT_USED); UNIV_MEM_ALLOC(block->frame, UNIV_PAGE_SIZE); #ifdef UNIV_DEBUG /* Wipe contents of page to reveal possible stale pointers to it */ memset(block->frame, '\0', UNIV_PAGE_SIZE); #else /* Wipe page_no and space_id */ memset(block->frame + FIL_PAGE_OFFSET, 0xfe, 4); memset(block->frame + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID, 0xfe, 4); #endif data = block->page.zip.data; if (data) { block->page.zip.data = NULL; mutex_exit(&block->mutex); buf_pool_mutex_exit_forbid(); buf_buddy_free(data, page_zip_get_size(&block->page.zip)); buf_pool_mutex_exit_allow(); mutex_enter(&block->mutex); page_zip_set_size(&block->page.zip, 0); } UT_LIST_ADD_FIRST(list, buf_pool->free, (&block->page)); ut_d(block->page.in_free_list = TRUE); UNIV_MEM_ASSERT_AND_FREE(block->frame, UNIV_PAGE_SIZE); } /******************************************************************//** Takes a block out of the LRU list and page hash table. If the block is compressed-only (BUF_BLOCK_ZIP_PAGE), the object will be freed and buf_pool_zip_mutex will be released. If a compressed page or a compressed-only block descriptor is freed, other compressed pages or compressed-only block descriptors may be relocated. @return the new state of the block (BUF_BLOCK_ZIP_FREE if the state was BUF_BLOCK_ZIP_PAGE, or BUF_BLOCK_REMOVE_HASH otherwise) */ static enum buf_page_state buf_LRU_block_remove_hashed_page( /*=============================*/ buf_page_t* bpage, /*!< in: block, must contain a file page and be in a state where it can be freed; there may or may not be a hash index to the page */ ibool zip) /*!< in: TRUE if should remove also the compressed page of an uncompressed page */ { const buf_page_t* hashed_bpage; ut_ad(bpage); ut_ad(buf_pool_mutex_own()); ut_ad(mutex_own(buf_page_get_mutex(bpage))); ut_a(buf_page_get_io_fix(bpage) == BUF_IO_NONE); ut_a(bpage->buf_fix_count == 0); UNIV_MEM_ASSERT_RW(bpage, sizeof *bpage); buf_LRU_remove_block(bpage); buf_pool->freed_page_clock += 1; switch (buf_page_get_state(bpage)) { case BUF_BLOCK_FILE_PAGE: UNIV_MEM_ASSERT_W(bpage, sizeof(buf_block_t)); UNIV_MEM_ASSERT_W(((buf_block_t*) bpage)->frame, UNIV_PAGE_SIZE); buf_block_modify_clock_inc((buf_block_t*) bpage); if (bpage->zip.data) { const page_t* page = ((buf_block_t*) bpage)->frame; const ulint zip_size = page_zip_get_size(&bpage->zip); ut_a(!zip || bpage->oldest_modification == 0); switch (UNIV_EXPECT(fil_page_get_type(page), FIL_PAGE_INDEX)) { case FIL_PAGE_TYPE_ALLOCATED: case FIL_PAGE_INODE: case FIL_PAGE_IBUF_BITMAP: case FIL_PAGE_TYPE_FSP_HDR: case FIL_PAGE_TYPE_XDES: /* These are essentially uncompressed pages. */ if (!zip) { /* InnoDB writes the data to the uncompressed page frame. Copy it to the compressed page, which will be preserved. */ memcpy(bpage->zip.data, page, zip_size); } break; case FIL_PAGE_TYPE_ZBLOB: case FIL_PAGE_TYPE_ZBLOB2: break; case FIL_PAGE_INDEX: #ifdef UNIV_ZIP_DEBUG ut_a(page_zip_validate(&bpage->zip, page)); #endif /* UNIV_ZIP_DEBUG */ break; default: ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: ERROR: The compressed page" " to be evicted seems corrupt:"); ut_print_buf(ib_stream, page, zip_size); ib_logger(ib_stream, "\nInnoDB: Possibly older version" " of the page:"); ut_print_buf(ib_stream, bpage->zip.data, zip_size); ib_logger(ib_stream, "\n"); ut_error; } break; } /* fall through */ case BUF_BLOCK_ZIP_PAGE: ut_a(bpage->oldest_modification == 0); UNIV_MEM_ASSERT_W(bpage->zip.data, page_zip_get_size(&bpage->zip)); break; case BUF_BLOCK_ZIP_FREE: case BUF_BLOCK_ZIP_DIRTY: case BUF_BLOCK_NOT_USED: case BUF_BLOCK_READY_FOR_USE: case BUF_BLOCK_MEMORY: case BUF_BLOCK_REMOVE_HASH: ut_error; break; } hashed_bpage = buf_page_hash_get(bpage->space, bpage->offset); if (UNIV_UNLIKELY(bpage != hashed_bpage)) { ib_logger(ib_stream, "InnoDB: Error: page %lu %lu not found" " in the hash table\n", (ulong) bpage->space, (ulong) bpage->offset); if (hashed_bpage) { ib_logger(ib_stream, "InnoDB: In hash table we find block" " %p of %lu %lu which is not %p\n", (const void*) hashed_bpage, (ulong) hashed_bpage->space, (ulong) hashed_bpage->offset, (const void*) bpage); } #if defined UNIV_DEBUG || defined UNIV_BUF_DEBUG mutex_exit(buf_page_get_mutex(bpage)); buf_pool_mutex_exit(); buf_print(); buf_LRU_print(); buf_validate(); buf_LRU_validate(); #endif /* UNIV_DEBUG || UNIV_BUF_DEBUG */ ut_error; } ut_ad(!bpage->in_zip_hash); ut_ad(bpage->in_page_hash); ut_d(bpage->in_page_hash = FALSE); HASH_DELETE(buf_page_t, hash, buf_pool->page_hash, buf_page_address_fold(bpage->space, bpage->offset), bpage); switch (buf_page_get_state(bpage)) { case BUF_BLOCK_ZIP_PAGE: ut_ad(!bpage->in_free_list); ut_ad(!bpage->in_flush_list); ut_ad(!bpage->in_LRU_list); ut_a(bpage->zip.data); ut_a(buf_page_get_zip_size(bpage)); UT_LIST_REMOVE(list, buf_pool->zip_clean, bpage); mutex_exit(&buf_pool_zip_mutex); buf_pool_mutex_exit_forbid(); buf_buddy_free(bpage->zip.data, page_zip_get_size(&bpage->zip)); buf_buddy_free(bpage, sizeof(*bpage)); buf_pool_mutex_exit_allow(); UNIV_MEM_UNDESC(bpage); return(BUF_BLOCK_ZIP_FREE); case BUF_BLOCK_FILE_PAGE: memset(((buf_block_t*) bpage)->frame + FIL_PAGE_OFFSET, 0xff, 4); memset(((buf_block_t*) bpage)->frame + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID, 0xff, 4); UNIV_MEM_INVALID(((buf_block_t*) bpage)->frame, UNIV_PAGE_SIZE); buf_page_set_state(bpage, BUF_BLOCK_REMOVE_HASH); if (zip && bpage->zip.data) { /* Free the compressed page. */ void* data = bpage->zip.data; bpage->zip.data = NULL; ut_ad(!bpage->in_free_list); ut_ad(!bpage->in_flush_list); ut_ad(!bpage->in_LRU_list); mutex_exit(&((buf_block_t*) bpage)->mutex); buf_pool_mutex_exit_forbid(); buf_buddy_free(data, page_zip_get_size(&bpage->zip)); buf_pool_mutex_exit_allow(); mutex_enter(&((buf_block_t*) bpage)->mutex); page_zip_set_size(&bpage->zip, 0); } return(BUF_BLOCK_REMOVE_HASH); case BUF_BLOCK_ZIP_FREE: case BUF_BLOCK_ZIP_DIRTY: case BUF_BLOCK_NOT_USED: case BUF_BLOCK_READY_FOR_USE: case BUF_BLOCK_MEMORY: case BUF_BLOCK_REMOVE_HASH: break; } ut_error; return(BUF_BLOCK_ZIP_FREE); } /******************************************************************//** Puts a file page whose has no hash index to the free list. */ static void buf_LRU_block_free_hashed_page( /*===========================*/ buf_block_t* block) /*!< in: block, must contain a file page and be in a state where it can be freed */ { ut_ad(buf_pool_mutex_own()); ut_ad(mutex_own(&block->mutex)); buf_block_set_state(block, BUF_BLOCK_MEMORY); buf_LRU_block_free_non_file_page(block); } /**********************************************************************//** Updates buf_LRU_old_ratio. @return updated old_pct */ UNIV_INTERN ulint buf_LRU_old_ratio_update( /*=====================*/ ulint old_pct,/*!< in: Reserve this percentage of the buffer pool for "old" blocks. */ ibool adjust) /*!< in: TRUE=adjust the LRU list; FALSE=just assign buf_LRU_old_ratio during the initialization of InnoDB */ { ulint ratio; ratio = old_pct * BUF_LRU_OLD_RATIO_DIV / 100; if (ratio < BUF_LRU_OLD_RATIO_MIN) { ratio = BUF_LRU_OLD_RATIO_MIN; } else if (ratio > BUF_LRU_OLD_RATIO_MAX) { ratio = BUF_LRU_OLD_RATIO_MAX; } if (adjust) { buf_pool_mutex_enter(); if (ratio != buf_LRU_old_ratio) { buf_LRU_old_ratio = ratio; if (UT_LIST_GET_LEN(buf_pool->LRU) >= BUF_LRU_OLD_MIN_LEN) { buf_LRU_old_adjust_len(); } } buf_pool_mutex_exit(); } else { buf_LRU_old_ratio = ratio; } /* the reverse of ratio = old_pct * BUF_LRU_OLD_RATIO_DIV / 100 */ return((ulint) (ratio * 100 / (double) BUF_LRU_OLD_RATIO_DIV + 0.5)); } /********************************************************************//** Update the historical stats that we are collecting for LRU eviction policy at the end of each interval. */ UNIV_INTERN void buf_LRU_stat_update(void) /*=====================*/ { buf_LRU_stat_t* item; /* If we haven't started eviction yet then don't update stats. */ if (buf_pool->freed_page_clock == 0) { goto func_exit; } buf_pool_mutex_enter(); /* Update the index. */ item = &buf_LRU_stat_arr[buf_LRU_stat_arr_ind]; buf_LRU_stat_arr_ind++; buf_LRU_stat_arr_ind %= BUF_LRU_STAT_N_INTERVAL; /* Add the current value and subtract the obsolete entry. */ buf_LRU_stat_sum.io += buf_LRU_stat_cur.io - item->io; buf_LRU_stat_sum.unzip += buf_LRU_stat_cur.unzip - item->unzip; /* Put current entry in the array. */ memcpy(item, &buf_LRU_stat_cur, sizeof *item); buf_pool_mutex_exit(); func_exit: /* Clear the current entry. */ memset(&buf_LRU_stat_cur, 0, sizeof buf_LRU_stat_cur); } #if defined UNIV_DEBUG || defined UNIV_BUF_DEBUG /**********************************************************************//** Validates the LRU list. @return TRUE */ UNIV_INTERN ibool buf_LRU_validate(void) /*==================*/ { buf_page_t* bpage; buf_block_t* block; ulint old_len; ulint new_len; ut_ad(buf_pool); buf_pool_mutex_enter(); if (UT_LIST_GET_LEN(buf_pool->LRU) >= BUF_LRU_OLD_MIN_LEN) { ut_a(buf_pool->LRU_old); old_len = buf_pool->LRU_old_len; new_len = ut_min(UT_LIST_GET_LEN(buf_pool->LRU) * buf_LRU_old_ratio / BUF_LRU_OLD_RATIO_DIV, UT_LIST_GET_LEN(buf_pool->LRU) - (BUF_LRU_OLD_TOLERANCE + BUF_LRU_NON_OLD_MIN_LEN)); ut_a(old_len >= new_len - BUF_LRU_OLD_TOLERANCE); ut_a(old_len <= new_len + BUF_LRU_OLD_TOLERANCE); } UT_LIST_VALIDATE(LRU, buf_page_t, buf_pool->LRU, ut_ad(ut_list_node_313->in_LRU_list)); bpage = UT_LIST_GET_FIRST(buf_pool->LRU); old_len = 0; while (bpage != NULL) { switch (buf_page_get_state(bpage)) { case BUF_BLOCK_ZIP_FREE: case BUF_BLOCK_NOT_USED: case BUF_BLOCK_READY_FOR_USE: case BUF_BLOCK_MEMORY: case BUF_BLOCK_REMOVE_HASH: ut_error; break; case BUF_BLOCK_FILE_PAGE: ut_ad(((buf_block_t*) bpage)->in_unzip_LRU_list == buf_page_belongs_to_unzip_LRU(bpage)); case BUF_BLOCK_ZIP_PAGE: case BUF_BLOCK_ZIP_DIRTY: break; } if (buf_page_is_old(bpage)) { const buf_page_t* prev = UT_LIST_GET_PREV(LRU, bpage); const buf_page_t* next = UT_LIST_GET_NEXT(LRU, bpage); if (!old_len++) { ut_a(buf_pool->LRU_old == bpage); } else { ut_a(!prev || buf_page_is_old(prev)); } ut_a(!next || buf_page_is_old(next)); } bpage = UT_LIST_GET_NEXT(LRU, bpage); } ut_a(buf_pool->LRU_old_len == old_len); UT_LIST_VALIDATE(list, buf_page_t, buf_pool->free, ut_ad(ut_list_node_313->in_free_list)); for (bpage = UT_LIST_GET_FIRST(buf_pool->free); bpage != NULL; bpage = UT_LIST_GET_NEXT(list, bpage)) { ut_a(buf_page_get_state(bpage) == BUF_BLOCK_NOT_USED); } UT_LIST_VALIDATE(unzip_LRU, buf_block_t, buf_pool->unzip_LRU, ut_ad(ut_list_node_313->in_unzip_LRU_list && ut_list_node_313->page.in_LRU_list)); for (block = UT_LIST_GET_FIRST(buf_pool->unzip_LRU); block; block = UT_LIST_GET_NEXT(unzip_LRU, block)) { ut_ad(block->in_unzip_LRU_list); ut_ad(block->page.in_LRU_list); ut_a(buf_page_belongs_to_unzip_LRU(&block->page)); } buf_pool_mutex_exit(); return(TRUE); } #endif /* UNIV_DEBUG || UNIV_BUF_DEBUG */ #if defined UNIV_DEBUG_PRINT || defined UNIV_DEBUG || defined UNIV_BUF_DEBUG /**********************************************************************//** Prints the LRU list. */ UNIV_INTERN void buf_LRU_print(void) /*===============*/ { const buf_page_t* bpage; ut_ad(buf_pool); buf_pool_mutex_enter(); bpage = UT_LIST_GET_FIRST(buf_pool->LRU); while (bpage != NULL) { ib_logger(ib_stream, "BLOCK space %lu page %lu ", (ulong) buf_page_get_space(bpage), (ulong) buf_page_get_page_no(bpage)); if (buf_page_is_old(bpage)) { ib_logger(ib_stream, "old "); } if (bpage->buf_fix_count) { ib_logger(ib_stream, "buffix count %lu ", (ulong) bpage->buf_fix_count); } if (buf_page_get_io_fix(bpage)) { ib_logger(ib_stream, "io_fix %lu ", (ulong) buf_page_get_io_fix(bpage)); } if (bpage->oldest_modification) { ib_logger(ib_stream, "modif. "); } switch (buf_page_get_state(bpage)) { const byte* frame; case BUF_BLOCK_FILE_PAGE: frame = buf_block_get_frame((buf_block_t*) bpage); ib_logger(ib_stream, "\ntype %lu" " index id %lu\n", (ulong) fil_page_get_type(frame), (ulong) ut_dulint_get_low( btr_page_get_index_id(frame))); break; case BUF_BLOCK_ZIP_PAGE: frame = bpage->zip.data; ib_logger(ib_stream, "\ntype %lu size %lu" " index id %lu\n", (ulong) fil_page_get_type(frame), (ulong) buf_page_get_zip_size(bpage), (ulong) ut_dulint_get_low( btr_page_get_index_id(frame))); break; default: ib_logger(ib_stream, "\n!state %lu!\n", (ulong) buf_page_get_state(bpage)); break; } bpage = UT_LIST_GET_NEXT(LRU, bpage); } buf_pool_mutex_exit(); } #endif /* UNIV_DEBUG_PRINT || UNIV_DEBUG || UNIV_BUF_DEBUG */ haildb-2.3.2/buf/buf0buddy.c0000644000175000017500000004722311513177357016457 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 2006, 2010, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file buf/buf0buddy.c Binary buddy allocator for compressed pages Created December 2006 by Marko Makela *******************************************************/ #define THIS_MODULE #include "buf0buddy.h" #ifdef UNIV_NONINL # include "buf0buddy.ic" #endif #undef THIS_MODULE #include "buf0buf.h" #include "buf0lru.h" #include "buf0flu.h" #include "page0zip.h" /* Statistic counters */ #ifdef UNIV_DEBUG /** Number of frames allocated from the buffer pool to the buddy system. Protected by buf_pool_mutex. */ static ulint buf_buddy_n_frames; #endif /* UNIV_DEBUG */ /** Statistics of the buddy system, indexed by block size. Protected by buf_pool_mutex. */ UNIV_INTERN buf_buddy_stat_t buf_buddy_stat[BUF_BUDDY_SIZES + 1]; /**********************************************************************//** Get the offset of the buddy of a compressed page frame. @return the buddy relative of page */ UNIV_INTERN void buf_buddy_var_init(void) /*====================*/ { #ifdef UNIV_DEBUG buf_buddy_n_frames = 0; #endif /* UNIV_DEBUG */ memset(buf_buddy_stat, 0x0, sizeof(buf_buddy_stat)); } /************************************************************************** Get the offset of the buddy of a compressed page frame. @return the buddy relative of page */ UNIV_INLINE byte* buf_buddy_get( /*==========*/ byte* page, /*!< in: compressed page */ ulint size) /*!< in: page size in bytes */ { ut_ad(ut_is_2pow(size)); ut_ad(size >= BUF_BUDDY_LOW); ut_ad(size < BUF_BUDDY_HIGH); ut_ad(!ut_align_offset(page, size)); if (((ulint) page) & size) { return(page - size); } else { return(page + size); } } /**********************************************************************//** Add a block to the head of the appropriate buddy free list. */ UNIV_INLINE void buf_buddy_add_to_free( /*==================*/ buf_page_t* bpage, /*!< in,own: block to be freed */ ulint i) /*!< in: index of buf_pool->zip_free[] */ { #ifdef UNIV_DEBUG_VALGRIND buf_page_t* b = UT_LIST_GET_FIRST(buf_pool->zip_free[i]); if (b) UNIV_MEM_VALID(b, BUF_BUDDY_LOW << i); #endif /* UNIV_DEBUG_VALGRIND */ ut_ad(buf_pool_mutex_own()); ut_ad(buf_page_get_state(bpage) == BUF_BLOCK_ZIP_FREE); ut_ad(buf_pool->zip_free[i].start != bpage); UT_LIST_ADD_FIRST(list, buf_pool->zip_free[i], bpage); #ifdef UNIV_DEBUG_VALGRIND if (b) UNIV_MEM_FREE(b, BUF_BUDDY_LOW << i); UNIV_MEM_ASSERT_AND_FREE(bpage, BUF_BUDDY_LOW << i); #endif /* UNIV_DEBUG_VALGRIND */ } /**********************************************************************//** Remove a block from the appropriate buddy free list. */ UNIV_INLINE void buf_buddy_remove_from_free( /*=======================*/ buf_page_t* bpage, /*!< in: block to be removed */ ulint i) /*!< in: index of buf_pool->zip_free[] */ { #ifdef UNIV_DEBUG_VALGRIND buf_page_t* prev = UT_LIST_GET_PREV(list, bpage); buf_page_t* next = UT_LIST_GET_NEXT(list, bpage); if (prev) UNIV_MEM_VALID(prev, BUF_BUDDY_LOW << i); if (next) UNIV_MEM_VALID(next, BUF_BUDDY_LOW << i); ut_ad(!prev || buf_page_get_state(prev) == BUF_BLOCK_ZIP_FREE); ut_ad(!next || buf_page_get_state(next) == BUF_BLOCK_ZIP_FREE); #endif /* UNIV_DEBUG_VALGRIND */ ut_ad(buf_pool_mutex_own()); ut_ad(buf_page_get_state(bpage) == BUF_BLOCK_ZIP_FREE); UT_LIST_REMOVE(list, buf_pool->zip_free[i], bpage); #ifdef UNIV_DEBUG_VALGRIND if (prev) UNIV_MEM_FREE(prev, BUF_BUDDY_LOW << i); if (next) UNIV_MEM_FREE(next, BUF_BUDDY_LOW << i); #endif /* UNIV_DEBUG_VALGRIND */ } /**********************************************************************//** Try to allocate a block from buf_pool->zip_free[]. @return allocated block, or NULL if buf_pool->zip_free[] was empty */ static void* buf_buddy_alloc_zip( /*================*/ ulint i) /*!< in: index of buf_pool->zip_free[] */ { buf_page_t* bpage; ut_ad(buf_pool_mutex_own()); ut_a(i < BUF_BUDDY_SIZES); #ifndef UNIV_DEBUG_VALGRIND /* Valgrind would complain about accessing free memory. */ ut_d(UT_LIST_VALIDATE(list, buf_page_t, buf_pool->zip_free[i], ut_ad(buf_page_get_state(ut_list_node_313) == BUF_BLOCK_ZIP_FREE))); #endif /* !UNIV_DEBUG_VALGRIND */ bpage = UT_LIST_GET_FIRST(buf_pool->zip_free[i]); if (bpage) { UNIV_MEM_VALID(bpage, BUF_BUDDY_LOW << i); ut_a(buf_page_get_state(bpage) == BUF_BLOCK_ZIP_FREE); buf_buddy_remove_from_free(bpage, i); } else if (i + 1 < BUF_BUDDY_SIZES) { /* Attempt to split. */ bpage = buf_buddy_alloc_zip(i + 1); if (bpage) { buf_page_t* buddy = (buf_page_t*) (((char*) bpage) + (BUF_BUDDY_LOW << i)); ut_ad(!buf_pool_contains_zip(buddy)); ut_d(memset(buddy, i, BUF_BUDDY_LOW << i)); buddy->state = BUF_BLOCK_ZIP_FREE; buf_buddy_add_to_free(buddy, i); } } #ifdef UNIV_DEBUG if (bpage) { memset(bpage, ~i, BUF_BUDDY_LOW << i); } #endif /* UNIV_DEBUG */ UNIV_MEM_ALLOC(bpage, BUF_BUDDY_SIZES << i); return(bpage); } /**********************************************************************//** Deallocate a buffer frame of UNIV_PAGE_SIZE. */ static void buf_buddy_block_free( /*=================*/ void* buf) /*!< in: buffer frame to deallocate */ { const ulint fold = BUF_POOL_ZIP_FOLD_PTR(buf); buf_page_t* bpage; buf_block_t* block; ut_ad(buf_pool_mutex_own()); ut_ad(!mutex_own(&buf_pool_zip_mutex)); ut_a(!ut_align_offset(buf, UNIV_PAGE_SIZE)); HASH_SEARCH(hash, buf_pool->zip_hash, fold, buf_page_t*, bpage, ut_ad(buf_page_get_state(bpage) == BUF_BLOCK_MEMORY && bpage->in_zip_hash && !bpage->in_page_hash), ((buf_block_t*) bpage)->frame == buf); ut_a(bpage); ut_a(buf_page_get_state(bpage) == BUF_BLOCK_MEMORY); ut_ad(!bpage->in_page_hash); ut_ad(bpage->in_zip_hash); ut_d(bpage->in_zip_hash = FALSE); HASH_DELETE(buf_page_t, hash, buf_pool->zip_hash, fold, bpage); ut_d(memset(buf, 0, UNIV_PAGE_SIZE)); UNIV_MEM_INVALID(buf, UNIV_PAGE_SIZE); block = (buf_block_t*) bpage; mutex_enter(&block->mutex); buf_LRU_block_free_non_file_page(block); mutex_exit(&block->mutex); ut_ad(buf_buddy_n_frames > 0); ut_d(buf_buddy_n_frames--); } /**********************************************************************//** Allocate a buffer block to the buddy allocator. */ static void buf_buddy_block_register( /*=====================*/ buf_block_t* block) /*!< in: buffer frame to allocate */ { const ulint fold = BUF_POOL_ZIP_FOLD(block); ut_ad(buf_pool_mutex_own()); ut_ad(!mutex_own(&buf_pool_zip_mutex)); ut_ad(buf_block_get_state(block) == BUF_BLOCK_READY_FOR_USE); buf_block_set_state(block, BUF_BLOCK_MEMORY); ut_a(block->frame); ut_a(!ut_align_offset(block->frame, UNIV_PAGE_SIZE)); ut_ad(!block->page.in_page_hash); ut_ad(!block->page.in_zip_hash); ut_d(block->page.in_zip_hash = TRUE); HASH_INSERT(buf_page_t, hash, buf_pool->zip_hash, fold, &block->page); ut_d(buf_buddy_n_frames++); } /**********************************************************************//** Allocate a block from a bigger object. @return allocated block */ static void* buf_buddy_alloc_from( /*=================*/ void* buf, /*!< in: a block that is free to use */ ulint i, /*!< in: index of buf_pool->zip_free[] */ ulint j) /*!< in: size of buf as an index of buf_pool->zip_free[] */ { ulint offs = BUF_BUDDY_LOW << j; ut_ad(j <= BUF_BUDDY_SIZES); ut_ad(j >= i); ut_ad(!ut_align_offset(buf, offs)); /* Add the unused parts of the block to the free lists. */ while (j > i) { buf_page_t* bpage; offs >>= 1; j--; bpage = (buf_page_t*) ((byte*) buf + offs); ut_d(memset(bpage, j, BUF_BUDDY_LOW << j)); bpage->state = BUF_BLOCK_ZIP_FREE; #ifndef UNIV_DEBUG_VALGRIND /* Valgrind would complain about accessing free memory. */ ut_d(UT_LIST_VALIDATE(list, buf_page_t, buf_pool->zip_free[i], ut_ad(buf_page_get_state( ut_list_node_313) == BUF_BLOCK_ZIP_FREE))); #endif /* !UNIV_DEBUG_VALGRIND */ buf_buddy_add_to_free(bpage, j); } return(buf); } /**********************************************************************//** Allocate a block. The thread calling this function must hold buf_pool_mutex and must not hold buf_pool_zip_mutex or any block->mutex. The buf_pool_mutex may only be released and reacquired if lru != NULL. @return allocated block, possibly NULL if lru==NULL */ UNIV_INTERN void* buf_buddy_alloc_low( /*================*/ ulint i, /*!< in: index of buf_pool->zip_free[], or BUF_BUDDY_SIZES */ ibool* lru) /*!< in: pointer to a variable that will be assigned TRUE if storage was allocated from the LRU list and buf_pool_mutex was temporarily released, or NULL if the LRU list should not be used */ { buf_block_t* block; ut_ad(buf_pool_mutex_own()); ut_ad(!mutex_own(&buf_pool_zip_mutex)); if (i < BUF_BUDDY_SIZES) { /* Try to allocate from the buddy system. */ block = buf_buddy_alloc_zip(i); if (block) { goto func_exit; } } /* Try allocating from the buf_pool->free list. */ block = buf_LRU_get_free_only(); if (block) { goto alloc_big; } if (!lru) { return(NULL); } /* Try replacing an uncompressed page in the buffer pool. */ buf_pool_mutex_exit(); block = buf_LRU_get_free_block(0); *lru = TRUE; buf_pool_mutex_enter(); alloc_big: buf_buddy_block_register(block); block = buf_buddy_alloc_from(block->frame, i, BUF_BUDDY_SIZES); func_exit: buf_buddy_stat[i].used++; return(block); } /**********************************************************************//** Try to relocate the control block of a compressed page. @return TRUE if relocated */ static ibool buf_buddy_relocate_block( /*=====================*/ buf_page_t* bpage, /*!< in: block to relocate */ buf_page_t* dpage) /*!< in: free block to relocate to */ { buf_page_t* b; ut_ad(buf_pool_mutex_own()); switch (buf_page_get_state(bpage)) { case BUF_BLOCK_ZIP_FREE: case BUF_BLOCK_NOT_USED: case BUF_BLOCK_READY_FOR_USE: case BUF_BLOCK_FILE_PAGE: case BUF_BLOCK_MEMORY: case BUF_BLOCK_REMOVE_HASH: ut_error; case BUF_BLOCK_ZIP_DIRTY: /* Cannot relocate dirty pages. */ return(FALSE); case BUF_BLOCK_ZIP_PAGE: break; } mutex_enter(&buf_pool_zip_mutex); if (!buf_page_can_relocate(bpage)) { mutex_exit(&buf_pool_zip_mutex); return(FALSE); } buf_relocate(bpage, dpage); ut_d(bpage->state = BUF_BLOCK_ZIP_FREE); /* relocate buf_pool->zip_clean */ b = UT_LIST_GET_PREV(list, dpage); UT_LIST_REMOVE(list, buf_pool->zip_clean, dpage); if (b) { UT_LIST_INSERT_AFTER(list, buf_pool->zip_clean, b, dpage); } else { UT_LIST_ADD_FIRST(list, buf_pool->zip_clean, dpage); } UNIV_MEM_INVALID(bpage, sizeof *bpage); mutex_exit(&buf_pool_zip_mutex); return(TRUE); } /**********************************************************************//** Try to relocate a block. @return TRUE if relocated */ static ibool buf_buddy_relocate( /*===============*/ void* src, /*!< in: block to relocate */ void* dst, /*!< in: free block to relocate to */ ulint i) /*!< in: index of buf_pool->zip_free[] */ { buf_page_t* bpage; const ulint size = BUF_BUDDY_LOW << i; ib_uint64_t usec = ut_time_us(NULL); ut_ad(buf_pool_mutex_own()); ut_ad(!mutex_own(&buf_pool_zip_mutex)); ut_ad(!ut_align_offset(src, size)); ut_ad(!ut_align_offset(dst, size)); UNIV_MEM_ASSERT_W(dst, size); /* We assume that all memory from buf_buddy_alloc() is used for either compressed pages or buf_page_t objects covering compressed pages. */ /* We look inside the allocated objects returned by buf_buddy_alloc() and assume that anything of PAGE_ZIP_MIN_SIZE or larger is a compressed page that contains a valid space_id and page_no in the page header. Should the fields be invalid, we will be unable to relocate the block. We also assume that anything that fits sizeof(buf_page_t) actually is a properly initialized buf_page_t object. */ if (size >= PAGE_ZIP_MIN_SIZE) { /* This is a compressed page. */ mutex_t* mutex; /* The src block may be split into smaller blocks, some of which may be free. Thus, the mach_read_from_4() calls below may attempt to read from free memory. The memory is "owned" by the buddy allocator (and it has been allocated from the buffer pool), so there is nothing wrong about this. The mach_read_from_4() calls here will only trigger bogus Valgrind memcheck warnings in UNIV_DEBUG_VALGRIND builds. */ bpage = buf_page_hash_get( mach_read_from_4((const byte*) src + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID), mach_read_from_4((const byte*) src + FIL_PAGE_OFFSET)); if (!bpage || bpage->zip.data != src) { /* The block has probably been freshly allocated by buf_LRU_get_free_block() but not added to buf_pool->page_hash yet. Obviously, it cannot be relocated. */ return(FALSE); } if (page_zip_get_size(&bpage->zip) != size) { /* The block is of different size. We would have to relocate all blocks covered by src. For the sake of simplicity, give up. */ ut_ad(page_zip_get_size(&bpage->zip) < size); return(FALSE); } /* The block must have been allocated, but it may contain uninitialized data. */ UNIV_MEM_ASSERT_W(src, size); mutex = buf_page_get_mutex(bpage); mutex_enter(mutex); if (buf_page_can_relocate(bpage)) { /* Relocate the compressed page. */ ut_a(bpage->zip.data == src); memcpy(dst, src, size); bpage->zip.data = dst; mutex_exit(mutex); success: UNIV_MEM_INVALID(src, size); { buf_buddy_stat_t* buddy_stat = &buf_buddy_stat[i]; buddy_stat->relocated++; buddy_stat->relocated_usec += ut_time_us(NULL) - usec; } return(TRUE); } mutex_exit(mutex); } else if (i == buf_buddy_get_slot(sizeof(buf_page_t))) { /* This must be a buf_page_t object. */ UNIV_MEM_ASSERT_RW(src, size); if (buf_buddy_relocate_block(src, dst)) { goto success; } } return(FALSE); } /**********************************************************************//** Deallocate a block. */ UNIV_INTERN void buf_buddy_free_low( /*===============*/ void* buf, /*!< in: block to be freed, must not be pointed to by the buffer pool */ ulint i) /*!< in: index of buf_pool->zip_free[], or BUF_BUDDY_SIZES */ { buf_page_t* bpage; buf_page_t* buddy; ut_ad(buf_pool_mutex_own()); ut_ad(!mutex_own(&buf_pool_zip_mutex)); ut_ad(i <= BUF_BUDDY_SIZES); ut_ad(buf_buddy_stat[i].used > 0); buf_buddy_stat[i].used--; recombine: UNIV_MEM_ASSERT_AND_ALLOC(buf, BUF_BUDDY_LOW << i); ut_d(((buf_page_t*) buf)->state = BUF_BLOCK_ZIP_FREE); if (i == BUF_BUDDY_SIZES) { buf_buddy_block_free(buf); return; } ut_ad(i < BUF_BUDDY_SIZES); ut_ad(buf == ut_align_down(buf, BUF_BUDDY_LOW << i)); ut_ad(!buf_pool_contains_zip(buf)); /* Try to combine adjacent blocks. */ buddy = (buf_page_t*) buf_buddy_get(((byte*) buf), BUF_BUDDY_LOW << i); #ifndef UNIV_DEBUG_VALGRIND /* Valgrind would complain about accessing free memory. */ if (buddy->state != BUF_BLOCK_ZIP_FREE) { goto buddy_nonfree; } /* The field buddy->state can only be trusted for free blocks. If buddy->state == BUF_BLOCK_ZIP_FREE, the block is free if it is in the free list. */ #endif /* !UNIV_DEBUG_VALGRIND */ for (bpage = UT_LIST_GET_FIRST(buf_pool->zip_free[i]); bpage; ) { UNIV_MEM_VALID(bpage, BUF_BUDDY_LOW << i); ut_ad(buf_page_get_state(bpage) == BUF_BLOCK_ZIP_FREE); if (bpage == buddy) { buddy_free: /* The buddy is free: recombine */ buf_buddy_remove_from_free(bpage, i); buddy_free2: ut_ad(buf_page_get_state(buddy) == BUF_BLOCK_ZIP_FREE); ut_ad(!buf_pool_contains_zip(buddy)); i++; buf = ut_align_down(buf, BUF_BUDDY_LOW << i); goto recombine; } ut_a(bpage != buf); { buf_page_t* next = UT_LIST_GET_NEXT(list, bpage); UNIV_MEM_ASSERT_AND_FREE(bpage, BUF_BUDDY_LOW << i); bpage = next; } } #ifndef UNIV_DEBUG_VALGRIND buddy_nonfree: /* Valgrind would complain about accessing free memory. */ ut_d(UT_LIST_VALIDATE(list, buf_page_t, buf_pool->zip_free[i], ut_ad(buf_page_get_state(ut_list_node_313) == BUF_BLOCK_ZIP_FREE))); #endif /* UNIV_DEBUG_VALGRIND */ /* The buddy is not free. Is there a free block of this size? */ bpage = UT_LIST_GET_FIRST(buf_pool->zip_free[i]); if (bpage) { /* Remove the block from the free list, because a successful buf_buddy_relocate() will overwrite bpage->list. */ UNIV_MEM_VALID(bpage, BUF_BUDDY_LOW << i); buf_buddy_remove_from_free(bpage, i); /* Try to relocate the buddy of buf to the free block. */ if (buf_buddy_relocate(buddy, bpage, i)) { ut_d(buddy->state = BUF_BLOCK_ZIP_FREE); goto buddy_free2; } buf_buddy_add_to_free(bpage, i); /* Try to relocate the buddy of the free block to buf. */ buddy = (buf_page_t*) buf_buddy_get(((byte*) bpage), BUF_BUDDY_LOW << i); #ifndef UNIV_DEBUG_VALGRIND /* Valgrind would complain about accessing free memory. */ /* The buddy must not be (completely) free, because we always recombine adjacent free blocks. (Parts of the buddy can be free in buf_pool->zip_free[j] with j < i.) */ ut_d(UT_LIST_VALIDATE(list, buf_page_t, buf_pool->zip_free[i], ut_ad(buf_page_get_state( ut_list_node_313) == BUF_BLOCK_ZIP_FREE && ut_list_node_313 != buddy))); #endif /* !UNIV_DEBUG_VALGRIND */ if (buf_buddy_relocate(buddy, buf, i)) { buf = bpage; UNIV_MEM_VALID(bpage, BUF_BUDDY_LOW << i); ut_d(buddy->state = BUF_BLOCK_ZIP_FREE); goto buddy_free; } } /* Free the block to the buddy list. */ bpage = buf; #ifdef UNIV_DEBUG if (i < buf_buddy_get_slot(PAGE_ZIP_MIN_SIZE)) { /* This area has most likely been allocated for at least one compressed-only block descriptor. Check that there are no live objects in the area. This is not a complete check: it may yield false positives as well as false negatives. Also, due to buddy blocks being recombined, it is possible (although unlikely) that this branch is never reached. */ char* c; # ifndef UNIV_DEBUG_VALGRIND /* Valgrind would complain about accessing uninitialized memory. Besides, Valgrind performs a more exhaustive check, at every memory access. */ const buf_page_t* b = buf; const buf_page_t* const b_end = (buf_page_t*) ((char*) b + (BUF_BUDDY_LOW << i)); for (; b < b_end; b++) { /* Avoid false positives (and cause false negatives) by checking for b->space < 1000. */ if ((b->state == BUF_BLOCK_ZIP_PAGE || b->state == BUF_BLOCK_ZIP_DIRTY) && b->space > 0 && b->space < 1000) { ib_logger(ib_stream, "buddy dirty %p %u (%u,%u) %p,%lu\n", (void*) b, b->state, b->space, b->offset, buf, i); } } # endif /* !UNIV_DEBUG_VALGRIND */ /* Scramble the block. This should make any pointers invalid and trigger a segmentation violation. Because the scrambling can be reversed, it may be possible to track down the object pointing to the freed data by dereferencing the unscrambled bpage->LRU or bpage->list pointers. */ for (c = (char*) buf + (BUF_BUDDY_LOW << i); c-- > (char*) buf; ) { *c = ~*c ^ i; } } else { /* Fill large blocks with a constant pattern. */ memset(bpage, i, BUF_BUDDY_LOW << i); } #endif /* UNIV_DEBUG */ bpage->state = BUF_BLOCK_ZIP_FREE; buf_buddy_add_to_free(bpage, i); } haildb-2.3.2/buf/buf0flu.c0000644000175000017500000013447611513177357016145 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2010, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file buf/buf0flu.c The database buffer buf_pool flush algorithm Created 11/11/1995 Heikki Tuuri *******************************************************/ #include "buf0flu.h" #ifdef UNIV_NONINL #include "buf0flu.ic" #endif #include "buf0buf.h" #include "srv0srv.h" #include "page0zip.h" #ifndef UNIV_HOTBACKUP #include "ut0byte.h" #include "ut0lst.h" #include "page0page.h" #include "fil0fil.h" #include "buf0lru.h" #include "buf0rea.h" #include "ibuf0ibuf.h" #include "log0log.h" #include "os0file.h" #include "trx0sys.h" /********************************************************************** These statistics are generated for heuristics used in estimating the rate at which we should flush the dirty blocks to avoid bursty IO activity. Note that the rate of flushing not only depends on how many dirty pages we have in the buffer pool but it is also a fucntion of how much redo the workload is generating and at what rate. */ /* @{ */ /** Number of intervals for which we keep the history of these stats. Each interval is 1 second, defined by the rate at which srv_error_monitor_thread() calls buf_flush_stat_update(). */ #define BUF_FLUSH_STAT_N_INTERVAL 20 /** Sampled values buf_flush_stat_cur. Not protected by any mutex. Updated by buf_flush_stat_update(). */ static buf_flush_stat_t buf_flush_stat_arr[BUF_FLUSH_STAT_N_INTERVAL]; /** Cursor to buf_flush_stat_arr[]. Updated in a round-robin fashion. */ static ulint buf_flush_stat_arr_ind; /** Values at start of the current interval. Reset by buf_flush_stat_update(). */ static buf_flush_stat_t buf_flush_stat_cur; /** Running sum of past values of buf_flush_stat_cur. Updated by buf_flush_stat_update(). Not protected by any mutex. */ static buf_flush_stat_t buf_flush_stat_sum; /** Number of pages flushed through non flush_list flushes. */ static ulint buf_lru_flush_page_count = 0; /* @} */ #if defined UNIV_DEBUG || defined UNIV_BUF_DEBUG /******************************************************************//** Validates the flush list. @return TRUE if ok */ static ibool buf_flush_validate_low(void); /*========================*/ #endif /* UNIV_DEBUG || UNIV_BUF_DEBUG */ /********************************************************************//** Insert a block in the flush_rbt and returns a pointer to its predecessor or NULL if no predecessor. The ordering is maintained on the basis of the key. @return pointer to the predecessor or NULL if no predecessor. */ static buf_page_t* buf_flush_insert_in_flush_rbt( /*==========================*/ buf_page_t* bpage) /*!< in: bpage to be inserted. */ { buf_page_t* prev = NULL; const ib_rbt_node_t* c_node; const ib_rbt_node_t* p_node; ut_ad(buf_pool_mutex_own()); /* Insert this buffer into the rbt. */ c_node = rbt_insert(buf_pool->flush_rbt, &bpage, &bpage); ut_a(c_node != NULL); /* Get the predecessor. */ p_node = rbt_prev(buf_pool->flush_rbt, c_node); if (p_node != NULL) { prev = *rbt_value(buf_page_t*, p_node); ut_a(prev != NULL); } return(prev); } /********************************************************************//** Delete a bpage from the flush_rbt. */ static void buf_flush_delete_from_flush_rbt( /*============================*/ buf_page_t* bpage) /*!< in: bpage to be removed. */ { ibool ret = FALSE; ut_ad(buf_pool_mutex_own()); ret = rbt_delete(buf_pool->flush_rbt, &bpage); ut_ad(ret); } /********************************************************************//** Compare two modified blocks in the buffer pool. The key for comparison is: key = This comparison is used to maintian ordering of blocks in the buf_pool->flush_rbt. Note that for the purpose of flush_rbt, we only need to order blocks on the oldest_modification. The other two fields are used to uniquely identify the blocks. @return < 0 if b2 < b1, 0 if b2 == b1, > 0 if b2 > b1 */ static int buf_flush_block_cmp( /*================*/ const void* p1, /*!< in: block1 */ const void* p2) /*!< in: block2 */ { int ret; const buf_page_t* b1; const buf_page_t* b2; ut_ad(p1 != NULL); ut_ad(p2 != NULL); b1 = *(const buf_page_t**) p1; b2 = *(const buf_page_t**) p2; ut_ad(b1 != NULL); ut_ad(b2 != NULL); ut_ad(b1->in_flush_list); ut_ad(b2->in_flush_list); if (b2->oldest_modification > b1->oldest_modification) { return(1); } if (b2->oldest_modification < b1->oldest_modification) { return(-1); } /* If oldest_modification is same then decide on the space. */ ret = (int)(b2->space - b1->space); /* Or else decide ordering on the offset field. */ return(ret ? ret : (int)(b2->offset - b1->offset)); } /********************************************************************//** Initialize the red-black tree to speed up insertions into the flush_list during recovery process. Should be called at the start of recovery process before any page has been read/written. */ UNIV_INTERN void buf_flush_init_flush_rbt(void) /*==========================*/ { buf_pool_mutex_enter(); /* Create red black tree for speedy insertions in flush list. */ buf_pool->flush_rbt = rbt_create(sizeof(buf_page_t*), buf_flush_block_cmp); buf_pool_mutex_exit(); } /********************************************************************//** Frees up the red-black tree. */ UNIV_INTERN void buf_flush_free_flush_rbt(void) /*==========================*/ { buf_pool_mutex_enter(); #if defined UNIV_DEBUG || defined UNIV_BUF_DEBUG ut_a(buf_flush_validate_low()); #endif /* UNIV_DEBUG || UNIV_BUF_DEBUG */ rbt_free(buf_pool->flush_rbt); buf_pool->flush_rbt = NULL; buf_pool_mutex_exit(); } /********************************************************************//** Inserts a modified block into the flush list. */ UNIV_INTERN void buf_flush_insert_into_flush_list( /*=============================*/ buf_block_t* block) /*!< in/out: block which is modified */ { ut_ad(buf_pool_mutex_own()); ut_ad((UT_LIST_GET_FIRST(buf_pool->flush_list) == NULL) || (UT_LIST_GET_FIRST(buf_pool->flush_list)->oldest_modification <= block->page.oldest_modification)); /* If we are in the recovery then we need to update the flush red-black tree as well. */ if (UNIV_LIKELY_NULL(buf_pool->flush_rbt)) { buf_flush_insert_sorted_into_flush_list(block); return; } ut_ad(buf_block_get_state(block) == BUF_BLOCK_FILE_PAGE); ut_ad(block->page.in_LRU_list); ut_ad(block->page.in_page_hash); ut_ad(!block->page.in_zip_hash); ut_ad(!block->page.in_flush_list); ut_d(block->page.in_flush_list = TRUE); UT_LIST_ADD_FIRST(list, buf_pool->flush_list, &block->page); #if defined UNIV_DEBUG || defined UNIV_BUF_DEBUG ut_a(buf_flush_validate_low()); #endif /* UNIV_DEBUG || UNIV_BUF_DEBUG */ } /********************************************************************//** Inserts a modified block into the flush list in the right sorted position. This function is used by recovery, because there the modifications do not necessarily come in the order of lsn's. */ UNIV_INTERN void buf_flush_insert_sorted_into_flush_list( /*====================================*/ buf_block_t* block) /*!< in/out: block which is modified */ { buf_page_t* prev_b; buf_page_t* b; ut_ad(buf_pool_mutex_own()); ut_ad(buf_block_get_state(block) == BUF_BLOCK_FILE_PAGE); ut_ad(block->page.in_LRU_list); ut_ad(block->page.in_page_hash); ut_ad(!block->page.in_zip_hash); ut_ad(!block->page.in_flush_list); ut_d(block->page.in_flush_list = TRUE); prev_b = NULL; /* For the most part when this function is called the flush_rbt should not be NULL. In a very rare boundary case it is possible that the flush_rbt has already been freed by the recovery thread before the last page was hooked up in the flush_list by the io-handler thread. In that case we'll just do a simple linear search in the else block. */ if (buf_pool->flush_rbt) { prev_b = buf_flush_insert_in_flush_rbt(&block->page); } else { b = UT_LIST_GET_FIRST(buf_pool->flush_list); while (b && b->oldest_modification > block->page.oldest_modification) { ut_ad(b->in_flush_list); prev_b = b; b = UT_LIST_GET_NEXT(list, b); } } if (prev_b == NULL) { UT_LIST_ADD_FIRST(list, buf_pool->flush_list, &block->page); } else { UT_LIST_INSERT_AFTER(list, buf_pool->flush_list, prev_b, &block->page); } #if defined UNIV_DEBUG || defined UNIV_BUF_DEBUG ut_a(buf_flush_validate_low()); #endif /* UNIV_DEBUG || UNIV_BUF_DEBUG */ } /********************************************************************//** Returns TRUE if the file page block is immediately suitable for replacement, i.e., the transition FILE_PAGE => NOT_USED allowed. @return TRUE if can replace immediately */ UNIV_INTERN ibool buf_flush_ready_for_replace( /*========================*/ buf_page_t* bpage) /*!< in: buffer control block, must be buf_page_in_file(bpage) and in the LRU list */ { ut_ad(buf_pool_mutex_own()); ut_ad(mutex_own(buf_page_get_mutex(bpage))); ut_ad(bpage->in_LRU_list); if (UNIV_LIKELY(buf_page_in_file(bpage))) { return(bpage->oldest_modification == 0 && buf_page_get_io_fix(bpage) == BUF_IO_NONE && bpage->buf_fix_count == 0); } ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Error: buffer block state %lu" " in the LRU list!\n", (ulong) buf_page_get_state(bpage)); ut_print_buf(ib_stream, bpage, sizeof(buf_page_t)); ib_logger(ib_stream, "\n"); return(FALSE); } /********************************************************************//** Returns TRUE if the block is modified and ready for flushing. @return TRUE if can flush immediately */ UNIV_INLINE ibool buf_flush_ready_for_flush( /*======================*/ buf_page_t* bpage, /*!< in: buffer control block, must be buf_page_in_file(bpage) */ enum buf_flush flush_type)/*!< in: BUF_FLUSH_LRU or BUF_FLUSH_LIST */ { ut_a(buf_page_in_file(bpage)); ut_ad(buf_pool_mutex_own()); ut_ad(mutex_own(buf_page_get_mutex(bpage))); ut_ad(flush_type == BUF_FLUSH_LRU || BUF_FLUSH_LIST); if (bpage->oldest_modification != 0 && buf_page_get_io_fix(bpage) == BUF_IO_NONE) { ut_ad(bpage->in_flush_list); if (flush_type != BUF_FLUSH_LRU) { return(TRUE); } else if (bpage->buf_fix_count == 0) { /* If we are flushing the LRU list, to avoid deadlocks we require the block not to be bufferfixed, and hence not latched. */ return(TRUE); } } return(FALSE); } /********************************************************************//** Remove a block from the flush list of modified blocks. */ UNIV_INTERN void buf_flush_remove( /*=============*/ buf_page_t* bpage) /*!< in: pointer to the block in question */ { ut_ad(buf_pool_mutex_own()); ut_ad(mutex_own(buf_page_get_mutex(bpage))); ut_ad(bpage->in_flush_list); switch (buf_page_get_state(bpage)) { case BUF_BLOCK_ZIP_PAGE: /* clean compressed pages should not be on the flush list */ case BUF_BLOCK_ZIP_FREE: case BUF_BLOCK_NOT_USED: case BUF_BLOCK_READY_FOR_USE: case BUF_BLOCK_MEMORY: case BUF_BLOCK_REMOVE_HASH: ut_error; return; case BUF_BLOCK_ZIP_DIRTY: buf_page_set_state(bpage, BUF_BLOCK_ZIP_PAGE); UT_LIST_REMOVE(list, buf_pool->flush_list, bpage); buf_LRU_insert_zip_clean(bpage); break; case BUF_BLOCK_FILE_PAGE: UT_LIST_REMOVE(list, buf_pool->flush_list, bpage); break; } /* If the flush_rbt is active then delete from it as well. */ if (UNIV_LIKELY_NULL(buf_pool->flush_rbt)) { buf_flush_delete_from_flush_rbt(bpage); } /* Must be done after we have removed it from the flush_rbt because we assert on in_flush_list in comparison function. */ ut_d(bpage->in_flush_list = FALSE); bpage->oldest_modification = 0; ut_d(UT_LIST_VALIDATE(list, buf_page_t, buf_pool->flush_list, ut_ad(ut_list_node_313->in_flush_list))); } /********************************************************************//** Relocates a buffer control block on the flush_list. Note that it is assumed that the contents of bpage has already been copied to dpage. */ UNIV_INTERN void buf_flush_relocate_on_flush_list( /*=============================*/ buf_page_t* bpage, /*!< in/out: control block being moved */ buf_page_t* dpage) /*!< in/out: destination block */ { buf_page_t* prev; buf_page_t* prev_b = NULL; ut_ad(buf_pool_mutex_own()); ut_ad(mutex_own(buf_page_get_mutex(bpage))); ut_ad(bpage->in_flush_list); ut_ad(dpage->in_flush_list); /* If recovery is active we must swap the control blocks in the flush_rbt as well. */ if (UNIV_LIKELY_NULL(buf_pool->flush_rbt)) { buf_flush_delete_from_flush_rbt(bpage); prev_b = buf_flush_insert_in_flush_rbt(dpage); } /* Must be done after we have removed it from the flush_rbt because we assert on in_flush_list in comparison function. */ ut_d(bpage->in_flush_list = FALSE); prev = UT_LIST_GET_PREV(list, bpage); UT_LIST_REMOVE(list, buf_pool->flush_list, bpage); if (prev) { ut_ad(prev->in_flush_list); UT_LIST_INSERT_AFTER( list, buf_pool->flush_list, prev, dpage); } else { UT_LIST_ADD_FIRST( list, buf_pool->flush_list, dpage); } /* Just an extra check. Previous in flush_list should be the same control block as in flush_rbt. */ ut_a(!buf_pool->flush_rbt || prev_b == prev); #if defined UNIV_DEBUG || defined UNIV_BUF_DEBUG ut_a(buf_flush_validate_low()); #endif /* UNIV_DEBUG || UNIV_BUF_DEBUG */ } /********************************************************************//** Updates the flush system data structures when a write is completed. */ UNIV_INTERN void buf_flush_write_complete( /*=====================*/ buf_page_t* bpage) /*!< in: pointer to the block in question */ { enum buf_flush flush_type; ut_ad(bpage); buf_flush_remove(bpage); flush_type = buf_page_get_flush_type(bpage); buf_pool->n_flush[flush_type]--; if (flush_type == BUF_FLUSH_LRU) { /* Put the block to the end of the LRU list to wait to be moved to the free list */ buf_LRU_make_block_old(bpage); buf_pool->LRU_flush_ended++; } /* ib_logger(ib_stream, "n pending flush %lu\n", buf_pool->n_flush[flush_type]); */ if ((buf_pool->n_flush[flush_type] == 0) && (buf_pool->init_flush[flush_type] == FALSE)) { /* The running flush batch has ended */ os_event_set(buf_pool->no_flush[flush_type]); } } /********************************************************************//** Flush a batch of writes to the datafiles that have already been written by the OS. */ static void buf_flush_sync_datafiles(void) /*==========================*/ { /* Wake possible simulated aio thread to actually post the writes to the operating system */ os_aio_simulated_wake_handler_threads(); /* Wait that all async writes to tablespaces have been posted to the OS */ os_aio_wait_until_no_pending_writes(); /* Now we flush the data to disk (for example, with fsync) */ fil_flush_file_spaces(FIL_TABLESPACE); return; } /********************************************************************//** Flushes possible buffered writes from the doublewrite memory buffer to disk, and also wakes up the aio thread if simulated aio is used. It is very important to call this function after a batch of writes has been posted, and also when we may have to wait for a page latch! Otherwise a deadlock of threads can occur. */ static void buf_flush_buffered_writes(void) /*===========================*/ { byte* write_buf; ulint len; ulint len2; ulint i; if (!srv_use_doublewrite_buf || trx_doublewrite == NULL) { /* Sync the writes to the disk. */ buf_flush_sync_datafiles(); return; } mutex_enter(&(trx_doublewrite->mutex)); /* Write first to doublewrite buffer blocks. We use synchronous aio and thus know that file write has been completed when the control returns. */ if (trx_doublewrite->first_free == 0) { mutex_exit(&(trx_doublewrite->mutex)); return; } for (i = 0; i < trx_doublewrite->first_free; i++) { const buf_block_t* block; block = (buf_block_t*) trx_doublewrite->buf_block_arr[i]; if (buf_block_get_state(block) != BUF_BLOCK_FILE_PAGE || block->page.zip.data ) { /* No simple validate for compressed pages exists. */ continue; } if (UNIV_UNLIKELY (memcmp(block->frame + (FIL_PAGE_LSN + 4), block->frame + (UNIV_PAGE_SIZE - FIL_PAGE_END_LSN_OLD_CHKSUM + 4), 4))) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: ERROR: The page to be written" " seems corrupt!\n" "InnoDB: The lsn fields do not match!" " Noticed in the buffer pool\n" "InnoDB: before posting to the" " doublewrite buffer.\n"); } if (!block->check_index_page_at_flush) { } else if (page_is_comp(block->frame)) { if (UNIV_UNLIKELY (!page_simple_validate_new(block->frame))) { corrupted_page: buf_page_print(block->frame, 0); ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Apparent corruption of an" " index page n:o %lu in space %lu\n" "InnoDB: to be written to data file." " We intentionally crash server\n" "InnoDB: to prevent corrupt data" " from ending up in data\n" "InnoDB: files.\n", (ulong) buf_block_get_page_no(block), (ulong) buf_block_get_space(block)); ut_error; } } else if (UNIV_UNLIKELY (!page_simple_validate_old(block->frame))) { goto corrupted_page; } } /* increment the doublewrite flushed pages counter */ srv_dblwr_pages_written+= trx_doublewrite->first_free; srv_dblwr_writes++; len = ut_min(TRX_SYS_DOUBLEWRITE_BLOCK_SIZE, trx_doublewrite->first_free) * UNIV_PAGE_SIZE; write_buf = trx_doublewrite->write_buf; i = 0; fil_io(OS_FILE_WRITE, TRUE, TRX_SYS_SPACE, 0, trx_doublewrite->block1, 0, len, (void*) write_buf, NULL); for (len2 = 0; len2 + UNIV_PAGE_SIZE <= len; len2 += UNIV_PAGE_SIZE, i++) { const buf_block_t* block = (buf_block_t*) trx_doublewrite->buf_block_arr[i]; if ( UNIV_LIKELY(!block->page.zip.data) && UNIV_LIKELY(buf_block_get_state(block) == BUF_BLOCK_FILE_PAGE) && UNIV_UNLIKELY (memcmp(write_buf + len2 + (FIL_PAGE_LSN + 4), write_buf + len2 + (UNIV_PAGE_SIZE - FIL_PAGE_END_LSN_OLD_CHKSUM + 4), 4))) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: ERROR: The page to be written" " seems corrupt!\n" "InnoDB: The lsn fields do not match!" " Noticed in the doublewrite block1.\n"); } } if (trx_doublewrite->first_free <= TRX_SYS_DOUBLEWRITE_BLOCK_SIZE) { goto flush; } len = (trx_doublewrite->first_free - TRX_SYS_DOUBLEWRITE_BLOCK_SIZE) * UNIV_PAGE_SIZE; write_buf = trx_doublewrite->write_buf + TRX_SYS_DOUBLEWRITE_BLOCK_SIZE * UNIV_PAGE_SIZE; ut_ad(i == TRX_SYS_DOUBLEWRITE_BLOCK_SIZE); fil_io(OS_FILE_WRITE, TRUE, TRX_SYS_SPACE, 0, trx_doublewrite->block2, 0, len, (void*) write_buf, NULL); for (len2 = 0; len2 + UNIV_PAGE_SIZE <= len; len2 += UNIV_PAGE_SIZE, i++) { const buf_block_t* block = (buf_block_t*) trx_doublewrite->buf_block_arr[i]; if ( UNIV_LIKELY(!block->page.zip.data) && UNIV_LIKELY(buf_block_get_state(block) == BUF_BLOCK_FILE_PAGE) && UNIV_UNLIKELY (memcmp(write_buf + len2 + (FIL_PAGE_LSN + 4), write_buf + len2 + (UNIV_PAGE_SIZE - FIL_PAGE_END_LSN_OLD_CHKSUM + 4), 4))) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: ERROR: The page to be" " written seems corrupt!\n" "InnoDB: The lsn fields do not match!" " Noticed in" " the doublewrite block2.\n"); } } flush: /* Now flush the doublewrite buffer data to disk */ fil_flush(TRX_SYS_SPACE); /* We know that the writes have been flushed to disk now and in recovery we will find them in the doublewrite buffer blocks. Next do the writes to the intended positions. */ for (i = 0; i < trx_doublewrite->first_free; i++) { const buf_block_t* block = (buf_block_t*) trx_doublewrite->buf_block_arr[i]; ut_a(buf_page_in_file(&block->page)); if (UNIV_LIKELY_NULL(block->page.zip.data)) { fil_io(OS_FILE_WRITE | OS_AIO_SIMULATED_WAKE_LATER, FALSE, buf_page_get_space(&block->page), buf_page_get_zip_size(&block->page), buf_page_get_page_no(&block->page), 0, buf_page_get_zip_size(&block->page), (void*)block->page.zip.data, (void*)block); /* Increment the counter of I/O operations used for selecting LRU policy. */ buf_LRU_stat_inc_io(); continue; } ut_a(buf_block_get_state(block) == BUF_BLOCK_FILE_PAGE); if (UNIV_UNLIKELY(memcmp(block->frame + (FIL_PAGE_LSN + 4), block->frame + (UNIV_PAGE_SIZE - FIL_PAGE_END_LSN_OLD_CHKSUM + 4), 4))) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: ERROR: The page to be written" " seems corrupt!\n" "InnoDB: The lsn fields do not match!" " Noticed in the buffer pool\n" "InnoDB: after posting and flushing" " the doublewrite buffer.\n" "InnoDB: Page buf fix count %lu," " io fix %lu, state %lu\n", (ulong)block->page.buf_fix_count, (ulong)buf_block_get_io_fix(block), (ulong)buf_block_get_state(block)); } fil_io(OS_FILE_WRITE | OS_AIO_SIMULATED_WAKE_LATER, FALSE, buf_block_get_space(block), 0, buf_block_get_page_no(block), 0, UNIV_PAGE_SIZE, (void*)block->frame, (void*)block); /* Increment the counter of I/O operations used for selecting LRU policy. */ buf_LRU_stat_inc_io(); } /* Sync the writes to the disk. */ buf_flush_sync_datafiles(); /* We can now reuse the doublewrite memory buffer: */ trx_doublewrite->first_free = 0; mutex_exit(&(trx_doublewrite->mutex)); } /********************************************************************//** Posts a buffer page for writing. If the doublewrite memory buffer is full, calls buf_flush_buffered_writes and waits for for free space to appear. */ static void buf_flush_post_to_doublewrite_buf( /*==============================*/ buf_page_t* bpage) /*!< in: buffer block to write */ { ulint zip_size; try_again: mutex_enter(&(trx_doublewrite->mutex)); ut_a(buf_page_in_file(bpage)); if (trx_doublewrite->first_free >= 2 * TRX_SYS_DOUBLEWRITE_BLOCK_SIZE) { mutex_exit(&(trx_doublewrite->mutex)); buf_flush_buffered_writes(); goto try_again; } zip_size = buf_page_get_zip_size(bpage); if (UNIV_UNLIKELY(zip_size)) { /* Copy the compressed page and clear the rest. */ memcpy(trx_doublewrite->write_buf + UNIV_PAGE_SIZE * trx_doublewrite->first_free, bpage->zip.data, zip_size); memset(trx_doublewrite->write_buf + UNIV_PAGE_SIZE * trx_doublewrite->first_free + zip_size, 0, UNIV_PAGE_SIZE - zip_size); } else { ut_a(buf_page_get_state(bpage) == BUF_BLOCK_FILE_PAGE); memcpy(trx_doublewrite->write_buf + UNIV_PAGE_SIZE * trx_doublewrite->first_free, ((buf_block_t*) bpage)->frame, UNIV_PAGE_SIZE); } trx_doublewrite->buf_block_arr[trx_doublewrite->first_free] = bpage; trx_doublewrite->first_free++; if (trx_doublewrite->first_free >= 2 * TRX_SYS_DOUBLEWRITE_BLOCK_SIZE) { mutex_exit(&(trx_doublewrite->mutex)); buf_flush_buffered_writes(); return; } mutex_exit(&(trx_doublewrite->mutex)); } #endif /* !UNIV_HOTBACKUP */ /********************************************************************//** Initializes a page for writing to the tablespace. */ UNIV_INTERN void buf_flush_init_for_writing( /*=======================*/ byte* page, /*!< in/out: page */ void* page_zip_, /*!< in/out: compressed page, or NULL */ ib_uint64_t newest_lsn) /*!< in: newest modification lsn to the page */ { ut_ad(page); if (page_zip_) { page_zip_des_t* page_zip = page_zip_; ulint zip_size = page_zip_get_size(page_zip); ut_ad(zip_size); ut_ad(ut_is_2pow(zip_size)); ut_ad(zip_size <= UNIV_PAGE_SIZE); switch (UNIV_EXPECT(fil_page_get_type(page), FIL_PAGE_INDEX)) { case FIL_PAGE_TYPE_ALLOCATED: case FIL_PAGE_INODE: case FIL_PAGE_IBUF_BITMAP: case FIL_PAGE_TYPE_FSP_HDR: case FIL_PAGE_TYPE_XDES: /* These are essentially uncompressed pages. */ memcpy(page_zip->data, page, zip_size); /* fall through */ case FIL_PAGE_TYPE_ZBLOB: case FIL_PAGE_TYPE_ZBLOB2: case FIL_PAGE_INDEX: mach_write_ull(page_zip->data + FIL_PAGE_LSN, newest_lsn); memset(page_zip->data + FIL_PAGE_FILE_FLUSH_LSN, 0, 8); mach_write_to_4(page_zip->data + FIL_PAGE_SPACE_OR_CHKSUM, srv_use_checksums ? page_zip_calc_checksum( page_zip->data, zip_size) : BUF_NO_CHECKSUM_MAGIC); return; } ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: ERROR: The compressed page to be written" " seems corrupt:"); ut_print_buf(ib_stream, page, zip_size); ib_logger(ib_stream, "\nInnoDB: Possibly older version of the page:"); ut_print_buf(ib_stream, page_zip->data, zip_size); ib_logger(ib_stream, "\n"); ut_error; } /* Write the newest modification lsn to the page header and trailer */ mach_write_ull(page + FIL_PAGE_LSN, newest_lsn); mach_write_ull(page + UNIV_PAGE_SIZE - FIL_PAGE_END_LSN_OLD_CHKSUM, newest_lsn); /* Store the new formula checksum */ mach_write_to_4(page + FIL_PAGE_SPACE_OR_CHKSUM, srv_use_checksums ? buf_calc_page_new_checksum(page) : BUF_NO_CHECKSUM_MAGIC); /* We overwrite the first 4 bytes of the end lsn field to store the old formula checksum. Since it depends also on the field FIL_PAGE_SPACE_OR_CHKSUM, it has to be calculated after storing the new formula checksum. */ mach_write_to_4(page + UNIV_PAGE_SIZE - FIL_PAGE_END_LSN_OLD_CHKSUM, srv_use_checksums ? buf_calc_page_old_checksum(page) : BUF_NO_CHECKSUM_MAGIC); } #ifndef UNIV_HOTBACKUP /********************************************************************//** Does an asynchronous write of a buffer page. NOTE: in simulated aio and also when the doublewrite buffer is used, we must call buf_flush_buffered_writes after we have posted a batch of writes! */ static void buf_flush_write_block_low( /*======================*/ buf_page_t* bpage) /*!< in: buffer block to write */ { ulint zip_size = buf_page_get_zip_size(bpage); page_t* frame = NULL; #ifdef UNIV_LOG_DEBUG static ibool univ_log_debug_warned; #endif /* UNIV_LOG_DEBUG */ ut_ad(buf_page_in_file(bpage)); /* We are not holding buf_pool_mutex or block_mutex here. Nevertheless, it is safe to access bpage, because it is io_fixed and oldest_modification != 0. Thus, it cannot be relocated in the buffer pool or removed from flush_list or LRU_list. */ ut_ad(!buf_pool_mutex_own()); ut_ad(!mutex_own(buf_page_get_mutex(bpage))); ut_ad(buf_page_get_io_fix(bpage) == BUF_IO_WRITE); ut_ad(bpage->oldest_modification != 0); #ifdef UNIV_IBUF_COUNT_DEBUG ut_a(ibuf_count_get(bpage->space, bpage->offset) == 0); #endif ut_ad(bpage->newest_modification != 0); #ifdef UNIV_LOG_DEBUG if (!univ_log_debug_warned) { univ_log_debug_warned = TRUE; ib_logger(ib_stream, "Warning: cannot force log to disk if" " UNIV_LOG_DEBUG is defined!\n" "Crash recovery will not work!\n"); } #else /* Force the log to the disk before writing the modified block */ log_write_up_to(bpage->newest_modification, LOG_WAIT_ALL_GROUPS, TRUE); #endif switch (buf_page_get_state(bpage)) { case BUF_BLOCK_ZIP_FREE: case BUF_BLOCK_ZIP_PAGE: /* The page should be dirty. */ case BUF_BLOCK_NOT_USED: case BUF_BLOCK_READY_FOR_USE: case BUF_BLOCK_MEMORY: case BUF_BLOCK_REMOVE_HASH: ut_error; break; case BUF_BLOCK_ZIP_DIRTY: frame = bpage->zip.data; if (UNIV_LIKELY(srv_use_checksums)) { ut_a(mach_read_from_4(frame + FIL_PAGE_SPACE_OR_CHKSUM) == page_zip_calc_checksum(frame, zip_size)); } mach_write_ull(frame + FIL_PAGE_LSN, bpage->newest_modification); memset(frame + FIL_PAGE_FILE_FLUSH_LSN, 0, 8); break; case BUF_BLOCK_FILE_PAGE: frame = bpage->zip.data; if (!frame) { frame = ((buf_block_t*) bpage)->frame; } buf_flush_init_for_writing(((buf_block_t*) bpage)->frame, bpage->zip.data ? &bpage->zip : NULL, bpage->newest_modification); break; } if (!srv_use_doublewrite_buf || !trx_doublewrite) { fil_io(OS_FILE_WRITE | OS_AIO_SIMULATED_WAKE_LATER, FALSE, buf_page_get_space(bpage), zip_size, buf_page_get_page_no(bpage), 0, zip_size ? zip_size : UNIV_PAGE_SIZE, frame, bpage); } else { buf_flush_post_to_doublewrite_buf(bpage); } } /********************************************************************//** Writes a flushable page asynchronously from the buffer pool to a file. NOTE: in simulated aio we must call os_aio_simulated_wake_handler_threads after we have posted a batch of writes! NOTE: buf_pool_mutex and buf_page_get_mutex(bpage) must be held upon entering this function, and they will be released by this function. */ static void buf_flush_page( /*===========*/ buf_page_t* bpage, /*!< in: buffer control block */ enum buf_flush flush_type) /*!< in: BUF_FLUSH_LRU or BUF_FLUSH_LIST */ { mutex_t* block_mutex; ibool is_uncompressed; ut_ad(flush_type == BUF_FLUSH_LRU || flush_type == BUF_FLUSH_LIST); ut_ad(buf_pool_mutex_own()); ut_ad(buf_page_in_file(bpage)); block_mutex = buf_page_get_mutex(bpage); ut_ad(mutex_own(block_mutex)); ut_ad(buf_flush_ready_for_flush(bpage, flush_type)); buf_page_set_io_fix(bpage, BUF_IO_WRITE); buf_page_set_flush_type(bpage, flush_type); if (buf_pool->n_flush[flush_type] == 0) { os_event_reset(buf_pool->no_flush[flush_type]); } buf_pool->n_flush[flush_type]++; is_uncompressed = (buf_page_get_state(bpage) == BUF_BLOCK_FILE_PAGE); ut_ad(is_uncompressed == (block_mutex != &buf_pool_zip_mutex)); switch (flush_type) { ibool is_s_latched; case BUF_FLUSH_LIST: /* If the simulated aio thread is not running, we must not wait for any latch, as we may end up in a deadlock: if buf_fix_count == 0, then we know we need not wait */ is_s_latched = (bpage->buf_fix_count == 0); if (is_s_latched && is_uncompressed) { rw_lock_s_lock_gen(&((buf_block_t*) bpage)->lock, BUF_IO_WRITE); } mutex_exit(block_mutex); buf_pool_mutex_exit(); /* Even though bpage is not protected by any mutex at this point, it is safe to access bpage, because it is io_fixed and oldest_modification != 0. Thus, it cannot be relocated in the buffer pool or removed from flush_list or LRU_list. */ if (!is_s_latched) { buf_flush_buffered_writes(); if (is_uncompressed) { rw_lock_s_lock_gen(&((buf_block_t*) bpage) ->lock, BUF_IO_WRITE); } } break; case BUF_FLUSH_LRU: /* VERY IMPORTANT: Because any thread may call the LRU flush, even when owning locks on pages, to avoid deadlocks, we must make sure that the s-lock is acquired on the page without waiting: this is accomplished because buf_flush_ready_for_flush() must hold, and that requires the page not to be bufferfixed. */ if (is_uncompressed) { rw_lock_s_lock_gen(&((buf_block_t*) bpage)->lock, BUF_IO_WRITE); } /* Note that the s-latch is acquired before releasing the buf_pool mutex: this ensures that the latch is acquired immediately. */ mutex_exit(block_mutex); buf_pool_mutex_exit(); break; default: ut_error; } /* Even though bpage is not protected by any mutex at this point, it is safe to access bpage, because it is io_fixed and oldest_modification != 0. Thus, it cannot be relocated in the buffer pool or removed from flush_list or LRU_list. */ #ifdef UNIV_DEBUG if (buf_debug_prints) { ib_logger(ib_stream, "Flushing %u space %u page %u\n", flush_type, bpage->space, bpage->offset); } #endif /* UNIV_DEBUG */ buf_flush_write_block_low(bpage); } /***********************************************************//** Flushes to disk all flushable pages within the flush area. @return number of pages flushed */ static ulint buf_flush_try_neighbors( /*====================*/ ulint space, /*!< in: space id */ ulint offset, /*!< in: page offset */ enum buf_flush flush_type) /*!< in: BUF_FLUSH_LRU or BUF_FLUSH_LIST */ { buf_page_t* bpage; ulint low, high; ulint count = 0; ulint i; ut_ad(flush_type == BUF_FLUSH_LRU || flush_type == BUF_FLUSH_LIST); if (UT_LIST_GET_LEN(buf_pool->LRU) < BUF_LRU_OLD_MIN_LEN) { /* If there is little space, it is better not to flush any block except from the end of the LRU list */ low = offset; high = offset + 1; } else { /* When flushed, dirty blocks are searched in neighborhoods of this size, and flushed along with the original page. */ ulint buf_flush_area = ut_min(BUF_READ_AHEAD_AREA, buf_pool->curr_size / 16); low = (offset / buf_flush_area) * buf_flush_area; high = (offset / buf_flush_area + 1) * buf_flush_area; } /* ib_logger(ib_stream, "Flush area: low %lu high %lu\n", low, high); */ if (high > fil_space_get_size(space)) { high = fil_space_get_size(space); } buf_pool_mutex_enter(); for (i = low; i < high; i++) { bpage = buf_page_hash_get(space, i); if (!bpage) { continue; } ut_a(buf_page_in_file(bpage)); /* We avoid flushing 'non-old' blocks in an LRU flush, because the flushed blocks are soon freed */ if (flush_type != BUF_FLUSH_LRU || i == offset || buf_page_is_old(bpage)) { mutex_t* block_mutex = buf_page_get_mutex(bpage); mutex_enter(block_mutex); if (buf_flush_ready_for_flush(bpage, flush_type) && (i == offset || !bpage->buf_fix_count)) { /* We only try to flush those neighbors != offset where the buf fix count is zero, as we then know that we probably can latch the page without a semaphore wait. Semaphore waits are expensive because we must flush the doublewrite buffer before we start waiting. */ buf_flush_page(bpage, flush_type); ut_ad(!mutex_own(block_mutex)); count++; buf_pool_mutex_enter(); } else { mutex_exit(block_mutex); } } } buf_pool_mutex_exit(); return(count); } /*******************************************************************//** This utility flushes dirty blocks from the end of the LRU list or flush_list. NOTE 1: in the case of an LRU flush the calling thread may own latches to pages: to avoid deadlocks, this function must be written so that it cannot end up waiting for these latches! NOTE 2: in the case of a flush list flush, the calling thread is not allowed to own any latches on pages! @return number of blocks for which the write request was queued; ULINT_UNDEFINED if there was a flush of the same type already running */ UNIV_INTERN ulint buf_flush_batch( /*============*/ enum buf_flush flush_type, /*!< in: BUF_FLUSH_LRU or BUF_FLUSH_LIST; if BUF_FLUSH_LIST, then the caller must not own any latches on pages */ ulint min_n, /*!< in: wished minimum mumber of blocks flushed (it is not guaranteed that the actual number is that big, though) */ ib_uint64_t lsn_limit) /*!< in the case BUF_FLUSH_LIST all blocks whose oldest_modification is smaller than this should be flushed (if their number does not exceed min_n), otherwise ignored */ { buf_page_t* bpage; ulint page_count = 0; ulint old_page_count; ulint space; ulint offset; ut_ad((flush_type == BUF_FLUSH_LRU) || (flush_type == BUF_FLUSH_LIST)); #ifdef UNIV_SYNC_DEBUG ut_ad((flush_type != BUF_FLUSH_LIST) || sync_thread_levels_empty_gen(TRUE)); #endif /* UNIV_SYNC_DEBUG */ buf_pool_mutex_enter(); if ((buf_pool->n_flush[flush_type] > 0) || (buf_pool->init_flush[flush_type] == TRUE)) { /* There is already a flush batch of the same type running */ buf_pool_mutex_exit(); return(ULINT_UNDEFINED); } buf_pool->init_flush[flush_type] = TRUE; for (;;) { flush_next: /* If we have flushed enough, leave the loop */ if (page_count >= min_n) { break; } /* Start from the end of the list looking for a suitable block to be flushed. */ if (flush_type == BUF_FLUSH_LRU) { bpage = UT_LIST_GET_LAST(buf_pool->LRU); } else { ut_ad(flush_type == BUF_FLUSH_LIST); bpage = UT_LIST_GET_LAST(buf_pool->flush_list); if (!bpage || bpage->oldest_modification >= lsn_limit) { /* We have flushed enough */ break; } ut_ad(bpage->in_flush_list); } /* Note that after finding a single flushable page, we try to flush also all its neighbors, and after that start from the END of the LRU list or flush list again: the list may change during the flushing and we cannot safely preserve within this function a pointer to a block in the list! */ do { mutex_t*block_mutex = buf_page_get_mutex(bpage); ibool ready; ut_a(buf_page_in_file(bpage)); mutex_enter(block_mutex); ready = buf_flush_ready_for_flush(bpage, flush_type); mutex_exit(block_mutex); if (ready) { space = buf_page_get_space(bpage); offset = buf_page_get_page_no(bpage); buf_pool_mutex_exit(); old_page_count = page_count; /* Try to flush also all the neighbors */ page_count += buf_flush_try_neighbors( space, offset, flush_type); /* ib_logger(ib_stream, "Flush type %lu, page no %lu, neighb %lu\n", flush_type, offset, page_count - old_page_count); */ buf_pool_mutex_enter(); goto flush_next; } else if (flush_type == BUF_FLUSH_LRU) { bpage = UT_LIST_GET_PREV(LRU, bpage); } else { ut_ad(flush_type == BUF_FLUSH_LIST); bpage = UT_LIST_GET_PREV(list, bpage); ut_ad(!bpage || bpage->in_flush_list); } } while (bpage != NULL); /* If we could not find anything to flush, leave the loop */ break; } buf_pool->init_flush[flush_type] = FALSE; if (buf_pool->n_flush[flush_type] == 0) { /* The running flush batch has ended */ os_event_set(buf_pool->no_flush[flush_type]); } buf_pool_mutex_exit(); buf_flush_buffered_writes(); #ifdef UNIV_DEBUG if (buf_debug_prints && page_count > 0) { ut_a(flush_type == BUF_FLUSH_LRU || flush_type == BUF_FLUSH_LIST); ib_logger(ib_stream, flush_type == BUF_FLUSH_LRU ? "Flushed %lu pages in LRU flush\n" : "Flushed %lu pages in flush list flush\n", (ulong) page_count); } #endif /* UNIV_DEBUG */ srv_buf_pool_flushed += page_count; /* We keep track of all flushes happening as part of LRU flush. When estimating the desired rate at which flush_list should be flushed we factor in this value. */ if (flush_type == BUF_FLUSH_LRU) { buf_lru_flush_page_count += page_count; } return(page_count); } /******************************************************************//** Waits until a flush batch of the given type ends */ UNIV_INTERN void buf_flush_wait_batch_end( /*=====================*/ enum buf_flush type) /*!< in: BUF_FLUSH_LRU or BUF_FLUSH_LIST */ { ut_ad((type == BUF_FLUSH_LRU) || (type == BUF_FLUSH_LIST)); os_event_wait(buf_pool->no_flush[type]); } /******************************************************************//** Gives a recommendation of how many blocks should be flushed to establish a big enough margin of replaceable blocks near the end of the LRU list and in the free list. @return number of blocks which should be flushed from the end of the LRU list */ static ulint buf_flush_LRU_recommendation(void) /*==============================*/ { buf_page_t* bpage; ulint n_replaceable; ulint distance = 0; buf_pool_mutex_enter(); n_replaceable = UT_LIST_GET_LEN(buf_pool->free); bpage = UT_LIST_GET_LAST(buf_pool->LRU); while ((bpage != NULL) && (n_replaceable < BUF_FLUSH_FREE_BLOCK_MARGIN + BUF_FLUSH_EXTRA_MARGIN) && (distance < BUF_LRU_FREE_SEARCH_LEN)) { mutex_t* block_mutex = buf_page_get_mutex(bpage); mutex_enter(block_mutex); if (buf_flush_ready_for_replace(bpage)) { n_replaceable++; } mutex_exit(block_mutex); distance++; bpage = UT_LIST_GET_PREV(LRU, bpage); } buf_pool_mutex_exit(); if (n_replaceable >= BUF_FLUSH_FREE_BLOCK_MARGIN) { return(0); } return(BUF_FLUSH_FREE_BLOCK_MARGIN + BUF_FLUSH_EXTRA_MARGIN - n_replaceable); } /*********************************************************************//** Flushes pages from the end of the LRU list if there is too small a margin of replaceable pages there or in the free list. VERY IMPORTANT: this function is called also by threads which have locks on pages. To avoid deadlocks, we flush only pages such that the s-lock required for flushing can be acquired immediately, without waiting. */ UNIV_INTERN void buf_flush_free_margin(void) /*=======================*/ { ulint n_to_flush; ulint n_flushed; n_to_flush = buf_flush_LRU_recommendation(); if (n_to_flush > 0) { n_flushed = buf_flush_batch(BUF_FLUSH_LRU, n_to_flush, 0); if (n_flushed == ULINT_UNDEFINED) { /* There was an LRU type flush batch already running; let us wait for it to end */ buf_flush_wait_batch_end(BUF_FLUSH_LRU); } } } /********************************************************************* Update the historical stats that we are collecting for flush rate heuristics at the end of each interval. Flush rate heuristic depends on (a) rate of redo log generation and (b) the rate at which LRU flush is happening. */ UNIV_INTERN void buf_flush_stat_update(void) /*=======================*/ { buf_flush_stat_t* item; ib_uint64_t lsn_diff; ib_uint64_t lsn; ulint n_flushed; lsn = log_get_lsn(); if (buf_flush_stat_cur.redo == 0) { /* First time around. Just update the current LSN and return. */ buf_flush_stat_cur.redo = lsn; return; } item = &buf_flush_stat_arr[buf_flush_stat_arr_ind]; /* values for this interval */ lsn_diff = lsn - buf_flush_stat_cur.redo; n_flushed = buf_lru_flush_page_count - buf_flush_stat_cur.n_flushed; /* add the current value and subtract the obsolete entry. */ buf_flush_stat_sum.redo += lsn_diff - item->redo; buf_flush_stat_sum.n_flushed += n_flushed - item->n_flushed; /* put current entry in the array. */ item->redo = lsn_diff; item->n_flushed = n_flushed; /* update the index */ buf_flush_stat_arr_ind++; buf_flush_stat_arr_ind %= BUF_FLUSH_STAT_N_INTERVAL; /* reset the current entry. */ buf_flush_stat_cur.redo = lsn; buf_flush_stat_cur.n_flushed = buf_lru_flush_page_count; } /********************************************************************* Determines the fraction of dirty pages that need to be flushed based on the speed at which we generate redo log. Note that if redo log is generated at a significant rate without corresponding increase in the number of dirty pages (for example, an in-memory workload) it can cause IO bursts of flushing. This function implements heuristics to avoid this burstiness. @return number of dirty pages to be flushed / second */ UNIV_INTERN ulint buf_flush_get_desired_flush_rate(void) /*==================================*/ { ulint redo_avg; ulint lru_flush_avg; ulint n_dirty; ulint n_flush_req; lint rate; ib_uint64_t lsn = log_get_lsn(); ulint log_capacity = log_get_capacity(); /* log_capacity should never be zero after the initialization of log subsystem. */ ut_ad(log_capacity != 0); /* Get total number of dirty pages. It is OK to access flush_list without holding any mtex as we are using this only for heuristics. */ n_dirty = UT_LIST_GET_LEN(buf_pool->flush_list); /* An overflow can happen if we generate more than 2^32 bytes of redo in this interval i.e.: 4G of redo in 1 second. We can safely consider this as infinity because if we ever come close to 4G we'll start a synchronous flush of dirty pages. */ /* redo_avg below is average at which redo is generated in past BUF_FLUSH_STAT_N_INTERVAL + redo generated in the current interval. */ redo_avg = (ulint) (buf_flush_stat_sum.redo / BUF_FLUSH_STAT_N_INTERVAL + (lsn - buf_flush_stat_cur.redo)); /* An overflow can happen possibly if we flush more than 2^32 pages in BUF_FLUSH_STAT_N_INTERVAL. This is a very very unlikely scenario. Even when this happens it means that our flush rate will be off the mark. It won't affect correctness of any subsystem. */ /* lru_flush_avg below is rate at which pages are flushed as part of LRU flush in past BUF_FLUSH_STAT_N_INTERVAL + the number of pages flushed in the current interval. */ lru_flush_avg = buf_flush_stat_sum.n_flushed / BUF_FLUSH_STAT_N_INTERVAL + (buf_lru_flush_page_count - buf_flush_stat_cur.n_flushed); n_flush_req = (n_dirty * redo_avg) / log_capacity; /* The number of pages that we want to flush from the flush list is the difference between the required rate and the number of pages that we are historically flushing from the LRU list */ rate = n_flush_req - lru_flush_avg; return(rate > 0 ? (ulint) rate : 0); } #if defined UNIV_DEBUG || defined UNIV_BUF_DEBUG /******************************************************************//** Validates the flush list. @return TRUE if ok */ static ibool buf_flush_validate_low(void) /*========================*/ { buf_page_t* bpage; const ib_rbt_node_t* rnode = NULL; UT_LIST_VALIDATE(list, buf_page_t, buf_pool->flush_list, ut_ad(ut_list_node_313->in_flush_list)); bpage = UT_LIST_GET_FIRST(buf_pool->flush_list); /* If we are in recovery mode i.e.: flush_rbt != NULL then each block in the flush_list must also be present in the flush_rbt. */ if (UNIV_LIKELY_NULL(buf_pool->flush_rbt)) { rnode = rbt_first(buf_pool->flush_rbt); } while (bpage != NULL) { const ib_uint64_t om = bpage->oldest_modification; ut_ad(bpage->in_flush_list); ut_a(buf_page_in_file(bpage)); ut_a(om > 0); if (UNIV_LIKELY_NULL(buf_pool->flush_rbt)) { ut_a(rnode); buf_page_t* rpage = *rbt_value(buf_page_t*, rnode); ut_a(rpage); ut_a(rpage == bpage); rnode = rbt_next(buf_pool->flush_rbt, rnode); } bpage = UT_LIST_GET_NEXT(list, bpage); ut_a(!bpage || om >= bpage->oldest_modification); } /* By this time we must have exhausted the traversal of flush_rbt (if active) as well. */ ut_a(rnode == NULL); return(TRUE); } /******************************************************************//** Validates the flush list. @return TRUE if ok */ UNIV_INTERN ibool buf_flush_validate(void) /*====================*/ { ibool ret; buf_pool_mutex_enter(); ret = buf_flush_validate_low(); buf_pool_mutex_exit(); return(ret); } #endif /* UNIV_DEBUG || UNIV_BUF_DEBUG */ #endif /* !UNIV_HOTBACKUP */ haildb-2.3.2/buf/buf0buf.c0000644000175000017500000033541011513177357016122 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2010, Innobase Oy. All Rights Reserved. Copyright (c) 2008, Google Inc. Portions of this file contain modifications contributed and copyrighted by Google, Inc. Those modifications are gratefully acknowledged and are described briefly in the InnoDB documentation. The contributions by Google are incorporated with their permission, and subject to the conditions contained in the file COPYING.Google. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file buf/buf0buf.c The database buffer buf_pool Created 11/5/1995 Heikki Tuuri *******************************************************/ #include "buf0buf.h" #ifdef UNIV_NONINL #include "buf0buf.ic" #endif #include "mem0mem.h" #include "btr0btr.h" #include "fil0fil.h" #ifndef UNIV_HOTBACKUP #include "lock0lock.h" #include "btr0sea.h" #include "ibuf0ibuf.h" #include "trx0undo.h" #include "log0log.h" #endif /* !UNIV_HOTBACKUP */ #include "srv0srv.h" #include "dict0dict.h" #include "log0recv.h" #include "buf0buddy.h" #include "page0zip.h" /* IMPLEMENTATION OF THE BUFFER POOL ================================= Performance improvement: ------------------------ Thread scheduling in NT may be so slow that the OS wait mechanism should not be used even in waiting for disk reads to complete. Rather, we should put waiting query threads to the queue of waiting jobs, and let the OS thread do something useful while the i/o is processed. In this way we could remove most OS thread switches in an i/o-intensive benchmark like TPC-C. A possibility is to put a user space thread library between the database and NT. User space thread libraries might be very fast. SQL Server 7.0 can be configured to use 'fibers' which are lightweight threads in NT. These should be studied. Buffer frames and blocks ------------------------ Following the terminology of Gray and Reuter, we call the memory blocks where file pages are loaded buffer frames. For each buffer frame there is a control block, or shortly, a block, in the buffer control array. The control info which does not need to be stored in the file along with the file page, resides in the control block. Buffer pool struct ------------------ The buffer buf_pool contains a single mutex which protects all the control data structures of the buf_pool. The content of a buffer frame is protected by a separate read-write lock in its control block, though. These locks can be locked and unlocked without owning the buf_pool mutex. The OS events in the buf_pool struct can be waited for without owning the buf_pool mutex. The buf_pool mutex is a hot-spot in main memory, causing a lot of memory bus traffic on multiprocessor systems when processors alternately access the mutex. On our Pentium, the mutex is accessed maybe every 10 microseconds. We gave up the solution to have mutexes for each control block, for instance, because it seemed to be complicated. A solution to reduce mutex contention of the buf_pool mutex is to create a separate mutex for the page hash table. On Pentium, accessing the hash table takes 2 microseconds, about half of the total buf_pool mutex hold time. Control blocks -------------- The control block contains, for instance, the bufferfix count which is incremented when a thread wants a file page to be fixed in a buffer frame. The bufferfix operation does not lock the contents of the frame, however. For this purpose, the control block contains a read-write lock. The buffer frames have to be aligned so that the start memory address of a frame is divisible by the universal page size, which is a power of two. We intend to make the buffer buf_pool size on-line reconfigurable, that is, the buf_pool size can be changed without closing the database. Then the database administarator may adjust it to be bigger at night, for example. The control block array must contain enough control blocks for the maximum buffer buf_pool size which is used in the particular database. If the buf_pool size is cut, we exploit the virtual memory mechanism of the OS, and just refrain from using frames at high addresses. Then the OS can swap them to disk. The control blocks containing file pages are put to a hash table according to the file address of the page. We could speed up the access to an individual page by using "pointer swizzling": we could replace the page references on non-leaf index pages by direct pointers to the page, if it exists in the buf_pool. We could make a separate hash table where we could chain all the page references in non-leaf pages residing in the buf_pool, using the page reference as the hash key, and at the time of reading of a page update the pointers accordingly. Drawbacks of this solution are added complexity and, possibly, extra space required on non-leaf pages for memory pointers. A simpler solution is just to speed up the hash table mechanism in the database, using tables whose size is a power of 2. Lists of blocks --------------- There are several lists of control blocks. The free list (buf_pool->free) contains blocks which are currently not used. The common LRU list contains all the blocks holding a file page except those for which the bufferfix count is non-zero. The pages are in the LRU list roughly in the order of the last access to the page, so that the oldest pages are at the end of the list. We also keep a pointer to near the end of the LRU list, which we can use when we want to artificially age a page in the buf_pool. This is used if we know that some page is not needed again for some time: we insert the block right after the pointer, causing it to be replaced sooner than would noramlly be the case. Currently this aging mechanism is used for read-ahead mechanism of pages, and it can also be used when there is a scan of a full table which cannot fit in the memory. Putting the pages near the of the LRU list, we make sure that most of the buf_pool stays in the main memory, undisturbed. The unzip_LRU list contains a subset of the common LRU list. The blocks on the unzip_LRU list hold a compressed file page and the corresponding uncompressed page frame. A block is in unzip_LRU if and only if the predicate buf_page_belongs_to_unzip_LRU(&block->page) holds. The blocks in unzip_LRU will be in same order as they are in the common LRU list. That is, each manipulation of the common LRU list will result in the same manipulation of the unzip_LRU list. The chain of modified blocks (buf_pool->flush_list) contains the blocks holding file pages that have been modified in the memory but not written to disk yet. The block with the oldest modification which has not yet been written to disk is at the end of the chain. The chain of unmodified compressed blocks (buf_pool->zip_clean) contains the control blocks (buf_page_t) of those compressed pages that are not in buf_pool->flush_list and for which no uncompressed page has been allocated in the buffer pool. The control blocks for uncompressed pages are accessible via buf_block_t objects that are reachable via buf_pool->chunks[]. The chains of free memory blocks (buf_pool->zip_free[]) are used by the buddy allocator (buf0buddy.c) to keep track of currently unused memory blocks of size sizeof(buf_page_t)..UNIV_PAGE_SIZE / 2. These blocks are inside the UNIV_PAGE_SIZE-sized memory blocks of type BUF_BLOCK_MEMORY that the buddy allocator requests from the buffer pool. The buddy allocator is solely used for allocating control blocks for compressed pages (buf_page_t) and compressed page frames. Loading a file page ------------------- First, a victim block for replacement has to be found in the buf_pool. It is taken from the free list or searched for from the end of the LRU-list. An exclusive lock is reserved for the frame, the io_fix field is set in the block fixing the block in buf_pool, and the io-operation for loading the page is queued. The io-handler thread releases the X-lock on the frame and resets the io_fix field when the io operation completes. A thread may request the above operation using the function buf_page_get(). It may then continue to request a lock on the frame. The lock is granted when the io-handler releases the x-lock. Read-ahead ---------- The read-ahead mechanism is intended to be intelligent and isolated from the semantically higher levels of the database index management. From the higher level we only need the information if a file page has a natural successor or predecessor page. On the leaf level of a B-tree index, these are the next and previous pages in the natural order of the pages. Let us first explain the read-ahead mechanism when the leafs of a B-tree are scanned in an ascending or descending order. When a read page is the first time referenced in the buf_pool, the buffer manager checks if it is at the border of a so-called linear read-ahead area. The tablespace is divided into these areas of size 64 blocks, for example. So if the page is at the border of such an area, the read-ahead mechanism checks if all the other blocks in the area have been accessed in an ascending or descending order. If this is the case, the system looks at the natural successor or predecessor of the page, checks if that is at the border of another area, and in this case issues read-requests for all the pages in that area. Maybe we could relax the condition that all the pages in the area have to be accessed: if data is deleted from a table, there may appear holes of unused pages in the area. A different read-ahead mechanism is used when there appears to be a random access pattern to a file. If a new page is referenced in the buf_pool, and several pages of its random access area (for instance, 32 consecutive pages in a tablespace) have recently been referenced, we may predict that the whole area may be needed in the near future, and issue the read requests for the whole area. */ #ifndef UNIV_HOTBACKUP /** Value in microseconds */ static const int WAIT_FOR_READ = 5000; /** Number of attemtps made to read in a page in the buffer pool */ static const ulint BUF_PAGE_READ_MAX_RETRIES = 100; /** The buffer buf_pool of the database */ UNIV_INTERN buf_pool_t* buf_pool = NULL; /** mutex protecting the buffer pool struct and control blocks, except the read-write lock in them */ UNIV_INTERN mutex_t buf_pool_mutex; /** mutex protecting the control blocks of compressed-only pages (of type buf_page_t, not buf_block_t) */ UNIV_INTERN mutex_t buf_pool_zip_mutex; #if defined UNIV_DEBUG || defined UNIV_BUF_DEBUG static ulint buf_dbg_counter = 0; /*!< This is used to insert validation operations in excution in the debug version */ /** Flag to forbid the release of the buffer pool mutex. Protected by buf_pool_mutex. */ UNIV_INTERN ulint buf_pool_mutex_exit_forbidden = 0; #endif /* UNIV_DEBUG || UNIV_BUF_DEBUG */ #ifdef UNIV_DEBUG /** If this is set TRUE, the program prints info whenever read-ahead or flush occurs */ UNIV_INTERN ibool buf_debug_prints = FALSE; #endif /* UNIV_DEBUG */ /** A chunk of buffers. The buffer pool is allocated in chunks. */ struct buf_chunk_struct{ ulint mem_size; /*!< allocated size of the chunk */ ulint size; /*!< size of frames[] and blocks[] */ void* mem; /*!< pointer to the memory area which was allocated for the frames */ buf_block_t* blocks; /*!< array of buffer control blocks */ }; #endif /* !UNIV_HOTBACKUP */ /********************************************************************//** Reset the buffer variables. */ UNIV_INTERN void buf_var_init(void) /*==============*/ { buf_pool = NULL; memset(&buf_pool_mutex, 0x0, sizeof(buf_pool_mutex)); memset(&buf_pool_zip_mutex, 0x0, sizeof(buf_pool_zip_mutex)); #if defined UNIV_DEBUG || defined UNIV_BUF_DEBUG buf_dbg_counter = 0; buf_pool_mutex_exit_forbidden = 0; #endif /* UNIV_DEBUG || UNIV_BUF_DEBUG */ #ifdef UNIV_DEBUG buf_debug_prints = FALSE; #endif /* UNIV_DEBUG */ } /************************************************************************ Calculates a page checksum which is stored to the page when it is written to a file. Note that we must be careful to calculate the same value on 32-bit and 64-bit architectures. @return checksum */ UNIV_INTERN ulint buf_calc_page_new_checksum( /*=======================*/ const byte* page) /*!< in: buffer page */ { ulint checksum; /* Since the field FIL_PAGE_FILE_FLUSH_LSN, and in versions <= 4.1.x ..._ARCH_LOG_NO, are written outside the buffer pool to the first pages of data files, we have to skip them in the page checksum calculation. We must also skip the field FIL_PAGE_SPACE_OR_CHKSUM where the checksum is stored, and also the last 8 bytes of page because there we store the old formula checksum. */ checksum = ut_fold_binary(page + FIL_PAGE_OFFSET, FIL_PAGE_FILE_FLUSH_LSN - FIL_PAGE_OFFSET) + ut_fold_binary(page + FIL_PAGE_DATA, UNIV_PAGE_SIZE - FIL_PAGE_DATA - FIL_PAGE_END_LSN_OLD_CHKSUM); checksum = checksum & 0xFFFFFFFFUL; return(checksum); } /********************************************************************//** In versions < 4.0.14 and < 4.1.1 there was a bug that the checksum only looked at the first few bytes of the page. This calculates that old checksum. NOTE: we must first store the new formula checksum to FIL_PAGE_SPACE_OR_CHKSUM before calculating and storing this old checksum because this takes that field as an input! @return checksum */ UNIV_INTERN ulint buf_calc_page_old_checksum( /*=======================*/ const byte* page) /*!< in: buffer page */ { ulint checksum; checksum = ut_fold_binary(page, FIL_PAGE_FILE_FLUSH_LSN); checksum = checksum & 0xFFFFFFFFUL; return(checksum); } /********************************************************************//** Checks if a page is corrupt. @return TRUE if corrupted */ UNIV_INTERN ibool buf_page_is_corrupted( /*==================*/ const byte* read_buf, /*!< in: a database page */ ulint zip_size) /*!< in: size of compressed page; 0 for uncompressed pages */ { ulint checksum_field; ulint old_checksum_field; if (UNIV_LIKELY(!zip_size) && memcmp(read_buf + FIL_PAGE_LSN + 4, read_buf + UNIV_PAGE_SIZE - FIL_PAGE_END_LSN_OLD_CHKSUM + 4, 4)) { /* Stored log sequence numbers at the start and the end of page do not match */ return(TRUE); } #ifndef UNIV_HOTBACKUP if (recv_lsn_checks_on) { ib_uint64_t current_lsn; if (log_peek_lsn(¤t_lsn) && current_lsn < mach_read_ull(read_buf + FIL_PAGE_LSN)) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Error: page %lu log sequence number" " %llu\n" "InnoDB: is in the future! Current system " "log sequence number %llu.\n" "InnoDB: Your database may be corrupt or " "you may have copied the InnoDB\n" "InnoDB: tablespace but not the InnoDB " "log files. See\n" "InnoDB: the InnoDB website for details\n" "InnoDB: for more information.\n", (ulong) mach_read_from_4(read_buf + FIL_PAGE_OFFSET), mach_read_ull(read_buf + FIL_PAGE_LSN), current_lsn); } } #endif /* If we use checksums validation, make additional check before returning TRUE to ensure that the checksum is not equal to BUF_NO_CHECKSUM_MAGIC which might be stored by InnoDB with checksums disabled. Otherwise, skip checksum calculation and return FALSE */ if (UNIV_LIKELY(srv_use_checksums)) { checksum_field = mach_read_from_4(read_buf + FIL_PAGE_SPACE_OR_CHKSUM); if (UNIV_UNLIKELY(zip_size)) { return(checksum_field != BUF_NO_CHECKSUM_MAGIC && checksum_field != page_zip_calc_checksum(read_buf, zip_size)); } old_checksum_field = mach_read_from_4( read_buf + UNIV_PAGE_SIZE - FIL_PAGE_END_LSN_OLD_CHKSUM); /* There are 2 valid formulas for old_checksum_field: 1. Very old versions of InnoDB only stored 8 byte lsn to the start and the end of the page. 2. Newer InnoDB versions store the old formula checksum there. */ if (old_checksum_field != mach_read_from_4(read_buf + FIL_PAGE_LSN) && old_checksum_field != BUF_NO_CHECKSUM_MAGIC && old_checksum_field != buf_calc_page_old_checksum(read_buf)) { return(TRUE); } /* InnoDB versions < 4.0.14 and < 4.1.1 stored the space id (always equal to 0), to FIL_PAGE_SPACE_OR_CHKSUM */ if (checksum_field != 0 && checksum_field != BUF_NO_CHECKSUM_MAGIC && checksum_field != buf_calc_page_new_checksum(read_buf)) { return(TRUE); } } return(FALSE); } /********************************************************************//** Prints a page to ib_stream. */ UNIV_INTERN void buf_page_print( /*===========*/ const byte* read_buf, /*!< in: a database page */ ulint zip_size) /*!< in: compressed page size, or 0 for uncompressed pages */ { #ifndef UNIV_HOTBACKUP dict_index_t* index; #endif /* !UNIV_HOTBACKUP */ ulint checksum; ulint old_checksum; ulint size = zip_size; if (!size) { size = UNIV_PAGE_SIZE; } ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Page dump in ascii and hex (%lu bytes):\n", (ulong) size); ut_print_buf(ib_stream, read_buf, size); ib_logger(ib_stream, "\nInnoDB: End of page dump\n"); if (zip_size) { /* Print compressed page. */ switch (fil_page_get_type(read_buf)) { case FIL_PAGE_TYPE_ZBLOB: case FIL_PAGE_TYPE_ZBLOB2: checksum = srv_use_checksums ? page_zip_calc_checksum(read_buf, zip_size) : BUF_NO_CHECKSUM_MAGIC; ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Compressed BLOB page" " checksum %lu, stored %lu\n" "InnoDB: Page lsn %lu %lu\n" "InnoDB: Page number (if stored" " to page already) %lu,\n" "InnoDB: space id (if stored" " to page already) %lu\n", (ulong) checksum, (ulong) mach_read_from_4( read_buf + FIL_PAGE_SPACE_OR_CHKSUM), (ulong) mach_read_from_4( read_buf + FIL_PAGE_LSN), (ulong) mach_read_from_4( read_buf + (FIL_PAGE_LSN + 4)), (ulong) mach_read_from_4( read_buf + FIL_PAGE_OFFSET), (ulong) mach_read_from_4( read_buf + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID)); return; default: ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: unknown page type %lu," " assuming FIL_PAGE_INDEX\n", fil_page_get_type(read_buf)); /* fall through */ case FIL_PAGE_INDEX: checksum = srv_use_checksums ? page_zip_calc_checksum(read_buf, zip_size) : BUF_NO_CHECKSUM_MAGIC; ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Compressed page checksum %lu," " stored %lu\n" "InnoDB: Page lsn %lu %lu\n" "InnoDB: Page number (if stored" " to page already) %lu,\n" "InnoDB: space id (if stored" " to page already) %lu\n", (ulong) checksum, (ulong) mach_read_from_4( read_buf + FIL_PAGE_SPACE_OR_CHKSUM), (ulong) mach_read_from_4( read_buf + FIL_PAGE_LSN), (ulong) mach_read_from_4( read_buf + (FIL_PAGE_LSN + 4)), (ulong) mach_read_from_4( read_buf + FIL_PAGE_OFFSET), (ulong) mach_read_from_4( read_buf + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID)); return; case FIL_PAGE_TYPE_XDES: /* This is an uncompressed page. */ break; } } checksum = srv_use_checksums ? buf_calc_page_new_checksum(read_buf) : BUF_NO_CHECKSUM_MAGIC; old_checksum = srv_use_checksums ? buf_calc_page_old_checksum(read_buf) : BUF_NO_CHECKSUM_MAGIC; ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Page checksum %lu, prior-to-4.0.14-form" " checksum %lu\n" "InnoDB: stored checksum %lu, prior-to-4.0.14-form" " stored checksum %lu\n" "InnoDB: Page lsn %lu %lu, low 4 bytes of lsn" " at page end %lu\n" "InnoDB: Page number (if stored to page already) %lu,\n" "InnoDB: space id (if created with >= v4.1.1" " and stored already) %lu\n", (ulong) checksum, (ulong) old_checksum, (ulong) mach_read_from_4(read_buf + FIL_PAGE_SPACE_OR_CHKSUM), (ulong) mach_read_from_4(read_buf + UNIV_PAGE_SIZE - FIL_PAGE_END_LSN_OLD_CHKSUM), (ulong) mach_read_from_4(read_buf + FIL_PAGE_LSN), (ulong) mach_read_from_4(read_buf + FIL_PAGE_LSN + 4), (ulong) mach_read_from_4(read_buf + UNIV_PAGE_SIZE - FIL_PAGE_END_LSN_OLD_CHKSUM + 4), (ulong) mach_read_from_4(read_buf + FIL_PAGE_OFFSET), (ulong) mach_read_from_4(read_buf + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID)); #ifndef UNIV_HOTBACKUP if (mach_read_from_2(read_buf + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_TYPE) == TRX_UNDO_INSERT) { ib_logger(ib_stream, "InnoDB: Page may be an insert undo log page\n"); } else if (mach_read_from_2(read_buf + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_TYPE) == TRX_UNDO_UPDATE) { ib_logger(ib_stream, "InnoDB: Page may be an update undo log page\n"); } #endif /* !UNIV_HOTBACKUP */ switch (fil_page_get_type(read_buf)) { case FIL_PAGE_INDEX: ib_logger(ib_stream, "InnoDB: Page may be an index page where" " index id is %lu %lu\n", (ulong) ut_dulint_get_high( btr_page_get_index_id(read_buf)), (ulong) ut_dulint_get_low( btr_page_get_index_id(read_buf))); #ifndef UNIV_HOTBACKUP index = dict_index_find_on_id_low( btr_page_get_index_id(read_buf)); if (index) { ib_logger(ib_stream, "InnoDB: ("); dict_index_name_print(ib_stream, NULL, index); ib_logger(ib_stream, ")\n"); } #endif /* !UNIV_HOTBACKUP */ break; case FIL_PAGE_INODE: ib_logger(ib_stream, "InnoDB: Page may be an 'inode' page\n"); break; case FIL_PAGE_IBUF_FREE_LIST: ib_logger(ib_stream, "InnoDB: Page may be an insert buffer free " "list page\n"); break; case FIL_PAGE_TYPE_ALLOCATED: ib_logger(ib_stream, "InnoDB: Page may be a freshly allocated page\n"); break; case FIL_PAGE_IBUF_BITMAP: ib_logger(ib_stream, "InnoDB: Page may be an insert buffer bitmap page\n"); break; case FIL_PAGE_TYPE_SYS: ib_logger(ib_stream, "InnoDB: Page may be a system page\n"); break; case FIL_PAGE_TYPE_TRX_SYS: ib_logger(ib_stream, "InnoDB: Page may be a transaction system page\n"); break; case FIL_PAGE_TYPE_FSP_HDR: ib_logger(ib_stream, "InnoDB: Page may be a file space header page\n"); break; case FIL_PAGE_TYPE_XDES: ib_logger(ib_stream, "InnoDB: Page may be an extent descriptor page\n"); break; case FIL_PAGE_TYPE_BLOB: ib_logger(ib_stream, "InnoDB: Page may be a BLOB page\n"); break; case FIL_PAGE_TYPE_ZBLOB: case FIL_PAGE_TYPE_ZBLOB2: ib_logger(ib_stream, "InnoDB: Page may be a compressed BLOB page\n"); break; } } #ifndef UNIV_HOTBACKUP /********************************************************************//** Initializes a buffer control block when the buf_pool is created. */ static void buf_block_init( /*===========*/ buf_block_t* block, /*!< in: pointer to control block */ byte* frame) /*!< in: pointer to buffer frame */ { UNIV_MEM_DESC(frame, UNIV_PAGE_SIZE, block); block->frame = frame; block->page.state = BUF_BLOCK_NOT_USED; block->page.buf_fix_count = 0; block->page.io_fix = BUF_IO_NONE; block->modify_clock = 0; #ifdef UNIV_DEBUG_FILE_ACCESSES block->page.file_page_was_freed = FALSE; #endif /* UNIV_DEBUG_FILE_ACCESSES */ block->check_index_page_at_flush = FALSE; block->index = NULL; #ifdef UNIV_DEBUG block->page.in_page_hash = FALSE; block->page.in_zip_hash = FALSE; block->page.in_flush_list = FALSE; block->page.in_free_list = FALSE; block->page.in_LRU_list = FALSE; block->in_unzip_LRU_list = FALSE; #endif /* UNIV_DEBUG */ #if defined UNIV_AHI_DEBUG || defined UNIV_DEBUG block->n_pointers = 0; #endif /* UNIV_AHI_DEBUG || UNIV_DEBUG */ page_zip_des_init(&block->page.zip); mutex_create(&block->mutex, SYNC_BUF_BLOCK); rw_lock_create(&block->lock, SYNC_LEVEL_VARYING); ut_ad(rw_lock_validate(&(block->lock))); #ifdef UNIV_SYNC_DEBUG rw_lock_create(&block->debug_latch, SYNC_NO_ORDER_CHECK); #endif /* UNIV_SYNC_DEBUG */ } /********************************************************************//** Allocates a chunk of buffer frames. @return chunk, or NULL on failure */ static buf_chunk_t* buf_chunk_init( /*===========*/ buf_chunk_t* chunk, /*!< out: chunk of buffers */ ulint mem_size) /*!< in: requested size in bytes */ { buf_block_t* block; byte* frame; ulint i; /* Round down to a multiple of page size, although it already should be. */ mem_size = ut_2pow_round(mem_size, UNIV_PAGE_SIZE); /* Reserve space for the block descriptors. */ mem_size += ut_2pow_round((mem_size / UNIV_PAGE_SIZE) * (sizeof *block) + (UNIV_PAGE_SIZE - 1), UNIV_PAGE_SIZE); chunk->mem_size = mem_size; chunk->mem = os_mem_alloc_large(&chunk->mem_size); if (UNIV_UNLIKELY(chunk->mem == NULL)) { return(NULL); } /* Allocate the block descriptors from the start of the memory block. */ chunk->blocks = chunk->mem; /* Align a pointer to the first frame. Note that when os_large_page_size is smaller than UNIV_PAGE_SIZE, we may allocate one fewer block than requested. When it is bigger, we may allocate more blocks than requested. */ frame = ut_align(chunk->mem, UNIV_PAGE_SIZE); chunk->size = chunk->mem_size / UNIV_PAGE_SIZE - (frame != chunk->mem); /* Subtract the space needed for block descriptors. */ { ulint size = chunk->size; while (frame < (byte*) (chunk->blocks + size)) { frame += UNIV_PAGE_SIZE; size--; } chunk->size = size; } /* Init block structs and assign frames for them. Then we assign the frames to the first blocks (we already mapped the memory above). */ block = chunk->blocks; for (i = chunk->size; i--; ) { buf_block_init(block, frame); #ifdef HAVE_purify /* Wipe contents of frame to eliminate a Purify warning */ memset(block->frame, '\0', UNIV_PAGE_SIZE); #endif /* Add the block to the free list */ UT_LIST_ADD_LAST(list, buf_pool->free, (&block->page)); ut_d(block->page.in_free_list = TRUE); block++; frame += UNIV_PAGE_SIZE; } return(chunk); } #ifdef UNIV_DEBUG /*********************************************************************//** Finds a block in the given buffer chunk that points to a given compressed page. @return buffer block pointing to the compressed page, or NULL */ static buf_block_t* buf_chunk_contains_zip( /*===================*/ buf_chunk_t* chunk, /*!< in: chunk being checked */ const void* data) /*!< in: pointer to compressed page */ { buf_block_t* block; ulint i; ut_ad(buf_pool); ut_ad(buf_pool_mutex_own()); block = chunk->blocks; for (i = chunk->size; i--; block++) { if (block->page.zip.data == data) { return(block); } } return(NULL); } /*********************************************************************//** Finds a block in the buffer pool that points to a given compressed page. @return buffer block pointing to the compressed page, or NULL */ UNIV_INTERN buf_block_t* buf_pool_contains_zip( /*==================*/ const void* data) /*!< in: pointer to compressed page */ { ulint n; buf_chunk_t* chunk = buf_pool->chunks; for (n = buf_pool->n_chunks; n--; chunk++) { buf_block_t* block = buf_chunk_contains_zip(chunk, data); if (block) { return(block); } } return(NULL); } #endif /* UNIV_DEBUG */ /*********************************************************************//** Checks that all file pages in the buffer chunk are in a replaceable state. @return address of a non-free block, or NULL if all freed */ static const buf_block_t* buf_chunk_not_freed( /*================*/ buf_chunk_t* chunk) /*!< in: chunk being checked */ { buf_block_t* block; ulint i; ut_ad(buf_pool); ut_ad(buf_pool_mutex_own()); block = chunk->blocks; for (i = chunk->size; i--; block++) { ibool ready; switch (buf_block_get_state(block)) { case BUF_BLOCK_ZIP_FREE: case BUF_BLOCK_ZIP_PAGE: case BUF_BLOCK_ZIP_DIRTY: /* The uncompressed buffer pool should never contain compressed block descriptors. */ ut_error; break; case BUF_BLOCK_NOT_USED: case BUF_BLOCK_READY_FOR_USE: case BUF_BLOCK_MEMORY: case BUF_BLOCK_REMOVE_HASH: /* Skip blocks that are not being used for file pages. */ break; case BUF_BLOCK_FILE_PAGE: mutex_enter(&block->mutex); ready = buf_flush_ready_for_replace(&block->page); mutex_exit(&block->mutex); if (!ready) { return(block); } break; } } return(NULL); } #if 0 /*********************************************************************//** Checks that all blocks in the buffer chunk are in BUF_BLOCK_NOT_USED state. @return TRUE if all freed */ static ibool buf_chunk_all_free( /*===============*/ const buf_chunk_t* chunk) /*!< in: chunk being checked */ { const buf_block_t* block; ulint i; ut_ad(buf_pool); ut_ad(buf_pool_mutex_own()); block = chunk->blocks; for (i = chunk->size; i--; block++) { if (buf_block_get_state(block) != BUF_BLOCK_NOT_USED) { return(FALSE); } } return(TRUE); } /********************************************************************//** Frees a chunk of buffer frames. */ static void buf_chunk_free( /*===========*/ buf_chunk_t* chunk) /*!< out: chunk of buffers */ { buf_block_t* block; const buf_block_t* block_end; ut_ad(buf_pool_mutex_own()); block_end = chunk->blocks + chunk->size; for (block = chunk->blocks; block < block_end; block++) { ut_a(buf_block_get_state(block) == BUF_BLOCK_NOT_USED); ut_a(!block->page.zip.data); ut_ad(!block->page.in_LRU_list); ut_ad(!block->in_unzip_LRU_list); ut_ad(!block->page.in_flush_list); /* Remove the block from the free list. */ ut_ad(block->page.in_free_list); UT_LIST_REMOVE(list, buf_pool->free, (&block->page)); /* Free the latches. */ mutex_free(&block->mutex); rw_lock_free(&block->lock); #ifdef UNIV_SYNC_DEBUG rw_lock_free(&block->debug_latch); #endif /* UNIV_SYNC_DEBUG */ UNIV_MEM_UNDESC(block); } os_mem_free_large(chunk->mem, chunk->mem_size); } #endif /* 0 */ /********************************************************************//** Creates the buffer pool. @return own: buf_pool object, NULL if not enough memory or error */ UNIV_INTERN buf_pool_t* buf_pool_init(void) /*===============*/ { buf_chunk_t* chunk; ulint i; buf_pool = mem_zalloc(sizeof(buf_pool_t)); /* 1. Initialize general fields ------------------------------- */ mutex_create(&buf_pool_mutex, SYNC_BUF_POOL); mutex_create(&buf_pool_zip_mutex, SYNC_BUF_BLOCK); buf_pool_mutex_enter(); buf_pool->n_chunks = 1; buf_pool->chunks = chunk = mem_alloc(sizeof *chunk); UT_LIST_INIT(buf_pool->free); if (!buf_chunk_init(chunk, srv_buf_pool_size)) { mem_free(chunk); mem_free(buf_pool); buf_pool = NULL; buf_pool_mutex_exit(); return(NULL); } srv_buf_pool_old_size = srv_buf_pool_size; buf_pool->curr_size = chunk->size; srv_buf_pool_curr_size = buf_pool->curr_size * UNIV_PAGE_SIZE; buf_pool->page_hash = hash_create(2 * buf_pool->curr_size); buf_pool->zip_hash = hash_create(2 * buf_pool->curr_size); buf_pool->last_printout_time = ut_time(); /* 2. Initialize flushing fields -------------------------------- */ for (i = BUF_FLUSH_LRU; i < BUF_FLUSH_N_TYPES; i++) { buf_pool->no_flush[i] = os_event_create(NULL); } /* 3. Initialize LRU fields --------------------------- */ /* All fields are initialized by mem_zalloc(). */ buf_pool_mutex_exit(); btr_search_sys_create(buf_pool->curr_size * UNIV_PAGE_SIZE / sizeof(void*) / 64); /* 4. Initialize the buddy allocator fields */ /* All fields are initialized by mem_zalloc(). */ return(buf_pool); } /********************************************************************//** Prepares the buffer pool for shutdown. */ UNIV_INTERN void buf_close(void) /*===========*/ { ulint i; btr_search_sys_close(); /* This can happen if we abort during the startup phase. */ if (buf_pool == NULL) { return; } hash_table_free(buf_pool->page_hash); buf_pool->page_hash = NULL; hash_table_free(buf_pool->zip_hash); buf_pool->zip_hash = NULL; for (i = BUF_FLUSH_LRU; i < BUF_FLUSH_N_TYPES; i++) { os_event_free(buf_pool->no_flush[i]); buf_pool->no_flush[i] = NULL; } } /************************************************************************ Frees the buffer pool at shutdown. This must not be invoked before freeing all mutexes. */ UNIV_INTERN void buf_mem_free(void) /*===============*/ { if (buf_pool != NULL) { buf_chunk_t* chunk; buf_chunk_t* chunks; chunks = buf_pool->chunks; chunk = chunks + buf_pool->n_chunks; while (--chunk >= chunks) { /* Bypass the checks of buf_chunk_free(), since they would fail at shutdown. */ os_mem_free_large(chunk->mem, chunk->mem_size); } buf_pool->n_chunks = 0; mem_free(buf_pool->chunks); mem_free(buf_pool); buf_pool = NULL; } } /********************************************************************//** Drops the adaptive hash index. To prevent a livelock, this function is only to be called while holding btr_search_latch and while btr_search_enabled == FALSE. */ UNIV_INTERN void buf_pool_drop_hash_index(void) /*==========================*/ { ibool released_search_latch; #ifdef UNIV_SYNC_DEBUG ut_ad(rw_lock_own(&btr_search_latch, RW_LOCK_EX)); #endif /* UNIV_SYNC_DEBUG */ ut_ad(!btr_search_enabled); do { buf_chunk_t* chunks = buf_pool->chunks; buf_chunk_t* chunk = chunks + buf_pool->n_chunks; released_search_latch = FALSE; while (--chunk >= chunks) { buf_block_t* block = chunk->blocks; ulint i = chunk->size; for (; i--; block++) { /* block->is_hashed cannot be modified when we have an x-latch on btr_search_latch; see the comment in buf0buf.h */ if (buf_block_get_state(block) != BUF_BLOCK_FILE_PAGE || !block->is_hashed) { continue; } /* To follow the latching order, we have to release btr_search_latch before acquiring block->latch. */ rw_lock_x_unlock(&btr_search_latch); /* When we release the search latch, we must rescan all blocks, because some may become hashed again. */ released_search_latch = TRUE; rw_lock_x_lock(&block->lock); /* This should be guaranteed by the callers, which will be holding btr_search_enabled_mutex. */ ut_ad(!btr_search_enabled); /* Because we did not buffer-fix the block by calling buf_block_get_gen(), it is possible that the block has been allocated for some other use after btr_search_latch was released above. We do not care which file page the block is mapped to. All we want to do is to drop any hash entries referring to the page. */ /* It is possible that block->page.state != BUF_FILE_PAGE. Even that does not matter, because btr_search_drop_page_hash_index() will check block->is_hashed before doing anything. block->is_hashed can only be set on uncompressed file pages. */ btr_search_drop_page_hash_index(block); rw_lock_x_unlock(&block->lock); rw_lock_x_lock(&btr_search_latch); ut_ad(!btr_search_enabled); } } } while (released_search_latch); } /********************************************************************//** Relocate a buffer control block. Relocates the block on the LRU list and in buf_pool->page_hash. Does not relocate bpage->list. The caller must take care of relocating bpage->list. */ UNIV_INTERN void buf_relocate( /*=========*/ buf_page_t* bpage, /*!< in/out: control block being relocated; buf_page_get_state(bpage) must be BUF_BLOCK_ZIP_DIRTY or BUF_BLOCK_ZIP_PAGE */ buf_page_t* dpage) /*!< in/out: destination control block */ { buf_page_t* b; ulint fold; ut_ad(buf_pool_mutex_own()); ut_ad(mutex_own(buf_page_get_mutex(bpage))); ut_a(buf_page_get_io_fix(bpage) == BUF_IO_NONE); ut_a(bpage->buf_fix_count == 0); ut_ad(bpage->in_LRU_list); ut_ad(!bpage->in_zip_hash); ut_ad(bpage->in_page_hash); ut_ad(bpage == buf_page_hash_get(bpage->space, bpage->offset)); #ifdef UNIV_DEBUG switch (buf_page_get_state(bpage)) { case BUF_BLOCK_ZIP_FREE: case BUF_BLOCK_NOT_USED: case BUF_BLOCK_READY_FOR_USE: case BUF_BLOCK_FILE_PAGE: case BUF_BLOCK_MEMORY: case BUF_BLOCK_REMOVE_HASH: ut_error; case BUF_BLOCK_ZIP_DIRTY: case BUF_BLOCK_ZIP_PAGE: break; } #endif /* UNIV_DEBUG */ memcpy(dpage, bpage, sizeof *dpage); ut_d(bpage->in_LRU_list = FALSE); ut_d(bpage->in_page_hash = FALSE); /* relocate buf_pool->LRU */ b = UT_LIST_GET_PREV(LRU, bpage); UT_LIST_REMOVE(LRU, buf_pool->LRU, bpage); if (b) { UT_LIST_INSERT_AFTER(LRU, buf_pool->LRU, b, dpage); } else { UT_LIST_ADD_FIRST(LRU, buf_pool->LRU, dpage); } if (UNIV_UNLIKELY(buf_pool->LRU_old == bpage)) { buf_pool->LRU_old = dpage; #ifdef UNIV_LRU_DEBUG /* buf_pool->LRU_old must be the first item in the LRU list whose "old" flag is set. */ ut_a(buf_pool->LRU_old->old); ut_a(!UT_LIST_GET_PREV(LRU, buf_pool->LRU_old) || !UT_LIST_GET_PREV(LRU, buf_pool->LRU_old)->old); ut_a(!UT_LIST_GET_NEXT(LRU, buf_pool->LRU_old) || UT_LIST_GET_NEXT(LRU, buf_pool->LRU_old)->old); } else { /* Check that the "old" flag is consistent in the block and its neighbours. */ buf_page_set_old(dpage, buf_page_is_old(dpage)); #endif /* UNIV_LRU_DEBUG */ } ut_d(UT_LIST_VALIDATE(LRU, buf_page_t, buf_pool->LRU, ut_ad(ut_list_node_313->in_LRU_list))); /* relocate buf_pool->page_hash */ fold = buf_page_address_fold(bpage->space, bpage->offset); HASH_DELETE(buf_page_t, hash, buf_pool->page_hash, fold, bpage); HASH_INSERT(buf_page_t, hash, buf_pool->page_hash, fold, dpage); } #if 0 /********************************************************************//** Shrinks the buffer pool. */ static void buf_pool_shrink( /*============*/ ulint chunk_size) /*!< in: number of pages to remove */ { buf_chunk_t* chunks; buf_chunk_t* chunk; ulint max_size; ulint max_free_size; buf_chunk_t* max_chunk; buf_chunk_t* max_free_chunk; ut_ad(!buf_pool_mutex_own()); try_again: btr_search_disable(); /* Empty the adaptive hash index again */ buf_pool_mutex_enter(); shrink_again: if (buf_pool->n_chunks <= 1) { /* Cannot shrink if there is only one chunk */ goto func_done; } /* Search for the largest free chunk not larger than the size difference */ chunks = buf_pool->chunks; chunk = chunks + buf_pool->n_chunks; max_size = max_free_size = 0; max_chunk = max_free_chunk = NULL; while (--chunk >= chunks) { if (chunk->size <= chunk_size && chunk->size > max_free_size) { if (chunk->size > max_size) { max_size = chunk->size; max_chunk = chunk; } if (buf_chunk_all_free(chunk)) { max_free_size = chunk->size; max_free_chunk = chunk; } } } if (!max_free_size) { ulint dirty = 0; ulint nonfree = 0; buf_block_t* block; buf_block_t* bend; /* Cannot shrink: try again later (do not assign srv_buf_pool_old_size) */ if (!max_chunk) { goto func_exit; } block = max_chunk->blocks; bend = block + max_chunk->size; /* Move the blocks of chunk to the end of the LRU list and try to flush them. */ for (; block < bend; block++) { switch (buf_block_get_state(block)) { case BUF_BLOCK_NOT_USED: continue; case BUF_BLOCK_FILE_PAGE: break; default: nonfree++; continue; } mutex_enter(&block->mutex); /* The following calls will temporarily release block->mutex and buf_pool_mutex. Therefore, we have to always retry, even if !dirty && !nonfree. */ if (!buf_flush_ready_for_replace(&block->page)) { buf_LRU_make_block_old(&block->page); dirty++; } else if (buf_LRU_free_block(&block->page, TRUE, NULL) != BUF_LRU_FREED) { nonfree++; } mutex_exit(&block->mutex); } buf_pool_mutex_exit(); /* Request for a flush of the chunk if it helps. Do not flush if there are non-free blocks, since flushing will not make the chunk freeable. */ if (nonfree) { /* Avoid busy-waiting. */ os_thread_sleep(100000); } else if (dirty && buf_flush_batch(BUF_FLUSH_LRU, dirty, 0) == ULINT_UNDEFINED) { buf_flush_wait_batch_end(BUF_FLUSH_LRU); } goto try_again; } max_size = max_free_size; max_chunk = max_free_chunk; srv_buf_pool_old_size = srv_buf_pool_size; /* Rewrite buf_pool->chunks. Copy everything but max_chunk. */ chunks = mem_alloc((buf_pool->n_chunks - 1) * sizeof *chunks); memcpy(chunks, buf_pool->chunks, (max_chunk - buf_pool->chunks) * sizeof *chunks); memcpy(chunks + (max_chunk - buf_pool->chunks), max_chunk + 1, buf_pool->chunks + buf_pool->n_chunks - (max_chunk + 1)); ut_a(buf_pool->curr_size > max_chunk->size); buf_pool->curr_size -= max_chunk->size; srv_buf_pool_curr_size = buf_pool->curr_size * UNIV_PAGE_SIZE; chunk_size -= max_chunk->size; buf_chunk_free(max_chunk); mem_free(buf_pool->chunks); buf_pool->chunks = chunks; buf_pool->n_chunks--; /* Allow a slack of one megabyte. */ if (chunk_size > 1048576 / UNIV_PAGE_SIZE) { goto shrink_again; } func_done: srv_buf_pool_old_size = srv_buf_pool_size; func_exit: buf_pool_mutex_exit(); btr_search_enable(); } /********************************************************************//** Rebuild buf_pool->page_hash. */ static void buf_pool_page_hash_rebuild(void) /*============================*/ { ulint i; ulint n_chunks; buf_chunk_t* chunk; hash_table_t* page_hash; hash_table_t* zip_hash; buf_page_t* b; buf_pool_mutex_enter(); /* Free, create, and populate the hash table. */ hash_table_free(buf_pool->page_hash); buf_pool->page_hash = page_hash = hash_create(2 * buf_pool->curr_size); zip_hash = hash_create(2 * buf_pool->curr_size); HASH_MIGRATE(buf_pool->zip_hash, zip_hash, buf_page_t, hash, BUF_POOL_ZIP_FOLD_BPAGE); hash_table_free(buf_pool->zip_hash); buf_pool->zip_hash = zip_hash; /* Insert the uncompressed file pages to buf_pool->page_hash. */ chunk = buf_pool->chunks; n_chunks = buf_pool->n_chunks; for (i = 0; i < n_chunks; i++, chunk++) { ulint j; buf_block_t* block = chunk->blocks; for (j = 0; j < chunk->size; j++, block++) { if (buf_block_get_state(block) == BUF_BLOCK_FILE_PAGE) { ut_ad(!block->page.in_zip_hash); ut_ad(block->page.in_page_hash); HASH_INSERT(buf_page_t, hash, page_hash, buf_page_address_fold( block->page.space, block->page.offset), &block->page); } } } /* Insert the compressed-only pages to buf_pool->page_hash. All such blocks are either in buf_pool->zip_clean or in buf_pool->flush_list. */ for (b = UT_LIST_GET_FIRST(buf_pool->zip_clean); b; b = UT_LIST_GET_NEXT(list, b)) { ut_a(buf_page_get_state(b) == BUF_BLOCK_ZIP_PAGE); ut_ad(!b->in_flush_list); ut_ad(b->in_LRU_list); ut_ad(b->in_page_hash); ut_ad(!b->in_zip_hash); HASH_INSERT(buf_page_t, hash, page_hash, buf_page_address_fold(b->space, b->offset), b); } for (b = UT_LIST_GET_FIRST(buf_pool->flush_list); b; b = UT_LIST_GET_NEXT(list, b)) { ut_ad(b->in_flush_list); ut_ad(b->in_LRU_list); ut_ad(b->in_page_hash); ut_ad(!b->in_zip_hash); switch (buf_page_get_state(b)) { case BUF_BLOCK_ZIP_DIRTY: HASH_INSERT(buf_page_t, hash, page_hash, buf_page_address_fold(b->space, b->offset), b); break; case BUF_BLOCK_FILE_PAGE: /* uncompressed page */ break; case BUF_BLOCK_ZIP_FREE: case BUF_BLOCK_ZIP_PAGE: case BUF_BLOCK_NOT_USED: case BUF_BLOCK_READY_FOR_USE: case BUF_BLOCK_MEMORY: case BUF_BLOCK_REMOVE_HASH: ut_error; break; } } buf_pool_mutex_exit(); } /********************************************************************//** Resizes the buffer pool. */ UNIV_INTERN void buf_pool_resize(void) /*=================*/ { buf_pool_mutex_enter(); if (srv_buf_pool_old_size == srv_buf_pool_size) { buf_pool_mutex_exit(); return; } if (srv_buf_pool_curr_size + 1048576 > srv_buf_pool_size) { buf_pool_mutex_exit(); /* Disable adaptive hash indexes and empty the index in order to free up memory in the buffer pool chunks. */ buf_pool_shrink((srv_buf_pool_curr_size - srv_buf_pool_size) / UNIV_PAGE_SIZE); } else if (srv_buf_pool_curr_size + 1048576 < srv_buf_pool_size) { /* Enlarge the buffer pool by at least one megabyte */ ulint mem_size = srv_buf_pool_size - srv_buf_pool_curr_size; buf_chunk_t* chunks; buf_chunk_t* chunk; chunks = mem_alloc((buf_pool->n_chunks + 1) * sizeof *chunks); memcpy(chunks, buf_pool->chunks, buf_pool->n_chunks * sizeof *chunks); chunk = &chunks[buf_pool->n_chunks]; if (!buf_chunk_init(chunk, mem_size)) { mem_free(chunks); } else { buf_pool->curr_size += chunk->size; srv_buf_pool_curr_size = buf_pool->curr_size * UNIV_PAGE_SIZE; mem_free(buf_pool->chunks); buf_pool->chunks = chunks; buf_pool->n_chunks++; } srv_buf_pool_old_size = srv_buf_pool_size; buf_pool_mutex_exit(); } buf_pool_page_hash_rebuild(); } #endif /* 0 */ /********************************************************************//** Moves a page to the start of the buffer pool LRU list. This high-level function can be used to prevent an important page from slipping out of the buffer pool. */ UNIV_INTERN void buf_page_make_young( /*================*/ buf_page_t* bpage) /*!< in: buffer block of a file page */ { buf_pool_mutex_enter(); ut_a(buf_page_in_file(bpage)); buf_LRU_make_block_young(bpage); buf_pool_mutex_exit(); } /********************************************************************//** Sets the time of the first access of a page and moves a page to the start of the buffer pool LRU list if it is too old. This high-level function can be used to prevent an important page from slipping out of the buffer pool. */ static void buf_page_set_accessed_make_young( /*=============================*/ buf_page_t* bpage, /*!< in/out: buffer block of a file page */ unsigned access_time) /*!< in: bpage->access_time read under mutex protection, or 0 if unknown */ { ut_ad(!buf_pool_mutex_own()); ut_a(buf_page_in_file(bpage)); if (buf_page_peek_if_too_old(bpage)) { buf_pool_mutex_enter(); buf_LRU_make_block_young(bpage); buf_pool_mutex_exit(); } else if (!access_time) { ulint time_ms = ut_time_ms(); buf_pool_mutex_enter(); buf_page_set_accessed(bpage, time_ms); buf_pool_mutex_exit(); } } /********************************************************************//** Resets the check_index_page_at_flush field of a page if found in the buffer pool. */ UNIV_INTERN void buf_reset_check_index_page_at_flush( /*================================*/ ulint space, /*!< in: space id */ ulint offset) /*!< in: page number */ { buf_block_t* block; buf_pool_mutex_enter(); block = (buf_block_t*) buf_page_hash_get(space, offset); if (block && buf_block_get_state(block) == BUF_BLOCK_FILE_PAGE) { block->check_index_page_at_flush = FALSE; } buf_pool_mutex_exit(); } /********************************************************************//** Returns the current state of is_hashed of a page. FALSE if the page is not in the pool. NOTE that this operation does not fix the page in the pool if it is found there. @return TRUE if page hash index is built in search system */ UNIV_INTERN ibool buf_page_peek_if_search_hashed( /*===========================*/ ulint space, /*!< in: space id */ ulint offset) /*!< in: page number */ { buf_block_t* block; ibool is_hashed; buf_pool_mutex_enter(); block = (buf_block_t*) buf_page_hash_get(space, offset); if (!block || buf_block_get_state(block) != BUF_BLOCK_FILE_PAGE) { is_hashed = FALSE; } else { is_hashed = block->is_hashed; } buf_pool_mutex_exit(); return(is_hashed); } #ifdef UNIV_DEBUG_FILE_ACCESSES /********************************************************************//** Sets file_page_was_freed TRUE if the page is found in the buffer pool. This function should be called when we free a file page and want the debug version to check that it is not accessed any more unless reallocated. @return control block if found in page hash table, otherwise NULL */ UNIV_INTERN buf_page_t* buf_page_set_file_page_was_freed( /*=============================*/ ulint space, /*!< in: space id */ ulint offset) /*!< in: page number */ { buf_page_t* bpage; buf_pool_mutex_enter(); bpage = buf_page_hash_get(space, offset); if (bpage) { bpage->file_page_was_freed = TRUE; } buf_pool_mutex_exit(); return(bpage); } /********************************************************************//** Sets file_page_was_freed FALSE if the page is found in the buffer pool. This function should be called when we free a file page and want the debug version to check that it is not accessed any more unless reallocated. @return control block if found in page hash table, otherwise NULL */ UNIV_INTERN buf_page_t* buf_page_reset_file_page_was_freed( /*===============================*/ ulint space, /*!< in: space id */ ulint offset) /*!< in: page number */ { buf_page_t* bpage; buf_pool_mutex_enter(); bpage = buf_page_hash_get(space, offset); if (bpage) { bpage->file_page_was_freed = FALSE; } buf_pool_mutex_exit(); return(bpage); } #endif /* UNIV_DEBUG_FILE_ACCESSES */ /********************************************************************//** Get read access to a compressed page (usually of type FIL_PAGE_TYPE_ZBLOB or FIL_PAGE_TYPE_ZBLOB2). The page must be released with buf_page_release_zip(). NOTE: the page is not protected by any latch. Mutual exclusion has to be implemented at a higher level. In other words, all possible accesses to a given page through this function must be protected by the same set of mutexes or latches. @return pointer to the block */ UNIV_INTERN buf_page_t* buf_page_get_zip( /*=============*/ ulint space, /*!< in: space id */ ulint zip_size,/*!< in: compressed page size */ ulint offset) /*!< in: page number */ { buf_page_t* bpage; mutex_t* block_mutex; ibool must_read; unsigned access_time; #ifndef UNIV_LOG_DEBUG ut_ad(!ibuf_inside()); #endif buf_pool->stat.n_page_gets++; for (;;) { buf_pool_mutex_enter(); lookup: bpage = buf_page_hash_get(space, offset); if (bpage) { break; } /* Page not in buf_pool: needs to be read from file */ buf_pool_mutex_exit(); buf_read_page(space, zip_size, offset); #if defined UNIV_DEBUG || defined UNIV_BUF_DEBUG ut_a(++buf_dbg_counter % 37 || buf_validate()); #endif /* UNIV_DEBUG || UNIV_BUF_DEBUG */ } if (UNIV_UNLIKELY(!bpage->zip.data)) { /* There is no compressed page. */ err_exit: buf_pool_mutex_exit(); return(NULL); } switch (buf_page_get_state(bpage)) { case BUF_BLOCK_NOT_USED: case BUF_BLOCK_READY_FOR_USE: case BUF_BLOCK_MEMORY: case BUF_BLOCK_REMOVE_HASH: case BUF_BLOCK_ZIP_FREE: break; case BUF_BLOCK_ZIP_PAGE: case BUF_BLOCK_ZIP_DIRTY: block_mutex = &buf_pool_zip_mutex; mutex_enter(block_mutex); bpage->buf_fix_count++; goto got_block; case BUF_BLOCK_FILE_PAGE: block_mutex = &((buf_block_t*) bpage)->mutex; mutex_enter(block_mutex); /* Discard the uncompressed page frame if possible. */ if (buf_LRU_free_block(bpage, FALSE, NULL) == BUF_LRU_FREED) { mutex_exit(block_mutex); goto lookup; } buf_block_buf_fix_inc((buf_block_t*) bpage, __FILE__, __LINE__); goto got_block; } ut_error; goto err_exit; got_block: must_read = buf_page_get_io_fix(bpage) == BUF_IO_READ; access_time = buf_page_is_accessed(bpage); buf_pool_mutex_exit(); mutex_exit(block_mutex); buf_page_set_accessed_make_young(bpage, access_time); #ifdef UNIV_DEBUG_FILE_ACCESSES ut_a(!bpage->file_page_was_freed); #endif #if defined UNIV_DEBUG || defined UNIV_BUF_DEBUG ut_a(++buf_dbg_counter % 5771 || buf_validate()); ut_a(bpage->buf_fix_count > 0); ut_a(buf_page_in_file(bpage)); #endif /* UNIV_DEBUG || UNIV_BUF_DEBUG */ if (must_read) { /* Let us wait until the read operation completes */ for (;;) { enum buf_io_fix io_fix; mutex_enter(block_mutex); io_fix = buf_page_get_io_fix(bpage); mutex_exit(block_mutex); if (io_fix == BUF_IO_READ) { os_thread_sleep(WAIT_FOR_READ); } else { break; } } } #ifdef UNIV_IBUF_COUNT_DEBUG ut_a(ibuf_count_get(buf_page_get_space(bpage), buf_page_get_page_no(bpage)) == 0); #endif return(bpage); } /********************************************************************//** Initialize some fields of a control block. */ UNIV_INLINE void buf_block_init_low( /*===============*/ buf_block_t* block) /*!< in: block to init */ { block->check_index_page_at_flush = FALSE; block->index = NULL; block->n_hash_helps = 0; block->is_hashed = FALSE; block->n_fields = 1; block->n_bytes = 0; block->left_side = TRUE; } #endif /* !UNIV_HOTBACKUP */ /********************************************************************//** Decompress a block. @return TRUE if successful */ UNIV_INTERN ibool buf_zip_decompress( /*===============*/ buf_block_t* block, /*!< in/out: block */ ibool check) /*!< in: TRUE=verify the page checksum */ { const byte* frame = block->page.zip.data; ut_ad(buf_block_get_zip_size(block)); ut_a(buf_block_get_space(block) != 0); if (UNIV_LIKELY(check)) { ulint stamp_checksum = mach_read_from_4( frame + FIL_PAGE_SPACE_OR_CHKSUM); ulint calc_checksum = page_zip_calc_checksum( frame, page_zip_get_size(&block->page.zip)); if (UNIV_UNLIKELY(stamp_checksum != calc_checksum)) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: compressed page checksum mismatch" " (space %u page %u): %lu != %lu\n", block->page.space, block->page.offset, stamp_checksum, calc_checksum); return(FALSE); } } switch (fil_page_get_type(frame)) { case FIL_PAGE_INDEX: if (page_zip_decompress(&block->page.zip, block->frame, TRUE)) { return(TRUE); } ib_logger(ib_stream, "InnoDB: unable to decompress space %lu page %lu\n", (ulong) block->page.space, (ulong) block->page.offset); return(FALSE); case FIL_PAGE_TYPE_ALLOCATED: case FIL_PAGE_INODE: case FIL_PAGE_IBUF_BITMAP: case FIL_PAGE_TYPE_FSP_HDR: case FIL_PAGE_TYPE_XDES: case FIL_PAGE_TYPE_ZBLOB: case FIL_PAGE_TYPE_ZBLOB2: /* Copy to uncompressed storage. */ memcpy(block->frame, frame, buf_block_get_zip_size(block)); return(TRUE); } ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: unknown compressed page" " type %lu\n", fil_page_get_type(frame)); return(FALSE); } #ifndef UNIV_HOTBACKUP /*******************************************************************//** Gets the block to whose frame the pointer is pointing to. @return pointer to block, never NULL */ UNIV_INTERN buf_block_t* buf_block_align( /*============*/ const byte* ptr) /*!< in: pointer to a frame */ { buf_chunk_t* chunk; ulint i; /* TODO: protect buf_pool->chunks with a mutex (it will currently remain constant after buf_pool_init()) */ for (chunk = buf_pool->chunks, i = buf_pool->n_chunks; i--; chunk++) { lint offs = ptr - chunk->blocks->frame; if (UNIV_UNLIKELY(offs < 0)) { continue; } offs >>= UNIV_PAGE_SIZE_SHIFT; if (UNIV_LIKELY((ulint) offs < chunk->size)) { buf_block_t* block = &chunk->blocks[offs]; /* The function buf_chunk_init() invokes buf_block_init() so that block[n].frame == block->frame + n * UNIV_PAGE_SIZE. Check it. */ ut_ad(block->frame == page_align(ptr)); #ifdef UNIV_DEBUG /* A thread that updates these fields must hold buf_pool_mutex and block->mutex. Acquire only the latter. */ mutex_enter(&block->mutex); switch (buf_block_get_state(block)) { case BUF_BLOCK_ZIP_FREE: case BUF_BLOCK_ZIP_PAGE: case BUF_BLOCK_ZIP_DIRTY: /* These types should only be used in the compressed buffer pool, whose memory is allocated from buf_pool->chunks, in UNIV_PAGE_SIZE blocks flagged as BUF_BLOCK_MEMORY. */ ut_error; break; case BUF_BLOCK_NOT_USED: case BUF_BLOCK_READY_FOR_USE: case BUF_BLOCK_MEMORY: /* Some data structures contain "guess" pointers to file pages. The file pages may have been freed and reused. Do not complain. */ break; case BUF_BLOCK_REMOVE_HASH: /* buf_LRU_block_remove_hashed_page() will overwrite the FIL_PAGE_OFFSET and FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID with 0xff and set the state to BUF_BLOCK_REMOVE_HASH. */ ut_ad(page_get_space_id(page_align(ptr)) == 0xffffffff); ut_ad(page_get_page_no(page_align(ptr)) == 0xffffffff); break; case BUF_BLOCK_FILE_PAGE: ut_ad(block->page.space == page_get_space_id(page_align(ptr))); ut_ad(block->page.offset == page_get_page_no(page_align(ptr))); break; } mutex_exit(&block->mutex); #endif /* UNIV_DEBUG */ return(block); } } /* The block should always be found. */ ut_error; return(NULL); } /********************************************************************//** Find out if a pointer belongs to a buf_block_t. It can be a pointer to the buf_block_t itself or a member of it @return TRUE if ptr belongs to a buf_block_t struct */ UNIV_INTERN ibool buf_pointer_is_block_field( /*=======================*/ const void* ptr) /*!< in: pointer not dereferenced */ { const buf_chunk_t* chunk = buf_pool->chunks; const buf_chunk_t* const echunk = chunk + buf_pool->n_chunks; /* TODO: protect buf_pool->chunks with a mutex (it will currently remain constant after buf_pool_init()) */ while (chunk < echunk) { if (ptr >= (void *)chunk->blocks && ptr < (void *)(chunk->blocks + chunk->size)) { return(TRUE); } chunk++; } return(FALSE); } /********************************************************************//** Find out if a buffer block was created by buf_chunk_init(). @return TRUE if "block" has been added to buf_pool->free by buf_chunk_init() */ static ibool buf_block_is_uncompressed( /*======================*/ const buf_block_t* block) /*!< in: pointer to block, not dereferenced */ { ut_ad(buf_pool_mutex_own()); if (UNIV_UNLIKELY((((ulint) block) % sizeof *block) != 0)) { /* The pointer should be aligned. */ return(FALSE); } return(buf_pointer_is_block_field((void *)block)); } /********************************************************************//** This is the general function used to get access to a database page. @return pointer to the block or NULL */ UNIV_INTERN buf_block_t* buf_page_get_gen( /*=============*/ ulint space, /*!< in: space id */ ulint zip_size,/*!< in: compressed page size in bytes or 0 for uncompressed pages */ ulint offset, /*!< in: page number */ ulint rw_latch,/*!< in: RW_S_LATCH, RW_X_LATCH, RW_NO_LATCH */ buf_block_t* guess, /*!< in: guessed block or NULL */ ulint mode, /*!< in: BUF_GET, BUF_GET_IF_IN_POOL, BUF_GET_NO_LATCH */ const char* file, /*!< in: file name */ ulint line, /*!< in: line where called */ mtr_t* mtr) /*!< in: mini-transaction */ { buf_block_t* block; unsigned access_time; ulint fix_type; ibool must_read; ulint retries = 0; ut_ad(mtr); ut_ad(mtr->state == MTR_ACTIVE); ut_ad((rw_latch == RW_S_LATCH) || (rw_latch == RW_X_LATCH) || (rw_latch == RW_NO_LATCH)); ut_ad((mode != BUF_GET_NO_LATCH) || (rw_latch == RW_NO_LATCH)); ut_ad((mode == BUF_GET) || (mode == BUF_GET_IF_IN_POOL) || (mode == BUF_GET_NO_LATCH)); ut_ad(zip_size == fil_space_get_zip_size(space)); ut_ad(ut_is_2pow(zip_size)); #ifndef UNIV_LOG_DEBUG ut_ad(!ibuf_inside() || ibuf_page(space, zip_size, offset, NULL)); #endif buf_pool->stat.n_page_gets++; loop: buf_pool_mutex_enter(); block = guess; if (block) { /* If the guess is a compressed page descriptor that has been allocated by buf_buddy_alloc(), it may have been invalidated by buf_buddy_relocate(). In that case, block could point to something that happens to contain the expected bits in block->page. Similarly, the guess may be pointing to a buffer pool chunk that has been released when resizing the buffer pool. */ if (!buf_block_is_uncompressed(block) || offset != block->page.offset || space != block->page.space || buf_block_get_state(block) != BUF_BLOCK_FILE_PAGE) { block = guess = NULL; } else { ut_ad(!block->page.in_zip_hash); ut_ad(block->page.in_page_hash); } } if (block == NULL) { block = (buf_block_t*) buf_page_hash_get(space, offset); } loop2: if (block == NULL) { /* Page not in buf_pool: needs to be read from file */ buf_pool_mutex_exit(); if (mode == BUF_GET_IF_IN_POOL) { return(NULL); } if (buf_read_page(space, zip_size, offset)) { retries = 0; } else if (retries < BUF_PAGE_READ_MAX_RETRIES) { ++retries; } else { ib_logger(ib_stream, "InnoDB: Error: Unable" " to read tablespace %lu page no" " %lu into the buffer pool after" " %lu attempts\n" "InnoDB: The most probable cause" " of this error may be that the" " table has been corrupted.\n" "InnoDB: You can try to fix this" " problem by using" " innodb_force_recovery.\n" "InnoDB: Please see reference manual" " for more details.\n" "InnoDB: Aborting...\n", space, offset, BUF_PAGE_READ_MAX_RETRIES); ut_error; } #if defined UNIV_DEBUG || defined UNIV_BUF_DEBUG ut_a(++buf_dbg_counter % 37 || buf_validate()); #endif /* UNIV_DEBUG || UNIV_BUF_DEBUG */ goto loop; } ut_ad(page_zip_get_size(&block->page.zip) == zip_size); must_read = buf_block_get_io_fix(block) == BUF_IO_READ; if (must_read && mode == BUF_GET_IF_IN_POOL) { /* The page is only being read to buffer */ buf_pool_mutex_exit(); return(NULL); } switch (buf_block_get_state(block)) { buf_page_t* bpage; ibool success; case BUF_BLOCK_FILE_PAGE: break; case BUF_BLOCK_ZIP_PAGE: case BUF_BLOCK_ZIP_DIRTY: bpage = &block->page; /* Protect bpage->buf_fix_count. */ mutex_enter(&buf_pool_zip_mutex); if (bpage->buf_fix_count || buf_page_get_io_fix(bpage) != BUF_IO_NONE) { /* This condition often occurs when the buffer is not buffer-fixed, but I/O-fixed by buf_page_init_for_read(). */ mutex_exit(&buf_pool_zip_mutex); wait_until_unfixed: /* The block is buffer-fixed or I/O-fixed. Try again later. */ buf_pool_mutex_exit(); os_thread_sleep(WAIT_FOR_READ); goto loop; } /* Allocate an uncompressed page. */ buf_pool_mutex_exit(); mutex_exit(&buf_pool_zip_mutex); block = buf_LRU_get_free_block(0); ut_a(block); buf_pool_mutex_enter(); mutex_enter(&block->mutex); { buf_page_t* hash_bpage = buf_page_hash_get(space, offset); if (UNIV_UNLIKELY(bpage != hash_bpage)) { /* The buf_pool->page_hash was modified while buf_pool_mutex was released. Free the block that was allocated. */ buf_LRU_block_free_non_file_page(block); mutex_exit(&block->mutex); block = (buf_block_t*) hash_bpage; goto loop2; } } if (UNIV_UNLIKELY (bpage->buf_fix_count || buf_page_get_io_fix(bpage) != BUF_IO_NONE)) { /* The block was buffer-fixed or I/O-fixed while buf_pool_mutex was not held by this thread. Free the block that was allocated and try again. This should be extremely unlikely. */ buf_LRU_block_free_non_file_page(block); mutex_exit(&block->mutex); goto wait_until_unfixed; } /* Move the compressed page from bpage to block, and uncompress it. */ mutex_enter(&buf_pool_zip_mutex); buf_relocate(bpage, &block->page); buf_block_init_low(block); block->lock_hash_val = lock_rec_hash(space, offset); UNIV_MEM_DESC(&block->page.zip.data, page_zip_get_size(&block->page.zip), block); if (buf_page_get_state(&block->page) == BUF_BLOCK_ZIP_PAGE) { UT_LIST_REMOVE(list, buf_pool->zip_clean, &block->page); ut_ad(!block->page.in_flush_list); } else { /* Relocate buf_pool->flush_list. */ buf_flush_relocate_on_flush_list(bpage, &block->page); } /* Buffer-fix, I/O-fix, and X-latch the block for the duration of the decompression. Also add the block to the unzip_LRU list. */ block->page.state = BUF_BLOCK_FILE_PAGE; /* Insert at the front of unzip_LRU list */ buf_unzip_LRU_add_block(block, FALSE); block->page.buf_fix_count = 1; buf_block_set_io_fix(block, BUF_IO_READ); rw_lock_x_lock(&block->lock); UNIV_MEM_INVALID(bpage, sizeof *bpage); mutex_exit(&block->mutex); mutex_exit(&buf_pool_zip_mutex); buf_pool->n_pend_unzip++; buf_buddy_free(bpage, sizeof *bpage); buf_pool_mutex_exit(); /* Decompress the page and apply buffered operations while not holding buf_pool_mutex or block->mutex. */ success = buf_zip_decompress(block, srv_use_checksums); if (UNIV_LIKELY(success && !recv_no_ibuf_operations)) { ibuf_merge_or_delete_for_page(block, space, offset, zip_size, TRUE); } /* Unfix and unlatch the block. */ buf_pool_mutex_enter(); mutex_enter(&block->mutex); block->page.buf_fix_count--; buf_block_set_io_fix(block, BUF_IO_NONE); mutex_exit(&block->mutex); buf_pool->n_pend_unzip--; rw_lock_x_unlock(&block->lock); if (UNIV_UNLIKELY(!success)) { buf_pool_mutex_exit(); return(NULL); } break; case BUF_BLOCK_ZIP_FREE: case BUF_BLOCK_NOT_USED: case BUF_BLOCK_READY_FOR_USE: case BUF_BLOCK_MEMORY: case BUF_BLOCK_REMOVE_HASH: ut_error; break; } ut_ad(buf_block_get_state(block) == BUF_BLOCK_FILE_PAGE); mutex_enter(&block->mutex); UNIV_MEM_ASSERT_RW(&block->page, sizeof block->page); buf_block_buf_fix_inc(block, file, line); mutex_exit(&block->mutex); /* Check if this is the first access to the page */ access_time = buf_page_is_accessed(&block->page); buf_pool_mutex_exit(); buf_page_set_accessed_make_young(&block->page, access_time); #ifdef UNIV_DEBUG_FILE_ACCESSES ut_a(!block->page.file_page_was_freed); #endif #if defined UNIV_DEBUG || defined UNIV_BUF_DEBUG ut_a(++buf_dbg_counter % 5771 || buf_validate()); ut_a(block->page.buf_fix_count > 0); ut_a(buf_block_get_state(block) == BUF_BLOCK_FILE_PAGE); #endif /* UNIV_DEBUG || UNIV_BUF_DEBUG */ switch (rw_latch) { case RW_NO_LATCH: if (must_read) { /* Let us wait until the read operation completes */ for (;;) { enum buf_io_fix io_fix; mutex_enter(&block->mutex); io_fix = buf_block_get_io_fix(block); mutex_exit(&block->mutex); if (io_fix == BUF_IO_READ) { os_thread_sleep(WAIT_FOR_READ); } else { break; } } } fix_type = MTR_MEMO_BUF_FIX; break; case RW_S_LATCH: rw_lock_s_lock_func(&(block->lock), 0, file, line); fix_type = MTR_MEMO_PAGE_S_FIX; break; default: ut_ad(rw_latch == RW_X_LATCH); rw_lock_x_lock_func(&(block->lock), 0, file, line); fix_type = MTR_MEMO_PAGE_X_FIX; break; } mtr_memo_push(mtr, block, fix_type); if (!access_time) { /* In the case of a first access, try to apply linear read-ahead */ buf_read_ahead_linear(space, zip_size, offset); } #ifdef UNIV_IBUF_COUNT_DEBUG ut_a(ibuf_count_get(buf_block_get_space(block), buf_block_get_page_no(block)) == 0); #endif return(block); } /********************************************************************//** This is the general function used to get optimistic access to a database page. @return TRUE if success */ UNIV_INTERN ibool buf_page_optimistic_get( /*====================*/ ulint rw_latch,/*!< in: RW_S_LATCH, RW_X_LATCH */ buf_block_t* block, /*!< in: guessed buffer block */ ib_uint64_t modify_clock,/*!< in: modify clock value if mode is ..._GUESS_ON_CLOCK */ const char* file, /*!< in: file name */ ulint line, /*!< in: line where called */ mtr_t* mtr) /*!< in: mini-transaction */ { unsigned access_time; ibool success; ulint fix_type; ut_ad(block); ut_ad(mtr); ut_ad(mtr->state == MTR_ACTIVE); ut_ad((rw_latch == RW_S_LATCH) || (rw_latch == RW_X_LATCH)); mutex_enter(&block->mutex); if (UNIV_UNLIKELY(buf_block_get_state(block) != BUF_BLOCK_FILE_PAGE)) { mutex_exit(&block->mutex); return(FALSE); } buf_block_buf_fix_inc(block, file, line); mutex_exit(&block->mutex); /* Check if this is the first access to the page. We do a dirty read on purpose, to avoid mutex contention. This field is only used for heuristic purposes; it does not affect correctness. */ access_time = buf_page_is_accessed(&block->page); buf_page_set_accessed_make_young(&block->page, access_time); ut_ad(!ibuf_inside() || ibuf_page(buf_block_get_space(block), buf_block_get_zip_size(block), buf_block_get_page_no(block), NULL)); if (rw_latch == RW_S_LATCH) { success = rw_lock_s_lock_nowait(&(block->lock), file, line); fix_type = MTR_MEMO_PAGE_S_FIX; } else { success = rw_lock_x_lock_func_nowait(&(block->lock), file, line); fix_type = MTR_MEMO_PAGE_X_FIX; } if (UNIV_UNLIKELY(!success)) { mutex_enter(&block->mutex); buf_block_buf_fix_dec(block); mutex_exit(&block->mutex); return(FALSE); } if (UNIV_UNLIKELY(modify_clock != block->modify_clock)) { buf_block_dbg_add_level(block, SYNC_NO_ORDER_CHECK); if (rw_latch == RW_S_LATCH) { rw_lock_s_unlock(&(block->lock)); } else { rw_lock_x_unlock(&(block->lock)); } mutex_enter(&block->mutex); buf_block_buf_fix_dec(block); mutex_exit(&block->mutex); return(FALSE); } mtr_memo_push(mtr, block, fix_type); #if defined UNIV_DEBUG || defined UNIV_BUF_DEBUG ut_a(++buf_dbg_counter % 5771 || buf_validate()); ut_a(block->page.buf_fix_count > 0); ut_a(buf_block_get_state(block) == BUF_BLOCK_FILE_PAGE); #endif /* UNIV_DEBUG || UNIV_BUF_DEBUG */ #ifdef UNIV_DEBUG_FILE_ACCESSES ut_a(block->page.file_page_was_freed == FALSE); #endif if (UNIV_UNLIKELY(!access_time)) { /* In the case of a first access, try to apply linear read-ahead */ buf_read_ahead_linear(buf_block_get_space(block), buf_block_get_zip_size(block), buf_block_get_page_no(block)); } #ifdef UNIV_IBUF_COUNT_DEBUG ut_a(ibuf_count_get(buf_block_get_space(block), buf_block_get_page_no(block)) == 0); #endif buf_pool->stat.n_page_gets++; return(TRUE); } /********************************************************************//** This is used to get access to a known database page, when no waiting can be done. For example, if a search in an adaptive hash index leads us to this frame. @return TRUE if success */ UNIV_INTERN ibool buf_page_get_known_nowait( /*======================*/ ulint rw_latch,/*!< in: RW_S_LATCH, RW_X_LATCH */ buf_block_t* block, /*!< in: the known page */ ulint mode, /*!< in: BUF_MAKE_YOUNG or BUF_KEEP_OLD */ const char* file, /*!< in: file name */ ulint line, /*!< in: line where called */ mtr_t* mtr) /*!< in: mini-transaction */ { ibool success; ulint fix_type; ut_ad(mtr); ut_ad(mtr->state == MTR_ACTIVE); ut_ad((rw_latch == RW_S_LATCH) || (rw_latch == RW_X_LATCH)); mutex_enter(&block->mutex); if (buf_block_get_state(block) == BUF_BLOCK_REMOVE_HASH) { /* Another thread is just freeing the block from the LRU list of the buffer pool: do not try to access this page; this attempt to access the page can only come through the hash index because when the buffer block state is ..._REMOVE_HASH, we have already removed it from the page address hash table of the buffer pool. */ mutex_exit(&block->mutex); return(FALSE); } ut_a(buf_block_get_state(block) == BUF_BLOCK_FILE_PAGE); buf_block_buf_fix_inc(block, file, line); mutex_exit(&block->mutex); if (mode == BUF_MAKE_YOUNG && buf_page_peek_if_too_old(&block->page)) { buf_pool_mutex_enter(); buf_LRU_make_block_young(&block->page); buf_pool_mutex_exit(); } else if (!buf_page_is_accessed(&block->page)) { /* Above, we do a dirty read on purpose, to avoid mutex contention. The field buf_page_t::access_time is only used for heuristic purposes. Writes to the field must be protected by mutex, however. */ ulint time_ms = ut_time_ms(); buf_pool_mutex_enter(); buf_page_set_accessed(&block->page, time_ms); buf_pool_mutex_exit(); } ut_ad(!ibuf_inside() || (mode == BUF_KEEP_OLD)); if (rw_latch == RW_S_LATCH) { success = rw_lock_s_lock_nowait(&(block->lock), file, line); fix_type = MTR_MEMO_PAGE_S_FIX; } else { success = rw_lock_x_lock_func_nowait(&(block->lock), file, line); fix_type = MTR_MEMO_PAGE_X_FIX; } if (!success) { mutex_enter(&block->mutex); buf_block_buf_fix_dec(block); mutex_exit(&block->mutex); return(FALSE); } mtr_memo_push(mtr, block, fix_type); #if defined UNIV_DEBUG || defined UNIV_BUF_DEBUG ut_a(++buf_dbg_counter % 5771 || buf_validate()); ut_a(block->page.buf_fix_count > 0); ut_a(buf_block_get_state(block) == BUF_BLOCK_FILE_PAGE); #endif /* UNIV_DEBUG || UNIV_BUF_DEBUG */ #ifdef UNIV_DEBUG_FILE_ACCESSES ut_a(block->page.file_page_was_freed == FALSE); #endif #ifdef UNIV_IBUF_COUNT_DEBUG ut_a((mode == BUF_KEEP_OLD) || (ibuf_count_get(buf_block_get_space(block), buf_block_get_page_no(block)) == 0)); #endif buf_pool->stat.n_page_gets++; return(TRUE); } /*******************************************************************//** Given a tablespace id and page number tries to get that page. If the page is not in the buffer pool it is not loaded and NULL is returned. Suitable for using when holding the kernel mutex. @return pointer to a page or NULL */ UNIV_INTERN const buf_block_t* buf_page_try_get_func( /*==================*/ ulint space_id,/*!< in: tablespace id */ ulint page_no,/*!< in: page number */ const char* file, /*!< in: file name */ ulint line, /*!< in: line where called */ mtr_t* mtr) /*!< in: mini-transaction */ { buf_block_t* block; ibool success; ulint fix_type; ut_ad(mtr); ut_ad(mtr->state == MTR_ACTIVE); buf_pool_mutex_enter(); block = buf_block_hash_get(space_id, page_no); if (!block) { buf_pool_mutex_exit(); return(NULL); } mutex_enter(&block->mutex); buf_pool_mutex_exit(); #if defined UNIV_DEBUG || defined UNIV_BUF_DEBUG ut_a(buf_block_get_state(block) == BUF_BLOCK_FILE_PAGE); ut_a(buf_block_get_space(block) == space_id); ut_a(buf_block_get_page_no(block) == page_no); #endif /* UNIV_DEBUG || UNIV_BUF_DEBUG */ buf_block_buf_fix_inc(block, file, line); mutex_exit(&block->mutex); fix_type = MTR_MEMO_PAGE_S_FIX; success = rw_lock_s_lock_nowait(&block->lock, file, line); if (!success) { /* Let us try to get an X-latch. If the current thread is holding an X-latch on the page, we cannot get an S-latch. */ fix_type = MTR_MEMO_PAGE_X_FIX; success = rw_lock_x_lock_func_nowait(&block->lock, file, line); } if (!success) { mutex_enter(&block->mutex); buf_block_buf_fix_dec(block); mutex_exit(&block->mutex); return(NULL); } mtr_memo_push(mtr, block, fix_type); #if defined UNIV_DEBUG || defined UNIV_BUF_DEBUG ut_a(++buf_dbg_counter % 5771 || buf_validate()); ut_a(block->page.buf_fix_count > 0); ut_a(buf_block_get_state(block) == BUF_BLOCK_FILE_PAGE); #endif /* UNIV_DEBUG || UNIV_BUF_DEBUG */ #ifdef UNIV_DEBUG_FILE_ACCESSES ut_a(block->page.file_page_was_freed == FALSE); #endif /* UNIV_DEBUG_FILE_ACCESSES */ buf_block_dbg_add_level(block, SYNC_NO_ORDER_CHECK); buf_pool->stat.n_page_gets++; #ifdef UNIV_IBUF_COUNT_DEBUG ut_a(ibuf_count_get(buf_block_get_space(block), buf_block_get_page_no(block)) == 0); #endif return(block); } /********************************************************************//** Initialize some fields of a control block. */ UNIV_INLINE void buf_page_init_low( /*==============*/ buf_page_t* bpage) /*!< in: block to init */ { bpage->flush_type = BUF_FLUSH_LRU; bpage->io_fix = BUF_IO_NONE; bpage->buf_fix_count = 0; bpage->freed_page_clock = 0; bpage->access_time = 0; bpage->newest_modification = 0; bpage->oldest_modification = 0; HASH_INVALIDATE(bpage, hash); #ifdef UNIV_DEBUG_FILE_ACCESSES bpage->file_page_was_freed = FALSE; #endif /* UNIV_DEBUG_FILE_ACCESSES */ } /********************************************************************//** Inits a page to the buffer buf_pool. */ static void buf_page_init( /*==========*/ ulint space, /*!< in: space id */ ulint offset, /*!< in: offset of the page within space in units of a page */ buf_block_t* block) /*!< in: block to init */ { buf_page_t* hash_page; ut_ad(buf_pool_mutex_own()); ut_ad(mutex_own(&(block->mutex))); ut_a(buf_block_get_state(block) != BUF_BLOCK_FILE_PAGE); /* Set the state of the block */ buf_block_set_file_page(block, space, offset); #ifdef UNIV_DEBUG_VALGRIND if (!space) { /* Silence valid Valgrind warnings about uninitialized data being written to data files. There are some unused bytes on some pages that InnoDB does not initialize. */ UNIV_MEM_VALID(block->frame, UNIV_PAGE_SIZE); } #endif /* UNIV_DEBUG_VALGRIND */ buf_block_init_low(block); block->lock_hash_val = lock_rec_hash(space, offset); /* Insert into the hash table of file pages */ hash_page = buf_page_hash_get(space, offset); if (UNIV_LIKELY_NULL(hash_page)) { ib_logger(ib_stream, "InnoDB: Error: page %lu %lu already found" " in the hash table: %p, %p\n", (ulong) space, (ulong) offset, (const void*) hash_page, (const void*) block); #if defined UNIV_DEBUG || defined UNIV_BUF_DEBUG mutex_exit(&block->mutex); buf_pool_mutex_exit(); buf_print(); buf_LRU_print(); buf_validate(); buf_LRU_validate(); #endif /* UNIV_DEBUG || UNIV_BUF_DEBUG */ ut_error; } buf_page_init_low(&block->page); ut_ad(!block->page.in_zip_hash); ut_ad(!block->page.in_page_hash); ut_d(block->page.in_page_hash = TRUE); HASH_INSERT(buf_page_t, hash, buf_pool->page_hash, buf_page_address_fold(space, offset), &block->page); } /********************************************************************//** Function which inits a page for read to the buffer buf_pool. If the page is (1) already in buf_pool, or (2) if we specify to read only ibuf pages and the page is not an ibuf page, or (3) if the space is deleted or being deleted, then this function does nothing. Sets the io_fix flag to BUF_IO_READ and sets a non-recursive exclusive lock on the buffer frame. The io-handler must take care that the flag is cleared and the lock released later. @return pointer to the block or NULL */ UNIV_INTERN buf_page_t* buf_page_init_for_read( /*===================*/ ulint* err, /*!< out: DB_SUCCESS or DB_TABLESPACE_DELETED */ ulint mode, /*!< in: BUF_READ_IBUF_PAGES_ONLY, ... */ ulint space, /*!< in: space id */ ulint zip_size,/*!< in: compressed page size, or 0 */ ibool unzip, /*!< in: TRUE=request uncompressed page */ ib_int64_t tablespace_version,/*!< in: prevents reading from a wrong version of the tablespace in case we have done DISCARD + IMPORT */ ulint offset) /*!< in: page number */ { buf_block_t* block; buf_page_t* bpage = NULL; mtr_t mtr; ibool lru = FALSE; void* data; ut_ad(buf_pool); *err = DB_SUCCESS; if (mode == BUF_READ_IBUF_PAGES_ONLY) { /* It is a read-ahead within an ibuf routine */ ut_ad(!ibuf_bitmap_page(zip_size, offset)); ut_ad(ibuf_inside()); mtr_start(&mtr); if (!recv_no_ibuf_operations && !ibuf_page(space, zip_size, offset, &mtr)) { mtr_commit(&mtr); return(NULL); } } else { ut_ad(mode == BUF_READ_ANY_PAGE); } if (zip_size && UNIV_LIKELY(!unzip) && UNIV_LIKELY(!recv_recovery_is_on())) { block = NULL; } else { block = buf_LRU_get_free_block(0); ut_ad(block); } buf_pool_mutex_enter(); if (buf_page_hash_get(space, offset)) { /* The page is already in the buffer pool. */ err_exit: if (block) { mutex_enter(&block->mutex); buf_LRU_block_free_non_file_page(block); mutex_exit(&block->mutex); } bpage = NULL; goto func_exit; } if (fil_tablespace_deleted_or_being_deleted_in_mem( space, tablespace_version)) { /* The page belongs to a space which has been deleted or is being deleted. */ *err = DB_TABLESPACE_DELETED; goto err_exit; } if (block) { bpage = &block->page; mutex_enter(&block->mutex); buf_page_init(space, offset, block); /* The block must be put to the LRU list, to the old blocks */ buf_LRU_add_block(bpage, TRUE/* to old blocks */); /* We set a pass-type x-lock on the frame because then the same thread which called for the read operation (and is running now at this point of code) can wait for the read to complete by waiting for the x-lock on the frame; if the x-lock were recursive, the same thread would illegally get the x-lock before the page read is completed. The x-lock is cleared by the io-handler thread. */ rw_lock_x_lock_gen(&block->lock, BUF_IO_READ); buf_page_set_io_fix(bpage, BUF_IO_READ); if (UNIV_UNLIKELY(zip_size)) { page_zip_set_size(&block->page.zip, zip_size); /* buf_pool_mutex may be released and reacquired by buf_buddy_alloc(). Thus, we must release block->mutex in order not to break the latching order in the reacquisition of buf_pool_mutex. We also must defer this operation until after the block descriptor has been added to buf_pool->LRU and buf_pool->page_hash. */ mutex_exit(&block->mutex); data = buf_buddy_alloc(zip_size, &lru); mutex_enter(&block->mutex); block->page.zip.data = data; /* To maintain the invariant block->in_unzip_LRU_list == buf_page_belongs_to_unzip_LRU(&block->page) we have to add this block to unzip_LRU after block->page.zip.data is set. */ ut_ad(buf_page_belongs_to_unzip_LRU(&block->page)); buf_unzip_LRU_add_block(block, TRUE); } mutex_exit(&block->mutex); } else { /* Defer buf_buddy_alloc() until after the block has been found not to exist. The buf_buddy_alloc() and buf_buddy_free() calls may be expensive because of buf_buddy_relocate(). */ /* The compressed page must be allocated before the control block (bpage), in order to avoid the invocation of buf_buddy_relocate_block() on uninitialized data. */ data = buf_buddy_alloc(zip_size, &lru); bpage = buf_buddy_alloc(sizeof *bpage, &lru); /* If buf_buddy_alloc() allocated storage from the LRU list, it released and reacquired buf_pool_mutex. Thus, we must check the page_hash again, as it may have been modified. */ if (UNIV_UNLIKELY(lru) && UNIV_LIKELY_NULL(buf_page_hash_get(space, offset))) { /* The block was added by some other thread. */ buf_buddy_free(bpage, sizeof *bpage); buf_buddy_free(data, zip_size); bpage = NULL; goto func_exit; } page_zip_des_init(&bpage->zip); page_zip_set_size(&bpage->zip, zip_size); bpage->zip.data = data; mutex_enter(&buf_pool_zip_mutex); UNIV_MEM_DESC(bpage->zip.data, page_zip_get_size(&bpage->zip), bpage); buf_page_init_low(bpage); bpage->state = BUF_BLOCK_ZIP_PAGE; bpage->space = space; bpage->offset = offset; #ifdef UNIV_DEBUG bpage->in_page_hash = FALSE; bpage->in_zip_hash = FALSE; bpage->in_flush_list = FALSE; bpage->in_free_list = FALSE; bpage->in_LRU_list = FALSE; #endif /* UNIV_DEBUG */ ut_d(bpage->in_page_hash = TRUE); HASH_INSERT(buf_page_t, hash, buf_pool->page_hash, buf_page_address_fold(space, offset), bpage); /* The block must be put to the LRU list, to the old blocks */ buf_LRU_add_block(bpage, TRUE/* to old blocks */); buf_LRU_insert_zip_clean(bpage); buf_page_set_io_fix(bpage, BUF_IO_READ); mutex_exit(&buf_pool_zip_mutex); } buf_pool->n_pend_reads++; func_exit: buf_pool_mutex_exit(); if (mode == BUF_READ_IBUF_PAGES_ONLY) { mtr_commit(&mtr); } ut_ad(!bpage || buf_page_in_file(bpage)); return(bpage); } /********************************************************************//** Initializes a page to the buffer buf_pool. The page is usually not read from a file even if it cannot be found in the buffer buf_pool. This is one of the functions which perform to a block a state transition NOT_USED => FILE_PAGE (the other is buf_page_get_gen). @return pointer to the block, page bufferfixed */ UNIV_INTERN buf_block_t* buf_page_create( /*============*/ ulint space, /*!< in: space id */ ulint offset, /*!< in: offset of the page within space in units of a page */ ulint zip_size,/*!< in: compressed page size, or 0 */ mtr_t* mtr) /*!< in: mini-transaction handle */ { buf_frame_t* frame; buf_block_t* block; buf_block_t* free_block = NULL; ulint time_ms = ut_time_ms(); ut_ad(mtr); ut_ad(mtr->state == MTR_ACTIVE); ut_ad(space || !zip_size); free_block = buf_LRU_get_free_block(0); buf_pool_mutex_enter(); block = (buf_block_t*) buf_page_hash_get(space, offset); if (block && buf_page_in_file(&block->page)) { #ifdef UNIV_IBUF_COUNT_DEBUG ut_a(ibuf_count_get(space, offset) == 0); #endif #ifdef UNIV_DEBUG_FILE_ACCESSES block->page.file_page_was_freed = FALSE; #endif /* UNIV_DEBUG_FILE_ACCESSES */ /* Page can be found in buf_pool */ buf_pool_mutex_exit(); buf_block_free(free_block); return(buf_page_get_with_no_latch(space, zip_size, offset, mtr)); } /* If we get here, the page was not in buf_pool: init it there */ #ifdef UNIV_DEBUG if (buf_debug_prints) { ib_logger(ib_stream, "Creating space %lu page %lu to buffer\n", (ulong) space, (ulong) offset); } #endif /* UNIV_DEBUG */ block = free_block; mutex_enter(&block->mutex); buf_page_init(space, offset, block); /* The block must be put to the LRU list */ buf_LRU_add_block(&block->page, FALSE); buf_block_buf_fix_inc(block, __FILE__, __LINE__); buf_pool->stat.n_pages_created++; if (zip_size) { void* data; ibool lru; /* Prevent race conditions during buf_buddy_alloc(), which may release and reacquire buf_pool_mutex, by IO-fixing and X-latching the block. */ buf_page_set_io_fix(&block->page, BUF_IO_READ); rw_lock_x_lock(&block->lock); page_zip_set_size(&block->page.zip, zip_size); mutex_exit(&block->mutex); /* buf_pool_mutex may be released and reacquired by buf_buddy_alloc(). Thus, we must release block->mutex in order not to break the latching order in the reacquisition of buf_pool_mutex. We also must defer this operation until after the block descriptor has been added to buf_pool->LRU and buf_pool->page_hash. */ data = buf_buddy_alloc(zip_size, &lru); mutex_enter(&block->mutex); block->page.zip.data = data; /* To maintain the invariant block->in_unzip_LRU_list == buf_page_belongs_to_unzip_LRU(&block->page) we have to add this block to unzip_LRU after block->page.zip.data is set. */ ut_ad(buf_page_belongs_to_unzip_LRU(&block->page)); buf_unzip_LRU_add_block(block, FALSE); buf_page_set_io_fix(&block->page, BUF_IO_NONE); rw_lock_x_unlock(&block->lock); } buf_page_set_accessed(&block->page, time_ms); buf_pool_mutex_exit(); mtr_memo_push(mtr, block, MTR_MEMO_BUF_FIX); mutex_exit(&block->mutex); /* Delete possible entries for the page from the insert buffer: such can exist if the page belonged to an index which was dropped */ ibuf_merge_or_delete_for_page(NULL, space, offset, zip_size, TRUE); /* Flush pages from the end of the LRU list if necessary */ buf_flush_free_margin(); frame = block->frame; memset(frame + FIL_PAGE_PREV, 0xff, 4); memset(frame + FIL_PAGE_NEXT, 0xff, 4); mach_write_to_2(frame + FIL_PAGE_TYPE, FIL_PAGE_TYPE_ALLOCATED); /* Reset to zero the file flush lsn field in the page; if the first page of an ibdata file is 'created' in this function into the buffer pool then we lose the original contents of the file flush lsn stamp. Then InnoDB could in a crash recovery print a big, false, corruption warning if the stamp contains an lsn bigger than the ib_logfile lsn. */ memset(frame + FIL_PAGE_FILE_FLUSH_LSN, 0, 8); #if defined UNIV_DEBUG || defined UNIV_BUF_DEBUG ut_a(++buf_dbg_counter % 357 || buf_validate()); #endif /* UNIV_DEBUG || UNIV_BUF_DEBUG */ #ifdef UNIV_IBUF_COUNT_DEBUG ut_a(ibuf_count_get(buf_block_get_space(block), buf_block_get_page_no(block)) == 0); #endif return(block); } /********************************************************************//** Completes an asynchronous read or write request of a file page to or from the buffer pool. */ UNIV_INTERN void buf_page_io_complete( /*=================*/ buf_page_t* bpage) /*!< in: pointer to the block in question */ { enum buf_io_fix io_type; const ibool uncompressed = (buf_page_get_state(bpage) == BUF_BLOCK_FILE_PAGE); ut_a(buf_page_in_file(bpage)); /* We do not need protect io_fix here by mutex to read it because this is the only function where we can change the value from BUF_IO_READ or BUF_IO_WRITE to some other value, and our code ensures that this is the only thread that handles the i/o for this block. */ io_type = buf_page_get_io_fix(bpage); ut_ad(io_type == BUF_IO_READ || io_type == BUF_IO_WRITE); if (io_type == BUF_IO_READ) { ulint read_page_no; ulint read_space_id; byte* frame; if (buf_page_get_zip_size(bpage)) { frame = bpage->zip.data; buf_pool->n_pend_unzip++; if (uncompressed && !buf_zip_decompress((buf_block_t*) bpage, FALSE)) { buf_pool->n_pend_unzip--; goto corrupt; } buf_pool->n_pend_unzip--; } else { ut_a(uncompressed); frame = ((buf_block_t*) bpage)->frame; } /* If this page is not uninitialized and not in the doublewrite buffer, then the page number and space id should be the same as in block. */ read_page_no = mach_read_from_4(frame + FIL_PAGE_OFFSET); read_space_id = mach_read_from_4( frame + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID); if (bpage->space == TRX_SYS_SPACE && trx_doublewrite_page_inside(bpage->offset)) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Error: reading page %lu\n" "InnoDB: which is in the" " doublewrite buffer!\n", (ulong) bpage->offset); } else if (!read_space_id && !read_page_no) { /* This is likely an uninitialized page. */ } else if ((bpage->space && bpage->space != read_space_id) || bpage->offset != read_page_no) { /* We did not compare space_id to read_space_id if bpage->space == 0, because the field on the page may contain garbage in version < 4.1.1, which only supported bpage->space == 0. */ ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Error: space id and page n:o" " stored in the page\n" "InnoDB: read in are %lu:%lu," " should be %lu:%lu!\n", (ulong) read_space_id, (ulong) read_page_no, (ulong) bpage->space, (ulong) bpage->offset); } /* From version 3.23.38 up we store the page checksum to the 4 first bytes of the page end lsn field */ if (buf_page_is_corrupted(frame, buf_page_get_zip_size(bpage))) { corrupt: ib_logger(ib_stream, "InnoDB: Database page corruption on disk" " or a failed\n" "InnoDB: file read of page %lu.\n" "InnoDB: You may have to recover" " from a backup.\n", (ulong) bpage->offset); buf_page_print(frame, buf_page_get_zip_size(bpage)); ib_logger(ib_stream, "InnoDB: Database page corruption on disk" " or a failed\n" "InnoDB: file read of page %lu.\n" "InnoDB: You may have to recover" " from a backup.\n", (ulong) bpage->offset); ib_logger(ib_stream, "InnoDB: It is also possible that" " your operating\n" "InnoDB: system has corrupted its" " own file cache\n" "InnoDB: and rebooting your computer" " removes the\n" "InnoDB: error.\n" "InnoDB: If the corrupt page is an index page\n" "InnoDB: you can also try to" " fix the corruption\n" "InnoDB: by dumping, dropping," " and reimporting\n" "InnoDB: the corrupt table." " You can use CHECK\n" "InnoDB: TABLE to scan your" " table for corruption.\n" "InnoDB: See also" " the InnoDB website for details\n" "InnoDB: about forcing recovery.\n"); if (srv_force_recovery < IB_RECOVERY_IGNORE_CORRUPT) { srv_panic(DB_CORRUPTION, "InnoDB: Ending processing because of" " a corrupt database page.\n"); } } if (recv_recovery_is_on()) { /* Pages must be uncompressed for crash recovery. */ ut_a(uncompressed); recv_recover_page(TRUE, (buf_block_t*) bpage); } if (uncompressed && !recv_no_ibuf_operations) { ibuf_merge_or_delete_for_page( (buf_block_t*) bpage, bpage->space, bpage->offset, buf_page_get_zip_size(bpage), TRUE); } } buf_pool_mutex_enter(); mutex_enter(buf_page_get_mutex(bpage)); #ifdef UNIV_IBUF_COUNT_DEBUG if (io_type == BUF_IO_WRITE || uncompressed) { /* For BUF_IO_READ of compressed-only blocks, the buffered operations will be merged by buf_page_get_gen() after the block has been uncompressed. */ ut_a(ibuf_count_get(bpage->space, bpage->offset) == 0); } #endif /* Because this thread which does the unlocking is not the same that did the locking, we use a pass value != 0 in unlock, which simply removes the newest lock debug record, without checking the thread id. */ buf_page_set_io_fix(bpage, BUF_IO_NONE); switch (io_type) { case BUF_IO_READ: /* NOTE that the call to ibuf may have moved the ownership of the x-latch to this OS thread: do not let this confuse you in debugging! */ ut_ad(buf_pool->n_pend_reads > 0); buf_pool->n_pend_reads--; buf_pool->stat.n_pages_read++; if (uncompressed) { rw_lock_x_unlock_gen(&((buf_block_t*) bpage)->lock, BUF_IO_READ); } break; case BUF_IO_WRITE: /* Write means a flush operation: call the completion routine in the flush system */ buf_flush_write_complete(bpage); if (uncompressed) { rw_lock_s_unlock_gen(&((buf_block_t*) bpage)->lock, BUF_IO_WRITE); } buf_pool->stat.n_pages_written++; break; default: ut_error; } #ifdef UNIV_DEBUG if (buf_debug_prints) { ib_logger(ib_stream, "Has %s page space %lu page no %lu\n", io_type == BUF_IO_READ ? "read" : "written", (ulong) buf_page_get_space(bpage), (ulong) buf_page_get_page_no(bpage)); } #endif /* UNIV_DEBUG */ mutex_exit(buf_page_get_mutex(bpage)); buf_pool_mutex_exit(); } /*********************************************************************//** Invalidates the file pages in the buffer pool when an archive recovery is completed. All the file pages buffered must be in a replaceable state when this function is called: not latched and not modified. */ UNIV_INTERN void buf_pool_invalidate(void) /*=====================*/ { ibool freed; enum buf_flush i; buf_pool_mutex_enter(); for (i = BUF_FLUSH_LRU; i < BUF_FLUSH_N_TYPES; i++) { /* As this function is called during startup and during redo application phase during recovery, InnoDB is single threaded (apart from IO helper threads) at this stage. No new write batch can be in intialization stage at this point. */ ut_ad(buf_pool->init_flush[i] == FALSE); /* However, it is possible that a write batch that has been posted earlier is still not complete. For buffer pool invalidation to proceed we must ensure there is NO write activity happening. */ if (buf_pool->n_flush[i] > 0) { buf_pool_mutex_exit(); buf_flush_wait_batch_end(i); buf_pool_mutex_enter(); } } buf_pool_mutex_exit(); ut_ad(buf_all_freed()); freed = TRUE; while (freed) { freed = buf_LRU_search_and_free_block(100); } buf_pool_mutex_enter(); ut_ad(UT_LIST_GET_LEN(buf_pool->LRU) == 0); ut_ad(UT_LIST_GET_LEN(buf_pool->unzip_LRU) == 0); buf_pool->freed_page_clock = 0; buf_pool->LRU_old = NULL; buf_pool->LRU_old_len = 0; buf_pool->LRU_flush_ended = 0; memset(&buf_pool->stat, 0x00, sizeof(buf_pool->stat)); buf_refresh_io_stats(); buf_pool_mutex_exit(); } #if defined UNIV_DEBUG || defined UNIV_BUF_DEBUG /*********************************************************************//** Validates the buffer buf_pool data structure. @return TRUE */ UNIV_INTERN ibool buf_validate(void) /*==============*/ { buf_page_t* b; buf_chunk_t* chunk; ulint i; ulint n_single_flush = 0; ulint n_lru_flush = 0; ulint n_list_flush = 0; ulint n_lru = 0; ulint n_flush = 0; ulint n_free = 0; ulint n_zip = 0; ut_ad(buf_pool); buf_pool_mutex_enter(); chunk = buf_pool->chunks; /* Check the uncompressed blocks. */ for (i = buf_pool->n_chunks; i--; chunk++) { ulint j; buf_block_t* block = chunk->blocks; for (j = chunk->size; j--; block++) { mutex_enter(&block->mutex); switch (buf_block_get_state(block)) { case BUF_BLOCK_ZIP_FREE: case BUF_BLOCK_ZIP_PAGE: case BUF_BLOCK_ZIP_DIRTY: /* These should only occur on zip_clean, zip_free[], or flush_list. */ ut_error; break; case BUF_BLOCK_FILE_PAGE: ut_a(buf_page_hash_get(buf_block_get_space( block), buf_block_get_page_no( block)) == &block->page); #ifdef UNIV_IBUF_COUNT_DEBUG ut_a(buf_page_get_io_fix(&block->page) == BUF_IO_READ || !ibuf_count_get(buf_block_get_space( block), buf_block_get_page_no( block))); #endif switch (buf_page_get_io_fix(&block->page)) { case BUF_IO_NONE: break; case BUF_IO_WRITE: switch (buf_page_get_flush_type( &block->page)) { case BUF_FLUSH_LRU: n_lru_flush++; ut_a(rw_lock_is_locked( &block->lock, RW_LOCK_SHARED)); break; case BUF_FLUSH_LIST: n_list_flush++; break; case BUF_FLUSH_SINGLE_PAGE: n_single_flush++; break; default: ut_error; } break; case BUF_IO_READ: ut_a(rw_lock_is_locked(&block->lock, RW_LOCK_EX)); break; } n_lru++; if (block->page.oldest_modification > 0) { n_flush++; } break; case BUF_BLOCK_NOT_USED: n_free++; break; case BUF_BLOCK_READY_FOR_USE: case BUF_BLOCK_MEMORY: case BUF_BLOCK_REMOVE_HASH: /* do nothing */ break; } mutex_exit(&block->mutex); } } mutex_enter(&buf_pool_zip_mutex); /* Check clean compressed-only blocks. */ for (b = UT_LIST_GET_FIRST(buf_pool->zip_clean); b; b = UT_LIST_GET_NEXT(list, b)) { ut_a(buf_page_get_state(b) == BUF_BLOCK_ZIP_PAGE); switch (buf_page_get_io_fix(b)) { case BUF_IO_NONE: /* All clean blocks should be I/O-unfixed. */ break; case BUF_IO_READ: /* In buf_LRU_free_block(), we temporarily set b->io_fix = BUF_IO_READ for a newly allocated control block in order to prevent buf_page_get_gen() from decompressing the block. */ break; default: ut_error; break; } ut_a(!b->oldest_modification); ut_a(buf_page_hash_get(b->space, b->offset) == b); n_lru++; n_zip++; } /* Check dirty compressed-only blocks. */ for (b = UT_LIST_GET_FIRST(buf_pool->flush_list); b; b = UT_LIST_GET_NEXT(list, b)) { ut_ad(b->in_flush_list); switch (buf_page_get_state(b)) { case BUF_BLOCK_ZIP_DIRTY: ut_a(b->oldest_modification); n_lru++; n_flush++; n_zip++; switch (buf_page_get_io_fix(b)) { case BUF_IO_NONE: case BUF_IO_READ: break; case BUF_IO_WRITE: switch (buf_page_get_flush_type(b)) { case BUF_FLUSH_LRU: n_lru_flush++; break; case BUF_FLUSH_LIST: n_list_flush++; break; case BUF_FLUSH_SINGLE_PAGE: n_single_flush++; break; default: ut_error; } break; } break; case BUF_BLOCK_FILE_PAGE: /* uncompressed page */ break; case BUF_BLOCK_ZIP_FREE: case BUF_BLOCK_ZIP_PAGE: case BUF_BLOCK_NOT_USED: case BUF_BLOCK_READY_FOR_USE: case BUF_BLOCK_MEMORY: case BUF_BLOCK_REMOVE_HASH: ut_error; break; } ut_a(buf_page_hash_get(b->space, b->offset) == b); } mutex_exit(&buf_pool_zip_mutex); if (n_lru + n_free > buf_pool->curr_size + n_zip) { ib_logger(ib_stream, "n LRU %lu, n free %lu, pool %lu zip %lu\n", (ulong) n_lru, (ulong) n_free, (ulong) buf_pool->curr_size, (ulong) n_zip); ut_error; } ut_a(UT_LIST_GET_LEN(buf_pool->LRU) == n_lru); if (UT_LIST_GET_LEN(buf_pool->free) != n_free) { ib_logger(ib_stream, "Free list len %lu, free blocks %lu\n", (ulong) UT_LIST_GET_LEN(buf_pool->free), (ulong) n_free); ut_error; } ut_a(UT_LIST_GET_LEN(buf_pool->flush_list) == n_flush); ut_a(buf_pool->n_flush[BUF_FLUSH_SINGLE_PAGE] == n_single_flush); ut_a(buf_pool->n_flush[BUF_FLUSH_LIST] == n_list_flush); ut_a(buf_pool->n_flush[BUF_FLUSH_LRU] == n_lru_flush); buf_pool_mutex_exit(); ut_a(buf_LRU_validate()); ut_a(buf_flush_validate()); return(TRUE); } #endif /* UNIV_DEBUG || UNIV_BUF_DEBUG */ #if defined UNIV_DEBUG_PRINT || defined UNIV_DEBUG || defined UNIV_BUF_DEBUG /*********************************************************************//** Prints info of the buffer buf_pool data structure. */ UNIV_INTERN void buf_print(void) /*===========*/ { dulint* index_ids; ulint* counts; ulint size; ulint i; ulint j; dulint id; ulint n_found; buf_chunk_t* chunk; dict_index_t* index; ut_ad(buf_pool); size = buf_pool->curr_size; index_ids = mem_alloc(sizeof(dulint) * size); counts = mem_alloc(sizeof(ulint) * size); buf_pool_mutex_enter(); ib_logger(ib_stream, "buf_pool size %lu\n" "database pages %lu\n" "free pages %lu\n" "modified database pages %lu\n" "n pending decompressions %lu\n" "n pending reads %lu\n" "n pending flush LRU %lu list %lu single page %lu\n" "pages made young %lu, not young %lu\n" "pages read %lu, created %lu, written %lu\n", (ulong) size, (ulong) UT_LIST_GET_LEN(buf_pool->LRU), (ulong) UT_LIST_GET_LEN(buf_pool->free), (ulong) UT_LIST_GET_LEN(buf_pool->flush_list), (ulong) buf_pool->n_pend_unzip, (ulong) buf_pool->n_pend_reads, (ulong) buf_pool->n_flush[BUF_FLUSH_LRU], (ulong) buf_pool->n_flush[BUF_FLUSH_LIST], (ulong) buf_pool->n_flush[BUF_FLUSH_SINGLE_PAGE], (ulong) buf_pool->stat.n_pages_made_young, (ulong) buf_pool->stat.n_pages_not_made_young, (ulong) buf_pool->stat.n_pages_read, (ulong) buf_pool->stat.n_pages_created, (ulong) buf_pool->stat.n_pages_written); /* Count the number of blocks belonging to each index in the buffer */ n_found = 0; chunk = buf_pool->chunks; for (i = buf_pool->n_chunks; i--; chunk++) { buf_block_t* block = chunk->blocks; ulint n_blocks = chunk->size; for (; n_blocks--; block++) { const buf_frame_t* frame = block->frame; if (fil_page_get_type(frame) == FIL_PAGE_INDEX) { id = btr_page_get_index_id(frame); /* Look for the id in the index_ids array */ j = 0; while (j < n_found) { if (ut_dulint_cmp(index_ids[j], id) == 0) { counts[j]++; break; } j++; } if (j == n_found) { n_found++; index_ids[j] = id; counts[j] = 1; } } } } buf_pool_mutex_exit(); for (i = 0; i < n_found; i++) { index = dict_index_get_if_in_cache(index_ids[i]); ib_logger(ib_stream, "Block count for index %lu in buffer is about %lu", (ulong) ut_dulint_get_low(index_ids[i]), (ulong) counts[i]); if (index) { ib_logger(ib_stream, " "); dict_index_name_print(ib_stream, NULL, index); } ib_logger(ib_stream, "\n"); } mem_free(index_ids); mem_free(counts); ut_a(buf_validate()); } #endif /* UNIV_DEBUG_PRINT || UNIV_DEBUG || UNIV_BUF_DEBUG */ #ifdef UNIV_DEBUG /*********************************************************************//** Returns the number of latched pages in the buffer pool. @return number of latched pages */ UNIV_INTERN ulint buf_get_latched_pages_number(void) /*==============================*/ { buf_chunk_t* chunk; buf_page_t* b; ulint i; ulint fixed_pages_number = 0; buf_pool_mutex_enter(); chunk = buf_pool->chunks; for (i = buf_pool->n_chunks; i--; chunk++) { buf_block_t* block; ulint j; block = chunk->blocks; for (j = chunk->size; j--; block++) { if (buf_block_get_state(block) != BUF_BLOCK_FILE_PAGE) { continue; } mutex_enter(&block->mutex); if (block->page.buf_fix_count != 0 || buf_page_get_io_fix(&block->page) != BUF_IO_NONE) { fixed_pages_number++; } mutex_exit(&block->mutex); } } mutex_enter(&buf_pool_zip_mutex); /* Traverse the lists of clean and dirty compressed-only blocks. */ for (b = UT_LIST_GET_FIRST(buf_pool->zip_clean); b; b = UT_LIST_GET_NEXT(list, b)) { ut_a(buf_page_get_state(b) == BUF_BLOCK_ZIP_PAGE); ut_a(buf_page_get_io_fix(b) != BUF_IO_WRITE); if (b->buf_fix_count != 0 || buf_page_get_io_fix(b) != BUF_IO_NONE) { fixed_pages_number++; } } for (b = UT_LIST_GET_FIRST(buf_pool->flush_list); b; b = UT_LIST_GET_NEXT(list, b)) { ut_ad(b->in_flush_list); switch (buf_page_get_state(b)) { case BUF_BLOCK_ZIP_DIRTY: if (b->buf_fix_count != 0 || buf_page_get_io_fix(b) != BUF_IO_NONE) { fixed_pages_number++; } break; case BUF_BLOCK_FILE_PAGE: /* uncompressed page */ break; case BUF_BLOCK_ZIP_FREE: case BUF_BLOCK_ZIP_PAGE: case BUF_BLOCK_NOT_USED: case BUF_BLOCK_READY_FOR_USE: case BUF_BLOCK_MEMORY: case BUF_BLOCK_REMOVE_HASH: ut_error; break; } } mutex_exit(&buf_pool_zip_mutex); buf_pool_mutex_exit(); return(fixed_pages_number); } #endif /* UNIV_DEBUG */ /*********************************************************************//** Returns the number of pending buf pool ios. @return number of pending I/O operations */ UNIV_INTERN ulint buf_get_n_pending_ios(void) /*=======================*/ { return(buf_pool->n_pend_reads + buf_pool->n_flush[BUF_FLUSH_LRU] + buf_pool->n_flush[BUF_FLUSH_LIST] + buf_pool->n_flush[BUF_FLUSH_SINGLE_PAGE]); } /*********************************************************************//** Returns the ratio in percents of modified pages in the buffer pool / database pages in the buffer pool. @return modified page percentage ratio */ UNIV_INTERN ulint buf_get_modified_ratio_pct(void) /*============================*/ { ulint ratio; buf_pool_mutex_enter(); ratio = (100 * UT_LIST_GET_LEN(buf_pool->flush_list)) / (1 + UT_LIST_GET_LEN(buf_pool->LRU) + UT_LIST_GET_LEN(buf_pool->free)); /* 1 + is there to avoid division by zero */ buf_pool_mutex_exit(); return(ratio); } /*********************************************************************//** Prints info of the buffer i/o. */ UNIV_INTERN void buf_print_io( /*=========*/ ib_stream_t ib_stream) /*!< in/out: buffer where to print */ { time_t current_time; double time_elapsed; ulint n_gets_diff; ut_ad(buf_pool); buf_pool_mutex_enter(); ib_logger(ib_stream, "Buffer pool size %lu\n" "Free buffers %lu\n" "Database pages %lu\n" "Old database pages %lu\n" "Modified db pages %lu\n" "Pending reads %lu\n" "Pending writes: LRU %lu, flush list %lu, single page %lu\n", (ulong) buf_pool->curr_size, (ulong) UT_LIST_GET_LEN(buf_pool->free), (ulong) UT_LIST_GET_LEN(buf_pool->LRU), (ulong) buf_pool->LRU_old_len, (ulong) UT_LIST_GET_LEN(buf_pool->flush_list), (ulong) buf_pool->n_pend_reads, (ulong) buf_pool->n_flush[BUF_FLUSH_LRU] + buf_pool->init_flush[BUF_FLUSH_LRU], (ulong) buf_pool->n_flush[BUF_FLUSH_LIST] + buf_pool->init_flush[BUF_FLUSH_LIST], (ulong) buf_pool->n_flush[BUF_FLUSH_SINGLE_PAGE]); current_time = time(NULL); time_elapsed = 0.001 + difftime(current_time, buf_pool->last_printout_time); ib_logger(ib_stream, "Pages made young %lu, not young %lu\n" "%.2f youngs/s, %.2f non-youngs/s\n" "Pages read %lu, created %lu, written %lu\n" "%.2f reads/s, %.2f creates/s, %.2f writes/s\n", (ulong) buf_pool->stat.n_pages_made_young, (ulong) buf_pool->stat.n_pages_not_made_young, (buf_pool->stat.n_pages_made_young - buf_pool->old_stat.n_pages_made_young) / time_elapsed, (buf_pool->stat.n_pages_not_made_young - buf_pool->old_stat.n_pages_not_made_young) / time_elapsed, (ulong) buf_pool->stat.n_pages_read, (ulong) buf_pool->stat.n_pages_created, (ulong) buf_pool->stat.n_pages_written, (buf_pool->stat.n_pages_read - buf_pool->old_stat.n_pages_read) / time_elapsed, (buf_pool->stat.n_pages_created - buf_pool->old_stat.n_pages_created) / time_elapsed, (buf_pool->stat.n_pages_written - buf_pool->old_stat.n_pages_written) / time_elapsed); n_gets_diff = buf_pool->stat.n_page_gets - buf_pool->old_stat.n_page_gets; if (n_gets_diff) { ib_logger(ib_stream, "Buffer pool hit rate %lu / 1000," " young-making rate %lu / 1000 not %lu / 1000\n", (ulong) (1000 - ((1000 * (buf_pool->stat.n_pages_read - buf_pool->old_stat.n_pages_read)) / (buf_pool->stat.n_page_gets - buf_pool->old_stat.n_page_gets))), (ulong) (1000 * (buf_pool->stat.n_pages_made_young - buf_pool->old_stat.n_pages_made_young) / n_gets_diff), (ulong) (1000 * (buf_pool->stat.n_pages_not_made_young - buf_pool->old_stat.n_pages_not_made_young) / n_gets_diff)); } else { ib_logger(ib_stream, "No buffer pool page gets since the last printout\n"); } /* Statistics about read ahead algorithm */ ib_logger(ib_stream, "Pages read ahead %.2f/s," " evicted without access %.2f/s\n", (buf_pool->stat.n_ra_pages_read - buf_pool->old_stat.n_ra_pages_read) / time_elapsed, (buf_pool->stat.n_ra_pages_evicted - buf_pool->old_stat.n_ra_pages_evicted) / time_elapsed); /* Print some values to help us with visualizing what is happening with LRU eviction. */ ib_logger(ib_stream, "LRU len: %lu, unzip_LRU len: %lu\n" "I/O sum[%lu]:cur[%lu], unzip sum[%lu]:cur[%lu]\n", UT_LIST_GET_LEN(buf_pool->LRU), UT_LIST_GET_LEN(buf_pool->unzip_LRU), buf_LRU_stat_sum.io, buf_LRU_stat_cur.io, buf_LRU_stat_sum.unzip, buf_LRU_stat_cur.unzip); buf_refresh_io_stats(); buf_pool_mutex_exit(); } /**********************************************************************//** Refreshes the statistics used to print per-second averages. */ UNIV_INTERN void buf_refresh_io_stats(void) /*======================*/ { buf_pool->last_printout_time = time(NULL); buf_pool->old_stat = buf_pool->stat; } /*********************************************************************//** Asserts that all file pages in the buffer are in a replaceable state. @return TRUE */ UNIV_INTERN ibool buf_all_freed(void) /*===============*/ { buf_chunk_t* chunk; ulint i; ut_ad(buf_pool); buf_pool_mutex_enter(); chunk = buf_pool->chunks; for (i = buf_pool->n_chunks; i--; chunk++) { const buf_block_t* block = buf_chunk_not_freed(chunk); if (UNIV_LIKELY_NULL(block)) { ib_logger(ib_stream, "Page %lu %lu still fixed or dirty\n", (ulong) block->page.space, (ulong) block->page.offset); ut_error; } } buf_pool_mutex_exit(); return(TRUE); } /*********************************************************************//** Checks that there currently are no pending i/o-operations for the buffer pool. @return TRUE if there is no pending i/o */ UNIV_INTERN ibool buf_pool_check_no_pending_io(void) /*==============================*/ { ibool ret; buf_pool_mutex_enter(); if (buf_pool->n_pend_reads + buf_pool->n_flush[BUF_FLUSH_LRU] + buf_pool->n_flush[BUF_FLUSH_LIST] + buf_pool->n_flush[BUF_FLUSH_SINGLE_PAGE]) { ret = FALSE; } else { ret = TRUE; } buf_pool_mutex_exit(); return(ret); } /*********************************************************************//** Gets the current length of the free list of buffer blocks. @return length of the free list */ UNIV_INTERN ulint buf_get_free_list_len(void) /*=======================*/ { ulint len; buf_pool_mutex_enter(); len = UT_LIST_GET_LEN(buf_pool->free); buf_pool_mutex_exit(); return(len); } #else /* !UNIV_HOTBACKUP */ /********************************************************************//** Inits a page to the buffer buf_pool, for use in ibbackup --restore. */ UNIV_INTERN void buf_page_init_for_backup_restore( /*=============================*/ ulint space, /*!< in: space id */ ulint offset, /*!< in: offset of the page within space in units of a page */ ulint zip_size,/*!< in: compressed page size in bytes or 0 for uncompressed pages */ buf_block_t* block) /*!< in: block to init */ { block->page.state = BUF_BLOCK_FILE_PAGE; block->page.space = space; block->page.offset = offset; page_zip_des_init(&block->page.zip); /* We assume that block->page.data has been allocated with zip_size == UNIV_PAGE_SIZE. */ ut_ad(zip_size <= UNIV_PAGE_SIZE); ut_ad(ut_is_2pow(zip_size)); page_zip_set_size(&block->page.zip, zip_size); if (zip_size) { block->page.zip.data = block->frame + UNIV_PAGE_SIZE; } } #endif /* !UNIV_HOTBACKUP */ haildb-2.3.2/buf/buf0rea.c0000644000175000017500000004555311513177357016123 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2010, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file buf/buf0rea.c The database buffer read Created 11/5/1995 Heikki Tuuri *******************************************************/ #include "buf0rea.h" #include "fil0fil.h" #include "mtr0mtr.h" #include "buf0buf.h" #include "buf0flu.h" #include "buf0lru.h" #include "ibuf0ibuf.h" #include "log0recv.h" #include "trx0sys.h" #include "os0file.h" #include "srv0start.h" #include "srv0srv.h" /** The linear read-ahead area size */ #define BUF_READ_AHEAD_LINEAR_AREA BUF_READ_AHEAD_AREA /** If there are buf_pool->curr_size per the number below pending reads, then read-ahead is not done: this is to prevent flooding the buffer pool with i/o-fixed buffer blocks */ #define BUF_READ_AHEAD_PEND_LIMIT 2 /********************************************************************//** Low-level function which reads a page asynchronously from a file to the buffer buf_pool if it is not already there, in which case does nothing. Sets the io_fix flag and sets an exclusive lock on the buffer frame. The flag is cleared and the x-lock released by an i/o-handler thread. @return 1 if a read request was queued, 0 if the page already resided in buf_pool, or if the page is in the doublewrite buffer blocks in which case it is never read into the pool, or if the tablespace does not exist or is being dropped @return 1 if read request is issued. 0 if it is not */ static ulint buf_read_page_low( /*==============*/ ulint* err, /*!< out: DB_SUCCESS or DB_TABLESPACE_DELETED if we are trying to read from a non-existent tablespace, or a tablespace which is just now being dropped */ ibool sync, /*!< in: TRUE if synchronous aio is desired */ ulint mode, /*!< in: BUF_READ_IBUF_PAGES_ONLY, ..., ORed to OS_AIO_SIMULATED_WAKE_LATER (see below at read-ahead functions) */ ulint space, /*!< in: space id */ ulint zip_size,/*!< in: compressed page size, or 0 */ ibool unzip, /*!< in: TRUE=request uncompressed page */ ib_int64_t tablespace_version, /*!< in: if the space memory object has this timestamp different from what we are giving here, treat the tablespace as dropped; this is a timestamp we use to stop dangling page reads from a tablespace which we have DISCARDed + IMPORTed back */ ulint offset) /*!< in: page number */ { buf_page_t* bpage; ulint wake_later; *err = DB_SUCCESS; wake_later = mode & OS_AIO_SIMULATED_WAKE_LATER; mode = mode & ~OS_AIO_SIMULATED_WAKE_LATER; if (trx_doublewrite && space == TRX_SYS_SPACE && ( (offset >= trx_doublewrite->block1 && offset < trx_doublewrite->block1 + TRX_SYS_DOUBLEWRITE_BLOCK_SIZE) || (offset >= trx_doublewrite->block2 && offset < trx_doublewrite->block2 + TRX_SYS_DOUBLEWRITE_BLOCK_SIZE))) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Warning: trying to read" " doublewrite buffer page %lu\n", (ulong) offset); return(0); } if (ibuf_bitmap_page(zip_size, offset) || trx_sys_hdr_page(space, offset)) { /* Trx sys header is so low in the latching order that we play safe and do not leave the i/o-completion to an asynchronous i/o-thread. Ibuf bitmap pages must always be read with syncronous i/o, to make sure they do not get involved in thread deadlocks. */ sync = TRUE; } /* The following call will also check if the tablespace does not exist or is being dropped; if we succeed in initing the page in the buffer pool for read, then DISCARD cannot proceed until the read has completed */ bpage = buf_page_init_for_read(err, mode, space, zip_size, unzip, tablespace_version, offset); if (bpage == NULL) { return(0); } #ifdef UNIV_DEBUG if (buf_debug_prints) { ib_logger(ib_stream, "Posting read request for page %lu, sync %lu\n", (ulong) offset, (ulong) sync); } #endif ut_ad(buf_page_in_file(bpage)); if (zip_size) { *err = fil_io(OS_FILE_READ | wake_later, sync, space, zip_size, offset, 0, zip_size, bpage->zip.data, bpage); } else { ut_a(buf_page_get_state(bpage) == BUF_BLOCK_FILE_PAGE); *err = fil_io(OS_FILE_READ | wake_later, sync, space, 0, offset, 0, UNIV_PAGE_SIZE, ((buf_block_t*) bpage)->frame, bpage); } ut_a(*err == DB_SUCCESS); if (sync) { /* The i/o is already completed when we arrive from fil_read */ buf_page_io_complete(bpage); } return(1); } /********************************************************************//** High-level function which reads a page asynchronously from a file to the buffer buf_pool if it is not already there. Sets the io_fix flag and sets an exclusive lock on the buffer frame. The flag is cleared and the x-lock released by the i/o-handler thread. @return TRUE if page has been read in, FALSE in case of failure */ UNIV_INTERN ibool buf_read_page( /*==========*/ ulint space, /*!< in: space id */ ulint zip_size,/*!< in: compressed page size in bytes, or 0 */ ulint offset) /*!< in: page number */ { ib_int64_t tablespace_version; ulint count; ulint err; tablespace_version = fil_space_get_version(space); /* We do the i/o in the synchronous aio mode to save thread switches: hence TRUE */ count = buf_read_page_low(&err, TRUE, BUF_READ_ANY_PAGE, space, zip_size, FALSE, tablespace_version, offset); srv_buf_pool_reads += count; if (err == DB_TABLESPACE_DELETED) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Error: trying to access" " tablespace %lu page no. %lu,\n" "InnoDB: but the tablespace does not exist" " or is just being dropped.\n", (ulong) space, (ulong) offset); } /* Flush pages from the end of the LRU list if necessary */ buf_flush_free_margin(); /* Increment number of I/O operations used for LRU policy. */ buf_LRU_stat_inc_io(); return(count > 0); } /********************************************************************//** Applies linear read-ahead if in the buf_pool the page is a border page of a linear read-ahead area and all the pages in the area have been accessed. Does not read any page if the read-ahead mechanism is not activated. Note that the algorithm looks at the 'natural' adjacent successor and predecessor of the page, which on the leaf level of a B-tree are the next and previous page in the chain of leaves. To know these, the page specified in (space, offset) must already be present in the buf_pool. Thus, the natural way to use this function is to call it when a page in the buf_pool is accessed the first time, calling this function just after it has been bufferfixed. NOTE 1: as this function looks at the natural predecessor and successor fields on the page, what happens, if these are not initialized to any sensible value? No problem, before applying read-ahead we check that the area to read is within the span of the space, if not, read-ahead is not applied. An uninitialized value may result in a useless read operation, but only very improbably. NOTE 2: the calling thread may own latches on pages: to avoid deadlocks this function must be written such that it cannot end up waiting for these latches! NOTE 3: the calling thread must want access to the page given: this rule is set to prevent unintended read-aheads performed by ibuf routines, a situation which could result in a deadlock if the OS does not support asynchronous io. @return number of page read requests issued */ UNIV_INTERN ulint buf_read_ahead_linear( /*==================*/ ulint space, /*!< in: space id */ ulint zip_size,/*!< in: compressed page size in bytes, or 0 */ ulint offset) /*!< in: page number of a page; NOTE: the current thread must want access to this page (see NOTE 3 above) */ { ib_int64_t tablespace_version; buf_page_t* bpage; buf_frame_t* frame; buf_page_t* pred_bpage = NULL; ulint pred_offset; ulint succ_offset; ulint count; int asc_or_desc; ulint new_offset; ulint fail_count; ulint ibuf_mode; ulint low, high; ulint err; ulint i; const ulint buf_read_ahead_linear_area = BUF_READ_AHEAD_LINEAR_AREA; ulint threshold; if (UNIV_UNLIKELY(srv_startup_is_before_trx_rollback_phase)) { /* No read-ahead to avoid thread deadlocks */ return(0); } low = (offset / buf_read_ahead_linear_area) * buf_read_ahead_linear_area; high = (offset / buf_read_ahead_linear_area + 1) * buf_read_ahead_linear_area; if ((offset != low) && (offset != high - 1)) { /* This is not a border page of the area: return */ return(0); } if (ibuf_bitmap_page(zip_size, offset) || trx_sys_hdr_page(space, offset)) { /* If it is an ibuf bitmap page or trx sys hdr, we do no read-ahead, as that could break the ibuf page access order */ return(0); } /* Remember the tablespace version before we ask te tablespace size below: if DISCARD + IMPORT changes the actual .ibd file meanwhile, we do not try to read outside the bounds of the tablespace! */ tablespace_version = fil_space_get_version(space); buf_pool_mutex_enter(); if (high > fil_space_get_size(space)) { buf_pool_mutex_exit(); /* The area is not whole, return */ return(0); } if (buf_pool->n_pend_reads > buf_pool->curr_size / BUF_READ_AHEAD_PEND_LIMIT) { buf_pool_mutex_exit(); return(0); } /* Check that almost all pages in the area have been accessed; if offset == low, the accesses must be in a descending order, otherwise, in an ascending order. */ asc_or_desc = 1; if (offset == low) { asc_or_desc = -1; } /* How many out of order accessed pages can we ignore when working out the access pattern for linear readahead */ threshold = ut_min((64 - srv_read_ahead_threshold), BUF_READ_AHEAD_AREA); fail_count = 0; for (i = low; i < high; i++) { bpage = buf_page_hash_get(space, i); if ((bpage == NULL) || !buf_page_is_accessed(bpage)) { /* Not accessed */ fail_count++; } else if (pred_bpage) { /* Note that buf_page_is_accessed() returns the time of the first access. If some blocks of the extent existed in the buffer pool at the time of a linear access pattern, the first access times may be nonmonotonic, even though the latest access times were linear. The threshold (srv_read_ahead_factor) should help a little against this. */ int res = ut_ulint_cmp( buf_page_is_accessed(bpage), buf_page_is_accessed(pred_bpage)); /* Accesses not in the right order */ if (res != 0 && res != asc_or_desc) { fail_count++; } } if (fail_count > threshold) { /* Too many failures: return */ buf_pool_mutex_exit(); return(0); } if (bpage && buf_page_is_accessed(bpage)) { pred_bpage = bpage; } } /* If we got this far, we know that enough pages in the area have been accessed in the right order: linear read-ahead can be sensible */ bpage = buf_page_hash_get(space, offset); if (bpage == NULL) { buf_pool_mutex_exit(); return(0); } switch (buf_page_get_state(bpage)) { case BUF_BLOCK_ZIP_PAGE: frame = bpage->zip.data; break; case BUF_BLOCK_FILE_PAGE: frame = ((buf_block_t*) bpage)->frame; break; default: ut_error; break; } /* Read the natural predecessor and successor page addresses from the page; NOTE that because the calling thread may have an x-latch on the page, we do not acquire an s-latch on the page, this is to prevent deadlocks. Even if we read values which are nonsense, the algorithm will work. */ pred_offset = fil_page_get_prev(frame); succ_offset = fil_page_get_next(frame); buf_pool_mutex_exit(); if ((offset == low) && (succ_offset == offset + 1)) { /* This is ok, we can continue */ new_offset = pred_offset; } else if ((offset == high - 1) && (pred_offset == offset - 1)) { /* This is ok, we can continue */ new_offset = succ_offset; } else { /* Successor or predecessor not in the right order */ return(0); } low = (new_offset / buf_read_ahead_linear_area) * buf_read_ahead_linear_area; high = (new_offset / buf_read_ahead_linear_area + 1) * buf_read_ahead_linear_area; if ((new_offset != low) && (new_offset != high - 1)) { /* This is not a border page of the area: return */ return(0); } if (high > fil_space_get_size(space)) { /* The area is not whole, return */ return(0); } /* If we got this far, read-ahead can be sensible: do it */ if (ibuf_inside()) { ibuf_mode = BUF_READ_IBUF_PAGES_ONLY; } else { ibuf_mode = BUF_READ_ANY_PAGE; } count = 0; /* Since Windows XP seems to schedule the i/o handler thread very eagerly, and consequently it does not wait for the full read batch to be posted, we use special heuristics here */ os_aio_simulated_put_read_threads_to_sleep(); for (i = low; i < high; i++) { /* It is only sensible to do read-ahead in the non-sync aio mode: hence FALSE as the first parameter */ if (!ibuf_bitmap_page(zip_size, i)) { count += buf_read_page_low( &err, FALSE, ibuf_mode | OS_AIO_SIMULATED_WAKE_LATER, space, zip_size, FALSE, tablespace_version, i); if (err == DB_TABLESPACE_DELETED) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Warning: in" " linear readahead trying to access\n" "InnoDB: tablespace %lu page %lu,\n" "InnoDB: but the tablespace does not" " exist or is just being dropped.\n", (ulong) space, (ulong) i); } } } /* In simulated aio we wake the aio handler threads only after queuing all aio requests, in native aio the following call does nothing: */ os_aio_simulated_wake_handler_threads(); /* Flush pages from the end of the LRU list if necessary */ buf_flush_free_margin(); #ifdef UNIV_DEBUG if (buf_debug_prints && (count > 0)) { ib_logger(ib_stream, "LINEAR read-ahead space %lu offset %lu pages %lu\n", (ulong) space, (ulong) offset, (ulong) count); } #endif /* UNIV_DEBUG */ /* Read ahead is considered one I/O operation for the purpose of LRU policy decision. */ buf_LRU_stat_inc_io(); buf_pool->stat.n_ra_pages_read += count; return(count); } /********************************************************************//** Issues read requests for pages which the ibuf module wants to read in, in order to contract the insert buffer tree. Technically, this function is like a read-ahead function. */ UNIV_INTERN void buf_read_ibuf_merge_pages( /*======================*/ ibool sync, /*!< in: TRUE if the caller wants this function to wait for the highest address page to get read in, before this function returns */ const ulint* space_ids, /*!< in: array of space ids */ const ib_int64_t* space_versions,/*!< in: the spaces must have this version number (timestamp), otherwise we discard the read; we use this to cancel reads if DISCARD + IMPORT may have changed the tablespace size */ const ulint* page_nos, /*!< in: array of page numbers to read, with the highest page number the last in the array */ ulint n_stored) /*!< in: number of elements in the arrays */ { ulint i; ut_ad(!ibuf_inside()); #ifdef UNIV_IBUF_DEBUG ut_a(n_stored < UNIV_PAGE_SIZE); #endif while (buf_pool->n_pend_reads > buf_pool->curr_size / BUF_READ_AHEAD_PEND_LIMIT) { os_thread_sleep(500000); } for (i = 0; i < n_stored; i++) { ulint zip_size = fil_space_get_zip_size(space_ids[i]); ulint err; if (UNIV_UNLIKELY(zip_size == ULINT_UNDEFINED)) { goto tablespace_deleted; } buf_read_page_low(&err, sync && (i + 1 == n_stored), BUF_READ_ANY_PAGE, space_ids[i], zip_size, TRUE, space_versions[i], page_nos[i]); if (UNIV_UNLIKELY(err == DB_TABLESPACE_DELETED)) { tablespace_deleted: /* We have deleted or are deleting the single-table tablespace: remove the entries for that page */ ibuf_merge_or_delete_for_page(NULL, space_ids[i], page_nos[i], zip_size, FALSE); } } os_aio_simulated_wake_handler_threads(); /* Flush pages from the end of the LRU list if necessary */ buf_flush_free_margin(); #ifdef UNIV_DEBUG if (buf_debug_prints) { ib_logger(ib_stream, "Ibuf merge read-ahead space %lu pages %lu\n", (ulong) space_ids[0], (ulong) n_stored); } #endif /* UNIV_DEBUG */ } /********************************************************************//** Issues read requests for pages which recovery wants to read in. */ UNIV_INTERN void buf_read_recv_pages( /*================*/ ibool sync, /*!< in: TRUE if the caller wants this function to wait for the highest address page to get read in, before this function returns */ ulint space, /*!< in: space id */ ulint zip_size, /*!< in: compressed page size in bytes, or 0 */ const ulint* page_nos, /*!< in: array of page numbers to read, with the highest page number the last in the array */ ulint n_stored) /*!< in: number of page numbers in the array */ { ib_int64_t tablespace_version; ulint count; ulint err; ulint i; zip_size = fil_space_get_zip_size(space); if (UNIV_UNLIKELY(zip_size == ULINT_UNDEFINED)) { /* It is a single table tablespace and the .ibd file is missing: do nothing */ return; } tablespace_version = fil_space_get_version(space); for (i = 0; i < n_stored; i++) { count = 0; os_aio_print_debug = FALSE; while (buf_pool->n_pend_reads >= recv_n_pool_free_frames / 2) { os_aio_simulated_wake_handler_threads(); os_thread_sleep(10000); count++; if (count > 1000) { ib_logger(ib_stream, "InnoDB: Error: InnoDB has waited for" " 10 seconds for pending\n" "InnoDB: reads to the buffer pool to" " be finished.\n" "InnoDB: Number of pending reads %lu," " pending pread calls %lu\n", (ulong) buf_pool->n_pend_reads, (ulong)os_file_n_pending_preads); os_aio_print_debug = TRUE; } } os_aio_print_debug = FALSE; if ((i + 1 == n_stored) && sync) { buf_read_page_low(&err, TRUE, BUF_READ_ANY_PAGE, space, zip_size, TRUE, tablespace_version, page_nos[i]); } else { buf_read_page_low(&err, FALSE, BUF_READ_ANY_PAGE | OS_AIO_SIMULATED_WAKE_LATER, space, zip_size, TRUE, tablespace_version, page_nos[i]); } } os_aio_simulated_wake_handler_threads(); /* Flush pages from the end of the LRU list if necessary */ buf_flush_free_margin(); #ifdef UNIV_DEBUG if (buf_debug_prints) { ib_logger(ib_stream, "Recovery applies read-ahead pages %lu\n", (ulong) n_stored); } #endif /* UNIV_DEBUG */ } haildb-2.3.2/os/0000755000175000017500000000000011513177437014263 5ustar00pcrewspcrews00000000000000haildb-2.3.2/os/os0proc.c0000644000175000017500000001606611513177357016026 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file os/os0proc.c The interface to the operating system process control primitives Created 9/30/1995 Heikki Tuuri *******************************************************/ #include "config.h" #ifndef __WIN__ #include #include #ifdef HAVE_UNISTD_H #include #endif #endif #include "os0proc.h" #ifdef UNIV_NONINL #include "os0proc.ic" #endif #include "ut0mem.h" #include "ut0byte.h" /* FreeBSD for example has only MAP_ANON, Linux has MAP_ANONYMOUS and MAP_ANON but MAP_ANON is marked as deprecated */ #if defined(MAP_ANONYMOUS) #define OS_MAP_ANON MAP_ANONYMOUS #elif defined(MAP_ANON) #define OS_MAP_ANON MAP_ANON #endif UNIV_INTERN ibool os_use_large_pages; /* Large page size. This may be a boot-time option on some platforms */ UNIV_INTERN ulint os_large_page_size; /****************************************************************//** Reset the variables. */ UNIV_INTERN void os_proc_var_init(void) /*==================*/ { os_use_large_pages = 0; os_large_page_size = 0; } /******************************************************************** Converts the current process id to a number. It is not guaranteed that the number is unique. In Linux returns the 'process number' of the current thread. That number is the same as one sees in 'top', for example. In Linux the thread id is not the same as one sees in 'top'. @return process id as a number */ UNIV_INTERN ulint os_proc_get_number(void) /*====================*/ { #ifdef __WIN__ return((ulint)GetCurrentProcessId()); #else return((ulint)getpid()); #endif } /****************************************************************//** Allocates large pages memory. @return allocated memory */ UNIV_INTERN void* os_mem_alloc_large( /*===============*/ ulint* n) /*!< in/out: number of bytes */ { void* ptr; ulint size; #if defined HAVE_LARGE_PAGES && defined UNIV_LINUX int shmid; struct shmid_ds buf; if (!os_use_large_pages || !os_large_page_size) { goto skip; } /* Align block size to os_large_page_size */ ut_ad(ut_is_2pow(os_large_page_size)); size = ut_2pow_round(*n + (os_large_page_size - 1), os_large_page_size); shmid = shmget(IPC_PRIVATE, (size_t)size, SHM_HUGETLB | SHM_R | SHM_W); if (shmid < 0) { ib_logger(ib_stream, "InnoDB: HugeTLB: Warning: Failed to allocate" " %lu bytes. errno %d\n", size, errno); ptr = NULL; } else { ptr = shmat(shmid, NULL, 0); if (ptr == (void *)-1) { ib_logger(ib_stream, "InnoDB: HugeTLB: Warning: Failed to" " attach shared memory segment, errno %d\n", errno); ptr = NULL; } /* Remove the shared memory segment so that it will be automatically freed after memory is detached or process exits */ shmctl(shmid, IPC_RMID, &buf); } if (ptr) { *n = size; os_fast_mutex_lock(&ut_list_mutex); ut_total_allocated_memory += size; os_fast_mutex_unlock(&ut_list_mutex); # ifdef UNIV_SET_MEM_TO_ZERO memset(ptr, '\0', size); # endif UNIV_MEM_ALLOC(ptr, size); return(ptr); } ib_logger(ib_stream, "InnoDB HugeTLB: Warning: Using conventional" " memory pool\n"); skip: #endif /* HAVE_LARGE_PAGES && UNIV_LINUX */ #ifdef __WIN__ SYSTEM_INFO system_info; GetSystemInfo(&system_info); /* Align block size to system page size */ ut_ad(ut_is_2pow(system_info.dwPageSize)); /* system_info.dwPageSize is only 32-bit. Casting to ulint is required on 64-bit Windows. */ size = *n = ut_2pow_round(*n + (system_info.dwPageSize - 1), (ulint) system_info.dwPageSize); ptr = VirtualAlloc(NULL, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); if (!ptr) { ib_logger(ib_stream, "InnoDB: VirtualAlloc(%lu bytes) failed;" " Windows error %lu\n", (ulong) size, (ulong) GetLastError()); } else { os_fast_mutex_lock(&ut_list_mutex); ut_total_allocated_memory += size; os_fast_mutex_unlock(&ut_list_mutex); UNIV_MEM_ALLOC(ptr, size); } #elif defined __NETWARE__ || !defined OS_MAP_ANON size = *n; ptr = ut_malloc_low(size, TRUE, FALSE); #else # ifdef HAVE_GETPAGESIZE size = getpagesize(); # else size = UNIV_PAGE_SIZE; # endif /* Align block size to system page size */ ut_ad(ut_is_2pow(size)); size = *n = ut_2pow_round(*n + (size - 1), size); ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | OS_MAP_ANON, -1, 0); if (UNIV_UNLIKELY(ptr == (void*) -1)) { ib_logger(ib_stream, "InnoDB: mmap(%lu bytes) failed;" " errno %lu\n", (ulong) size, (ulong) errno); ptr = NULL; } else { os_fast_mutex_lock(&ut_list_mutex); ut_total_allocated_memory += size; os_fast_mutex_unlock(&ut_list_mutex); UNIV_MEM_ALLOC(ptr, size); } #endif return(ptr); } /****************************************************************//** Frees large pages memory. */ UNIV_INTERN void os_mem_free_large( /*==============*/ void *ptr, /*!< in: pointer returned by os_mem_alloc_large() */ ulint size) /*!< in: size returned by os_mem_alloc_large() */ { os_fast_mutex_lock(&ut_list_mutex); ut_a(ut_total_allocated_memory >= size); os_fast_mutex_unlock(&ut_list_mutex); #if defined HAVE_LARGE_PAGES && defined UNIV_LINUX if (os_use_large_pages && os_large_page_size && !shmdt(ptr)) { os_fast_mutex_lock(&ut_list_mutex); ut_a(ut_total_allocated_memory >= size); ut_total_allocated_memory -= size; os_fast_mutex_unlock(&ut_list_mutex); UNIV_MEM_FREE(ptr, size); return; } #endif /* HAVE_LARGE_PAGES && UNIV_LINUX */ #ifdef __WIN__ /* When RELEASE memory, the size parameter must be 0. Do not use MEM_RELEASE with MEM_DECOMMIT. */ if (!VirtualFree(ptr, 0, MEM_RELEASE)) { ib_logger(ib_stream, "InnoDB: VirtualFree(%p, %lu) failed;" " Windows error %lu\n", ptr, (ulong) size, (ulong) GetLastError()); } else { os_fast_mutex_lock(&ut_list_mutex); ut_a(ut_total_allocated_memory >= size); ut_total_allocated_memory -= size; os_fast_mutex_unlock(&ut_list_mutex); UNIV_MEM_FREE(ptr, size); } #elif defined __NETWARE__ || !defined OS_MAP_ANON ut_free(ptr); #else if (munmap(ptr, size)) { ib_logger(ib_stream, "InnoDB: munmap(%p, %lu) failed;" " errno %lu\n", ptr, (ulong) size, (ulong) errno); } else { os_fast_mutex_lock(&ut_list_mutex); ut_a(ut_total_allocated_memory >= size); ut_total_allocated_memory -= size; os_fast_mutex_unlock(&ut_list_mutex); UNIV_MEM_FREE(ptr, size); } #endif } haildb-2.3.2/os/os0sync.c0000644000175000017500000004116311513177357016033 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file os/os0sync.c The interface to the operating system synchronization primitives. Created 9/6/1995 Heikki Tuuri *******************************************************/ #include "os0sync.h" #ifdef UNIV_NONINL #include "os0sync.ic" #endif #include "ut0mem.h" #include "srv0start.h" /* Type definition for an operating system mutex struct */ struct os_mutex_struct{ os_event_t event; /*!< Used by sync0arr.c for queing threads */ void* handle; /*!< OS handle to mutex */ ulint count; /*!< we use this counter to check that the same thread does not recursively lock the mutex: we do not assume that the OS mutex supports recursive locking, though NT seems to do that */ UT_LIST_NODE_T(os_mutex_str_t) os_mutex_list; /* list of all 'slow' OS mutexes created */ }; /** Mutex protecting counts and the lists of OS mutexes and events */ UNIV_INTERN os_mutex_t os_sync_mutex; /** TRUE if os_sync_mutex has been initialized */ static ibool os_sync_mutex_inited = FALSE; /** TRUE when os_sync_free() is being executed */ static ibool os_sync_free_called = FALSE; /** This is incremented by 1 in os_thread_create and decremented by 1 in os_thread_exit */ UNIV_INTERN ulint os_thread_count = 0; /** The list of all events created */ static UT_LIST_BASE_NODE_T(os_event_struct_t) os_event_list; /** The list of all OS 'slow' mutexes */ static UT_LIST_BASE_NODE_T(os_mutex_str_t) os_mutex_list; UNIV_INTERN ulint os_event_count = 0; UNIV_INTERN ulint os_mutex_count = 0; UNIV_INTERN ulint os_fast_mutex_count = 0; /* Because a mutex is embedded inside an event and there is an event embedded inside a mutex, on free, this generates a recursive call. This version of the free event function doesn't acquire the global lock */ static void os_event_free_internal(os_event_t event); /*********************************************************//** Reset the variables. */ UNIV_INTERN void os_sync_var_init(void) /*==================*/ { os_sync_mutex = NULL; os_sync_mutex_inited = FALSE; os_sync_free_called = FALSE; os_thread_count = 0; memset(&os_event_list, 0x0, sizeof(os_event_list)); memset(&os_mutex_list, 0x0, sizeof(os_mutex_list)); os_event_count = 0; os_mutex_count = 0; os_fast_mutex_count = 0; } /************************************************************* Initializes global event and OS 'slow' mutex lists. */ UNIV_INTERN void os_sync_init(void) /*==============*/ { UT_LIST_INIT(os_event_list); UT_LIST_INIT(os_mutex_list); os_sync_mutex = NULL; os_sync_mutex_inited = FALSE; os_sync_mutex = os_mutex_create(NULL); os_sync_mutex_inited = TRUE; } /*********************************************************//** Frees created events and OS 'slow' mutexes. */ UNIV_INTERN void os_sync_free(void) /*==============*/ { os_event_t event; os_mutex_t mutex; os_sync_free_called = TRUE; event = UT_LIST_GET_FIRST(os_event_list); while (event) { os_event_free(event); event = UT_LIST_GET_FIRST(os_event_list); } mutex = UT_LIST_GET_FIRST(os_mutex_list); while (mutex) { if (mutex == os_sync_mutex) { /* Set the flag to FALSE so that we do not try to reserve os_sync_mutex any more in remaining freeing operations in shutdown */ os_sync_mutex_inited = FALSE; } os_mutex_free(mutex); mutex = UT_LIST_GET_FIRST(os_mutex_list); } os_sync_free_called = FALSE; } /*********************************************************//** Creates an event semaphore, i.e., a semaphore which may just have two states: signaled and nonsignaled. The created event is manual reset: it must be reset explicitly by calling sync_os_reset_event. @return the event handle */ UNIV_INTERN os_event_t os_event_create( /*============*/ const char* name) /*!< in: the name of the event, if NULL the event is created without a name */ { #ifdef __WIN__ os_event_t event; event = ut_malloc(sizeof(struct os_event_struct)); event->handle = CreateEvent(NULL, /* No security attributes */ TRUE, /* Manual reset */ FALSE, /* Initial state nonsignaled */ (LPCTSTR) name); if (!event->handle) { ib_logger(ib_stream, "InnoDB: Could not create a Windows event semaphore;" " Windows error %lu\n", (ulong) GetLastError()); } #else /* Unix */ os_event_t event; UT_NOT_USED(name); event = ut_malloc(sizeof(struct os_event_struct)); os_fast_mutex_init(&(event->os_mutex)); ut_a(0 == pthread_cond_init(&(event->cond_var), NULL)); event->is_set = FALSE; /* We return this value in os_event_reset(), which can then be be used to pass to the os_event_wait_low(). The value of zero is reserved in os_event_wait_low() for the case when the caller does not want to pass any signal_count value. To distinguish between the two cases we initialize signal_count to 1 here. */ event->signal_count = 1; #endif /* __WIN__ */ /* The os_sync_mutex can be NULL because during startup an event can be created [ because it's embedded in the mutex/rwlock ] before this module has been initialized */ if (os_sync_mutex != NULL) { os_mutex_enter(os_sync_mutex); } /* Put to the list of events */ UT_LIST_ADD_FIRST(os_event_list, os_event_list, event); os_event_count++; if (os_sync_mutex != NULL) { os_mutex_exit(os_sync_mutex); } return(event); } /************************************************************** Sets an event semaphore to the signaled state: lets waiting threads proceed. */ UNIV_INTERN void os_event_set( /*=========*/ os_event_t event) /*!< in: event to set */ { #ifdef __WIN__ ut_a(event); ut_a(SetEvent(event->handle)); #else ut_a(event); os_fast_mutex_lock(&(event->os_mutex)); if (event->is_set) { /* Do nothing */ } else { event->is_set = TRUE; event->signal_count += 1; ut_a(0 == pthread_cond_broadcast(&(event->cond_var))); } os_fast_mutex_unlock(&(event->os_mutex)); #endif } /**********************************************************//** Resets an event semaphore to the nonsignaled state. Waiting threads will stop to wait for the event. The return value should be passed to os_even_wait_low() if it is desired that this thread should not wait in case of an intervening call to os_event_set() between this os_event_reset() and the os_event_wait_low() call. See comments for os_event_wait_low(). @return current signal_count. */ UNIV_INTERN ib_int64_t os_event_reset( /*===========*/ os_event_t event) /*!< in: event to reset */ { ib_int64_t ret = 0; #ifdef __WIN__ ut_a(event); ut_a(ResetEvent(event->handle)); #else ut_a(event); os_fast_mutex_lock(&(event->os_mutex)); if (!event->is_set) { /* Do nothing */ } else { event->is_set = FALSE; } ret = event->signal_count; os_fast_mutex_unlock(&(event->os_mutex)); #endif return(ret); } /**********************************************************//** Frees an event object, without acquiring the global lock. */ static void os_event_free_internal( /*===================*/ os_event_t event) /*!< in: event to free */ { #ifdef __WIN__ ut_a(event); ut_a(CloseHandle(event->handle)); #else ut_a(event); /* This is to avoid freeing the mutex twice */ os_fast_mutex_free(&(event->os_mutex)); ut_a(0 == pthread_cond_destroy(&(event->cond_var))); #endif /* Remove from the list of events */ UT_LIST_REMOVE(os_event_list, os_event_list, event); os_event_count--; ut_free(event); } /**********************************************************//** Frees an event object. */ UNIV_INTERN void os_event_free( /*==========*/ os_event_t event) /*!< in: event to free */ { #ifdef __WIN__ ut_a(event); ut_a(CloseHandle(event->handle)); #else ut_a(event); os_fast_mutex_free(&(event->os_mutex)); ut_a(0 == pthread_cond_destroy(&(event->cond_var))); #endif /* Remove from the list of events */ os_mutex_enter(os_sync_mutex); UT_LIST_REMOVE(os_event_list, os_event_list, event); os_event_count--; os_mutex_exit(os_sync_mutex); ut_free(event); } /**********************************************************//** Waits for an event object until it is in the signaled state. If srv_shutdown_state == SRV_SHUTDOWN_EXIT_THREADS this also exits the waiting thread when the event becomes signaled (or immediately if the event is already in the signaled state). Typically, if the event has been signalled after the os_event_reset() we'll return immediately because event->is_set == TRUE. There are, however, situations (e.g.: sync_array code) where we may lose this information. For example: thread A calls os_event_reset() thread B calls os_event_set() [event->is_set == TRUE] thread C calls os_event_reset() [event->is_set == FALSE] thread A calls os_event_wait() [infinite wait!] thread C calls os_event_wait() [infinite wait!] Where such a scenario is possible, to avoid infinite wait, the value returned by os_event_reset() should be passed in as reset_sig_count. */ UNIV_INTERN void os_event_wait_low( /*==============*/ os_event_t event, /*!< in: event to wait */ ib_int64_t reset_sig_count)/*!< in: zero or the value returned by previous call of os_event_reset(). */ { #ifdef __WIN__ DWORD err; ut_a(event); UT_NOT_USED(reset_sig_count); /* Specify an infinite time limit for waiting */ err = WaitForSingleObject(event->handle, INFINITE); ut_a(err == WAIT_OBJECT_0); if (srv_shutdown_state == SRV_SHUTDOWN_EXIT_THREADS) { os_thread_exit(NULL); } #else ib_int64_t old_signal_count; os_fast_mutex_lock(&(event->os_mutex)); if (reset_sig_count) { old_signal_count = reset_sig_count; } else { old_signal_count = event->signal_count; } for (;;) { if (event->is_set == TRUE || event->signal_count != old_signal_count) { os_fast_mutex_unlock(&(event->os_mutex)); if (srv_shutdown_state == SRV_SHUTDOWN_EXIT_THREADS) { os_thread_exit(NULL); } /* Ok, we may return */ return; } pthread_cond_wait(&(event->cond_var), &(event->os_mutex)); /* Solaris manual said that spurious wakeups may occur: we have to check if the event really has been signaled after we came here to wait */ } #endif } #ifdef __WIN__ /**********************************************************//** Waits for any event in an OS native event array. Returns if even a single one is signaled or becomes signaled. @return index of the event which was signaled */ UNIV_INTERN ulint os_event_wait_multiple( /*===================*/ ulint n, /*!< in: number of events in the array */ os_native_event_t* native_event_array) /*!< in: pointer to an array of event handles */ { DWORD index; ut_a(native_event_array); ut_a(n > 0); index = WaitForMultipleObjects((DWORD) n, native_event_array, FALSE, /* Wait for any 1 event */ INFINITE); /* Infinite wait time limit */ ut_a(index >= WAIT_OBJECT_0); /* NOTE: Pointless comparison */ ut_a(index < WAIT_OBJECT_0 + n); if (srv_shutdown_state == SRV_SHUTDOWN_EXIT_THREADS) { os_thread_exit(NULL); } return(index - WAIT_OBJECT_0); } #endif /*********************************************************//** Creates an operating system mutex semaphore. Because these are slow, the mutex semaphore of InnoDB itself (mutex_t) should be used where possible. @return the mutex handle */ UNIV_INTERN os_mutex_t os_mutex_create( /*============*/ const char* name) /*!< in: the name of the mutex, if NULL the mutex is created without a name */ { #ifdef __WIN__ HANDLE mutex; os_mutex_t mutex_str; mutex = CreateMutex(NULL, /* No security attributes */ FALSE, /* Initial state: no owner */ (LPCTSTR) name); ut_a(mutex); #else os_fast_mutex_t* mutex; os_mutex_t mutex_str; UT_NOT_USED(name); mutex = ut_malloc(sizeof(os_fast_mutex_t)); os_fast_mutex_init(mutex); #endif mutex_str = ut_malloc(sizeof(os_mutex_str_t)); mutex_str->handle = mutex; mutex_str->count = 0; mutex_str->event = os_event_create(NULL); if (UNIV_LIKELY(os_sync_mutex_inited)) { /* When creating os_sync_mutex itself we cannot reserve it */ os_mutex_enter(os_sync_mutex); } UT_LIST_ADD_FIRST(os_mutex_list, os_mutex_list, mutex_str); os_mutex_count++; if (UNIV_LIKELY(os_sync_mutex_inited)) { os_mutex_exit(os_sync_mutex); } return(mutex_str); } /**********************************************************//** Acquires ownership of a mutex semaphore. */ UNIV_INTERN void os_mutex_enter( /*===========*/ os_mutex_t mutex) /*!< in: mutex to acquire */ { #ifdef __WIN__ DWORD err; ut_a(mutex); /* Specify infinite time limit for waiting */ err = WaitForSingleObject(mutex->handle, INFINITE); ut_a(err == WAIT_OBJECT_0); (mutex->count)++; ut_a(mutex->count == 1); #else os_fast_mutex_lock(mutex->handle); (mutex->count)++; ut_a(mutex->count == 1); #endif } /**********************************************************//** Releases ownership of a mutex. */ UNIV_INTERN void os_mutex_exit( /*==========*/ os_mutex_t mutex) /*!< in: mutex to release */ { ut_a(mutex); ut_a(mutex->count == 1); (mutex->count)--; #ifdef __WIN__ ut_a(ReleaseMutex(mutex->handle)); #else os_fast_mutex_unlock(mutex->handle); #endif } /**********************************************************//** Frees a mutex object. */ UNIV_INTERN void os_mutex_free( /*==========*/ os_mutex_t mutex) /*!< in: mutex to free */ { ut_a(mutex); if (UNIV_LIKELY(!os_sync_free_called)) { os_event_free_internal(mutex->event); } if (UNIV_LIKELY(os_sync_mutex_inited)) { os_mutex_enter(os_sync_mutex); } UT_LIST_REMOVE(os_mutex_list, os_mutex_list, mutex); os_mutex_count--; if (UNIV_LIKELY(os_sync_mutex_inited)) { os_mutex_exit(os_sync_mutex); } #ifdef __WIN__ ut_a(CloseHandle(mutex->handle)); ut_free(mutex); #else os_fast_mutex_free(mutex->handle); ut_free(mutex->handle); ut_free(mutex); #endif } /*********************************************************//** Initializes an operating system fast mutex semaphore. */ UNIV_INTERN void os_fast_mutex_init( /*===============*/ os_fast_mutex_t* fast_mutex) /*!< in: fast mutex */ { #ifdef __WIN__ ut_a(fast_mutex); InitializeCriticalSection((LPCRITICAL_SECTION) fast_mutex); #else ut_a(0 == pthread_mutex_init(fast_mutex, NULL)); #endif if (UNIV_LIKELY(os_sync_mutex_inited)) { /* When creating os_sync_mutex itself (in Unix) we cannot reserve it */ os_mutex_enter(os_sync_mutex); } os_fast_mutex_count++; if (UNIV_LIKELY(os_sync_mutex_inited)) { os_mutex_exit(os_sync_mutex); } } /**********************************************************//** Acquires ownership of a fast mutex. */ UNIV_INTERN void os_fast_mutex_lock( /*===============*/ os_fast_mutex_t* fast_mutex) /*!< in: mutex to acquire */ { #ifdef __WIN__ EnterCriticalSection((LPCRITICAL_SECTION) fast_mutex); #else pthread_mutex_lock(fast_mutex); #endif } /**********************************************************//** Releases ownership of a fast mutex. */ UNIV_INTERN void os_fast_mutex_unlock( /*=================*/ os_fast_mutex_t* fast_mutex) /*!< in: mutex to release */ { #ifdef __WIN__ LeaveCriticalSection(fast_mutex); #else pthread_mutex_unlock(fast_mutex); #endif } /**********************************************************//** Frees a mutex object. */ UNIV_INTERN void os_fast_mutex_free( /*===============*/ os_fast_mutex_t* fast_mutex) /*!< in: mutex to free */ { #ifdef __WIN__ ut_a(fast_mutex); DeleteCriticalSection((LPCRITICAL_SECTION) fast_mutex); #else int ret; ret = pthread_mutex_destroy(fast_mutex); if (UNIV_UNLIKELY(ret != 0)) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: error: return value %lu when calling\n" "InnoDB: pthread_mutex_destroy().\n", (ulint)ret); ib_logger(ib_stream, "InnoDB: Byte contents of the pthread mutex at %p:\n", (void*) fast_mutex); ut_print_buf(ib_stream, fast_mutex, sizeof(os_fast_mutex_t)); ib_logger(ib_stream, "\n"); } #endif if (UNIV_LIKELY(os_sync_mutex_inited)) { /* When freeing the last mutexes, we have already freed os_sync_mutex */ os_mutex_enter(os_sync_mutex); } ut_ad(os_fast_mutex_count > 0); os_fast_mutex_count--; if (UNIV_LIKELY(os_sync_mutex_inited)) { os_mutex_exit(os_sync_mutex); } } haildb-2.3.2/os/os0file.c0000644000175000017500000033204311513177357015776 0ustar00pcrewspcrews00000000000000/*********************************************************************** Copyright (c) 1995, 2010, Innobase Oy. All Rights Reserved. Copyright (c) 2009, Percona Inc. Portions of this file contain modifications contributed and copyrighted by Percona Inc.. Those modifications are gratefully acknowledged and are described briefly in the InnoDB documentation. The contributions by Percona Inc. are incorporated with their permission, and subject to the conditions contained in the file COPYING.Percona. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ***********************************************************************/ /**************************************************//** @file os/os0file.c The interface to the operating system file i/o primitives Created 10/21/1995 Heikki Tuuri *******************************************************/ #include "os0file.h" #include "ut0mem.h" #include "srv0srv.h" #include "srv0start.h" #include "fil0fil.h" #include "buf0buf.h" #include "api0misc.h" #ifndef UNIV_HOTBACKUP # if !defined(__WIN__) && defined(HAVE_UNISTD_H) # define __USE_UNIX98 # include # include # include # include # include # endif # include "os0sync.h" # include "os0thread.h" # ifdef __WIN__ /* Add includes for the _stat() call to compile on Windows */ # include # include # include # include # endif /* __WIN__ */ #endif /* !UNIV_HOTBACKUP */ /* This specifies the file permissions InnoDB uses when it creates files in Unix; the value of os_innodb_umask is initialized in ha_innodb.cc to my_umask */ #ifndef __WIN__ /** Umask for creating files */ UNIV_INTERN ulint os_innodb_umask = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP; #else /** Umask for creating files */ UNIV_INTERN ulint os_innodb_umask = 0; #endif #ifdef UNIV_DO_FLUSH /* If the following is set to TRUE, we do not call os_file_flush in every os_file_write. We can set this TRUE when the doublewrite buffer is used. */ UNIV_INTERN ibool os_do_not_call_flush_at_each_write = FALSE; #else /* We do not call os_file_flush in every os_file_write. */ #endif /* UNIV_DO_FLUSH */ #ifdef UNIV_HOTBACKUP # define os_aio_use_native_aio FALSE #else /* UNIV_HOTBACKUP */ /* We use these mutexes to protect lseek + file i/o operation, if the OS does not provide an atomic pread or pwrite, or similar */ #define OS_FILE_N_SEEK_MUTEXES 16 UNIV_INTERN os_mutex_t os_file_seek_mutexes[OS_FILE_N_SEEK_MUTEXES]; /* In simulated aio, merge at most this many consecutive i/os */ #define OS_AIO_MERGE_N_CONSECUTIVE 64 /** If this flag is TRUE, then we will use the native aio of the OS (provided we compiled Innobase with it in), otherwise we will use simulated aio we build below with threads */ UNIV_INTERN ibool os_aio_use_native_aio = FALSE; /** Flag: enable debug printout for asynchronous i/o */ UNIV_INTERN ibool os_aio_print_debug = FALSE; /** The asynchronous i/o array slot structure */ typedef struct os_aio_slot_struct os_aio_slot_t; /** The asynchronous i/o array slot structure */ struct os_aio_slot_struct{ ibool is_read; /*!< TRUE if a read operation */ ulint pos; /*!< index of the slot in the aio array */ ibool reserved; /*!< TRUE if this slot is reserved */ time_t reservation_time;/*!< time when reserved */ ulint len; /*!< length of the block to read or write */ byte* buf; /*!< buffer used in i/o */ ulint type; /*!< OS_FILE_READ or OS_FILE_WRITE */ ulint offset; /*!< 32 low bits of file offset in bytes */ ulint offset_high; /*!< 32 high bits of file offset */ os_file_t file; /*!< file where to read or write */ const char* name; /*!< file name or path */ ibool io_already_done;/*!< used only in simulated aio: TRUE if the physical i/o already made and only the slot message needs to be passed to the caller of os_aio_simulated_handle */ fil_node_t* message1; /*!< message which is given by the */ void* message2; /*!< the requester of an aio operation and which can be used to identify which pending aio operation was completed */ #ifdef WIN_ASYNC_IO os_event_t event; /*!< event object we need in the OVERLAPPED struct */ OVERLAPPED control; /*!< Windows control block for the aio request */ #endif }; /** The asynchronous i/o array structure */ typedef struct os_aio_array_struct os_aio_array_t; /** The asynchronous i/o array structure */ struct os_aio_array_struct{ os_mutex_t mutex; /*!< the mutex protecting the aio array */ os_event_t not_full; /*!< The event which is set to the signaled state when there is space in the aio outside the ibuf segment */ os_event_t is_empty; /*!< The event which is set to the signaled state when there are no pending i/os in this array */ ulint n_slots;/*!< Total number of slots in the aio array. This must be divisible by n_threads. */ ulint n_segments; /*!< Number of segments in the aio array of pending aio requests. A thread can wait separately for any one of the segments. */ ulint n_reserved; /*!< Number of reserved slots in the aio array outside the ibuf segment */ os_aio_slot_t* slots; /*!< Pointer to the slots in the array */ #ifdef __WIN__ os_native_event_t* native_events; /*!< Pointer to an array of OS native event handles where we copied the handles from slots, in the same order. This can be used in WaitForMultipleObjects; used only in Windows */ #endif }; /** Array of events used in simulated aio */ static os_event_t* os_aio_segment_wait_events = NULL; /** The aio arrays for non-ibuf i/o and ibuf i/o, as well as sync aio. These are NULL when the module has not yet been initialized. @{ */ static os_aio_array_t* os_aio_read_array = NULL; /*!< Reads */ static os_aio_array_t* os_aio_write_array = NULL; /*!< Writes */ static os_aio_array_t* os_aio_ibuf_array = NULL; /*!< Insert buffer */ static os_aio_array_t* os_aio_log_array = NULL; /*!< Redo log */ static os_aio_array_t* os_aio_sync_array = NULL; /*!< Synchronous I/O */ /* @} */ /** Number of asynchronous I/O segments. Set by os_aio_init(). */ static ulint os_aio_n_segments = ULINT_UNDEFINED; /** If the following is TRUE, read i/o handler threads try to wait until a batch of new read requests have been posted */ static ibool os_aio_recommend_sleep_for_read_threads = FALSE; #endif /* UNIV_HOTBACKUP */ UNIV_INTERN ulint os_n_file_reads = 0; UNIV_INTERN ulint os_bytes_read_since_printout = 0; UNIV_INTERN ulint os_n_file_writes = 0; UNIV_INTERN ulint os_n_fsyncs = 0; UNIV_INTERN ulint os_n_file_reads_old = 0; UNIV_INTERN ulint os_n_file_writes_old = 0; UNIV_INTERN ulint os_n_fsyncs_old = 0; UNIV_INTERN time_t os_last_printout; UNIV_INTERN ibool os_has_said_disk_full = FALSE; #ifndef UNIV_HOTBACKUP /** The mutex protecting the following counts of pending I/O operations */ static os_mutex_t os_file_count_mutex; #endif /* !UNIV_HOTBACKUP */ /** Number of pending os_file_pread() operations */ UNIV_INTERN ulint os_file_n_pending_preads = 0; /** Number of pending os_file_pwrite() operations */ UNIV_INTERN ulint os_file_n_pending_pwrites = 0; /** Number of pending write operations */ UNIV_INTERN ulint os_n_pending_writes = 0; /** Number of pending read operations */ UNIV_INTERN ulint os_n_pending_reads = 0; /* Array of English strings describing the current state of an i/o handler thread */ static const char* srv_io_thread_op_info[SRV_MAX_N_IO_THREADS]; static const char* srv_io_thread_function[SRV_MAX_N_IO_THREADS]; /***********************************************************************//** Reset the variables. */ UNIV_INTERN void os_file_var_init(void) /*==================*/ { os_aio_segment_wait_events = NULL; os_aio_read_array = NULL; os_aio_write_array = NULL; os_aio_ibuf_array = NULL; os_aio_log_array = NULL; os_aio_sync_array = NULL; os_aio_n_segments = ULINT_UNDEFINED; os_aio_recommend_sleep_for_read_threads = FALSE; os_n_file_reads = 0; os_bytes_read_since_printout = 0; os_n_file_writes = 0; os_n_fsyncs = 0; os_n_file_reads_old = 0; os_n_file_writes_old = 0; os_n_fsyncs_old = 0; os_last_printout = 0; os_has_said_disk_full = FALSE; os_file_count_mutex = NULL; os_file_n_pending_preads = 0; os_file_n_pending_pwrites = 0; os_n_pending_writes = 0; os_n_pending_reads = 0; #ifdef UNIV_DO_FLUSH os_do_not_call_flush_at_each_write = FALSE; #endif /* UNIV_DO_FLUSH */ memset(os_file_seek_mutexes, 0x0, sizeof(os_file_seek_mutexes)); } #ifndef UNIV_HOTBACKUP /****************************************************************//** Returns a pointer to the nth slot in the aio array. @return pointer to slot */ static os_aio_slot_t* os_aio_array_get_nth_slot( /*======================*/ os_aio_array_t* array, /*!< in: aio array */ ulint index) /*!< in: index of the slot */ { ut_a(index < array->n_slots); return((array->slots) + index); } #endif /* UNIV_HOTBACKUP */ /************************************************************************//** Frees an aio wait array. */ static void os_aio_array_free( /*==============*/ os_aio_array_t* array) /*!< in, own: array to free */ { #ifdef WIN_ASYNC_IO ulint i; for (i = 0; i < array->n_slots; i++) { os_aio_slot_t* slot = os_aio_array_get_nth_slot(array, i); os_event_free(slot->event); } #endif /* WIN_ASYNC_IO */ #ifdef __WIN__ ut_free(array->native_events); array->native_events = NULL; #endif /* __WIN__ */ os_mutex_free(array->mutex); os_event_free(array->not_full); os_event_free(array->is_empty); ut_free(array->slots); array->slots = NULL; ut_free(array); } /***********************************************************************//** Shutdown the IO sub-system and free all the memory. */ UNIV_INTERN void os_aio_close(void) /*==============*/ { if (os_aio_segment_wait_events != NULL) { ut_free(os_aio_segment_wait_events); os_aio_segment_wait_events = NULL; } if (os_aio_read_array != NULL) { os_aio_array_free(os_aio_read_array); os_aio_read_array = NULL; } if (os_aio_write_array != NULL) { os_aio_array_free(os_aio_write_array); os_aio_write_array = NULL; } if (os_aio_ibuf_array != NULL) { os_aio_array_free(os_aio_ibuf_array); os_aio_ibuf_array = NULL; } if (os_aio_log_array != NULL) { os_aio_array_free(os_aio_log_array); os_aio_log_array = NULL; } if (os_aio_sync_array != NULL) { os_aio_array_free(os_aio_sync_array); os_aio_sync_array = NULL; } } /*************************************************************************** Gets the operating system version. Currently works only on Windows. @return OS_WIN95, OS_WIN31, OS_WINNT, OS_WIN2000 */ UNIV_INTERN ulint os_get_os_version(void) /*===================*/ { #ifdef __WIN__ OSVERSIONINFO os_info; os_info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); ut_a(GetVersionEx(&os_info)); if (os_info.dwPlatformId == VER_PLATFORM_WIN32s) { return(OS_WIN31); } else if (os_info.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) { return(OS_WIN95); } else if (os_info.dwPlatformId == VER_PLATFORM_WIN32_NT) { if (os_info.dwMajorVersion <= 4) { return(OS_WINNT); } else { return(OS_WIN2000); } } else { ut_error; return(0); } #else ut_error; return(0); #endif } /***********************************************************************//** Retrieves the last error number if an error occurs in a file io function. The number should be retrieved before any other OS calls (because they may overwrite the error number). If the number is not known to this program, the OS error number + 100 is returned. @return error number, or OS error number + 100 */ UNIV_INTERN ulint os_file_get_last_error( /*===================*/ ibool report_all_errors) /*!< in: TRUE if we want an error message printed of all errors */ { ulint err; #ifdef __WIN__ err = (ulint) GetLastError(); if (report_all_errors || (err != ERROR_DISK_FULL && err != ERROR_FILE_EXISTS)) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Operating system error number %lu" " in a file operation.\n", (ulong) err); if (err == ERROR_PATH_NOT_FOUND) { ib_logger(ib_stream, "InnoDB: The error means the system" " cannot find the path specified.\n"); if (srv_is_being_started) { ib_logger(ib_stream, "InnoDB: If you are installing InnoDB," " remember that you must create\n" "InnoDB: directories yourself, InnoDB" " does not create them.\n"); } } else if (err == ERROR_ACCESS_DENIED) { ib_logger(ib_stream, "InnoDB: The error means your application " "does not have the access rights to\n" "InnoDB: the directory. It may also be" " you have created a subdirectory\n" "InnoDB: of the same name as a data file.\n"); } else if (err == ERROR_SHARING_VIOLATION || err == ERROR_LOCK_VIOLATION) { ib_logger(ib_stream, "InnoDB: The error means that another program" " is using InnoDB's files.\n" "InnoDB: This might be a backup or antivirus" " software or another instance\n" "InnoDB: of this binary." " Please close it to get rid of this error.\n"); } else if (err == ERROR_WORKING_SET_QUOTA || err == ERROR_NO_SYSTEM_RESOURCES) { ib_logger(ib_stream, "InnoDB: The error means that there are no" " sufficient system resources or quota to" " complete the operation.\n"); } else if (err == ERROR_OPERATION_ABORTED) { ib_logger(ib_stream, "InnoDB: The error means that the I/O" " operation has been aborted\n" "InnoDB: because of either a thread exit" " or an application request.\n" "InnoDB: Retry attempt is made.\n"); } else { ib_logger(ib_stream, "InnoDB: Some operating system error numbers" " are described at\n" "InnoDB: " "Check InnoDB website for details\n"); } } if (err == ERROR_FILE_NOT_FOUND) { return(OS_FILE_NOT_FOUND); } else if (err == ERROR_DISK_FULL) { return(OS_FILE_DISK_FULL); } else if (err == ERROR_FILE_EXISTS) { return(OS_FILE_ALREADY_EXISTS); } else if (err == ERROR_SHARING_VIOLATION || err == ERROR_LOCK_VIOLATION) { return(OS_FILE_SHARING_VIOLATION); } else if (err == ERROR_WORKING_SET_QUOTA || err == ERROR_NO_SYSTEM_RESOURCES) { return(OS_FILE_INSUFFICIENT_RESOURCE); } else if (err == ERROR_OPERATION_ABORTED) { return(OS_FILE_OPERATION_ABORTED); } else { return(100 + err); } #else err = (ulint) errno; if (report_all_errors || (err != ENOSPC && err != EEXIST)) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Operating system error number %lu" " in a file operation.\n", (ulong) err); if (err == ENOENT) { ib_logger(ib_stream, "InnoDB: The error means the system" " cannot find the path specified.\n"); if (srv_is_being_started) { ib_logger(ib_stream, "InnoDB: If you are installing InnoDB," " remember that you must create\n" "InnoDB: directories yourself, InnoDB" " does not create them.\n"); } } else if (err == EACCES) { ib_logger(ib_stream, "InnoDB: The error means your application " "does not have the access rights to\n" "InnoDB: the directory.\n"); } else { if (strerror((int)err) != NULL) { ib_logger(ib_stream, "InnoDB: Error number %lu" " means '%s'.\n", err, strerror((int)err)); } ib_logger(ib_stream, "InnoDB: " "Check InnoDB website for details\n"); } } if (err == ENOSPC) { return(OS_FILE_DISK_FULL); } else if (err == ENOENT) { return(OS_FILE_NOT_FOUND); } else if (err == EEXIST) { return(OS_FILE_ALREADY_EXISTS); } else if (err == EXDEV || err == ENOTDIR || err == EISDIR) { return(OS_FILE_PATH_ERROR); } else { return(100 + err); } #endif } /****************************************************************//** Does error handling when a file operation fails. Conditionally exits (calling exit(3)) based on should_exit value and the error type @return TRUE if we should retry the operation */ static ibool os_file_handle_error_cond_exit( /*===========================*/ const char* name, /*!< in: name of a file or NULL */ const char* operation, /*!< in: operation */ ibool should_exit) /*!< in: call exit(3) if unknown error and this parameter is TRUE */ { ulint err; err = os_file_get_last_error(FALSE); if (err == OS_FILE_DISK_FULL) { /* We only print a warning about disk full once */ if (os_has_said_disk_full) { return(FALSE); } if (name) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Encountered a problem with" " file %s\n", name); } ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Disk is full. Try to clean the disk" " to free space.\n"); os_has_said_disk_full = TRUE; return(FALSE); } else if (err == OS_FILE_AIO_RESOURCES_RESERVED) { return(TRUE); } else if (err == OS_FILE_ALREADY_EXISTS || err == OS_FILE_PATH_ERROR) { return(FALSE); } else if (err == OS_FILE_SHARING_VIOLATION) { os_thread_sleep(10000000); /* 10 sec */ return(TRUE); } else if (err == OS_FILE_INSUFFICIENT_RESOURCE) { os_thread_sleep(100000); /* 100 ms */ return(TRUE); } else if (err == OS_FILE_OPERATION_ABORTED) { os_thread_sleep(100000); /* 100 ms */ return(TRUE); } else { if (name) { ib_logger(ib_stream, "InnoDB: File name %s\n", name); } ib_logger(ib_stream, "InnoDB: File operation call: '%s'.\n", operation); if (should_exit) { srv_panic(DB_ERROR, "InnoDB: Cannot continue operation.\n"); } } return(FALSE); } /****************************************************************//** Does error handling when a file operation fails. @return TRUE if we should retry the operation */ static ibool os_file_handle_error( /*=================*/ const char* name, /*!< in: name of a file or NULL */ const char* operation)/*!< in: operation */ { /* exit in case of unknown error */ return(os_file_handle_error_cond_exit(name, operation, TRUE)); } /****************************************************************//** Does error handling when a file operation fails. @return TRUE if we should retry the operation */ static ibool os_file_handle_error_no_exit( /*=========================*/ const char* name, /*!< in: name of a file or NULL */ const char* operation)/*!< in: operation */ { /* don't exit in case of unknown error */ return(os_file_handle_error_cond_exit(name, operation, FALSE)); } #undef USE_FILE_LOCK #define USE_FILE_LOCK #if defined(UNIV_HOTBACKUP) || defined(__WIN__) || defined(__NETWARE__) /* InnoDB Hot Backup does not lock the data files. * On Windows, mandatory locking is used. */ # undef USE_FILE_LOCK #endif #ifdef USE_FILE_LOCK /****************************************************************//** Obtain an exclusive lock on a file. @return 0 on success */ static int os_file_lock( /*=========*/ int fd, /*!< in: file descriptor */ const char* name) /*!< in: file name */ { struct flock lk; lk.l_type = F_WRLCK; lk.l_whence = SEEK_SET; lk.l_start = lk.l_len = 0; if (fcntl(fd, F_SETLK, &lk) == -1) { ib_logger(ib_stream, "InnoDB: Unable to lock %s, error: %d\n", name, errno); if (errno == EAGAIN || errno == EACCES) { ib_logger(ib_stream, "InnoDB: Check that you do not already have" " another instance of your application is\n" "InnoDB: using the same InnoDB data" " or log files.\n"); } return(-1); } return(0); } #endif /* USE_FILE_LOCK */ #ifndef UNIV_HOTBACKUP /****************************************************************//** Creates the seek mutexes used in positioned reads and writes. */ UNIV_INTERN void os_io_init_simple(void) /*===================*/ { ulint i; os_file_count_mutex = os_mutex_create(NULL); for (i = 0; i < OS_FILE_N_SEEK_MUTEXES; i++) { os_file_seek_mutexes[i] = os_mutex_create(NULL); } } /***********************************************************************//** Creates a temporary file. This function is like tmpfile(3)/ On Netware, this function is like tmpfile(3), because the C run-time library of Netware does not expose the delete-on-close flag. @return temporary file handle, or NULL on error */ UNIV_INTERN FILE* os_file_create_tmpfile(void) /*========================*/ { #ifdef __NETWARE__ FILE* file = tmpfile(); #else /* __NETWARE__ */ FILE* file = NULL; int fd = ib_create_tempfile("ib"); if (fd >= 0) { #ifdef __WIN__ file = _fdopen(fd, "w+b"); #else file = fdopen(fd, "w+b"); #endif } #endif /* __NETWARE__ */ if (!file) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Error: unable to create temporary file;" " errno: %d\n", errno); #ifndef __NETWARE__ if (fd >= 0) { #ifdef __WIN__ _close(fd); #else close(fd); #endif } #endif /* !__NETWARE__ */ } return(file); } #endif /* !UNIV_HOTBACKUP */ /***********************************************************************//** The os_file_opendir() function opens a directory stream corresponding to the directory named by the dirname argument. The directory stream is positioned at the first entry. In both Unix and Windows we automatically skip the '.' and '..' items at the start of the directory listing. @return directory stream, NULL if error */ UNIV_INTERN os_file_dir_t os_file_opendir( /*============*/ const char* dirname, /*!< in: directory name; it must not contain a trailing '\' or '/' */ ibool error_is_fatal) /*!< in: TRUE if we should treat an error as a fatal error; if we try to open symlinks then we do not wish a fatal error if it happens not to be a directory */ { os_file_dir_t dir; #ifdef __WIN__ LPWIN32_FIND_DATA lpFindFileData; char path[OS_FILE_MAX_PATH + 3]; ut_a(strlen(dirname) < OS_FILE_MAX_PATH); strcpy(path, dirname); strcpy(path + strlen(path), "\\*"); /* Note that in Windows opening the 'directory stream' also retrieves the first entry in the directory. Since it is '.', that is no problem, as we will skip over the '.' and '..' entries anyway. */ lpFindFileData = ut_malloc(sizeof(WIN32_FIND_DATA)); dir = FindFirstFile((LPCTSTR) path, lpFindFileData); ut_free(lpFindFileData); if (dir == INVALID_HANDLE_VALUE) { if (error_is_fatal) { os_file_handle_error(dirname, "opendir"); } return(NULL); } return(dir); #else dir = opendir(dirname); if (dir == NULL && error_is_fatal) { os_file_handle_error(dirname, "opendir"); } return(dir); #endif } /***********************************************************************//** Closes a directory stream. @return 0 if success, -1 if failure */ UNIV_INTERN int os_file_closedir( /*=============*/ os_file_dir_t dir) /*!< in: directory stream */ { #ifdef __WIN__ BOOL ret; ret = FindClose(dir); if (!ret) { os_file_handle_error_no_exit(NULL, "closedir"); return(-1); } return(0); #else int ret; ret = closedir(dir); if (ret) { os_file_handle_error_no_exit(NULL, "closedir"); } return(ret); #endif } /***********************************************************************//** This function returns information of the next file in the directory. We jump over the '.' and '..' entries in the directory. @return 0 if ok, -1 if error, 1 if at the end of the directory */ UNIV_INTERN int os_file_readdir_next_file( /*======================*/ const char* dirname,/*!< in: directory name or path */ os_file_dir_t dir, /*!< in: directory stream */ os_file_stat_t* info) /*!< in/out: buffer where the info is returned */ { #ifdef __WIN__ LPWIN32_FIND_DATA lpFindFileData; BOOL ret; lpFindFileData = ut_malloc(sizeof(WIN32_FIND_DATA)); next_file: ret = FindNextFile(dir, lpFindFileData); if (ret) { ut_a(strlen((char *) lpFindFileData->cFileName) < OS_FILE_MAX_PATH); if (strcmp((char *) lpFindFileData->cFileName, ".") == 0 || strcmp((char *) lpFindFileData->cFileName, "..") == 0) { goto next_file; } strcpy(info->name, (char *) lpFindFileData->cFileName); info->size = (ib_int64_t)(lpFindFileData->nFileSizeLow) + (((ib_int64_t)(lpFindFileData->nFileSizeHigh)) << 32); if (lpFindFileData->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { /* TODO: test Windows symlinks */ info->type = OS_FILE_TYPE_LINK; } else if (lpFindFileData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { info->type = OS_FILE_TYPE_DIR; } else { /* It is probably safest to assume that all other file types are normal. Better to check them rather than blindly skip them. */ info->type = OS_FILE_TYPE_FILE; } } ut_free(lpFindFileData); if (ret) { return(0); } else if (GetLastError() == ERROR_NO_MORE_FILES) { return(1); } else { os_file_handle_error_no_exit(dirname, "readdir_next_file"); return(-1); } #else ulint len; struct dirent* ent; char* full_path; int ret; struct stat statinfo; #ifdef HAVE_READDIR_R char dirent_buf[sizeof(struct dirent) + _POSIX_PATH_MAX + 100]; /* In /mysys/my_lib.c, _POSIX_PATH_MAX + 1 is used as the max file name len; but in most standards, the length is NAME_MAX; we add 100 to be even safer */ #endif next_file: #ifdef HAVE_READDIR_R ret = readdir_r(dir, (struct dirent*)dirent_buf, &ent); if (ret != 0 #ifdef UNIV_AIX /* On AIX, only if we got non-NULL 'ent' (result) value and a non-zero 'ret' (return) value, it indicates a failed readdir_r() call. An NULL 'ent' with an non-zero 'ret' would indicate the "end of the directory" is reached. */ && ent != NULL #endif ) { ib_logger(ib_stream, "InnoDB: cannot read directory %s, error %lu\n", dirname, (ulong)ret); return(-1); } if (ent == NULL) { /* End of directory */ return(1); } ut_a(ut_strlen(ent->d_name) < _POSIX_PATH_MAX + 100 - 1); #else ent = readdir(dir); if (ent == NULL) { return(1); } #endif ut_a(ut_strlen(ent->d_name) < OS_FILE_MAX_PATH); if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) { goto next_file; } ut_strcpy(info->name, ent->d_name); len = ut_strlen(dirname) + ut_strlen(ent->d_name) + 10; full_path = ut_malloc(len); ut_snprintf(full_path, len, "%s/%s", dirname, ent->d_name); ret = stat(full_path, &statinfo); if (ret) { if (errno == ENOENT) { /* readdir() returned a file that does not exist, it must have been deleted in the meantime. Do what would have happened if the file was deleted before readdir() - ignore and go to the next entry. If this is the last entry then info->name will still contain the name of the deleted file when this function returns, but this is not an issue since the caller shouldn't be looking at info when end of directory is returned. */ ut_free(full_path); goto next_file; } os_file_handle_error_no_exit(full_path, "stat"); ut_free(full_path); return(-1); } info->size = (ib_int64_t)statinfo.st_size; if (S_ISDIR(statinfo.st_mode)) { info->type = OS_FILE_TYPE_DIR; } else if (S_ISLNK(statinfo.st_mode)) { info->type = OS_FILE_TYPE_LINK; } else if (S_ISREG(statinfo.st_mode)) { info->type = OS_FILE_TYPE_FILE; } else { info->type = OS_FILE_TYPE_UNKNOWN; } ut_free(full_path); return(0); #endif } /*****************************************************************//** This function attempts to create a directory named pathname. The new directory gets default permissions. On Unix the permissions are (0770 & ~umask). If the directory exists already, nothing is done and the call succeeds, unless the fail_if_exists arguments is true. @return TRUE if call succeeds, FALSE on error */ UNIV_INTERN ibool os_file_create_directory( /*=====================*/ const char* pathname, /*!< in: directory name as null-terminated string */ ibool fail_if_exists) /*!< in: if TRUE, pre-existing directory is treated as an error. */ { #ifdef __WIN__ BOOL rcode; rcode = CreateDirectory((LPCTSTR) pathname, NULL); if (!(rcode != 0 || (GetLastError() == ERROR_ALREADY_EXISTS && !fail_if_exists))) { /* failure */ os_file_handle_error(pathname, "CreateDirectory"); return(FALSE); } return (TRUE); #else int rcode; rcode = mkdir(pathname, 0770); if (!(rcode == 0 || (errno == EEXIST && !fail_if_exists))) { /* failure */ os_file_handle_error(pathname, "mkdir"); return(FALSE); } return (TRUE); #endif } /****************************************************************//** A simple function to open or create a file. @return own: handle to the file, not defined if error, error number can be retrieved with os_file_get_last_error */ UNIV_INTERN os_file_t os_file_create_simple( /*==================*/ const char* name, /*!< in: name of the file or path as a null-terminated string */ ulint create_mode,/*!< in: OS_FILE_OPEN if an existing file is opened (if does not exist, error), or OS_FILE_CREATE if a new file is created (if exists, error), or OS_FILE_CREATE_PATH if new file (if exists, error) and subdirectories along its path are created (if needed)*/ ulint access_type,/*!< in: OS_FILE_READ_ONLY or OS_FILE_READ_WRITE */ ibool* success)/*!< out: TRUE if succeed, FALSE if error */ { #ifdef __WIN__ os_file_t file; DWORD create_flag; DWORD access; DWORD attributes = 0; ibool retry; try_again: ut_a(name); if (create_mode == OS_FILE_OPEN) { create_flag = OPEN_EXISTING; } else if (create_mode == OS_FILE_CREATE) { create_flag = CREATE_NEW; } else if (create_mode == OS_FILE_CREATE_PATH) { /* create subdirs along the path if needed */ *success = os_file_create_subdirs_if_needed(name); if (!*success) { ut_error; } create_flag = CREATE_NEW; create_mode = OS_FILE_CREATE; } else { create_flag = 0; ut_error; } if (access_type == OS_FILE_READ_ONLY) { access = GENERIC_READ; } else if (access_type == OS_FILE_READ_WRITE) { access = GENERIC_READ | GENERIC_WRITE; } else { access = 0; ut_error; } file = CreateFile((LPCTSTR) name, access, FILE_SHARE_READ | FILE_SHARE_WRITE, /* file can be read and written also by other processes */ NULL, /* default security attributes */ create_flag, attributes, NULL); /*!< no template file */ if (file == INVALID_HANDLE_VALUE) { *success = FALSE; retry = os_file_handle_error(name, create_mode == OS_FILE_OPEN ? "open" : "create"); if (retry) { goto try_again; } } else { *success = TRUE; } return(file); #else /* __WIN__ */ os_file_t file; int create_flag; ibool retry; try_again: ut_a(name); if (create_mode == OS_FILE_OPEN) { if (access_type == OS_FILE_READ_ONLY) { create_flag = O_RDONLY; } else { create_flag = O_RDWR; } } else if (create_mode == OS_FILE_CREATE) { create_flag = O_RDWR | O_CREAT | O_EXCL; } else if (create_mode == OS_FILE_CREATE_PATH) { /* create subdirs along the path if needed */ *success = os_file_create_subdirs_if_needed(name); if (!*success) { return (-1); } create_flag = O_RDWR | O_CREAT | O_EXCL; create_mode = OS_FILE_CREATE; } else { create_flag = 0; ut_error; } if (create_mode == OS_FILE_CREATE) { file = open(name, create_flag, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); } else { file = open(name, create_flag); } if (file == -1) { *success = FALSE; retry = os_file_handle_error(name, create_mode == OS_FILE_OPEN ? "open" : "create"); if (retry) { goto try_again; } #ifdef USE_FILE_LOCK } else if (access_type == OS_FILE_READ_WRITE && os_file_lock(file, name)) { *success = FALSE; close(file); file = -1; #endif } else { *success = TRUE; } return(file); #endif /* __WIN__ */ } /****************************************************************//** A simple function to open or create a file. @return own: handle to the file, not defined if error, error number can be retrieved with os_file_get_last_error */ UNIV_INTERN os_file_t os_file_create_simple_no_error_handling( /*====================================*/ const char* name, /*!< in: name of the file or path as a null-terminated string */ ulint create_mode,/*!< in: OS_FILE_OPEN if an existing file is opened (if does not exist, error), or OS_FILE_CREATE if a new file is created (if exists, error) */ ulint access_type,/*!< in: OS_FILE_READ_ONLY, OS_FILE_READ_WRITE, or OS_FILE_READ_ALLOW_DELETE; the last option is used by a backup program reading the file */ ibool* success)/*!< out: TRUE if succeed, FALSE if error */ { #ifdef __WIN__ os_file_t file; DWORD create_flag; DWORD access; DWORD attributes = 0; DWORD share_mode = FILE_SHARE_READ | FILE_SHARE_WRITE; ut_a(name); if (create_mode == OS_FILE_OPEN) { create_flag = OPEN_EXISTING; } else if (create_mode == OS_FILE_CREATE) { create_flag = CREATE_NEW; } else { create_flag = 0; ut_error; } if (access_type == OS_FILE_READ_ONLY) { access = GENERIC_READ; } else if (access_type == OS_FILE_READ_WRITE) { access = GENERIC_READ | GENERIC_WRITE; } else if (access_type == OS_FILE_READ_ALLOW_DELETE) { access = GENERIC_READ; share_mode = FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE; /*!< A backup program has to give user the maximum freedom to do what it likes with the file */ } else { access = 0; ut_error; } file = CreateFile((LPCTSTR) name, access, share_mode, NULL, /* default security attributes */ create_flag, attributes, NULL); /*!< no template file */ if (file == INVALID_HANDLE_VALUE) { *success = FALSE; } else { *success = TRUE; } return(file); #else /* __WIN__ */ os_file_t file; int create_flag; ut_a(name); if (create_mode == OS_FILE_OPEN) { if (access_type == OS_FILE_READ_ONLY) { create_flag = O_RDONLY; } else { create_flag = O_RDWR; } } else if (create_mode == OS_FILE_CREATE) { create_flag = O_RDWR | O_CREAT | O_EXCL; } else { create_flag = 0; ut_error; } if (create_mode == OS_FILE_CREATE) { file = open(name, create_flag, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); } else { file = open(name, create_flag); } if (file == -1) { *success = FALSE; #ifdef USE_FILE_LOCK } else if (access_type == OS_FILE_READ_WRITE && os_file_lock(file, name)) { *success = FALSE; close(file); file = -1; #endif } else { *success = TRUE; } return(file); #endif /* __WIN__ */ } /****************************************************************//** Tries to disable OS caching on an opened file descriptor. */ UNIV_INTERN void os_file_set_nocache( /*================*/ int fd, /*!< in: file descriptor to alter */ const char* file_name, /*!< in: file name, used in the diagnostic message */ const char* operation_name) /*!< in: "open" or "create"; used in the diagnostic message */ { /* some versions of Solaris may not have DIRECTIO_ON */ #if defined(UNIV_SOLARIS) && defined(DIRECTIO_ON) if (directio(fd, DIRECTIO_ON) == -1) { int errno_save; errno_save = (int)errno; ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Failed to set DIRECTIO_ON " "on file %s: %s: %s, continuing anyway\n", file_name, operation_name, strerror(errno_save)); } #elif defined(O_DIRECT) if (fcntl(fd, F_SETFL, O_DIRECT) == -1) { int errno_save; errno_save = (int)errno; ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Failed to set O_DIRECT " "on file %s: %s: %s, continuing anyway\n", file_name, operation_name, strerror(errno_save)); if (errno_save == EINVAL) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: O_DIRECT is known to result in " "'Invalid argument' on Linux on tmpfs."); } } #endif } /****************************************************************//** Opens an existing file or creates a new. @return own: handle to the file, not defined if error, error number can be retrieved with os_file_get_last_error */ UNIV_INTERN os_file_t os_file_create( /*===========*/ const char* name, /*!< in: name of the file or path as a null-terminated string */ ulint create_mode,/*!< in: OS_FILE_OPEN if an existing file is opened (if does not exist, error), or OS_FILE_CREATE if a new file is created (if exists, error), OS_FILE_OVERWRITE if a new file is created or an old overwritten; OS_FILE_OPEN_RAW, if a raw device or disk partition should be opened */ ulint purpose,/*!< in: OS_FILE_AIO, if asynchronous, non-buffered i/o is desired, OS_FILE_NORMAL, if any normal file; NOTE that it also depends on type, os_aio_.. and srv_.. variables whether we really use async i/o or unbuffered i/o: look in the function source code for the exact rules */ ulint type, /*!< in: OS_DATA_FILE or OS_LOG_FILE */ ibool* success)/*!< out: TRUE if succeed, FALSE if error */ { #ifdef __WIN__ os_file_t file; DWORD share_mode = FILE_SHARE_READ; DWORD create_flag; DWORD attributes; ibool retry; try_again: ut_a(name); if (create_mode == OS_FILE_OPEN_RAW) { create_flag = OPEN_EXISTING; share_mode = FILE_SHARE_WRITE; } else if (create_mode == OS_FILE_OPEN || create_mode == OS_FILE_OPEN_RETRY) { create_flag = OPEN_EXISTING; } else if (create_mode == OS_FILE_CREATE) { create_flag = CREATE_NEW; } else if (create_mode == OS_FILE_OVERWRITE) { create_flag = CREATE_ALWAYS; } else { create_flag = 0; ut_error; } if (purpose == OS_FILE_AIO) { /* If specified, use asynchronous (overlapped) io and no buffering of writes in the OS */ attributes = 0; #ifdef WIN_ASYNC_IO if (os_aio_use_native_aio) { attributes = attributes | FILE_FLAG_OVERLAPPED; } #endif #ifdef UNIV_NON_BUFFERED_IO # ifndef UNIV_HOTBACKUP if (type == OS_LOG_FILE && srv_flush_log_at_trx_commit == 2) { /* Do not use unbuffered i/o to log files because value 2 denotes that we do not flush the log at every commit, but only once per second */ } else if (srv_win_file_flush_method == SRV_WIN_IO_UNBUFFERED) { attributes = attributes | FILE_FLAG_NO_BUFFERING; } # else /* !UNIV_HOTBACKUP */ attributes = attributes | FILE_FLAG_NO_BUFFERING; # endif /* !UNIV_HOTBACKUP */ #endif /* UNIV_NON_BUFFERED_IO */ } else if (purpose == OS_FILE_NORMAL) { attributes = 0; #ifdef UNIV_NON_BUFFERED_IO # ifndef UNIV_HOTBACKUP if (type == OS_LOG_FILE && srv_flush_log_at_trx_commit == 2) { /* Do not use unbuffered i/o to log files because value 2 denotes that we do not flush the log at every commit, but only once per second */ } else if (srv_win_file_flush_method == SRV_WIN_IO_UNBUFFERED) { attributes = attributes | FILE_FLAG_NO_BUFFERING; } # else /* !UNIV_HOTBACKUP */ attributes = attributes | FILE_FLAG_NO_BUFFERING; # endif /* !UNIV_HOTBACKUP */ #endif /* UNIV_NON_BUFFERED_IO */ } else { attributes = 0; ut_error; } file = CreateFile((LPCTSTR) name, GENERIC_READ | GENERIC_WRITE, /* read and write access */ share_mode, /* File can be read also by other processes; we must give the read permission because of ibbackup. We do not give the write permission to others because if one would succeed to start 2 instances of the binary on the SAME files, that could cause severe database corruption! When opening raw disk partitions, Microsoft manuals say that we must give also the write permission. */ NULL, /* default security attributes */ create_flag, attributes, NULL); /*!< no template file */ if (file == INVALID_HANDLE_VALUE) { *success = FALSE; /* When srv_file_per_table is on, file creation failure may not be critical to the whole instance. Do not crash the server in case of unknown errors. */ if (srv_file_per_table) { retry = os_file_handle_error_no_exit(name, create_mode == OS_FILE_CREATE ? "create" : "open"); } else { retry = os_file_handle_error(name, create_mode == OS_FILE_CREATE ? "create" : "open"); } if (retry) { goto try_again; } } else { *success = TRUE; } return(file); #else /* __WIN__ */ os_file_t file; int create_flag; ibool retry; const char* mode_str = NULL; const char* type_str = NULL; const char* purpose_str = NULL; try_again: ut_a(name); if (create_mode == OS_FILE_OPEN || create_mode == OS_FILE_OPEN_RAW || create_mode == OS_FILE_OPEN_RETRY) { mode_str = "OPEN"; create_flag = O_RDWR; } else if (create_mode == OS_FILE_CREATE) { mode_str = "CREATE"; create_flag = O_RDWR | O_CREAT | O_EXCL; } else if (create_mode == OS_FILE_OVERWRITE) { mode_str = "OVERWRITE"; create_flag = O_RDWR | O_CREAT | O_TRUNC; } else { create_flag = 0; ut_error; } if (type == OS_LOG_FILE) { type_str = "LOG"; } else if (type == OS_DATA_FILE) { type_str = "DATA"; } else { ut_error; } if (purpose == OS_FILE_AIO) { purpose_str = "AIO"; } else if (purpose == OS_FILE_NORMAL) { purpose_str = "NORMAL"; } else { ut_error; } #if 0 ib_logger(ib_stream, "Opening file %s, mode %s, type %s, purpose %s\n", name, mode_str, type_str, purpose_str); #endif #ifdef O_SYNC /* We let O_SYNC only affect log files; note that we map O_DSYNC to O_SYNC because the datasync options seemed to corrupt files in 2001 in both Linux and Solaris */ if (type == OS_LOG_FILE && srv_unix_file_flush_method == SRV_UNIX_O_DSYNC) { # if 0 ib_logger(ib_stream, "Using O_SYNC for file %s\n", name); # endif create_flag = create_flag | O_SYNC; } #endif /* O_SYNC */ file = open(name, create_flag, os_innodb_umask); if (file == -1) { *success = FALSE; /* When srv_file_per_table is on, file creation failure may not be critical to the whole instance. Do not crash the server in case of unknown errors. */ if (srv_file_per_table) { retry = os_file_handle_error_no_exit(name, create_mode == OS_FILE_CREATE ? "create" : "open"); } else { retry = os_file_handle_error(name, create_mode == OS_FILE_CREATE ? "create" : "open"); } if (retry) { goto try_again; } else { return(file /* -1 */); } } /* else */ *success = TRUE; /* We disable OS caching (O_DIRECT) only on data files */ if (type != OS_LOG_FILE && srv_unix_file_flush_method == SRV_UNIX_O_DIRECT) { os_file_set_nocache(file, name, mode_str); } #ifdef USE_FILE_LOCK if (create_mode != OS_FILE_OPEN_RAW && os_file_lock(file, name)) { if (create_mode == OS_FILE_OPEN_RETRY) { int i; ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Retrying to lock" " the first data file\n"); for (i = 0; i < 100; i++) { os_thread_sleep(1000000); if (!os_file_lock(file, name)) { *success = TRUE; return(file); } } ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Unable to open the first " "data file\n"); } *success = FALSE; close(file); file = -1; } #endif /* USE_FILE_LOCK */ return(file); #endif /* __WIN__ */ } /***********************************************************************//** Deletes a file if it exists. The file has to be closed before calling this. @return TRUE if success */ UNIV_INTERN ibool os_file_delete_if_exists( /*=====================*/ const char* name) /*!< in: file path as a null-terminated string */ { #ifdef __WIN__ BOOL ret; ulint count = 0; loop: /* In Windows, deleting an .ibd file may fail if ibbackup is copying it */ ret = DeleteFile((LPCTSTR)name); if (ret) { return(TRUE); } if (GetLastError() == ERROR_FILE_NOT_FOUND) { /* the file does not exist, this not an error */ return(TRUE); } count++; if (count > 100 && 0 == (count % 10)) { ib_logger(ib_stream, "InnoDB: Warning: cannot delete file %s\n" "InnoDB: Are you running ibbackup" " to back up the file?\n", name); os_file_get_last_error(TRUE); /* print error information */ } os_thread_sleep(1000000); /* sleep for a second */ if (count > 2000) { return(FALSE); } goto loop; #else int ret; ret = unlink(name); if (ret != 0 && errno != ENOENT) { os_file_handle_error_no_exit(name, "delete"); return(FALSE); } return(TRUE); #endif } /***********************************************************************//** Deletes a file. The file has to be closed before calling this. @return TRUE if success */ UNIV_INTERN ibool os_file_delete( /*===========*/ const char* name) /*!< in: file path as a null-terminated string */ { #ifdef __WIN__ BOOL ret; ulint count = 0; loop: /* In Windows, deleting an .ibd file may fail if ibbackup is copying it */ ret = DeleteFile((LPCTSTR)name); if (ret) { return(TRUE); } if (GetLastError() == ERROR_FILE_NOT_FOUND) { /* If the file does not exist, we classify this as a 'mild' error and return */ return(FALSE); } count++; if (count > 100 && 0 == (count % 10)) { ib_logger(ib_stream, "InnoDB: Warning: cannot delete file %s\n" "InnoDB: Are you running ibbackup" " to back up the file?\n", name); os_file_get_last_error(TRUE); /* print error information */ } os_thread_sleep(1000000); /* sleep for a second */ if (count > 2000) { return(FALSE); } goto loop; #else int ret; ret = unlink(name); if (ret != 0) { os_file_handle_error_no_exit(name, "delete"); return(FALSE); } return(TRUE); #endif } /***********************************************************************//** Renames a file (can also move it to another directory). It is safest that the file is closed before calling this function. @return TRUE if success */ UNIV_INTERN ibool os_file_rename( /*===========*/ const char* oldpath,/*!< in: old file path as a null-terminated string */ const char* newpath)/*!< in: new file path */ { #ifdef __WIN__ BOOL ret; ret = MoveFile((LPCTSTR)oldpath, (LPCTSTR)newpath); if (ret) { return(TRUE); } os_file_handle_error_no_exit(oldpath, "rename"); return(FALSE); #else int ret; ret = rename(oldpath, newpath); if (ret != 0) { os_file_handle_error_no_exit(oldpath, "rename"); return(FALSE); } return(TRUE); #endif } /***********************************************************************//** Closes a file handle. In case of error, error number can be retrieved with os_file_get_last_error. @return TRUE if success */ UNIV_INTERN ibool os_file_close( /*==========*/ os_file_t file) /*!< in, own: handle to a file */ { #ifdef __WIN__ BOOL ret; ut_a(file); ret = CloseHandle(file); if (ret) { return(TRUE); } os_file_handle_error(NULL, "close"); return(FALSE); #else int ret; ret = close(file); if (ret == -1) { os_file_handle_error(NULL, "close"); return(FALSE); } return(TRUE); #endif } #ifdef UNIV_HOTBACKUP /***********************************************************************//** Closes a file handle. @return TRUE if success */ UNIV_INTERN ibool os_file_close_no_error_handling( /*============================*/ os_file_t file) /*!< in, own: handle to a file */ { #ifdef __WIN__ BOOL ret; ut_a(file); ret = CloseHandle(file); if (ret) { return(TRUE); } return(FALSE); #else int ret; ret = close(file); if (ret == -1) { return(FALSE); } return(TRUE); #endif } #endif /* UNIV_HOTBACKUP */ /***********************************************************************//** Gets a file size. @return TRUE if success */ UNIV_INTERN ibool os_file_get_size( /*=============*/ os_file_t file, /*!< in: handle to a file */ ulint* size, /*!< out: least significant 32 bits of file size */ ulint* size_high)/*!< out: most significant 32 bits of size */ { #ifdef __WIN__ DWORD high; DWORD low; low = GetFileSize(file, &high); if ((low == 0xFFFFFFFF) && (GetLastError() != NO_ERROR)) { return(FALSE); } *size = low; *size_high = high; return(TRUE); #else off_t offs; offs = lseek(file, 0, SEEK_END); if (offs == ((off_t)-1)) { return(FALSE); } if (sizeof(off_t) > 4) { *size = (ulint)(offs & 0xFFFFFFFFUL); *size_high = (ulint)((ib_u64_t) offs >> 32); } else { *size = (ulint) offs; *size_high = 0; } return(TRUE); #endif } /***********************************************************************//** Gets file size as a 64-bit integer ib_int64_t. @return size in bytes, -1 if error */ UNIV_INTERN ib_int64_t os_file_get_size_as_iblonglong( /*===========================*/ os_file_t file) /*!< in: handle to a file */ { ulint size; ulint size_high; ibool success; success = os_file_get_size(file, &size, &size_high); if (!success) { return(-1); } return((((ib_int64_t)size_high) << 32) + (ib_int64_t)size); } /***********************************************************************//** Write the specified number of zeros to a newly created file. @return TRUE if success */ UNIV_INTERN ibool os_file_set_size( /*=============*/ const char* name, /*!< in: name of the file or path as a null-terminated string */ os_file_t file, /*!< in: handle to a file */ ulint size, /*!< in: least significant 32 bits of file size */ ulint size_high)/*!< in: most significant 32 bits of size */ { ib_int64_t current_size; ib_int64_t desired_size; ibool ret; byte* buf; byte* buf2; ulint buf_size; ut_a(size == (size & 0xFFFFFFFF)); current_size = 0; desired_size = (ib_int64_t)size + (((ib_int64_t)size_high) << 32); /* Write up to 1 megabyte at a time. */ buf_size = ut_min(64, (ulint) (desired_size / UNIV_PAGE_SIZE)) * UNIV_PAGE_SIZE; buf2 = ut_malloc(buf_size + UNIV_PAGE_SIZE); /* Align the buffer for possible raw i/o */ buf = ut_align(buf2, UNIV_PAGE_SIZE); /* Write buffer full of zeros */ memset(buf, 0, buf_size); if (desired_size >= (ib_int64_t)(100 * 1024 * 1024)) { ib_logger(ib_stream, "InnoDB: Progress in MB:"); } while (current_size < desired_size) { ulint n_bytes; if (desired_size - current_size < (ib_int64_t) buf_size) { n_bytes = (ulint) (desired_size - current_size); } else { n_bytes = buf_size; } ret = os_file_write(name, file, buf, (ulint)(current_size & 0xFFFFFFFF), (ulint)(current_size >> 32), n_bytes); if (!ret) { ut_free(buf2); goto error_handling; } /* Print about progress for each 100 MB written */ if ((ib_int64_t) (current_size + n_bytes) / (ib_int64_t)(100 * 1024 * 1024) != current_size / (ib_int64_t)(100 * 1024 * 1024)) { ib_logger(ib_stream, " %lu00", (ulong) ((current_size + n_bytes) / (ib_int64_t)(100 * 1024 * 1024))); } current_size += n_bytes; } if (desired_size >= (ib_int64_t)(100 * 1024 * 1024)) { ib_logger(ib_stream, "\n"); } ut_free(buf2); ret = os_file_flush(file); if (ret) { return(TRUE); } error_handling: return(FALSE); } #ifdef UNIV_HOTBACKUP /***********************************************************************//** Truncates a file at its current position. @return TRUE if success */ UNIV_INTERN ibool os_file_set_eof( /*============*/ FILE* file) /*!< in: file to be truncated */ { #ifdef __WIN__ HANDLE h = (HANDLE) _get_osfhandle(fileno(file)); return(SetEndOfFile(h)); #else /* __WIN__ */ return(!ftruncate(fileno(file), ftell(file))); #endif /* __WIN__ */ } #endif #ifndef __WIN__ /***********************************************************************//** Wrapper to fsync(2) that retries the call on some errors. Returns the value 0 if successful; otherwise the value -1 is returned and the global variable errno is set to indicate the error. @return 0 if success, -1 otherwise */ static int os_file_fsync( /*==========*/ os_file_t file) /*!< in: handle to a file */ { int ret; int failures; ibool retry; failures = 0; do { ret = fsync(file); os_n_fsyncs++; if (ret == -1 && errno == ENOLCK) { if (failures % 100 == 0) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: fsync(): " "No locks available; retrying\n"); } os_thread_sleep(200000 /* 0.2 sec */); failures++; retry = TRUE; } else { retry = FALSE; } } while (retry); return(ret); } #endif /* !__WIN__ */ /***********************************************************************//** Flushes the write buffers of a given file to the disk. @return TRUE if success */ UNIV_INTERN ibool os_file_flush( /*==========*/ os_file_t file) /*!< in, own: handle to a file */ { #ifdef __WIN__ BOOL ret; ut_a(file); os_n_fsyncs++; ret = FlushFileBuffers(file); if (ret) { return(TRUE); } /* Since Windows returns ERROR_INVALID_FUNCTION if the 'file' is actually a raw device, we choose to ignore that error if we are using raw disks */ if (srv_start_raw_disk_in_use && GetLastError() == ERROR_INVALID_FUNCTION) { return(TRUE); } os_file_handle_error(NULL, "flush"); /* It is a fatal error if a file flush does not succeed, because then the database can get corrupt on disk */ ut_error; return(FALSE); #else int ret; #if defined(HAVE_DARWIN_THREADS) # ifndef F_FULLFSYNC /* The following definition is from the Mac OS X 10.3 */ # define F_FULLFSYNC 51 /* fsync + ask the drive to flush to the media */ # elif F_FULLFSYNC != 51 # error "F_FULLFSYNC != 51: ABI incompatibility with Mac OS X 10.3" # endif /* Apple has disabled fsync() for internal disk drives in OS X. That caused corruption for a user when he tested a power outage. Let us in OS X use a nonstandard flush method recommended by an Apple engineer. */ if (!srv_have_fullfsync) { /* If we are not on an operating system that supports this, then fall back to a plain fsync. */ ret = os_file_fsync(file); } else { ret = fcntl(file, F_FULLFSYNC, NULL); if (ret) { /* If we are not on a file system that supports this, then fall back to a plain fsync. */ ret = os_file_fsync(file); } } #else ret = os_file_fsync(file); #endif if (ret == 0) { return(TRUE); } /* Since Linux returns EINVAL if the 'file' is actually a raw device, we choose to ignore that error if we are using raw disks */ if (srv_start_raw_disk_in_use && errno == EINVAL) { return(TRUE); } ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Error: the OS said file flush did not succeed\n"); os_file_handle_error(NULL, "flush"); /* It is a fatal error if a file flush does not succeed, because then the database can get corrupt on disk */ ut_error; return(FALSE); #endif } #ifndef __WIN__ /*******************************************************************//** Does a synchronous read operation in Posix. @return number of bytes read, -1 if error */ static ssize_t os_file_pread( /*==========*/ os_file_t file, /*!< in: handle to a file */ void* buf, /*!< in: buffer where to read */ ulint n, /*!< in: number of bytes to read */ ulint offset, /*!< in: least significant 32 bits of file offset from where to read */ ulint offset_high) /*!< in: most significant 32 bits of offset */ { off_t offs; #if defined(HAVE_PREAD) && !defined(HAVE_BROKEN_PREAD) ssize_t n_bytes; #endif /* HAVE_PREAD && !HAVE_BROKEN_PREAD */ ut_a((offset & 0xFFFFFFFFUL) == offset); /* If off_t is > 4 bytes in size, then we assume we can pass a 64-bit address */ if (sizeof(off_t) > 4) { offs = (off_t)offset + (((ib_u64_t) offset_high) << 32); } else { offs = (off_t)offset; if (offset_high > 0) { ib_logger(ib_stream, "InnoDB: Error: file read at offset > 4 GB\n"); } } os_n_file_reads++; #if defined(HAVE_PREAD) && !defined(HAVE_BROKEN_PREAD) os_mutex_enter(os_file_count_mutex); os_file_n_pending_preads++; os_n_pending_reads++; os_mutex_exit(os_file_count_mutex); n_bytes = pread(file, buf, (ssize_t)n, offs); os_mutex_enter(os_file_count_mutex); os_file_n_pending_preads--; os_n_pending_reads--; os_mutex_exit(os_file_count_mutex); return(n_bytes); #else { off_t ret_offset; ssize_t ret; #ifndef UNIV_HOTBACKUP ulint i; #endif /* !UNIV_HOTBACKUP */ os_mutex_enter(os_file_count_mutex); os_n_pending_reads++; os_mutex_exit(os_file_count_mutex); #ifndef UNIV_HOTBACKUP /* Protect the seek / read operation with a mutex */ i = ((ulint) file) % OS_FILE_N_SEEK_MUTEXES; os_mutex_enter(os_file_seek_mutexes[i]); #endif /* !UNIV_HOTBACKUP */ ret_offset = lseek(file, offs, SEEK_SET); if (ret_offset < 0) { ret = -1; } else { ret = read(file, buf, (ssize_t)n); } #ifndef UNIV_HOTBACKUP os_mutex_exit(os_file_seek_mutexes[i]); #endif /* !UNIV_HOTBACKUP */ os_mutex_enter(os_file_count_mutex); os_n_pending_reads--; os_mutex_exit(os_file_count_mutex); return(ret); } #endif } /*******************************************************************//** Does a synchronous write operation in Posix. @return number of bytes written, -1 if error */ static ssize_t os_file_pwrite( /*===========*/ os_file_t file, /*!< in: handle to a file */ const void* buf, /*!< in: buffer from where to write */ ulint n, /*!< in: number of bytes to write */ ulint offset, /*!< in: least significant 32 bits of file offset where to write */ ulint offset_high) /*!< in: most significant 32 bits of offset */ { ssize_t ret; off_t offs; ut_a((offset & 0xFFFFFFFFUL) == offset); /* If off_t is > 4 bytes in size, then we assume we can pass a 64-bit address */ if (sizeof(off_t) > 4) { offs = (off_t)offset + (((ib_u64_t) offset_high) << 32); } else { offs = (off_t)offset; if (offset_high > 0) { ib_logger(ib_stream, "InnoDB: Error: file write" " at offset > 4 GB\n"); } } os_n_file_writes++; #if defined(HAVE_PWRITE) && !defined(HAVE_BROKEN_PREAD) os_mutex_enter(os_file_count_mutex); os_file_n_pending_pwrites++; os_n_pending_writes++; os_mutex_exit(os_file_count_mutex); ret = pwrite(file, buf, (ssize_t)n, offs); os_mutex_enter(os_file_count_mutex); os_file_n_pending_pwrites--; os_n_pending_writes--; os_mutex_exit(os_file_count_mutex); # ifdef UNIV_DO_FLUSH if (srv_unix_file_flush_method != SRV_UNIX_LITTLESYNC && srv_unix_file_flush_method != SRV_UNIX_NOSYNC && !os_do_not_call_flush_at_each_write) { /* Always do fsync to reduce the probability that when the OS crashes, a database page is only partially physically written to disk. */ ut_a(TRUE == os_file_flush(file)); } # endif /* UNIV_DO_FLUSH */ return(ret); #else { off_t ret_offset; # ifndef UNIV_HOTBACKUP ulint i; # endif /* !UNIV_HOTBACKUP */ os_mutex_enter(os_file_count_mutex); os_n_pending_writes++; os_mutex_exit(os_file_count_mutex); # ifndef UNIV_HOTBACKUP /* Protect the seek / write operation with a mutex */ i = ((ulint) file) % OS_FILE_N_SEEK_MUTEXES; os_mutex_enter(os_file_seek_mutexes[i]); # endif /* UNIV_HOTBACKUP */ ret_offset = lseek(file, offs, SEEK_SET); if (ret_offset < 0) { ret = -1; goto func_exit; } ret = write(file, buf, (ssize_t)n); # ifdef UNIV_DO_FLUSH if (srv_unix_file_flush_method != SRV_UNIX_LITTLESYNC && srv_unix_file_flush_method != SRV_UNIX_NOSYNC && !os_do_not_call_flush_at_each_write) { /* Always do fsync to reduce the probability that when the OS crashes, a database page is only partially physically written to disk. */ ut_a(TRUE == os_file_flush(file)); } # endif /* UNIV_DO_FLUSH */ func_exit: # ifndef UNIV_HOTBACKUP os_mutex_exit(os_file_seek_mutexes[i]); # endif /* !UNIV_HOTBACKUP */ os_mutex_enter(os_file_count_mutex); os_n_pending_writes--; os_mutex_exit(os_file_count_mutex); return(ret); } #endif } #endif /*******************************************************************//** Requests a synchronous positioned read operation. @return TRUE if request was successful, FALSE if fail */ UNIV_INTERN ibool os_file_read( /*=========*/ os_file_t file, /*!< in: handle to a file */ void* buf, /*!< in: buffer where to read */ ulint offset, /*!< in: least significant 32 bits of file offset where to read */ ulint offset_high, /*!< in: most significant 32 bits of offset */ ulint n) /*!< in: number of bytes to read */ { #ifdef __WIN__ BOOL ret; DWORD len; DWORD ret2; DWORD low; DWORD high; ibool retry; #ifndef UNIV_HOTBACKUP ulint i; #endif /* !UNIV_HOTBACKUP */ ut_a((offset & 0xFFFFFFFFUL) == offset); os_n_file_reads++; os_bytes_read_since_printout += n; try_again: ut_ad(file); ut_ad(buf); ut_ad(n > 0); low = (DWORD) offset; high = (DWORD) offset_high; os_mutex_enter(os_file_count_mutex); os_n_pending_reads++; os_mutex_exit(os_file_count_mutex); #ifndef UNIV_HOTBACKUP /* Protect the seek / read operation with a mutex */ i = ((ulint) file) % OS_FILE_N_SEEK_MUTEXES; os_mutex_enter(os_file_seek_mutexes[i]); #endif /* !UNIV_HOTBACKUP */ ret2 = SetFilePointer(file, low, &high, FILE_BEGIN); if (ret2 == 0xFFFFFFFF && GetLastError() != NO_ERROR) { #ifndef UNIV_HOTBACKUP os_mutex_exit(os_file_seek_mutexes[i]); #endif /* !UNIV_HOTBACKUP */ os_mutex_enter(os_file_count_mutex); os_n_pending_reads--; os_mutex_exit(os_file_count_mutex); goto error_handling; } ret = ReadFile(file, buf, (DWORD) n, &len, NULL); #ifndef UNIV_HOTBACKUP os_mutex_exit(os_file_seek_mutexes[i]); #endif /* !UNIV_HOTBACKUP */ os_mutex_enter(os_file_count_mutex); os_n_pending_reads--; os_mutex_exit(os_file_count_mutex); if (ret && len == n) { return(TRUE); } #else /* __WIN__ */ ibool retry; ssize_t ret; os_bytes_read_since_printout += n; try_again: ret = os_file_pread(file, buf, n, offset, offset_high); if ((ulint)ret == n) { return(TRUE); } ib_logger(ib_stream, "InnoDB: Error: tried to read %lu bytes at offset %lu %lu.\n" "InnoDB: Was only able to read %ld.\n", (ulong)n, (ulong)offset_high, (ulong)offset, (long)ret); #endif /* __WIN__ */ #ifdef __WIN__ error_handling: #endif retry = os_file_handle_error(NULL, "read"); if (retry) { goto try_again; } ib_logger(ib_stream, "InnoDB: Fatal error: cannot read from file." " OS error number %lu.\n", #ifdef __WIN__ (ulong) GetLastError() #else (ulong) errno #endif ); ut_error; return(FALSE); } /*******************************************************************//** Requests a synchronous positioned read operation. This function does not do any error handling. In case of error it returns FALSE. @return TRUE if request was successful, FALSE if fail */ UNIV_INTERN ibool os_file_read_no_error_handling( /*===========================*/ os_file_t file, /*!< in: handle to a file */ void* buf, /*!< in: buffer where to read */ ulint offset, /*!< in: least significant 32 bits of file offset where to read */ ulint offset_high, /*!< in: most significant 32 bits of offset */ ulint n) /*!< in: number of bytes to read */ { #ifdef __WIN__ BOOL ret; DWORD len; DWORD ret2; DWORD low; DWORD high; ibool retry; #ifndef UNIV_HOTBACKUP ulint i; #endif /* !UNIV_HOTBACKUP */ ut_a((offset & 0xFFFFFFFFUL) == offset); os_n_file_reads++; os_bytes_read_since_printout += n; try_again: ut_ad(file); ut_ad(buf); ut_ad(n > 0); low = (DWORD) offset; high = (DWORD) offset_high; os_mutex_enter(os_file_count_mutex); os_n_pending_reads++; os_mutex_exit(os_file_count_mutex); #ifndef UNIV_HOTBACKUP /* Protect the seek / read operation with a mutex */ i = ((ulint) file) % OS_FILE_N_SEEK_MUTEXES; os_mutex_enter(os_file_seek_mutexes[i]); #endif /* !UNIV_HOTBACKUP */ ret2 = SetFilePointer(file, low, &high, FILE_BEGIN); if (ret2 == 0xFFFFFFFF && GetLastError() != NO_ERROR) { #ifndef UNIV_HOTBACKUP os_mutex_exit(os_file_seek_mutexes[i]); #endif /* !UNIV_HOTBACKUP */ os_mutex_enter(os_file_count_mutex); os_n_pending_reads--; os_mutex_exit(os_file_count_mutex); goto error_handling; } ret = ReadFile(file, buf, (DWORD) n, &len, NULL); #ifndef UNIV_HOTBACKUP os_mutex_exit(os_file_seek_mutexes[i]); #endif /* !UNIV_HOTBACKUP */ os_mutex_enter(os_file_count_mutex); os_n_pending_reads--; os_mutex_exit(os_file_count_mutex); if (ret && len == n) { return(TRUE); } #else /* __WIN__ */ ibool retry; ssize_t ret; os_bytes_read_since_printout += n; try_again: ret = os_file_pread(file, buf, n, offset, offset_high); if ((ulint)ret == n) { return(TRUE); } #endif /* __WIN__ */ #ifdef __WIN__ error_handling: #endif retry = os_file_handle_error_no_exit(NULL, "read"); if (retry) { goto try_again; } return(FALSE); } /*******************************************************************//** Rewind file to its start, read at most size - 1 bytes from it to str, and NUL-terminate str. All errors are silently ignored. This function is mostly meant to be used with temporary files. */ UNIV_INTERN void os_file_read_string( /*================*/ FILE* file, /*!< in: file to read from */ char* str, /*!< in: buffer where to read */ ulint size) /*!< in: size of buffer */ { size_t flen; if (size == 0) { return; } rewind(file); flen = fread(str, 1, size - 1, file); str[flen] = '\0'; } /*******************************************************************//** Requests a synchronous write operation. @return TRUE if request was successful, FALSE if fail */ UNIV_INTERN ibool os_file_write( /*==========*/ const char* name, /*!< in: name of the file or path as a null-terminated string */ os_file_t file, /*!< in: handle to a file */ const void* buf, /*!< in: buffer from which to write */ ulint offset, /*!< in: least significant 32 bits of file offset where to write */ ulint offset_high, /*!< in: most significant 32 bits of offset */ ulint n) /*!< in: number of bytes to write */ { #ifdef __WIN__ BOOL ret; DWORD len; DWORD ret2; DWORD low; DWORD high; ulint n_retries = 0; ulint err; #ifndef UNIV_HOTBACKUP ulint i; #endif /* !UNIV_HOTBACKUP */ ut_a((offset & 0xFFFFFFFF) == offset); os_n_file_writes++; ut_ad(file); ut_ad(buf); ut_ad(n > 0); retry: low = (DWORD) offset; high = (DWORD) offset_high; os_mutex_enter(os_file_count_mutex); os_n_pending_writes++; os_mutex_exit(os_file_count_mutex); #ifndef UNIV_HOTBACKUP /* Protect the seek / write operation with a mutex */ i = ((ulint) file) % OS_FILE_N_SEEK_MUTEXES; os_mutex_enter(os_file_seek_mutexes[i]); #endif /* !UNIV_HOTBACKUP */ ret2 = SetFilePointer(file, low, &high, FILE_BEGIN); if (ret2 == 0xFFFFFFFF && GetLastError() != NO_ERROR) { #ifndef UNIV_HOTBACKUP os_mutex_exit(os_file_seek_mutexes[i]); #endif /* !UNIV_HOTBACKUP */ os_mutex_enter(os_file_count_mutex); os_n_pending_writes--; os_mutex_exit(os_file_count_mutex); ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Error: File pointer positioning to" " file %s failed at\n" "InnoDB: offset %lu %lu. Operating system" " error number %lu.\n" "InnoDB: " "Check InnoDB website for details\n", name, (ulong) offset_high, (ulong) offset, (ulong) GetLastError()); return(FALSE); } ret = WriteFile(file, buf, (DWORD) n, &len, NULL); /* Always do fsync to reduce the probability that when the OS crashes, a database page is only partially physically written to disk. */ # ifdef UNIV_DO_FLUSH if (!os_do_not_call_flush_at_each_write) { ut_a(TRUE == os_file_flush(file)); } # endif /* UNIV_DO_FLUSH */ #ifndef UNIV_HOTBACKUP os_mutex_exit(os_file_seek_mutexes[i]); #endif /* !UNIV_HOTBACKUP */ os_mutex_enter(os_file_count_mutex); os_n_pending_writes--; os_mutex_exit(os_file_count_mutex); if (ret && len == n) { return(TRUE); } /* If some background file system backup tool is running, then, at least in Windows 2000, we may get here a specific error. Let us retry the operation 100 times, with 1 second waits. */ if (GetLastError() == ERROR_LOCK_VIOLATION && n_retries < 100) { os_thread_sleep(1000000); n_retries++; goto retry; } if (!os_has_said_disk_full) { err = (ulint)GetLastError(); ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Error: Write to file %s failed" " at offset %lu %lu.\n" "InnoDB: %lu bytes should have been written," " only %lu were written.\n" "InnoDB: Operating system error number %lu.\n" "InnoDB: Check that your OS and file system" " support files of this size.\n" "InnoDB: Check also that the disk is not full" " or a disk quota exceeded.\n", name, (ulong) offset_high, (ulong) offset, (ulong) n, (ulong) len, (ulong) err); if (strerror((int)err) != NULL) { ib_logger(ib_stream, "InnoDB: Error number %lu means '%s'.\n", (ulong) err, strerror((int)err)); } ib_logger(ib_stream, "InnoDB: " "Check InnoDB website for details\n"); os_has_said_disk_full = TRUE; } return(FALSE); #else ssize_t ret; ret = os_file_pwrite(file, buf, n, offset, offset_high); if ((ulint)ret == n) { return(TRUE); } if (!os_has_said_disk_full) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Error: Write to file %s failed" " at offset %lu %lu.\n" "InnoDB: %lu bytes should have been written," " only %ld were written.\n" "InnoDB: Operating system error number %lu.\n" "InnoDB: Check that your OS and file system" " support files of this size.\n" "InnoDB: Check also that the disk is not full" " or a disk quota exceeded.\n", name, offset_high, offset, n, (long int)ret, (ulint)errno); if (strerror(errno) != NULL) { ib_logger(ib_stream, "InnoDB: Error number %lu means '%s'.\n", (ulint)errno, strerror(errno)); } ib_logger(ib_stream, "InnoDB: " "Check InnoDB website for details\n"); os_has_said_disk_full = TRUE; } return(FALSE); #endif } /*******************************************************************//** Check the existence and type of the given file. @return TRUE if call succeeded */ UNIV_INTERN ibool os_file_status( /*===========*/ const char* path, /*!< in: pathname of the file */ ibool* exists, /*!< out: TRUE if file exists */ os_file_type_t* type) /*!< out: type of the file (if it exists) */ { #ifdef __WIN__ int ret; struct _stat statinfo; ret = _stat(path, &statinfo); if (ret && (errno == ENOENT || errno == ENOTDIR)) { /* file does not exist */ *exists = FALSE; return(TRUE); } else if (ret) { /* file exists, but stat call failed */ os_file_handle_error_no_exit(path, "stat"); return(FALSE); } if (_S_IFDIR & statinfo.st_mode) { *type = OS_FILE_TYPE_DIR; } else if (_S_IFREG & statinfo.st_mode) { *type = OS_FILE_TYPE_FILE; } else { *type = OS_FILE_TYPE_UNKNOWN; } *exists = TRUE; return(TRUE); #else int ret; struct stat statinfo; ret = stat(path, &statinfo); if (ret && (errno == ENOENT || errno == ENOTDIR)) { /* file does not exist */ *exists = FALSE; return(TRUE); } else if (ret) { /* file exists, but stat call failed */ os_file_handle_error_no_exit(path, "stat"); return(FALSE); } if (S_ISDIR(statinfo.st_mode)) { *type = OS_FILE_TYPE_DIR; } else if (S_ISLNK(statinfo.st_mode)) { *type = OS_FILE_TYPE_LINK; } else if (S_ISREG(statinfo.st_mode)) { *type = OS_FILE_TYPE_FILE; } else { *type = OS_FILE_TYPE_UNKNOWN; } *exists = TRUE; return(TRUE); #endif } /*******************************************************************//** This function returns information about the specified file @return TRUE if stat information found */ UNIV_INTERN ibool os_file_get_status( /*===============*/ const char* path, /*!< in: pathname of the file */ os_file_stat_t* stat_info) /*!< information of a file in a directory */ { #ifdef __WIN__ int ret; struct _stat statinfo; ret = _stat(path, &statinfo); if (ret && (errno == ENOENT || errno == ENOTDIR)) { /* file does not exist */ return(FALSE); } else if (ret) { /* file exists, but stat call failed */ os_file_handle_error_no_exit(path, "stat"); return(FALSE); } if (_S_IFDIR & statinfo.st_mode) { stat_info->type = OS_FILE_TYPE_DIR; } else if (_S_IFREG & statinfo.st_mode) { stat_info->type = OS_FILE_TYPE_FILE; } else { stat_info->type = OS_FILE_TYPE_UNKNOWN; } stat_info->ctime = statinfo.st_ctime; stat_info->atime = statinfo.st_atime; stat_info->mtime = statinfo.st_mtime; stat_info->size = statinfo.st_size; return(TRUE); #else int ret; struct stat statinfo; ret = stat(path, &statinfo); if (ret && (errno == ENOENT || errno == ENOTDIR)) { /* file does not exist */ return(FALSE); } else if (ret) { /* file exists, but stat call failed */ os_file_handle_error_no_exit(path, "stat"); return(FALSE); } if (S_ISDIR(statinfo.st_mode)) { stat_info->type = OS_FILE_TYPE_DIR; } else if (S_ISLNK(statinfo.st_mode)) { stat_info->type = OS_FILE_TYPE_LINK; } else if (S_ISREG(statinfo.st_mode)) { stat_info->type = OS_FILE_TYPE_FILE; } else { stat_info->type = OS_FILE_TYPE_UNKNOWN; } stat_info->ctime = statinfo.st_ctime; stat_info->atime = statinfo.st_atime; stat_info->mtime = statinfo.st_mtime; stat_info->size = statinfo.st_size; return(TRUE); #endif } /****************************************************************//** The function os_file_dirname returns a directory component of a null-terminated pathname string. In the usual case, dirname returns the string up to, but not including, the final '/', and basename is the component following the final '/'. Trailing '/' charac­ ters are not counted as part of the pathname. If path does not contain a slash, dirname returns the string ".". Concatenating the string returned by dirname, a "/", and the basename yields a complete pathname. The return value is a copy of the directory component of the pathname. The copy is allocated from heap. It is the caller responsibility to free it after it is no longer needed. The following list of examples (taken from SUSv2) shows the strings returned by dirname and basename for different paths: path dirname basename "/usr/lib" "/usr" "lib" "/usr/" "/" "usr" "usr" "." "usr" "/" "/" "/" "." "." "." ".." "." ".." @return own: directory component of the pathname */ UNIV_INTERN char* os_file_dirname( /*============*/ const char* path) /*!< in: pathname */ { /* Find the offset of the last slash */ const char* last_slash = strrchr(path, SRV_PATH_SEPARATOR); if (!last_slash) { /* No slash in the path, return "." */ return(mem_strdup(".")); } /* Ok, there is a slash */ if (last_slash == path) { /* last slash is the first char of the path */ return(mem_strdup("/")); } /* Non-trivial directory component */ return(mem_strdupl(path, last_slash - path)); } /****************************************************************//** Creates all missing subdirectories along the given path. @return TRUE if call succeeded FALSE otherwise */ UNIV_INTERN ibool os_file_create_subdirs_if_needed( /*=============================*/ const char* path) /*!< in: path name */ { char* subdir; ibool success, subdir_exists; os_file_type_t type; subdir = os_file_dirname(path); if (strlen(subdir) == 1 && (*subdir == SRV_PATH_SEPARATOR || *subdir == '.')) { /* subdir is root or cwd, nothing to do */ mem_free(subdir); return(TRUE); } /* Test if subdir exists */ success = os_file_status(subdir, &subdir_exists, &type); if (success && !subdir_exists) { /* subdir does not exist, create it */ success = os_file_create_subdirs_if_needed(subdir); if (!success) { mem_free(subdir); return(FALSE); } success = os_file_create_directory(subdir, FALSE); } mem_free(subdir); return(success); } #ifndef UNIV_HOTBACKUP /************************************************************************//** Creates an aio wait array. @return own: aio array */ static os_aio_array_t* os_aio_array_create( /*================*/ ulint n, /*!< in: maximum number of pending aio operations allowed; n must be divisible by n_segments */ ulint n_segments) /*!< in: number of segments in the aio array */ { os_aio_array_t* array; ulint i; os_aio_slot_t* slot; #ifdef WIN_ASYNC_IO OVERLAPPED* over; #endif ut_a(n > 0); ut_a(n_segments > 0); array = ut_malloc(sizeof(os_aio_array_t)); array->mutex = os_mutex_create(NULL); array->not_full = os_event_create(NULL); array->is_empty = os_event_create(NULL); os_event_set(array->is_empty); array->n_slots = n; array->n_segments = n_segments; array->n_reserved = 0; array->slots = ut_malloc(n * sizeof(os_aio_slot_t)); #ifdef __WIN__ array->native_events = ut_malloc(n * sizeof(os_native_event_t)); #endif for (i = 0; i < n; i++) { slot = os_aio_array_get_nth_slot(array, i); slot->pos = i; slot->reserved = FALSE; #ifdef WIN_ASYNC_IO slot->event = os_event_create(NULL); over = &(slot->control); over->hEvent = slot->event->handle; *((array->native_events) + i) = over->hEvent; #endif } return(array); } /*********************************************************************** Initializes the asynchronous io system. Creates one array each for ibuf and log i/o. Also creates one array each for read and write where each array is divided logically into n_read_segs and n_write_segs respectively. The caller must create an i/o handler thread for each segment in these arrays. This function also creates the sync array. No i/o handler thread needs to be created for that */ UNIV_INTERN void os_aio_init( /*========*/ ulint n_per_seg, /*= 4); os_io_init_simple(); for (i = 0; i < n_segments; i++) { os_set_io_thread_op_info(i, "not started yet"); } /* ib_logger(ib_stream, "Array n per seg %lu\n", n_per_seg); */ os_aio_ibuf_array = os_aio_array_create(n_per_seg, 1); srv_io_thread_function[0] = "insert buffer thread"; os_aio_log_array = os_aio_array_create(n_per_seg, 1); srv_io_thread_function[1] = "log thread"; os_aio_read_array = os_aio_array_create(n_read_segs * n_per_seg, n_read_segs); for (i = 2; i < 2 + n_read_segs; i++) { ut_a(i < SRV_MAX_N_IO_THREADS); srv_io_thread_function[i] = "read thread"; } os_aio_write_array = os_aio_array_create(n_write_segs * n_per_seg, n_write_segs); for (i = 2 + n_read_segs; i < n_segments; i++) { ut_a(i < SRV_MAX_N_IO_THREADS); srv_io_thread_function[i] = "write thread"; } os_aio_sync_array = os_aio_array_create(n_slots_sync, 1); os_aio_n_segments = n_segments; os_aio_validate(); os_aio_segment_wait_events = ut_malloc(n_segments * sizeof(void*)); for (i = 0; i < n_segments; i++) { os_aio_segment_wait_events[i] = os_event_create(NULL); } os_last_printout = time(NULL); } #ifdef WIN_ASYNC_IO /************************************************************************//** Wakes up all async i/o threads in the array in Windows async i/o at shutdown. */ static void os_aio_array_wake_win_aio_at_shutdown( /*==================================*/ os_aio_array_t* array) /*!< in: aio array */ { ulint i; for (i = 0; i < array->n_slots; i++) { os_event_set((array->slots + i)->event); } } #endif /************************************************************************//** Wakes up all async i/o threads so that they know to exit themselves in shutdown. */ UNIV_INTERN void os_aio_wake_all_threads_at_shutdown(void) /*=====================================*/ { ulint i; /* This can happen if we have to abort during the startup phase. */ if (os_aio_segment_wait_events == NULL) { return; } #ifdef WIN_ASYNC_IO /* This code wakes up all ai/o threads in Windows native aio */ os_aio_array_wake_win_aio_at_shutdown(os_aio_read_array); os_aio_array_wake_win_aio_at_shutdown(os_aio_write_array); os_aio_array_wake_win_aio_at_shutdown(os_aio_ibuf_array); os_aio_array_wake_win_aio_at_shutdown(os_aio_log_array); #endif /* This loop wakes up all simulated ai/o threads */ for (i = 0; i < os_aio_n_segments; i++) { os_event_set(os_aio_segment_wait_events[i]); } } /************************************************************************//** Waits until there are no pending writes in os_aio_write_array. There can be other, synchronous, pending writes. */ UNIV_INTERN void os_aio_wait_until_no_pending_writes(void) /*=====================================*/ { os_event_wait(os_aio_write_array->is_empty); } /**********************************************************************//** Calculates segment number for a slot. @return segment number (which is the number used by, for example, i/o-handler threads) */ static ulint os_aio_get_segment_no_from_slot( /*============================*/ os_aio_array_t* array, /*!< in: aio wait array */ os_aio_slot_t* slot) /*!< in: slot in this array */ { ulint segment; ulint seg_len; if (array == os_aio_ibuf_array) { segment = 0; } else if (array == os_aio_log_array) { segment = 1; } else if (array == os_aio_read_array) { seg_len = os_aio_read_array->n_slots / os_aio_read_array->n_segments; segment = 2 + slot->pos / seg_len; } else { ut_a(array == os_aio_write_array); seg_len = os_aio_write_array->n_slots / os_aio_write_array->n_segments; segment = os_aio_read_array->n_segments + 2 + slot->pos / seg_len; } return(segment); } /**********************************************************************//** Calculates local segment number and aio array from global segment number. @return local segment number within the aio array */ static ulint os_aio_get_array_and_local_segment( /*===============================*/ os_aio_array_t** array, /*!< out: aio wait array */ ulint global_segment)/*!< in: global segment number */ { ulint segment; ut_a(global_segment < os_aio_n_segments); if (global_segment == 0) { *array = os_aio_ibuf_array; segment = 0; } else if (global_segment == 1) { *array = os_aio_log_array; segment = 0; } else if (global_segment < os_aio_read_array->n_segments + 2) { *array = os_aio_read_array; segment = global_segment - 2; } else { *array = os_aio_write_array; segment = global_segment - (os_aio_read_array->n_segments + 2); } return(segment); } /*******************************************************************//** Requests for a slot in the aio array. If no slot is available, waits until not_full-event becomes signaled. @return pointer to slot */ static os_aio_slot_t* os_aio_array_reserve_slot( /*======================*/ ulint type, /*!< in: OS_FILE_READ or OS_FILE_WRITE */ os_aio_array_t* array, /*!< in: aio array */ fil_node_t* message1,/*!< in: message to be passed along with the aio operation */ void* message2,/*!< in: message to be passed along with the aio operation */ os_file_t file, /*!< in: file handle */ const char* name, /*!< in: name of the file or path as a null-terminated string */ void* buf, /*!< in: buffer where to read or from which to write */ ulint offset, /*!< in: least significant 32 bits of file offset */ ulint offset_high, /*!< in: most significant 32 bits of offset */ ulint len) /*!< in: length of the block to read or write */ { os_aio_slot_t* slot; #ifdef WIN_ASYNC_IO OVERLAPPED* control; #endif ulint i; ulint slots_per_seg; ulint local_seg; /* No need of a mutex. Only reading constant fields */ slots_per_seg = array->n_slots / array->n_segments; /* We attempt to keep adjacent blocks in the same local segment. This can help in merging IO requests when we are doing simulated AIO */ local_seg = (offset >> (UNIV_PAGE_SIZE_SHIFT + 6)) % array->n_segments; loop: os_mutex_enter(array->mutex); if (array->n_reserved == array->n_slots) { os_mutex_exit(array->mutex); if (!os_aio_use_native_aio) { /* If the handler threads are suspended, wake them so that we get more slots */ os_aio_simulated_wake_handler_threads(); } os_event_wait(array->not_full); goto loop; } /* First try to find a slot in the preferred local segment */ for (i = local_seg * slots_per_seg; i < array->n_slots; i++) { slot = os_aio_array_get_nth_slot(array, i); if (slot->reserved == FALSE) { goto found; } } /* Fall back to a full scan. We are guaranteed to find a slot */ for (i = 0;; i++) { slot = os_aio_array_get_nth_slot(array, i); if (slot->reserved == FALSE) { goto found; } } found: ut_a(slot->reserved == FALSE); array->n_reserved++; if (array->n_reserved == 1) { os_event_reset(array->is_empty); } if (array->n_reserved == array->n_slots) { os_event_reset(array->not_full); } slot->reserved = TRUE; slot->reservation_time = time(NULL); slot->message1 = message1; slot->message2 = message2; slot->file = file; slot->name = name; slot->len = len; slot->type = type; slot->buf = buf; slot->offset = offset; slot->offset_high = offset_high; slot->io_already_done = FALSE; #ifdef WIN_ASYNC_IO control = &(slot->control); control->Offset = (DWORD)offset; control->OffsetHigh = (DWORD)offset_high; os_event_reset(slot->event); #endif os_mutex_exit(array->mutex); return(slot); } /*******************************************************************//** Frees a slot in the aio array. */ static void os_aio_array_free_slot( /*===================*/ os_aio_array_t* array, /*!< in: aio array */ os_aio_slot_t* slot) /*!< in: pointer to slot */ { ut_ad(array); ut_ad(slot); os_mutex_enter(array->mutex); ut_ad(slot->reserved); slot->reserved = FALSE; array->n_reserved--; if (array->n_reserved == array->n_slots - 1) { os_event_set(array->not_full); } if (array->n_reserved == 0) { os_event_set(array->is_empty); } #ifdef WIN_ASYNC_IO os_event_reset(slot->event); #endif os_mutex_exit(array->mutex); } /**********************************************************************//** Wakes up a simulated aio i/o-handler thread if it has something to do. */ static void os_aio_simulated_wake_handler_thread( /*=================================*/ ulint global_segment) /*!< in: the number of the segment in the aio arrays */ { os_aio_array_t* array; os_aio_slot_t* slot; ulint segment; ulint n; ulint i; ut_ad(!os_aio_use_native_aio); segment = os_aio_get_array_and_local_segment(&array, global_segment); n = array->n_slots / array->n_segments; /* Look through n slots after the segment * n'th slot */ os_mutex_enter(array->mutex); for (i = 0; i < n; i++) { slot = os_aio_array_get_nth_slot(array, i + segment * n); if (slot->reserved) { /* Found an i/o request */ break; } } os_mutex_exit(array->mutex); if (i < n) { os_event_set(os_aio_segment_wait_events[global_segment]); } } /**********************************************************************//** Wakes up simulated aio i/o-handler threads if they have something to do. */ UNIV_INTERN void os_aio_simulated_wake_handler_threads(void) /*=======================================*/ { ulint i; if (os_aio_use_native_aio) { /* We do not use simulated aio: do nothing */ return; } os_aio_recommend_sleep_for_read_threads = FALSE; for (i = 0; i < os_aio_n_segments; i++) { os_aio_simulated_wake_handler_thread(i); } } /**********************************************************************//** This function can be called if one wants to post a batch of reads and prefers an i/o-handler thread to handle them all at once later. You must call os_aio_simulated_wake_handler_threads later to ensure the threads are not left sleeping! */ UNIV_INTERN void os_aio_simulated_put_read_threads_to_sleep(void) /*============================================*/ { /* The idea of putting background IO threads to sleep is only for Windows when using simulated AIO. Windows XP seems to schedule background threads too eagerly to allow for coalescing during readahead requests. */ #ifdef __WIN__ os_aio_array_t* array; ulint g; if (os_aio_use_native_aio) { /* We do not use simulated aio: do nothing */ return; } os_aio_recommend_sleep_for_read_threads = TRUE; for (g = 0; g < os_aio_n_segments; g++) { os_aio_get_array_and_local_segment(&array, g); if (array == os_aio_read_array) { os_event_reset(os_aio_segment_wait_events[g]); } } #endif /* __WIN__ */ } /*******************************************************************//** Requests an asynchronous i/o operation. @return TRUE if request was queued successfully, FALSE if fail */ UNIV_INTERN ibool os_aio( /*===*/ ulint type, /*!< in: OS_FILE_READ or OS_FILE_WRITE */ ulint mode, /*!< in: OS_AIO_NORMAL, ..., possibly ORed to OS_AIO_SIMULATED_WAKE_LATER: the last flag advises this function not to wake i/o-handler threads, but the caller will do the waking explicitly later, in this way the caller can post several requests in a batch; NOTE that the batch must not be so big that it exhausts the slots in aio arrays! NOTE that a simulated batch may introduce hidden chances of deadlocks, because i/os are not actually handled until all have been posted: use with great caution! */ const char* name, /*!< in: name of the file or path as a null-terminated string */ os_file_t file, /*!< in: handle to a file */ void* buf, /*!< in: buffer where to read or from which to write */ ulint offset, /*!< in: least significant 32 bits of file offset where to read or write */ ulint offset_high, /*!< in: most significant 32 bits of offset */ ulint n, /*!< in: number of bytes to read or write */ fil_node_t* message1,/*!< in: message for the aio handler (can be used to identify a completed aio operation); ignored if mode is OS_AIO_SYNC */ void* message2)/*!< in: message for the aio handler (can be used to identify a completed aio operation); ignored if mode is OS_AIO_SYNC */ { os_aio_array_t* array; os_aio_slot_t* slot; #ifdef WIN_ASYNC_IO ibool retval; BOOL ret = TRUE; DWORD len = (DWORD) n; struct fil_node_struct * dummy_mess1; void* dummy_mess2; ulint dummy_type; #endif ulint err = 0; ibool retry; ulint wake_later; ut_ad(file); ut_ad(buf); ut_ad(n > 0); ut_ad(n % OS_FILE_LOG_BLOCK_SIZE == 0); ut_ad(offset % OS_FILE_LOG_BLOCK_SIZE == 0); ut_ad(os_aio_validate()); wake_later = mode & OS_AIO_SIMULATED_WAKE_LATER; mode = mode & (~OS_AIO_SIMULATED_WAKE_LATER); if (mode == OS_AIO_SYNC #ifdef WIN_ASYNC_IO && !os_aio_use_native_aio #endif ) { /* This is actually an ordinary synchronous read or write: no need to use an i/o-handler thread. NOTE that if we use Windows async i/o, Windows does not allow us to use ordinary synchronous os_file_read etc. on the same file, therefore we have built a special mechanism for synchronous wait in the Windows case. */ if (type == OS_FILE_READ) { return(os_file_read(file, buf, offset, offset_high, n)); } ut_a(type == OS_FILE_WRITE); return(os_file_write(name, file, buf, offset, offset_high, n)); } try_again: if (mode == OS_AIO_NORMAL) { if (type == OS_FILE_READ) { array = os_aio_read_array; } else { array = os_aio_write_array; } } else if (mode == OS_AIO_IBUF) { ut_ad(type == OS_FILE_READ); /* Reduce probability of deadlock bugs in connection with ibuf: do not let the ibuf i/o handler sleep */ wake_later = FALSE; array = os_aio_ibuf_array; } else if (mode == OS_AIO_LOG) { array = os_aio_log_array; } else if (mode == OS_AIO_SYNC) { array = os_aio_sync_array; } else { array = NULL; /* Eliminate compiler warning */ ut_error; } slot = os_aio_array_reserve_slot(type, array, message1, message2, file, name, buf, offset, offset_high, n); if (type == OS_FILE_READ) { if (os_aio_use_native_aio) { #ifdef WIN_ASYNC_IO os_n_file_reads++; os_bytes_read_since_printout += len; ret = ReadFile(file, buf, (DWORD)n, &len, &(slot->control)); #endif } else { if (!wake_later) { os_aio_simulated_wake_handler_thread( os_aio_get_segment_no_from_slot( array, slot)); } } } else if (type == OS_FILE_WRITE) { if (os_aio_use_native_aio) { #ifdef WIN_ASYNC_IO os_n_file_writes++; ret = WriteFile(file, buf, (DWORD)n, &len, &(slot->control)); #endif } else { if (!wake_later) { os_aio_simulated_wake_handler_thread( os_aio_get_segment_no_from_slot( array, slot)); } } } else { ut_error; } #ifdef WIN_ASYNC_IO if (os_aio_use_native_aio) { if ((ret && len == n) || (!ret && GetLastError() == ERROR_IO_PENDING)) { /* aio was queued successfully! */ if (mode == OS_AIO_SYNC) { /* We want a synchronous i/o operation on a file where we also use async i/o: in Windows we must use the same wait mechanism as for async i/o */ retval = os_aio_windows_handle(ULINT_UNDEFINED, slot->pos, &dummy_mess1, &dummy_mess2, &dummy_type); return(retval); } return(TRUE); } err = 1; /* Fall through the next if */ } #endif if (err == 0) { /* aio was queued successfully! */ return(TRUE); } os_aio_array_free_slot(array, slot); retry = os_file_handle_error(name, type == OS_FILE_READ ? "aio read" : "aio write"); if (retry) { goto try_again; } return(FALSE); } #ifdef WIN_ASYNC_IO /**********************************************************************//** This function is only used in Windows asynchronous i/o. Waits for an aio operation to complete. This function is used to wait the for completed requests. The aio array of pending requests is divided into segments. The thread specifies which segment or slot it wants to wait for. NOTE: this function will also take care of freeing the aio slot, therefore no other thread is allowed to do the freeing! @return TRUE if the aio operation succeeded */ UNIV_INTERN ibool os_aio_windows_handle( /*==================*/ ulint segment, /*!< in: the number of the segment in the aio arrays to wait for; segment 0 is the ibuf i/o thread, segment 1 the log i/o thread, then follow the non-ibuf read threads, and as the last are the non-ibuf write threads; if this is ULINT_UNDEFINED, then it means that sync aio is used, and this parameter is ignored */ ulint pos, /*!< this parameter is used only in sync aio: wait for the aio slot at this position */ fil_node_t**message1, /*!< out: the messages passed with the aio request; note that also in the case where the aio operation failed, these output parameters are valid and can be used to restart the operation, for example */ void** message2, ulint* type) /*!< out: OS_FILE_WRITE or ..._READ */ { ulint orig_seg = segment; os_aio_array_t* array; os_aio_slot_t* slot; ulint n; ulint i; ibool ret_val; BOOL ret; DWORD len; BOOL retry = FALSE; if (segment == ULINT_UNDEFINED) { array = os_aio_sync_array; segment = 0; } else { segment = os_aio_get_array_and_local_segment(&array, segment); } /* NOTE! We only access constant fields in os_aio_array. Therefore we do not have to acquire the protecting mutex yet */ ut_ad(os_aio_validate()); ut_ad(segment < array->n_segments); n = array->n_slots / array->n_segments; if (array == os_aio_sync_array) { os_event_wait(os_aio_array_get_nth_slot(array, pos)->event); i = pos; } else { os_set_io_thread_op_info(orig_seg, "wait Windows aio"); i = os_event_wait_multiple(n, (array->native_events) + segment * n); } os_mutex_enter(array->mutex); slot = os_aio_array_get_nth_slot(array, i + segment * n); ut_a(slot->reserved); if (orig_seg != ULINT_UNDEFINED) { os_set_io_thread_op_info(orig_seg, "get windows aio return value"); } ret = GetOverlappedResult(slot->file, &(slot->control), &len, TRUE); *message1 = slot->message1; *message2 = slot->message2; *type = slot->type; if (ret && len == slot->len) { ret_val = TRUE; #ifdef UNIV_DO_FLUSH if (slot->type == OS_FILE_WRITE && !os_do_not_call_flush_at_each_write) { ut_a(TRUE == os_file_flush(slot->file)); } #endif /* UNIV_DO_FLUSH */ } else if (os_file_handle_error(slot->name, "Windows aio")) { retry = TRUE; } else { ret_val = FALSE; } os_mutex_exit(array->mutex); if (retry) { /* retry failed read/write operation synchronously. No need to hold array->mutex. */ switch (slot->type) { case OS_FILE_WRITE: ret = WriteFile(slot->file, slot->buf, slot->len, &len, &(slot->control)); break; case OS_FILE_READ: ret = ReadFile(slot->file, slot->buf, slot->len, &len, &(slot->control)); break; default: ut_error; } if (!ret && GetLastError() == ERROR_IO_PENDING) { /* aio was queued successfully! We want a synchronous i/o operation on a file where we also use async i/o: in Windows we must use the same wait mechanism as for async i/o */ ret = GetOverlappedResult(slot->file, &(slot->control), &len, TRUE); } ret_val = ret && len == slot->len; } os_aio_array_free_slot(array, slot); return(ret_val); } #endif /**********************************************************************//** Does simulated aio. This function should be called by an i/o-handler thread. @return TRUE if the aio operation succeeded */ UNIV_INTERN ibool os_aio_simulated_handle( /*====================*/ ulint global_segment, /*!< in: the number of the segment in the aio arrays to wait for; segment 0 is the ibuf i/o thread, segment 1 the log i/o thread, then follow the non-ibuf read threads, and as the last are the non-ibuf write threads */ fil_node_t**message1, /*!< out: the messages passed with the aio request; note that also in the case where the aio operation failed, these output parameters are valid and can be used to restart the operation, for example */ void** message2, ulint* type) /*!< out: OS_FILE_WRITE or ..._READ */ { os_aio_array_t* array; ulint segment; os_aio_slot_t* slot; os_aio_slot_t* slot2; os_aio_slot_t* consecutive_ios[OS_AIO_MERGE_N_CONSECUTIVE]; ulint n_consecutive; ulint total_len; ulint offs; ulint lowest_offset; ulint biggest_age; ulint age; byte* combined_buf; byte* combined_buf2; ibool ret; ulint n; ulint i; /* Fix compiler warning */ *consecutive_ios = NULL; segment = os_aio_get_array_and_local_segment(&array, global_segment); restart: /* NOTE! We only access constant fields in os_aio_array. Therefore we do not have to acquire the protecting mutex yet */ os_set_io_thread_op_info(global_segment, "looking for i/o requests (a)"); ut_ad(os_aio_validate()); ut_ad(segment < array->n_segments); n = array->n_slots / array->n_segments; /* Look through n slots after the segment * n'th slot */ if (array == os_aio_read_array && os_aio_recommend_sleep_for_read_threads) { /* Give other threads chance to add several i/os to the array at once. */ goto recommended_sleep; } os_mutex_enter(array->mutex); os_set_io_thread_op_info(global_segment, "looking for i/o requests (b)"); /* Check if there is a slot for which the i/o has already been done */ for (i = 0; i < n; i++) { slot = os_aio_array_get_nth_slot(array, i + segment * n); if (slot->reserved && slot->io_already_done) { if (os_aio_print_debug) { ib_logger(ib_stream, "InnoDB: i/o for slot %lu" " already done, returning\n", (ulong) i); } ret = TRUE; goto slot_io_done; } } n_consecutive = 0; /* If there are at least 2 seconds old requests, then pick the oldest one to prevent starvation. If several requests have the same age, then pick the one at the lowest offset. */ biggest_age = 0; lowest_offset = ULINT_MAX; for (i = 0; i < n; i++) { slot = os_aio_array_get_nth_slot(array, i + segment * n); if (slot->reserved) { age = (ulint)difftime(time(NULL), slot->reservation_time); if ((age >= 2 && age > biggest_age) || (age >= 2 && age == biggest_age && slot->offset < lowest_offset)) { /* Found an i/o request */ consecutive_ios[0] = slot; n_consecutive = 1; biggest_age = age; lowest_offset = slot->offset; } } } if (n_consecutive == 0) { /* There were no old requests. Look for an i/o request at the lowest offset in the array (we ignore the high 32 bits of the offset in these heuristics) */ lowest_offset = ULINT_MAX; for (i = 0; i < n; i++) { slot = os_aio_array_get_nth_slot(array, i + segment * n); if (slot->reserved && slot->offset < lowest_offset) { /* Found an i/o request */ consecutive_ios[0] = slot; n_consecutive = 1; lowest_offset = slot->offset; } } } if (n_consecutive == 0) { /* No i/o requested at the moment */ goto wait_for_io; } slot = consecutive_ios[0]; /* Check if there are several consecutive blocks to read or write */ consecutive_loop: for (i = 0; i < n; i++) { slot2 = os_aio_array_get_nth_slot(array, i + segment * n); if (slot2->reserved && slot2 != slot && slot2->offset == slot->offset + slot->len /* check that sum does not wrap over */ && slot->offset + slot->len > slot->offset && slot2->offset_high == slot->offset_high && slot2->type == slot->type && slot2->file == slot->file) { /* Found a consecutive i/o request */ consecutive_ios[n_consecutive] = slot2; n_consecutive++; slot = slot2; if (n_consecutive < OS_AIO_MERGE_N_CONSECUTIVE) { goto consecutive_loop; } else { break; } } } os_set_io_thread_op_info(global_segment, "consecutive i/o requests"); /* We have now collected n_consecutive i/o requests in the array; allocate a single buffer which can hold all data, and perform the i/o */ total_len = 0; slot = consecutive_ios[0]; for (i = 0; i < n_consecutive; i++) { total_len += consecutive_ios[i]->len; } if (n_consecutive == 1) { /* We can use the buffer of the i/o request */ combined_buf = slot->buf; combined_buf2 = NULL; } else { combined_buf2 = ut_malloc(total_len + UNIV_PAGE_SIZE); ut_a(combined_buf2); combined_buf = ut_align(combined_buf2, UNIV_PAGE_SIZE); } /* We release the array mutex for the time of the i/o: NOTE that this assumes that there is just one i/o-handler thread serving a single segment of slots! */ os_mutex_exit(array->mutex); if (slot->type == OS_FILE_WRITE && n_consecutive > 1) { /* Copy the buffers to the combined buffer */ offs = 0; for (i = 0; i < n_consecutive; i++) { ut_memcpy(combined_buf + offs, consecutive_ios[i]->buf, consecutive_ios[i]->len); offs += consecutive_ios[i]->len; } } os_set_io_thread_op_info(global_segment, "doing file i/o"); if (os_aio_print_debug) { ib_logger(ib_stream, "InnoDB: doing i/o of type %lu at offset %lu %lu," " length %lu\n", (ulong) slot->type, (ulong) slot->offset_high, (ulong) slot->offset, (ulong) total_len); } /* Do the i/o with ordinary, synchronous i/o functions: */ if (slot->type == OS_FILE_WRITE) { ret = os_file_write(slot->name, slot->file, combined_buf, slot->offset, slot->offset_high, total_len); } else { ret = os_file_read(slot->file, combined_buf, slot->offset, slot->offset_high, total_len); } ut_a(ret); os_set_io_thread_op_info(global_segment, "file i/o done"); #if 0 ib_logger(ib_stream, "aio: %lu consecutive %lu:th segment, first offs %lu blocks\n", n_consecutive, global_segment, slot->offset / UNIV_PAGE_SIZE); #endif if (slot->type == OS_FILE_READ && n_consecutive > 1) { /* Copy the combined buffer to individual buffers */ offs = 0; for (i = 0; i < n_consecutive; i++) { ut_memcpy(consecutive_ios[i]->buf, combined_buf + offs, consecutive_ios[i]->len); offs += consecutive_ios[i]->len; } } if (combined_buf2) { ut_free(combined_buf2); } os_mutex_enter(array->mutex); /* Mark the i/os done in slots */ for (i = 0; i < n_consecutive; i++) { consecutive_ios[i]->io_already_done = TRUE; } /* We return the messages for the first slot now, and if there were several slots, the messages will be returned with subsequent calls of this function */ slot_io_done: ut_a(slot->reserved); *message1 = slot->message1; *message2 = slot->message2; *type = slot->type; os_mutex_exit(array->mutex); os_aio_array_free_slot(array, slot); return(ret); wait_for_io: os_set_io_thread_op_info(global_segment, "resetting wait event"); /* We wait here until there again can be i/os in the segment of this thread */ os_event_reset(os_aio_segment_wait_events[global_segment]); os_mutex_exit(array->mutex); recommended_sleep: os_set_io_thread_op_info(global_segment, "waiting for i/o request"); os_event_wait(os_aio_segment_wait_events[global_segment]); if (os_aio_print_debug) { ib_logger(ib_stream, "InnoDB: i/o handler thread for i/o" " segment %lu wakes up\n", (ulong) global_segment); } goto restart; } /**********************************************************************//** Validates the consistency of an aio array. @return TRUE if ok */ static ibool os_aio_array_validate( /*==================*/ os_aio_array_t* array) /*!< in: aio wait array */ { os_aio_slot_t* slot; ulint n_reserved = 0; ulint i; ut_a(array); os_mutex_enter(array->mutex); ut_a(array->n_slots > 0); ut_a(array->n_segments > 0); for (i = 0; i < array->n_slots; i++) { slot = os_aio_array_get_nth_slot(array, i); if (slot->reserved) { n_reserved++; ut_a(slot->len > 0); } } ut_a(array->n_reserved == n_reserved); os_mutex_exit(array->mutex); return(TRUE); } /**********************************************************************//** Validates the consistency the aio system. @return TRUE if ok */ UNIV_INTERN ibool os_aio_validate(void) /*=================*/ { os_aio_array_validate(os_aio_read_array); os_aio_array_validate(os_aio_write_array); os_aio_array_validate(os_aio_ibuf_array); os_aio_array_validate(os_aio_log_array); os_aio_array_validate(os_aio_sync_array); return(TRUE); } /**********************************************************************//** Prints info of the aio arrays. */ UNIV_INTERN void os_aio_print( /*=========*/ ib_stream_t ib_stream) /*!< in: stream where to print */ { os_aio_array_t* array; os_aio_slot_t* slot; ulint n_reserved; time_t current_time; double time_elapsed; double avg_bytes_read; ulint i; for (i = 0; i < srv_n_file_io_threads; i++) { ib_logger(ib_stream, "I/O thread %lu state: %s (%s)", (ulong) i, srv_io_thread_op_info[i], srv_io_thread_function[i]); #ifndef __WIN__ if (os_aio_segment_wait_events[i]->is_set) { ib_logger(ib_stream, " ev set"); } #endif ib_logger(ib_stream, "\n"); } ib_logger(ib_stream, "Pending normal aio reads:"); array = os_aio_read_array; loop: ut_a(array); os_mutex_enter(array->mutex); ut_a(array->n_slots > 0); ut_a(array->n_segments > 0); n_reserved = 0; for (i = 0; i < array->n_slots; i++) { slot = os_aio_array_get_nth_slot(array, i); if (slot->reserved) { n_reserved++; #if 0 ib_logger(ib_stream, "Reserved slot, messages %p %p\n", (void*) slot->message1, (void*) slot->message2); #endif ut_a(slot->len > 0); } } ut_a(array->n_reserved == n_reserved); ib_logger(ib_stream, " %lu", (ulong) n_reserved); os_mutex_exit(array->mutex); if (array == os_aio_read_array) { ib_logger(ib_stream, ", aio writes:"); array = os_aio_write_array; goto loop; } if (array == os_aio_write_array) { ib_logger(ib_stream, ",\n ibuf aio reads:"); array = os_aio_ibuf_array; goto loop; } if (array == os_aio_ibuf_array) { ib_logger(ib_stream, ", log i/o's:"); array = os_aio_log_array; goto loop; } if (array == os_aio_log_array) { ib_logger(ib_stream, ", sync i/o's:"); array = os_aio_sync_array; goto loop; } ib_logger(ib_stream, "\n"); current_time = time(NULL); time_elapsed = 0.001 + difftime(current_time, os_last_printout); ib_logger(ib_stream, "Pending flushes (fsync) log: %lu; buffer pool: %lu\n" "%lu OS file reads, %lu OS file writes, %lu OS fsyncs\n", (ulong) fil_n_pending_log_flushes, (ulong) fil_n_pending_tablespace_flushes, (ulong) os_n_file_reads, (ulong) os_n_file_writes, (ulong) os_n_fsyncs); if (os_file_n_pending_preads != 0 || os_file_n_pending_pwrites != 0) { ib_logger(ib_stream, "%lu pending preads, %lu pending pwrites\n", (ulong) os_file_n_pending_preads, (ulong) os_file_n_pending_pwrites); } if (os_n_file_reads == os_n_file_reads_old) { avg_bytes_read = 0.0; } else { avg_bytes_read = (double) os_bytes_read_since_printout / (os_n_file_reads - os_n_file_reads_old); } ib_logger(ib_stream, "%.2f reads/s, %lu avg bytes/read," " %.2f writes/s, %.2f fsyncs/s\n", (os_n_file_reads - os_n_file_reads_old) / time_elapsed, (ulong)avg_bytes_read, (os_n_file_writes - os_n_file_writes_old) / time_elapsed, (os_n_fsyncs - os_n_fsyncs_old) / time_elapsed); os_n_file_reads_old = os_n_file_reads; os_n_file_writes_old = os_n_file_writes; os_n_fsyncs_old = os_n_fsyncs; os_bytes_read_since_printout = 0; os_last_printout = current_time; } /**********************************************************************//** Refreshes the statistics used to print per-second averages. */ UNIV_INTERN void os_aio_refresh_stats(void) /*======================*/ { os_n_file_reads_old = os_n_file_reads; os_n_file_writes_old = os_n_file_writes; os_n_fsyncs_old = os_n_fsyncs; os_bytes_read_since_printout = 0; os_last_printout = time(NULL); } #ifdef UNIV_DEBUG /**********************************************************************//** Checks that all slots in the system have been freed, that is, there are no pending io operations. @return TRUE if all free */ UNIV_INTERN ibool os_aio_all_slots_free(void) /*=======================*/ { os_aio_array_t* array; ulint n_res = 0; array = os_aio_read_array; os_mutex_enter(array->mutex); n_res += array->n_reserved; os_mutex_exit(array->mutex); array = os_aio_write_array; os_mutex_enter(array->mutex); n_res += array->n_reserved; os_mutex_exit(array->mutex); array = os_aio_ibuf_array; os_mutex_enter(array->mutex); n_res += array->n_reserved; os_mutex_exit(array->mutex); array = os_aio_log_array; os_mutex_enter(array->mutex); n_res += array->n_reserved; os_mutex_exit(array->mutex); array = os_aio_sync_array; os_mutex_enter(array->mutex); n_res += array->n_reserved; os_mutex_exit(array->mutex); if (n_res == 0) { return(TRUE); } return(FALSE); } #endif /* UNIV_DEBUG */ /************************************************************************* Sets the info describing an i/o thread current state. */ UNIV_INTERN void os_set_io_thread_op_info( /*=====================*/ ulint i, /*!< in: the 'segment' of the i/o thread */ const char* str) /*!< in: constant char string describing the state */ { ut_a(i < SRV_MAX_N_IO_THREADS); srv_io_thread_op_info[i] = str; } #endif /* !UNIV_HOTBACKUP */ haildb-2.3.2/os/os0thread.c0000644000175000017500000002162611513177357016330 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2010, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file os/os0thread.c The interface to the operating system thread control primitives Created 9/8/1995 Heikki Tuuri *******************************************************/ #include "os0thread.h" #ifdef UNIV_NONINL #include "os0thread.ic" #endif #ifdef HAVE_PTHREAD_H #include #endif /* HAVE_PTHREAD_H */ #ifdef HAVE_SYS_SELECT_H #include #endif /* HAVE_SYS_SELECT_H */ #ifndef UNIV_HOTBACKUP #include "srv0srv.h" #include "os0sync.h" /***************************************************************//** Compares two thread ids for equality. @return TRUE if equal */ UNIV_INTERN ibool os_thread_eq( /*=========*/ os_thread_id_t a, /*!< in: OS thread or thread id */ os_thread_id_t b) /*!< in: OS thread or thread id */ { #ifdef __WIN__ if (a == b) { return(TRUE); } return(FALSE); #else if (pthread_equal(a, b)) { return(TRUE); } return(FALSE); #endif } /****************************************************************//** Converts an OS thread id to a ulint. It is NOT guaranteed that the ulint is unique for the thread though! @return thread identifier as a number */ UNIV_INTERN ulint os_thread_pf( /*=========*/ os_thread_id_t a) /*!< in: OS thread identifier */ { #ifdef UNIV_HPUX10 /* In HP-UX-10.20 a pthread_t is a struct of 3 fields: field1, field2, field3. We do not know if field1 determines the thread uniquely. */ return((ulint)(a.field1)); #else return((ulint)a); #endif } /*****************************************************************//** Returns the thread identifier of current thread. Currently the thread identifier in Unix is the thread handle itself. Note that in HP-UX pthread_t is a struct of 3 fields. @return current thread identifier */ UNIV_INTERN os_thread_id_t os_thread_get_curr_id(void) /*=======================*/ { #ifdef __WIN__ return(GetCurrentThreadId()); #else return(pthread_self()); #endif } /****************************************************************//** Creates a new thread of execution. The execution starts from the function given. The start function takes a void* parameter and returns an ulint. @return handle to the thread */ UNIV_INTERN os_thread_t os_thread_create( /*=============*/ #ifndef __WIN__ os_posix_f_t start_f, #else ulint (*start_f)(void*), /*!< in: pointer to function from which to start */ #endif void* arg, /*!< in: argument to start function */ os_thread_id_t* thread_id) /*!< out: id of the created thread, or NULL */ { #ifdef __WIN__ os_thread_t thread; DWORD win_thread_id; os_mutex_enter(os_sync_mutex); os_thread_count++; os_mutex_exit(os_sync_mutex); thread = CreateThread(NULL, /* no security attributes */ 0, /* default size stack */ (LPTHREAD_START_ROUTINE)start_f, arg, 0, /* thread runs immediately */ &win_thread_id); if (srv_set_thread_priorities) { /* Set created thread priority the same as a normal query, we try to prevent starvation of threads by assigning same priority QUERY_PRIOR to all */ ut_a(SetThreadPriority(thread, srv_query_thread_priority)); } if (thread_id) { *thread_id = win_thread_id; } return(thread); #else int ret; os_thread_t pthread; pthread_attr_t attr; #ifndef UNIV_HPUX10 pthread_attr_init(&attr); #endif #ifdef UNIV_AIX /* We must make sure a thread stack is at least 32 kB, otherwise InnoDB might crash; we do not know if the default stack size on AIX is always big enough. An empirical test on AIX-4.3 suggested the size was 96 kB, though. */ ret = pthread_attr_setstacksize(&attr, (size_t)(PTHREAD_STACK_MIN + 32 * 1024)); if (ret) { srv_panic(ret, "InnoDB: Error: pthread_attr_setstacksize" " returned %d\n", ret); } #endif #ifdef __NETWARE__ ret = pthread_attr_setstacksize(&attr, (size_t) NW_THD_STACKSIZE); if (ret) { srv_panic(ret, "InnoDB: Error: pthread_attr_setstacksize" " returned %d\n", ret); } #endif os_mutex_enter(os_sync_mutex); os_thread_count++; os_mutex_exit(os_sync_mutex); #ifdef UNIV_HPUX10 ret = pthread_create(&pthread, pthread_attr_default, start_f, arg); #else ret = pthread_create(&pthread, &attr, start_f, arg); #endif if (ret) { srv_panic(ret, "InnoDB: Error: pthread_create returned %d\n", ret); } #ifndef UNIV_HPUX10 pthread_attr_destroy(&attr); #endif if (srv_set_thread_priorities) { #ifdef HAVE_PTHREAD_SETPRIO pthread_setprio(pthread, srv_query_thread_priority); #endif } if (thread_id) { *thread_id = pthread; } return(pthread); #endif } /*****************************************************************//** Exits the current thread. */ UNIV_INTERN void os_thread_exit( /*===========*/ void* exit_value) /*!< in: exit value; in Windows this void* is cast as a DWORD */ { #ifndef __WIN__ int ret; #endif /* __WIN__ */ #ifdef UNIV_DEBUG_THREAD_CREATION ib_logger(ib_stream, "Thread exits, id %lu\n", os_thread_pf(os_thread_get_curr_id())); #endif /* UNIV_DEBUG_THREAD_CREATION */ os_mutex_enter(os_sync_mutex); os_thread_count--; os_mutex_exit(os_sync_mutex); #ifdef __WIN__ ExitThread((DWORD)exit_value); #else ret = pthread_detach(pthread_self()); ut_a(ret == 0); pthread_exit(exit_value); #endif /* __WIN__ */ } /*****************************************************************//** Returns handle to the current thread. @return current thread handle */ UNIV_INTERN os_thread_t os_thread_get_curr(void) /*====================*/ { #ifdef __WIN__ return(GetCurrentThread()); #else return(pthread_self()); #endif } /*****************************************************************//** Advises the os to give up remainder of the thread's time slice. */ UNIV_INTERN void os_thread_yield(void) /*=================*/ { #if defined(__WIN__) Sleep(0); #elif (defined(HAVE_SCHED_YIELD) && defined(HAVE_SCHED_H)) sched_yield(); #elif defined(HAVE_PTHREAD_YIELD_ZERO_ARG) pthread_yield(); #elif defined(HAVE_PTHREAD_YIELD_ONE_ARG) pthread_yield(0); #else os_thread_sleep(0); #endif } #endif /* !UNIV_HOTBACKUP */ /*****************************************************************//** The thread sleeps at least the time given in microseconds. */ UNIV_INTERN void os_thread_sleep( /*============*/ ulint tm) /*!< in: time in microseconds */ { #ifdef __WIN__ Sleep((DWORD) tm / 1000); #elif defined(__NETWARE__) delay(tm / 1000); #else struct timeval t; t.tv_sec = tm / 1000000; t.tv_usec = tm % 1000000; select(0, NULL, NULL, NULL, &t); #endif } #ifndef UNIV_HOTBACKUP /******************************************************************//** Sets a thread priority. */ UNIV_INTERN void os_thread_set_priority( /*===================*/ os_thread_t handle, /*!< in: OS handle to the thread */ ulint pri) /*!< in: priority */ { #ifdef __WIN__ int os_pri; if (pri == OS_THREAD_PRIORITY_BACKGROUND) { os_pri = THREAD_PRIORITY_BELOW_NORMAL; } else if (pri == OS_THREAD_PRIORITY_NORMAL) { os_pri = THREAD_PRIORITY_NORMAL; } else if (pri == OS_THREAD_PRIORITY_ABOVE_NORMAL) { os_pri = THREAD_PRIORITY_HIGHEST; } else { ut_error; } ut_a(SetThreadPriority(handle, os_pri)); #else UT_NOT_USED(handle); UT_NOT_USED(pri); #endif } /******************************************************************//** Gets a thread priority. @return priority */ UNIV_INTERN ulint os_thread_get_priority( /*===================*/ os_thread_t handle __attribute__((unused))) /*!< in: OS handle to the thread */ { #ifdef __WIN__ int os_pri; ulint pri; os_pri = GetThreadPriority(handle); if (os_pri == THREAD_PRIORITY_BELOW_NORMAL) { pri = OS_THREAD_PRIORITY_BACKGROUND; } else if (os_pri == THREAD_PRIORITY_NORMAL) { pri = OS_THREAD_PRIORITY_NORMAL; } else if (os_pri == THREAD_PRIORITY_HIGHEST) { pri = OS_THREAD_PRIORITY_ABOVE_NORMAL; } else { ut_error; } return(pri); #else return(0); #endif } /******************************************************************//** Gets the last operating system error code for the calling thread. @return last error on Windows, 0 otherwise */ UNIV_INTERN ulint os_thread_get_last_error(void) /*==========================*/ { #ifdef __WIN__ return(GetLastError()); #else return(0); #endif } #endif /* !UNIV_HOTBACKUP */ haildb-2.3.2/Makefile.am0000644000175000017500000002211211513177357015675 0ustar00pcrewspcrews00000000000000# Copyright (C) 2008, 2010 Oracle/Innobase Oy # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; version 2 of the License. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ACLOCAL_AMFLAGS = -I m4 --force include_HEADERS = haildb.h libhaildb_la_LDFLAGS = -version-info $(IB_API_VERSION) $(LD_VERSION_SCRIPT) libhaildb_la_CFLAGS= \ ${AM_CFLAGS} \ ${CFLAG_VISIBILITY} \ -DBUILDING_HAILDB noinst_HEADERS = \ include/btr0btr.h include/btr0btr.ic \ include/btr0cur.h include/btr0cur.ic \ include/btr0pcur.h include/btr0pcur.ic \ include/btr0sea.h include/btr0sea.ic \ include/btr0types.h \ include/buf0buf.h \ include/buf0buf.ic include/buf0flu.h \ include/buf0flu.ic include/buf0lru.h \ include/buf0lru.ic include/buf0rea.h \ include/buf0types.h include/data0data.h \ include/data0data.ic include/data0type.h \ include/data0type.ic include/data0types.h \ include/db0err.h include/dict0boot.h \ include/dict0boot.ic include/dict0crea.h \ include/dict0crea.ic include/dict0dict.h \ include/dict0dict.ic include/dict0load.h \ include/dict0load.ic include/dict0mem.h \ include/dict0mem.ic include/dict0types.h \ include/dyn0dyn.h include/dyn0dyn.ic \ include/eval0eval.h include/eval0eval.ic \ include/eval0proc.h include/eval0proc.ic \ include/fil0fil.h include/fsp0fsp.h \ include/fsp0fsp.ic include/fut0fut.h \ include/fsp0types.h \ include/fut0fut.ic include/fut0lst.h \ include/fut0lst.ic include/ha0ha.h \ include/ha0ha.ic \ include/ha0storage.h \ include/ha0storage.ic \ include/hash0hash.h \ include/hash0hash.ic include/ibuf0ibuf.h \ include/ibuf0ibuf.ic include/ibuf0types.h \ include/lock0iter.h \ include/lock0lock.h include/lock0lock.ic \ include/lock0priv.h include/lock0priv.ic \ include/lock0types.h include/log0log.h \ include/log0log.ic include/log0recv.h \ include/log0recv.ic include/mach0data.h \ include/mach0data.ic include/mem0dbg.h \ include/mem0dbg.ic \ include/mem0mem.h include/mem0mem.ic \ include/mtr0log.h include/mtr0log.ic \ include/mtr0mtr.h include/mtr0mtr.ic \ include/mtr0types.h \ include/os0file.h \ include/os0proc.h include/os0proc.ic \ include/os0sync.h include/os0sync.ic \ include/os0thread.h include/os0thread.ic \ include/page0cur.h include/page0cur.ic \ include/page0page.h include/page0page.ic \ include/page0types.h include/pars0grm.h \ include/pars0opt.h include/pars0opt.ic \ include/pars0pars.h include/pars0pars.ic \ include/pars0sym.h include/pars0sym.ic \ include/pars0types.h include/que0que.h \ include/que0que.ic include/que0types.h \ include/read0read.h include/read0read.ic \ include/read0types.h include/rem0cmp.h \ include/rem0cmp.ic include/rem0rec.h \ include/rem0rec.ic include/rem0types.h \ include/row0ext.h include/row0ext.ic \ include/row0ins.h include/row0ins.ic \ include/row0merge.h \ include/row0purge.h include/row0purge.ic \ include/row0row.h include/row0row.ic \ include/row0sel.h include/row0sel.ic \ include/row0types.h include/row0uins.h \ include/row0uins.ic include/row0umod.h \ include/row0umod.ic include/row0undo.h \ include/row0undo.ic include/row0upd.h \ include/row0upd.ic include/row0vers.h \ include/row0vers.ic include/srv0que.h \ include/row0prebuilt.h \ include/srv0srv.h include/srv0srv.ic \ include/srv0start.h include/sync0arr.h \ include/sync0arr.ic include/sync0rw.h \ include/sync0rw.ic include/sync0sync.h \ include/sync0sync.ic include/sync0types.h \ include/thr0loc.h include/thr0loc.ic \ include/trx0purge.h include/trx0purge.ic \ include/trx0rec.h include/trx0rec.ic \ include/trx0roll.h include/trx0roll.ic \ include/trx0rseg.h include/trx0rseg.ic \ include/trx0sys.h include/trx0sys.ic \ include/trx0trx.h include/trx0trx.ic \ include/trx0types.h include/trx0undo.h \ include/trx0undo.ic include/trx0xa.h \ include/univ.i include/usr0sess.h \ include/usr0sess.ic include/usr0types.h \ include/ut0byte.h include/ut0byte.ic \ include/ut0dbg.h include/ut0lst.h \ include/ut0mem.h include/ut0mem.ic \ include/ut0rnd.h include/ut0rnd.ic \ include/ut0sort.h include/ut0ut.h \ include/ut0ut.ic include/ut0vec.h \ include/ut0vec.ic include/ut0list.h \ include/ut0list.ic \ include/ut0rbt.h \ include/ddl0ddl.h \ include/api0misc.h include/api0ucode.h \ include/api0api.h \ tests/ib_mt_base.h \ tests/ib_mt_drv.h \ tests/test0aux.h if WITH_ZIP noinst_HEADERS += \ include/buf0buddy.h include/buf0buddy.ic \ include/page0zip.h include/page0zip.ic endif lib_LTLIBRARIES = libhaildb.la libhaildb_la_SOURCES = \ btr/btr0btr.c btr/btr0cur.c btr/btr0pcur.c \ btr/btr0sea.c \ buf/buf0buf.c buf/buf0flu.c \ buf/buf0lru.c buf/buf0rea.c data/data0data.c \ data/data0type.c dict/dict0boot.c \ dict/dict0crea.c dict/dict0dict.c \ dict/dict0load.c dict/dict0mem.c dyn/dyn0dyn.c \ eval/eval0eval.c eval/eval0proc.c \ fil/fil0fil.c fsp/fsp0fsp.c fut/fut0fut.c \ fut/fut0lst.c ha/ha0ha.c \ ha/ha0storage.c \ ha/hash0hash.c \ ibuf/ibuf0ibuf.c lock/lock0iter.c \ lock/lock0lock.c \ log/log0log.c log/log0recv.c mach/mach0data.c \ mem/mem0mem.c mtr/mtr0log.c \ mtr/mtr0mtr.c os/os0file.c os/os0proc.c \ os/os0sync.c os/os0thread.c page/page0cur.c \ page/page0page.c \ pars/lexyy.c pars/pars0grm.y \ pars/pars0opt.c pars/pars0pars.c \ pars/pars0sym.c que/que0que.c read/read0read.c \ rem/rem0cmp.c rem/rem0rec.c row/row0ext.c \ row/row0ins.c row/row0merge.c \ row/row0purge.c row/row0row.c \ row/row0sel.c row/row0uins.c row/row0umod.c \ row/row0undo.c row/row0upd.c row/row0vers.c \ row/row0prebuilt.c \ srv/srv0que.c srv/srv0srv.c srv/srv0start.c \ sync/sync0arr.c sync/sync0rw.c \ sync/sync0sync.c thr/thr0loc.c \ trx/trx0purge.c \ trx/trx0rec.c trx/trx0roll.c trx/trx0rseg.c \ trx/trx0sys.c trx/trx0trx.c trx/trx0undo.c \ usr/usr0sess.c ut/ut0byte.c ut/ut0dbg.c \ ut/ut0list.c ut/ut0mem.c ut/ut0rnd.c \ ut/ut0ut.c ut/ut0vec.c \ ut/ut0rbt.c \ api/api0api.c api/api0ucode.c ddl/ddl0ddl.c \ api/api0misc.c api/api0cfg.c api/api0status.c \ api/api0sql.c if WITH_ZIP libhaildb_la_SOURCES += buf/buf0buddy.c page/page0zip.c endif EXTRA_DIST= \ .quickly \ config/autorun.sh \ config/pandora-plugin \ config/uncrustify.cfg \ CMakeLists.txt \ COPYING \ COPYING.Google \ COPYING.Percona \ COPYING.Sun_Microsystems \ config.h.cmake \ libhaildb.ver \ ${top_srcdir}/m4/*m4 \ pars/make_bison.sh \ pars/make_flex.sh \ pars/pars0grm.y \ pars/pars0lex.l \ tests/CMakeLists.examples \ tests/CMakeLists.txt \ tests/Makefile.examples \ tests/README \ tests/ib_compressed.c \ tests/ib_deadlock.c \ tests/ib_index.c \ tests/ib_mt_stress.c \ tests/ib_perf1.c \ tests/ib_recover.c \ tests/ib_search.c \ tests/ib_zip.c \ tests/run.sh \ win/innodb.def DISTCLEANFILES= \ config/top.h \ tests/ibdata1 \ tests/log/ib_logfile1 \ tests/log/ib_logfile0 # keep the following lists alphabetically sorted TEST_EXECUTABLES=\ tests/bug579934_open_index_by_name_segv \ tests/ib_cfg \ tests/ib_cursor \ tests/ib_ddl \ tests/ib_dict \ tests/ib_drop \ tests/ib_duplicate_key_name \ tests/ib_logger \ tests/ib_mt_drv \ tests/ib_shutdown \ tests/ib_status \ tests/ib_panic \ tests/ib_trx_is_interrupted \ tests/ib_tablename \ tests/ib_table_statistics \ tests/ib_test1 \ tests/ib_client_compare \ tests/ib_test2 \ tests/ib_test3 \ tests/ib_test5 \ tests/ib_types \ tests/ib_update TESTS_ENVIRONMENT= ${top_srcdir}/tests/run.sh LDADD = libhaildb.la libtest0aux.la check_PROGRAMS= ${TEST_EXECUTABLES} check_LTLIBRARIES= libtest0aux.la tests_ib_mt_drv_SOURCES= \ tests/ib_mt_base.c \ tests/ib_mt_drv.c \ tests/ib_mt_t1.c \ tests/ib_mt_t2.c libtest0aux_la_SOURCES= tests/test0aux.c TESTS= $(check_PROGRAMS) TEST_STRESS_EXECUTABLES=\ ib_mt_stress \ ib_perf1 test: test-clean check # stress tests, these take longer to complete test-stress: all test-clean mkdir -p $(top_srcdir)/tests/t && \ cd $(top_srcdir)/tests/t && \ for test in $(TEST_STRESS_EXECUTABLES) ; do \ echo "Running $${test} ..." ; \ echo "" ; \ ../$${test} ; \ echo "" ; \ done test-clean: cd $(top_srcdir)/tests && \ rm -fr log/ dict_test/ t/ test/ ibdata1 haildb-2.3.2/thr/0000755000175000017500000000000011513177437014437 5ustar00pcrewspcrews00000000000000haildb-2.3.2/thr/thr0loc.c0000644000175000017500000001554111513177357016165 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file thr/thr0loc.c The thread local storage Created 10/5/1995 Heikki Tuuri *******************************************************/ #include "thr0loc.h" #ifdef UNIV_NONINL #include "thr0loc.ic" #endif #include "sync0sync.h" #include "hash0hash.h" #include "mem0mem.h" #include "srv0srv.h" /* IMPLEMENTATION OF THREAD LOCAL STORAGE ====================================== The threads sometimes need private data which depends on the thread id. This is implemented as a hash table, where the hash value is calculated from the thread id, to prepare for a large number of threads. The hash table is protected by a mutex. If you need modify the program and put new data to the thread local storage, just add it to struct thr_local_struct in the header file. */ /** Mutex protecting thr_local_hash */ static mutex_t thr_local_mutex; /** The hash table. The module is not yet initialized when it is NULL. */ static hash_table_t* thr_local_hash = NULL; /** Thread local data */ typedef struct thr_local_struct thr_local_t; /** @brief Thread local data. The private data for each thread should be put to the structure below and the accessor functions written for the field. */ struct thr_local_struct{ os_thread_id_t id; /*!< id of the thread which owns this struct */ os_thread_t handle; /*!< operating system handle to the thread */ ulint slot_no;/*!< the index of the slot in the thread table for this thread */ ibool in_ibuf;/*!< TRUE if the thread is doing an ibuf operation */ hash_node_t hash; /*!< hash chain node */ ulint magic_n;/*!< magic number (THR_LOCAL_MAGIC_N) */ }; /** The value of thr_local_struct::magic_n */ #define THR_LOCAL_MAGIC_N 1231234 /*******************************************************************//** Returns the local storage struct for a thread. @return local storage */ static thr_local_t* thr_local_get( /*==========*/ os_thread_id_t id) /*!< in: thread id of the thread */ { thr_local_t* local; try_again: ut_ad(thr_local_hash); ut_ad(mutex_own(&thr_local_mutex)); /* Look for the local struct in the hash table */ local = NULL; HASH_SEARCH(hash, thr_local_hash, os_thread_pf(id), thr_local_t*, local,, os_thread_eq(local->id, id)); if (local == NULL) { mutex_exit(&thr_local_mutex); thr_local_create(); mutex_enter(&thr_local_mutex); goto try_again; } ut_ad(local->magic_n == THR_LOCAL_MAGIC_N); return(local); } /*******************************************************************//** Gets the slot number in the thread table of a thread. @return slot number */ UNIV_INTERN ulint thr_local_get_slot_no( /*==================*/ os_thread_id_t id) /*!< in: thread id of the thread */ { ulint slot_no; thr_local_t* local; mutex_enter(&thr_local_mutex); local = thr_local_get(id); slot_no = local->slot_no; mutex_exit(&thr_local_mutex); return(slot_no); } /*******************************************************************//** Sets the slot number in the thread table of a thread. */ UNIV_INTERN void thr_local_set_slot_no( /*==================*/ os_thread_id_t id, /*!< in: thread id of the thread */ ulint slot_no)/*!< in: slot number */ { thr_local_t* local; mutex_enter(&thr_local_mutex); local = thr_local_get(id); local->slot_no = slot_no; mutex_exit(&thr_local_mutex); } /*******************************************************************//** Returns pointer to the 'in_ibuf' field within the current thread local storage. @return pointer to the in_ibuf field */ UNIV_INTERN ibool* thr_local_get_in_ibuf_field(void) /*=============================*/ { thr_local_t* local; mutex_enter(&thr_local_mutex); local = thr_local_get(os_thread_get_curr_id()); mutex_exit(&thr_local_mutex); return(&(local->in_ibuf)); } /*******************************************************************//** Creates a local storage struct for the calling new thread. */ UNIV_INTERN void thr_local_create(void) /*==================*/ { thr_local_t* local; if (thr_local_hash == NULL) { thr_local_init(); } local = mem_alloc(sizeof(thr_local_t)); local->id = os_thread_get_curr_id(); local->handle = os_thread_get_curr(); local->magic_n = THR_LOCAL_MAGIC_N; local->in_ibuf = FALSE; mutex_enter(&thr_local_mutex); HASH_INSERT(thr_local_t, hash, thr_local_hash, os_thread_pf(os_thread_get_curr_id()), local); mutex_exit(&thr_local_mutex); } /*******************************************************************//** Frees the local storage struct for the specified thread. */ UNIV_INTERN void thr_local_free( /*===========*/ os_thread_id_t id) /*!< in: thread id */ { thr_local_t* local; mutex_enter(&thr_local_mutex); /* Look for the local struct in the hash table */ HASH_SEARCH(hash, thr_local_hash, os_thread_pf(id), thr_local_t*, local,, os_thread_eq(local->id, id)); if (local == NULL) { mutex_exit(&thr_local_mutex); return; } HASH_DELETE(thr_local_t, hash, thr_local_hash, os_thread_pf(id), local); mutex_exit(&thr_local_mutex); ut_a(local->magic_n == THR_LOCAL_MAGIC_N); mem_free(local); } /****************************************************************//** Initializes the thread local storage module. */ UNIV_INTERN void thr_local_init(void) /*================*/ { ut_a(thr_local_hash == NULL); thr_local_hash = hash_create(OS_THREAD_MAX_N + 100); mutex_create(&thr_local_mutex, SYNC_THR_LOCAL); } /******************************************************************** Close the thread local storage module. */ UNIV_INTERN void thr_local_close(void) /*=================*/ { ulint i; ut_a(thr_local_hash != NULL); /* Free the hash elements. We don't remove them from the table because we are going to destroy the table anyway. */ for (i = 0; i < hash_get_n_cells(thr_local_hash); i++) { thr_local_t* local; local = HASH_GET_FIRST(thr_local_hash, i); while (local) { thr_local_t* prev_local = local; local = HASH_GET_NEXT(hash, prev_local); ut_a(prev_local->magic_n == THR_LOCAL_MAGIC_N); mem_free(prev_local); } } hash_table_free(thr_local_hash); thr_local_hash = NULL; } haildb-2.3.2/COPYING0000644000175000017500000004517611513177357014713 0ustar00pcrewspcrews00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble ======== The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a. You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b. You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c. If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a. Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b. Accompany it with a written offer, valid for at least three years, to give any third-party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c. Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs ============================================= If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. ONE LINE TO GIVE THE PROGRAM'S NAME AND A BRIEF IDEA OF WHAT IT DOES. Copyright (C) YYYY NAME OF AUTHOR This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19YY NAME OF AUTHOR Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. SIGNATURE OF TY COON, 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. haildb-2.3.2/COPYING.Percona0000644000175000017500000000322711513177357016270 0ustar00pcrewspcrews00000000000000Portions of this software contain modifications contributed by Percona, Inc. These contributions are used with the following license: Copyright (c) 2008, 2009, Percona Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the Percona Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. haildb-2.3.2/srv/0000755000175000017500000000000011513177437014454 5ustar00pcrewspcrews00000000000000haildb-2.3.2/srv/srv0que.c0000644000175000017500000000201611513177357016225 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file srv/srv0que.c Server query execution Created 6/5/1996 Heikki Tuuri *******************************************************/ haildb-2.3.2/srv/srv0srv.c0000644000175000017500000023057611513177357016263 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2010, Innobase Oy. All Rights Reserved. Copyright (c) 2008, 2009 Google Inc. Copyright (c) 2009, Percona Inc. Portions of this file contain modifications contributed and copyrighted by Google, Inc. Those modifications are gratefully acknowledged and are described briefly in the InnoDB documentation. The contributions by Google are incorporated with their permission, and subject to the conditions contained in the file COPYING.Google. Portions of this file contain modifications contributed and copyrighted by Percona Inc.. Those modifications are gratefully acknowledged and are described briefly in the InnoDB documentation. The contributions by Percona Inc. are incorporated with their permission, and subject to the conditions contained in the file COPYING.Percona. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file srv/srv0srv.c The database server main program NOTE: SQL Server 7 uses something which the documentation calls user mode scheduled threads (UMS threads). One such thread is usually allocated per processor. Win32 documentation does not know any UMS threads, which suggests that the concept is internal to SQL Server 7. It may mean that SQL Server 7 does all the scheduling of threads itself, even in i/o waits. We should maybe modify InnoDB to use the same technique, because thread switches within NT may be too slow. SQL Server 7 also mentions fibers, which are cooperatively scheduled threads. They can boost performance by 5 %, according to the Delaney and Soukup's book. Windows 2000 will have something called thread pooling (see msdn website), which we could possibly use. Another possibility could be to use some very fast user space thread library. This might confuse NT though. Created 10/8/1995 Heikki Tuuri *******************************************************/ #include "univ.i" /* Dummy comment */ #include "srv0srv.h" #include "ut0mem.h" #include "ut0ut.h" #include "os0proc.h" #include "mem0mem.h" #include "sync0sync.h" #include "thr0loc.h" #include "que0que.h" #include "srv0que.h" #include "log0recv.h" #include "pars0pars.h" #include "usr0sess.h" #include "lock0lock.h" #include "trx0purge.h" #include "ibuf0ibuf.h" #include "buf0flu.h" #include "buf0lru.h" #include "btr0sea.h" #include "btr0cur.h" #include "dict0load.h" #include "dict0boot.h" #include "srv0start.h" #include "ddl0ddl.h" #include "api0ucode.h" #include "buf0buddy.h" #include "page0zip.h" #include "os0sync.h" /* for HAVE_ATOMIC_BUILTINS */ /* FIXME: When we setup the session variables infrastructure. */ #define sess_lock_wait_timeout(t) (ses_lock_wait_timeout) UNIV_INTERN ulint ses_lock_wait_timeout = 1024 * 1024 * 1024; UNIV_INTERN ibool srv_lower_case_table_names = FALSE; /** The following counter is incremented whenever there is some user activity in the server */ UNIV_INTERN ulint srv_activity_count = 0; /** The following is the maximum allowed duration of a lock wait. */ UNIV_INTERN ulint srv_fatal_semaphore_wait_threshold = 600; /** How much data manipulation language (DML) statements need to be delayed, in microseconds, in order to reduce the lagging of the purge thread. */ UNIV_INTERN ulint srv_dml_needed_delay = 0; UNIV_INTERN ibool srv_lock_timeout_active = FALSE; UNIV_INTERN ibool srv_monitor_active = FALSE; UNIV_INTERN ibool srv_error_monitor_active = FALSE; UNIV_INTERN const char* srv_main_thread_op_info = ""; /* Server parameters which are read from the initfile */ /* The following three are dir paths which are catenated before file names, where the file name itself may also contain a path */ UNIV_INTERN char* srv_data_home = NULL; /** We copy the argument passed to ib_cfg_set_text("log_group_home_dir") because srv_parse_log_group_home_dirs() parses it's input argument destructively. The copy is done using ut_malloc(). */ UNIV_INTERN char* srv_log_group_home_dir = NULL; #ifdef UNIV_LOG_ARCHIVE UNIV_INTERN char* srv_arch_dir = NULL; #endif /* UNIV_LOG_ARCHIVE */ /** store to its own file each table created by an user; data dictionary tables are in the system tablespace 0 */ UNIV_INTERN ibool srv_file_per_table; /** The file format to use on new *.ibd files. */ UNIV_INTERN ulint srv_file_format = 0; /** Whether to check file format during startup a value of DICT_TF_FORMAT_MAX + 1 means no checking ie. FALSE. The default is to set it to the highest format we support. */ UNIV_INTERN ulint srv_check_file_format_at_startup = DICT_TF_FORMAT_MAX; #if DICT_TF_FORMAT_51 # error "DICT_TF_FORMAT_51 must be 0!" #endif UNIV_INTERN ulint srv_n_data_files = 0; /** Size in database pages */ UNIV_INTERN ulint* srv_data_file_sizes = NULL; /** If TRUE, then we auto-extend the last data file */ UNIV_INTERN ibool srv_auto_extend_last_data_file = FALSE; /* if != 0, this tells the max size auto-extending may increase the last data file size */ UNIV_INTERN ulint srv_last_file_size_max = 0; /** If the last data file is auto-extended, we add this many pages to it at a time */ UNIV_INTERN ulong srv_auto_extend_increment = 8; UNIV_INTERN ulint* srv_data_file_is_raw_partition = NULL; /* If the following is TRUE we do not allow inserts etc. This protects the user from forgetting the 'newraw' keyword. */ UNIV_INTERN ibool srv_created_new_raw = FALSE; UNIV_INTERN ulint srv_n_log_files = ULINT_MAX; /** Size in database pages */ UNIV_INTERN ulint srv_log_file_size = ULINT_MAX; UNIV_INTERN ulint srv_log_file_curr_size = ULINT_MAX; /** Size in database pages */ UNIV_INTERN ulint srv_log_buffer_size = ULINT_MAX; UNIV_INTERN ulint srv_log_buffer_curr_size = ULINT_MAX; UNIV_INTERN ulong srv_flush_log_at_trx_commit = 1; /** Try to flush dirty pages so as to avoid IO bursts at the checkpoints. */ UNIV_INTERN ibool srv_adaptive_flushing = TRUE; /** Use os/external memory allocator */ UNIV_INTERN ibool srv_use_sys_malloc = FALSE; /** Maximum number of times allowed to conditionally acquire mutex before switching to blocking wait on the mutex */ #define MAX_MUTEX_NOWAIT 20 /** Check whether the number of failed nonblocking mutex acquisition attempts exceeds maximum allowed value. If so, srv_printf_innodb_monitor() will request mutex acquisition with mutex_enter(), which will wait until it gets the mutex. */ #define MUTEX_NOWAIT(mutex_skipped) ((mutex_skipped) < MAX_MUTEX_NOWAIT) /** Requested size in kilobytes of the buffer pool. */ UNIV_INTERN ulint srv_buf_pool_size = ULINT_MAX; /** previously requested size of the buffer pool. */ UNIV_INTERN ulint srv_buf_pool_old_size; /** Current size in kilobytes of the buffer pool. */ UNIV_INTERN ulint srv_buf_pool_curr_size = 0; /** Memory pool size in bytes */ UNIV_INTERN ulint srv_mem_pool_size = ULINT_MAX; UNIV_INTERN ulint srv_lock_table_size = ULINT_MAX; /** This parameter is deprecated. Use srv_n_io_[read|write]_threads instead. */ UNIV_INTERN ulint srv_n_file_io_threads = ULINT_MAX; UNIV_INTERN ulint srv_n_read_io_threads = ULINT_MAX; UNIV_INTERN ulint srv_n_write_io_threads = ULINT_MAX; /** User settable value of the number of pages that must be present in the buffer cache and accessed sequentially for InnoDB to trigger a readahead request. */ UNIV_INTERN ulong srv_read_ahead_threshold = 56; #ifdef UNIV_LOG_ARCHIVE UNIV_INTERN ibool srv_log_archive_on = FALSE; UNIV_INTERN ibool srv_archive_recovery = 0; UNIV_INTERN ib_uint64_t srv_archive_recovery_limit_lsn; #endif /* UNIV_LOG_ARCHIVE */ UNIV_INTERN ulint srv_unix_file_flush_method = SRV_UNIX_FSYNC; UNIV_INTERN ulint srv_win_file_flush_method = SRV_WIN_IO_UNBUFFERED; UNIV_INTERN ulint srv_max_n_open_files = 300; /** Number of IO operations per second the server can do */ UNIV_INTERN ulong srv_io_capacity = 200; /** The InnoDB main thread tries to keep the ratio of modified pages in the buffer pool to all database pages in the buffer pool smaller than the following number. But it is not guaranteed that the value stays below that during a time of heavy update/insert activity. */ UNIV_INTERN ulong srv_max_buf_pool_modified_pct = 75; /** Variable counts amount of data read in total (in bytes) */ UNIV_INTERN ulint srv_data_read = 0; /** Here we count the amount of data written in total (in bytes) */ UNIV_INTERN ulint srv_data_written = 0; /** The number of the log write requests done */ UNIV_INTERN ulint srv_log_write_requests = 0; /** The number of physical writes to the log performed */ UNIV_INTERN ulint srv_log_writes = 0; /** Amount of data written to the log files in bytes */ UNIV_INTERN ulint srv_os_log_written = 0; /** Amount of writes being done to the log files */ UNIV_INTERN ulint srv_os_log_pending_writes = 0; /** We increase this counter, when there we don't have enough space in the log buffer and have to flush it */ UNIV_INTERN ulint srv_log_waits = 0; /** This variable counts the amount of times, when the doublewrite buffer was flushed */ UNIV_INTERN ulint srv_dblwr_writes = 0; /** Here we store the number of pages that have been flushed to the doublewrite buffer */ UNIV_INTERN ulint srv_dblwr_pages_written = 0; /** In this variable we store the number of write requests issued */ UNIV_INTERN ulint srv_buf_pool_write_requests = 0; /** Here we store the number of times when we had to wait for a free page in the buffer pool. It happens when the buffer pool is full and we need to make a flush, in order to be able to read or create a page. */ UNIV_INTERN ulint srv_buf_pool_wait_free = 0; /** Variable to count the number of pages that were written from buffer pool to the disk */ UNIV_INTERN ulint srv_buf_pool_flushed = 0; /** Number of buffer pool reads that led to the reading of a disk page */ UNIV_INTERN ulint srv_buf_pool_reads = 0; /** Structure to pass status variables to the client */ UNIV_INTERN export_struc export_vars; /* If the following is != 0 we do not allow inserts etc. This protects the user from forgetting the force_recovery keyword. */ UNIV_INTERN ulint srv_force_recovery = IB_RECOVERY_DEFAULT; /*-----------------------*/ /* We are prepared for a situation that we have this many threads waiting for a semaphore inside InnoDB. innobase_start_or_create() sets the value. */ UNIV_INTERN ulint srv_max_n_threads = 0; /** This mutex protects srv_conc data structures */ static os_fast_mutex_t srv_conc_mutex; /** Number of OS threads waiting in the FIFO for a permission to enter InnoDB */ UNIV_INTERN ulint srv_conc_n_waiting_threads = 0; typedef struct srv_conc_slot_struct srv_conc_slot_t; struct srv_conc_slot_struct{ os_event_t event; /*!< event to wait */ ibool reserved; /*!< TRUE if slot reserved */ ibool wait_ended; /*!< TRUE when another thread has already set the event and the thread in this slot is free to proceed; but reserved may still be TRUE at that point */ UT_LIST_NODE_T(srv_conc_slot_t) srv_conc_queue; /*!< queue node */ }; /** Queue of threads waiting to get in */ static UT_LIST_BASE_NODE_T(srv_conc_slot_t) srv_conc_queue; /** Array of wait slots */ static srv_conc_slot_t* srv_conc_slots; /*-----------------------*/ UNIV_INTERN ib_shutdown_t srv_fast_shutdown = IB_SHUTDOWN_NORMAL; /** Generate a innodb_status. file if this is TRUE. */ UNIV_INTERN ibool srv_innodb_status = FALSE; /* When estimating number of different key values in an index, sample this many index pages */ UNIV_INTERN unsigned long long srv_stats_sample_pages = 8; UNIV_INTERN ibool srv_use_doublewrite_buf = TRUE; UNIV_INTERN ibool srv_use_checksums = TRUE; UNIV_INTERN ibool srv_set_thread_priorities = TRUE; UNIV_INTERN int srv_query_thread_priority = 0; /*-------------------------------------------*/ UNIV_INTERN ulong srv_n_spin_wait_rounds = 30; UNIV_INTERN ulong srv_spin_wait_delay = 6; #ifdef UNIV_DEBUG UNIV_INTERN ibool srv_print_thread_releases = FALSE; UNIV_INTERN ibool srv_print_lock_waits = FALSE; UNIV_INTERN ibool srv_print_buf_io = FALSE; UNIV_INTERN ibool srv_print_log_io = FALSE; UNIV_INTERN ibool srv_print_latch_waits = FALSE; #endif /* UNIV_DEBUG */ UNIV_INTERN ulint srv_n_rows_inserted = 0; UNIV_INTERN ulint srv_n_rows_updated = 0; UNIV_INTERN ulint srv_n_rows_deleted = 0; UNIV_INTERN ulint srv_n_rows_read = 0; static ulint srv_n_rows_inserted_old = 0; static ulint srv_n_rows_updated_old = 0; static ulint srv_n_rows_deleted_old = 0; static ulint srv_n_rows_read_old = 0; UNIV_INTERN ulint srv_n_lock_wait_count = 0; UNIV_INTERN ulint srv_n_lock_wait_current_count = 0; UNIV_INTERN ib_int64_t srv_n_lock_wait_time = 0; UNIV_INTERN ulint srv_n_lock_max_wait_time = 0; /** Set the following to 0 if you want InnoDB to write messages on ib_stream on startup/shutdown */ UNIV_INTERN ibool srv_print_verbose_log = TRUE; UNIV_INTERN ibool srv_print_innodb_monitor = FALSE; UNIV_INTERN ibool srv_print_innodb_lock_monitor = FALSE; UNIV_INTERN ibool srv_print_innodb_tablespace_monitor = FALSE; UNIV_INTERN ibool srv_print_innodb_table_monitor = FALSE; static time_t srv_last_monitor_time; static mutex_t srv_innodb_monitor_mutex; /** Mutex for locking srv_monitor_file */ UNIV_INTERN mutex_t srv_monitor_file_mutex; #ifdef UNIV_LINUX static ulint srv_main_thread_process_no = 0; #endif /* UNIV_LINUX */ static ulint srv_main_thread_id = 0; /* The following count work done by srv_master_thread. */ /** Iterations by the 'once per second' loop. */ static ulint srv_main_1_second_loops = 0; /** Calls to sleep by the 'once per second' loop. */ static ulint srv_main_sleeps = 0; /** Iterations by the 'once per 10 seconds' loop. */ static ulint srv_main_10_second_loops = 0; /** Iterations of the loop bounded by the 'background_loop' label. */ static ulint srv_main_background_loops = 0; /** Iterations of the loop bounded by the 'flush_loop' label. */ static ulint srv_main_flush_loops = 0; /** Log writes involving flush. */ static ulint srv_log_writes_and_flush = 0; /** This is only ever touched by the master thread. It records the time when the last flush of log file has happened. The master thread ensures that we flush the log files at least once per second. */ static time_t srv_last_log_flush_time; /** The master thread performs various tasks based on the current state of IO activity and the level of IO utilization is past intervals. Following macros define thresholds for these conditions. */ #define SRV_PEND_IO_THRESHOLD (PCT_IO(3)) #define SRV_RECENT_IO_ACTIVITY (PCT_IO(5)) #define SRV_PAST_IO_ACTIVITY (PCT_IO(200)) /* IMPLEMENTATION OF THE SERVER MAIN PROGRAM ========================================= There is the following analogue between this database server and an operating system kernel: DB concept equivalent OS concept ---------- --------------------- transaction -- process; query thread -- thread; lock -- semaphore; transaction set to the rollback state -- kill signal delivered to a process; kernel -- kernel; query thread execution: (a) without kernel mutex reserved -- process executing in user mode; (b) with kernel mutex reserved -- process executing in kernel mode; The server is controlled by a master thread which runs at a priority higher than normal, that is, higher than user threads. It sleeps most of the time, and wakes up, say, every 300 milliseconds, to check whether there is anything happening in the server which requires intervention of the master thread. Such situations may be, for example, when flushing of dirty blocks is needed in the buffer pool or old version of database rows have to be cleaned away. The threads which we call user threads serve the queries of the clients and input from the console of the server. They run at normal priority. The server may have several communications endpoints. A dedicated set of user threads waits at each of these endpoints ready to receive a client request. Each request is taken by a single user thread, which then starts processing and, when the result is ready, sends it to the client and returns to wait at the same endpoint the thread started from. So, we do not have dedicated communication threads listening at the endpoints and dealing the jobs to dedicated worker threads. Our architecture saves one thread swithch per request, compared to the solution with dedicated communication threads which amounts to 15 microseconds on 100 MHz Pentium running NT. If the client is communicating over a network, this saving is negligible, but if the client resides in the same machine, maybe in an SMP machine on a different processor from the server thread, the saving can be important as the threads can communicate over shared memory with an overhead of a few microseconds. We may later implement a dedicated communication thread solution for those endpoints which communicate over a network. Our solution with user threads has two problems: for each endpoint there has to be a number of listening threads. If there are many communication endpoints, it may be difficult to set the right number of concurrent threads in the system, as many of the threads may always be waiting at less busy endpoints. Another problem is queuing of the messages, as the server internally does not offer any queue for jobs. Another group of user threads is intended for splitting the queries and processing them in parallel. Let us call these parallel communication threads. These threads are waiting for parallelized tasks, suspended on event semaphores. A single user thread waits for input from the console, like a command to shut the database. Utility threads are a different group of threads which takes care of the buffer pool flushing and other, mainly background operations, in the server. Some of these utility threads always run at a lower than normal priority, so that they are always in background. Some of them may dynamically boost their priority by the pri_adjust function, even to higher than normal priority, if their task becomes urgent. The running of utilities is controlled by high- and low-water marks of urgency. The urgency may be measured by the number of dirty blocks in the buffer pool, in the case of the flush thread, for example. When the high-water mark is exceeded, an utility starts running, until the urgency drops under the low-water mark. Then the utility thread suspend itself to wait for an event. The master thread is responsible of signaling this event when the utility thread is again needed. For each individual type of utility, some threads always remain at lower than normal priority. This is because pri_adjust is implemented so that the threads at normal or higher priority control their share of running time by calling sleep. Thus, if the load of the system sudenly drops, these threads cannot necessarily utilize the system fully. The background priority threads make up for this, starting to run when the load drops. When there is no activity in the system, also the master thread suspends itself to wait for an event making the server totally silent. The responsibility to signal this event is on the user thread which again receives a message from a client. There is still one complication in our server design. If a background utility thread obtains a resource (e.g., mutex) needed by a user thread, and there is also some other user activity in the system, the user thread may have to wait indefinitely long for the resource, as the OS does not schedule a background thread if there is some other runnable user thread. This problem is called priority inversion in real-time programming. One solution to the priority inversion problem would be to keep record of which thread owns which resource and in the above case boost the priority of the background thread so that it will be scheduled and it can release the resource. This solution is called priority inheritance in real-time programming. A drawback of this solution is that the overhead of acquiring a mutex increases slightly, maybe 0.2 microseconds on a 100 MHz Pentium, because the thread has to call os_thread_get_curr_id. This may be compared to 0.5 microsecond overhead for a mutex lock-unlock pair. Note that the thread cannot store the information in the resource, say mutex, itself, because competing threads could wipe out the information if it is stored before acquiring the mutex, and if it stored afterwards, the information is outdated for the time of one machine instruction, at least. (To be precise, the information could be stored to lock_word in mutex if the machine supports atomic swap.) The above solution with priority inheritance may become actual in the future, but at the moment we plan to implement a more coarse solution, which could be called a global priority inheritance. If a thread has to wait for a long time, say 300 milliseconds, for a resource, we just guess that it may be waiting for a resource owned by a background thread, and boost the priority of all runnable background threads to the normal level. The background threads then themselves adjust their fixed priority back to background after releasing all resources they had (or, at some fixed points in their program code). What is the performance of the global priority inheritance solution? We may weigh the length of the wait time 300 milliseconds, during which the system processes some other thread to the cost of boosting the priority of each runnable background thread, rescheduling it, and lowering the priority again. On 100 MHz Pentium + NT this overhead may be of the order 100 microseconds per thread. So, if the number of runnable background threads is not very big, say < 100, the cost is tolerable. Utility threads probably will access resources used by user threads not very often, so collisions of user threads to preempted utility threads should not happen very often. The thread table contains information of the current status of each thread existing in the system, and also the event semaphores used in suspending the master thread and utility and parallel communication threads when they have nothing to do. The thread table can be seen as an analogue to the process table in a traditional Unix implementation. The thread table is also used in the global priority inheritance scheme. This brings in one additional complication: threads accessing the thread table must have at least normal fixed priority, because the priority inheritance solution does not work if a background thread is preempted while possessing the mutex protecting the thread table. So, if a thread accesses the thread table, its priority has to be boosted at least to normal. This priority requirement can be seen similar to the privileged mode used when processing the kernel calls in traditional Unix.*/ /** Thread slot in the thread table */ typedef struct srv_slot_struct { os_thread_id_t id; /*!< thread id */ os_thread_t handle; /*!< thread handle */ unsigned type:3; /*!< thread type: user, utility etc. */ unsigned in_use:1; /*!< TRUE if this slot is in use */ unsigned suspended:1; /*!< TRUE if the thread is waiting for the event of this slot */ ib_time_t suspend_time; /*!< time when the thread was suspended */ os_event_t event; /*!< event used in suspending the thread when it has nothing to do */ que_thr_t* thr; /*!< suspended query thread (only used for client threads) */ } srv_slot_t; /** Thread table is an array of slots */ typedef srv_slot_t srv_table_t; /** The server system struct */ typedef struct srv_sys_struct{ srv_table_t* threads; /*!< server thread table */ UT_LIST_BASE_NODE_T(que_thr_t) tasks; /*!< task queue */ } srv_sys_t; /** Table for client threads where they will be suspended to wait for locks */ static srv_slot_t* srv_client_table = NULL; UNIV_INTERN os_event_t srv_lock_timeout_thread_event; static srv_sys_t* srv_sys = NULL; /* padding to prevent other memory update hotspots from residing on the same memory cache line */ UNIV_INTERN byte srv_pad1[64]; /** Mutex protecting the server, trx structs, query threads, and lock table */ UNIV_INTERN mutex_t* kernel_mutex_temp; /* padding to prevent other memory update hotspots from residing on the same memory cache line */ UNIV_INTERN byte srv_pad2[64]; /* The following values give info about the activity going on in the database. They are protected by the server mutex. The arrays are indexed by the type of the thread. */ UNIV_INTERN ulint srv_n_threads_active[SRV_MASTER + 1]; static ulint srv_n_threads[SRV_MASTER + 1]; /* global variable for indicating if we've paniced or not. If we have, we try not to do anything at all. */ UNIV_INTERN int srv_panic_status = 0; UNIV_INTERN void* ib_panic_data = NULL; ib_panic_function_t ib_panic = NULL; /*********************************************************************** Prints counters for work done by srv_master_thread. */ static void srv_print_master_thread_info( /*=========================*/ ib_stream_t stream) /* in: output stream */ { ib_logger(stream, "srv_master_thread loops: %lu 1_second, %lu sleeps, " "%lu 10_second, %lu background, %lu flush\n", srv_main_1_second_loops, srv_main_sleeps, srv_main_10_second_loops, srv_main_background_loops, srv_main_flush_loops); ib_logger(stream, "srv_master_thread log flush and writes: %lu\n", srv_log_writes_and_flush); } /*********************************************************************//** Reset variables. */ UNIV_INTERN void srv_var_init(void) /*==============*/ { #ifdef __NETWARE__ extern ibool panic_shutdown; panic_shutdown = FALSE; #endif srv_free_paths_and_sizes(); ses_lock_wait_timeout = 1024 * 1024 * 1024; srv_lower_case_table_names = FALSE; srv_activity_count = 0; srv_fatal_semaphore_wait_threshold = 600; srv_dml_needed_delay = 0; srv_monitor_active = FALSE; srv_lock_timeout_active = FALSE; srv_error_monitor_active = FALSE; srv_main_thread_op_info = ""; srv_adaptive_flushing = TRUE; srv_use_sys_malloc = FALSE; #ifdef UNIV_LOG_ARCHIVE srv_arch_dir = NULL; #endif /* UNIV_LOG_ARCHIVE */ srv_file_per_table = FALSE; srv_file_format = 0; srv_check_file_format_at_startup = DICT_TF_FORMAT_MAX; srv_n_data_files = 0; srv_auto_extend_last_data_file = FALSE; srv_last_file_size_max = 0; srv_auto_extend_increment = 8; srv_data_file_is_raw_partition = NULL; srv_created_new_raw = FALSE; srv_n_log_files = ULINT_MAX; srv_log_file_size = ULINT_MAX; srv_log_file_curr_size = ULINT_MAX; srv_log_buffer_size = ULINT_MAX; srv_log_buffer_curr_size = ULINT_MAX; srv_flush_log_at_trx_commit = 1; srv_buf_pool_size = ULINT_MAX; srv_buf_pool_old_size = 0; srv_buf_pool_curr_size = 0; srv_mem_pool_size = ULINT_MAX; srv_lock_table_size = ULINT_MAX; srv_n_file_io_threads = ULINT_MAX; #ifdef UNIV_LOG_ARCHIVE srv_log_archive_on = FALSE; srv_archive_recovery = 0; srv_archive_recovery_limit_lsn = 0; #endif /* UNIV_LOG_ARCHIVE */ srv_unix_file_flush_method = SRV_UNIX_FSYNC; srv_win_file_flush_method = SRV_WIN_IO_UNBUFFERED; srv_max_n_open_files = 300; srv_max_buf_pool_modified_pct = 90; srv_data_read = 0; srv_data_written = 0; srv_log_write_requests = 0; srv_log_writes = 0; srv_os_log_written = 0; srv_os_log_pending_writes = 0; srv_log_waits = 0; srv_dblwr_writes = 0; srv_dblwr_pages_written = 0; srv_buf_pool_write_requests = 0; srv_buf_pool_wait_free = 0; srv_buf_pool_flushed = 0; srv_buf_pool_reads = 0; srv_force_recovery = IB_RECOVERY_DEFAULT; srv_max_n_threads = 0; srv_conc_slots = NULL; srv_last_monitor_time = 0; #ifdef UNIV_LINUX srv_main_thread_process_no = 0; #endif /* UNIV_LINUX */ srv_conc_n_waiting_threads = 0; srv_use_doublewrite_buf = TRUE; srv_use_checksums = TRUE; btr_search_enabled = TRUE; srv_print_verbose_log = TRUE; srv_innodb_status = FALSE; ses_rollback_on_timeout = FALSE; srv_start_lsn = 0; srv_shutdown_lsn = 0; srv_client_table = NULL; srv_lock_timeout_thread_event = NULL; kernel_mutex_temp = NULL; srv_data_home = NULL; memset(srv_n_threads_active, 0x0, sizeof(srv_n_threads_active)); memset(srv_n_threads, 0x0, sizeof(srv_n_threads)); memset(&export_vars, 0x0, sizeof(export_vars)); srv_shutdown_state = SRV_SHUTDOWN_NONE; srv_fast_shutdown = IB_SHUTDOWN_NORMAL; } /*********************************************************************//** Accessor function to get pointer to n'th slot in the server thread table. @return pointer to the slot */ static srv_slot_t* srv_table_get_nth_slot( /*===================*/ ulint index) /*!< in: index of the slot */ { ut_a(index < OS_THREAD_MAX_N); return(srv_sys->threads + index); } /*********************************************************************//** Gets the number of threads in the system. @return sum of srv_n_threads[] */ UNIV_INTERN ulint srv_get_n_threads(void) /*===================*/ { ulint i; ulint n_threads = 0; mutex_enter(&kernel_mutex); for (i = SRV_COM; i < SRV_MASTER + 1; i++) { n_threads += srv_n_threads[i]; } mutex_exit(&kernel_mutex); return(n_threads); } /*********************************************************************//** Reserves a slot in the thread table for the current thread. Also creates the thread local storage struct for the current thread. NOTE! The server mutex has to be reserved by the caller! @return reserved slot index */ static ulint srv_table_reserve_slot( /*===================*/ enum srv_thread_type type) /*!< in: type of the thread */ { srv_slot_t* slot; ulint i; ut_a(type > 0); ut_a(type <= SRV_MASTER); i = 0; slot = srv_table_get_nth_slot(i); while (slot->in_use) { i++; slot = srv_table_get_nth_slot(i); } ut_a(slot->in_use == FALSE); slot->in_use = TRUE; slot->suspended = FALSE; slot->type = type; slot->id = os_thread_get_curr_id(); slot->handle = os_thread_get_curr(); thr_local_create(); thr_local_set_slot_no(os_thread_get_curr_id(), i); return(i); } /*********************************************************************//** Suspends the calling thread to wait for the event in its thread slot. NOTE! The server mutex has to be reserved by the caller! @return event for the calling thread to wait */ static os_event_t srv_suspend_thread(void) /*====================*/ { srv_slot_t* slot; os_event_t event; ulint slot_no; enum srv_thread_type type; ut_ad(mutex_own(&kernel_mutex)); slot_no = thr_local_get_slot_no(os_thread_get_curr_id()); if (srv_print_thread_releases) { ib_logger(ib_stream, "Suspending thread %lu to slot %lu\n", (ulong) os_thread_get_curr_id(), (ulong) slot_no); } slot = srv_table_get_nth_slot(slot_no); type = slot->type; ut_ad(type >= SRV_WORKER); ut_ad(type <= SRV_MASTER); event = slot->event; slot->suspended = TRUE; ut_ad(srv_n_threads_active[type] > 0); srv_n_threads_active[type]--; os_event_reset(event); return(event); } /*********************************************************************//** Releases threads of the type given from suspension in the thread table. NOTE! The server mutex has to be reserved by the caller! @return number of threads released: this may be less than n if not enough threads were suspended at the moment */ UNIV_INTERN ulint srv_release_threads( /*================*/ enum srv_thread_type type, /*!< in: thread type */ ulint n) /*!< in: number of threads to release */ { srv_slot_t* slot; ulint i; ulint count = 0; ut_ad(type >= SRV_WORKER); ut_ad(type <= SRV_MASTER); ut_ad(n > 0); ut_ad(mutex_own(&kernel_mutex)); for (i = 0; i < OS_THREAD_MAX_N; i++) { slot = srv_table_get_nth_slot(i); if (slot->in_use && slot->type == type && slot->suspended) { slot->suspended = FALSE; srv_n_threads_active[type]++; os_event_set(slot->event); if (srv_print_thread_releases) { ib_logger(ib_stream, "Releasing thread %lu type %lu" " from slot %lu\n", (ulong) slot->id, (ulong) type, (ulong) i); } count++; if (count == n) { break; } } } return(count); } /*********************************************************************//** Returns the calling thread type. @return SRV_COM, ... */ UNIV_INTERN enum srv_thread_type srv_get_thread_type(void) /*=====================*/ { ulint slot_no; srv_slot_t* slot; enum srv_thread_type type; mutex_enter(&kernel_mutex); slot_no = thr_local_get_slot_no(os_thread_get_curr_id()); slot = srv_table_get_nth_slot(slot_no); type = slot->type; ut_ad(type >= SRV_WORKER); ut_ad(type <= SRV_MASTER); mutex_exit(&kernel_mutex); return(type); } /*********************************************************************//** Initializes the server. */ static void srv_init(void) /*==========*/ { srv_conc_slot_t* conc_slot; srv_slot_t* slot; ulint i; srv_sys = mem_alloc(sizeof(srv_sys_t)); kernel_mutex_temp = mem_alloc(sizeof(mutex_t)); mutex_create(&kernel_mutex, SYNC_KERNEL); mutex_create(&srv_innodb_monitor_mutex, SYNC_NO_ORDER_CHECK); srv_sys->threads = mem_alloc(OS_THREAD_MAX_N * sizeof(srv_slot_t)); for (i = 0; i < OS_THREAD_MAX_N; i++) { slot = srv_table_get_nth_slot(i); slot->in_use = FALSE; slot->type=0; /* Avoid purify errors */ slot->event = os_event_create(NULL); ut_a(slot->event); } srv_client_table = mem_alloc(OS_THREAD_MAX_N * sizeof(srv_slot_t)); slot = srv_client_table; for (i = 0; i < OS_THREAD_MAX_N; ++i, ++slot) { slot->in_use = FALSE; slot->type = 0; slot->event = os_event_create(NULL); ut_a(slot->event); } srv_lock_timeout_thread_event = os_event_create(NULL); for (i = 0; i < SRV_MASTER + 1; i++) { srv_n_threads_active[i] = 0; srv_n_threads[i] = 0; } UT_LIST_INIT(srv_sys->tasks); /* Create dummy indexes for infimum and supremum records */ dict_ind_init(); /* Init the server concurrency restriction data structures */ os_fast_mutex_init(&srv_conc_mutex); UT_LIST_INIT(srv_conc_queue); srv_conc_slots = mem_alloc(OS_THREAD_MAX_N * sizeof(srv_conc_slot_t)); conc_slot = srv_conc_slots; for (i = 0; i < OS_THREAD_MAX_N; ++i, ++conc_slot) { conc_slot->reserved = FALSE; conc_slot->event = os_event_create(NULL); ut_a(conc_slot->event); } } /*********************************************************************//** Frees the data structures created in srv_init(). */ UNIV_INTERN void srv_free(void) /*==========*/ { ulint i; for (i = 0; i < OS_THREAD_MAX_N; i++) { srv_slot_t* slot; srv_conc_slot_t* conc_slot; slot = srv_table_get_nth_slot(i); conc_slot = srv_conc_slots + i; os_event_free(slot->event); os_event_free(conc_slot->event); } os_event_free(srv_lock_timeout_thread_event); srv_lock_timeout_thread_event = NULL; mem_free(srv_sys->threads); srv_sys->threads = NULL; mem_free(srv_client_table); srv_client_table = NULL; mem_free(srv_conc_slots); srv_conc_slots = NULL; os_fast_mutex_free(&srv_conc_mutex); mutex_free(&srv_innodb_monitor_mutex); mutex_free(&kernel_mutex); mem_free(kernel_mutex_temp); kernel_mutex_temp = NULL; mem_free(srv_sys); srv_sys = NULL; } /*********************************************************************//** Initializes the synchronization primitives, memory system, and the thread local storage. */ UNIV_INTERN void srv_general_init(void) /*==================*/ { /* The order here is siginificant. */ /* Reset the system variables in the recovery module. */ recv_sys_var_init(); os_sync_init(); sync_init(); thr_local_init(); } /*======================= InnoDB Server FIFO queue =======================*/ /* Maximum allowable purge history length. <=0 means 'infinite'. */ UNIV_INTERN ulong srv_max_purge_lag = 0; /*========================================================================*/ /*********************************************************************//** Normalizes init parameter values to use units we use inside InnoDB. @return DB_SUCCESS or error code */ static ulint srv_normalize_init_values(void) /*===========================*/ { ulint n; ulint i; n = srv_n_data_files; for (i = 0; i < n; i++) { srv_data_file_sizes[i] = srv_data_file_sizes[i] * ((1024 * 1024) / UNIV_PAGE_SIZE); } srv_last_file_size_max = srv_last_file_size_max * ((1024 * 1024) / UNIV_PAGE_SIZE); srv_log_file_size = srv_log_file_curr_size / UNIV_PAGE_SIZE; srv_log_file_curr_size = srv_log_file_size * UNIV_PAGE_SIZE; srv_log_buffer_size = srv_log_buffer_curr_size / UNIV_PAGE_SIZE; srv_log_buffer_curr_size = srv_log_buffer_size * UNIV_PAGE_SIZE; srv_lock_table_size = 5 * (srv_buf_pool_size / UNIV_PAGE_SIZE); return(DB_SUCCESS); } /*********************************************************************//** Resets the variables of all the InnoDB modules. */ UNIV_INTERN void srv_modules_var_init(void) /*======================*/ { /* The order here shouldn't matter. None of the functions below should have any dependencies. */ trx_var_init(); trx_sys_var_init(); rw_lock_var_init(); recv_sys_var_init(); que_var_init(); trx_purge_var_init(); pars_var_init(); page_zip_var_init(); os_proc_var_init(); os_file_var_init(); sync_var_init(); log_var_init(); lock_var_init(); ibuf_var_init(); fil_var_init(); dict_var_init(); dfield_var_init(); dtype_var_init(); buf_var_init(); buf_LRU_var_init(); buf_buddy_var_init(); btr_cur_var_init(); btr_search_var_init(); ut_mem_var_init(); os_sync_var_init(); } /************************************************************************* Boots the InnoDB server. @return DB_SUCCESS or error code */ UNIV_INTERN ulint srv_boot(void) /*==========*/ { ulint err; recv_sys_var_init(); /* Transform the init parameter values given by the user to use units we use inside InnoDB: */ err = srv_normalize_init_values(); if (err != DB_SUCCESS) { return(err); } /* Initialize synchronization primitives, memory management, and thread local storage */ srv_general_init(); /* Initialize this module */ srv_init(); return(DB_SUCCESS); } /*********************************************************************//** Reserves a slot in the thread table for the current user OS thread. NOTE! The kernel mutex has to be reserved by the caller! @return reserved slot */ static srv_slot_t* srv_table_reserve_slot_for_user_thread(void) /*========================================*/ { srv_slot_t* slot; ulint i; ut_ad(mutex_own(&kernel_mutex)); i = 0; slot = srv_client_table + i; while (slot->in_use) { i++; if (i >= OS_THREAD_MAX_N) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: There appear to be %lu user" " threads currently waiting\n" "InnoDB: inside InnoDB, which is the" " upper limit. Cannot continue operation.\n" "InnoDB: We intentionally generate" " a seg fault to print a stack trace\n" "InnoDB: on Linux. But first we print" " a list of waiting threads.\n", (ulong) i); for (i = 0; i < OS_THREAD_MAX_N; i++) { slot = srv_client_table + i; ib_logger(ib_stream, "Slot %lu: thread id %lu, type %lu," " in use %lu, susp %lu, time %lu\n", (ulong) i, (ulong) os_thread_pf(slot->id), (ulong) slot->type, (ulong) slot->in_use, (ulong) slot->suspended, (ulong) difftime(ut_time(), slot->suspend_time)); } ut_error; } slot = srv_client_table + i; } ut_a(slot->in_use == FALSE); slot->in_use = TRUE; slot->id = os_thread_get_curr_id(); slot->handle = os_thread_get_curr(); return(slot); } /***************************************************************//** Puts a user OS thread to wait for a lock to be released. If an error occurs during the wait trx->error_state associated with thr is != DB_SUCCESS when we return. DB_LOCK_WAIT_TIMEOUT and DB_DEADLOCK are possible errors. DB_DEADLOCK is returned if selective deadlock resolution chose this transaction as a victim. */ UNIV_INTERN void srv_suspend_user_thread( /*=====================*/ que_thr_t* thr) /*!< in: query thread associated with the user OS thread */ { srv_slot_t* slot; os_event_t event; double wait_time; trx_t* trx; ulint had_dict_lock; ib_int64_t start_time = 0; ib_int64_t finish_time; ulint diff_time; ulint sec; ulint ms; ulong lock_wait_timeout; ut_ad(!mutex_own(&kernel_mutex)); trx = thr_get_trx(thr); os_event_set(srv_lock_timeout_thread_event); mutex_enter(&kernel_mutex); trx->error_state = DB_SUCCESS; if (thr->state == QUE_THR_RUNNING) { ut_ad(thr->is_active == TRUE); /* The lock has already been released or this transaction was chosen as a deadlock victim: no need to suspend */ if (trx->was_chosen_as_deadlock_victim) { trx->error_state = DB_DEADLOCK; trx->was_chosen_as_deadlock_victim = FALSE; } mutex_exit(&kernel_mutex); return; } ut_ad(thr->is_active == FALSE); slot = srv_table_reserve_slot_for_user_thread(); event = slot->event; slot->thr = thr; os_event_reset(event); slot->suspend_time = ut_time(); if (thr->lock_state == QUE_THR_LOCK_ROW) { srv_n_lock_wait_count++; srv_n_lock_wait_current_count++; if (ut_usectime(&sec, &ms) == -1) { start_time = -1; } else { start_time = (ib_int64_t) sec * 1000000 + ms; } } /* Wake the lock timeout monitor thread, if it is suspended */ os_event_set(srv_lock_timeout_thread_event); mutex_exit(&kernel_mutex); had_dict_lock = trx->dict_operation_lock_mode; switch (had_dict_lock) { case RW_S_LATCH: /* Release foreign key check latch */ dict_unfreeze_data_dictionary(trx); break; case RW_X_LATCH: /* Release fast index creation latch */ dict_unlock_data_dictionary(trx); break; } ut_a(trx->dict_operation_lock_mode == 0); /* Suspend this thread and wait for the event. */ os_event_wait(event); /* After resuming, reacquire the data dictionary latch if necessary. */ switch (had_dict_lock) { case RW_S_LATCH: dict_freeze_data_dictionary(trx); break; case RW_X_LATCH: dict_lock_data_dictionary(trx); break; } mutex_enter(&kernel_mutex); /* Release the slot for others to use */ slot->in_use = FALSE; wait_time = ut_difftime(ut_time(), slot->suspend_time); if (thr->lock_state == QUE_THR_LOCK_ROW) { if (ut_usectime(&sec, &ms) == -1) { finish_time = -1; } else { finish_time = (ib_int64_t) sec * 1000000 + ms; } diff_time = (ulint) (finish_time - start_time); srv_n_lock_wait_current_count--; srv_n_lock_wait_time = srv_n_lock_wait_time + diff_time; if (diff_time > srv_n_lock_max_wait_time && /* only update the variable if we successfully retrieved the start and finish times. See Bug#36819. */ start_time != -1 && finish_time != -1) { srv_n_lock_max_wait_time = diff_time; } } if (trx->was_chosen_as_deadlock_victim) { trx->error_state = DB_DEADLOCK; trx->was_chosen_as_deadlock_victim = FALSE; } mutex_exit(&kernel_mutex); /* InnoDB system transactions (such as the purge, and incomplete transactions that are being rolled back after crash recovery) will use the global value of innodb_lock_wait_timeout, because trx->client_thd == NULL. */ lock_wait_timeout = sess_lock_wait_timeout(trx); if (trx_is_interrupted(trx) || (lock_wait_timeout < 100000000 && wait_time > (double) lock_wait_timeout)) { trx->error_state = DB_LOCK_WAIT_TIMEOUT; } } /********************************************************************//** Releases a user OS thread waiting for a lock to be released, if the thread is already suspended. */ UNIV_INTERN void srv_release_user_thread_if_suspended( /*==================================*/ que_thr_t* thr) /*!< in: query thread associated with the user OS thread */ { srv_slot_t* slot; ulint i; ut_ad(mutex_own(&kernel_mutex)); for (i = 0; i < OS_THREAD_MAX_N; i++) { slot = srv_client_table + i; if (slot->in_use && slot->thr == thr) { /* Found */ os_event_set(slot->event); return; } } /* not found */ } /******************************************************************//** Refreshes the values used to calculate per-second averages. */ static void srv_refresh_innodb_monitor_stats(void) /*==================================*/ { mutex_enter(&srv_innodb_monitor_mutex); srv_last_monitor_time = time(NULL); os_aio_refresh_stats(); btr_cur_n_sea_old = btr_cur_n_sea; btr_cur_n_non_sea_old = btr_cur_n_non_sea; log_refresh_stats(); buf_refresh_io_stats(); srv_n_rows_inserted_old = srv_n_rows_inserted; srv_n_rows_updated_old = srv_n_rows_updated; srv_n_rows_deleted_old = srv_n_rows_deleted; srv_n_rows_read_old = srv_n_rows_read; mutex_exit(&srv_innodb_monitor_mutex); } /******************************************************************//** Outputs to a file the output of the InnoDB Monitor. @return FALSE if not all information printed due to failure to obtain necessary mutex */ UNIV_INTERN ibool srv_printf_innodb_monitor( /*======================*/ ib_stream_t ib_stream, /*!< in: output stream */ ibool nowait, /*!< in: whether to wait for kernel mutex */ ulint* trx_start, /*!< out: file position of the start of the list of active transactions */ ulint* trx_end) /*!< out: file position of the end of the list of active transactions */ { double time_elapsed; time_t current_time; ulint n_reserved; ibool ret; mutex_enter(&srv_innodb_monitor_mutex); current_time = time(NULL); /* We add 0.001 seconds to time_elapsed to prevent division by zero if two users happen to call SHOW INNODB STATUS at the same time */ time_elapsed = difftime(current_time, srv_last_monitor_time) + 0.001; srv_last_monitor_time = time(NULL); ib_logger(ib_stream, "\n=====================================\n"); ut_print_timestamp(ib_stream); ib_logger(ib_stream, " INNODB MONITOR OUTPUT\n" "=====================================\n" "Per second averages calculated from the last %lu seconds\n", (ulong)time_elapsed); ib_logger(ib_stream, "----------\n" "BACKGROUND THREAD\n" "----------\n"); srv_print_master_thread_info(ib_stream); ib_logger(ib_stream, "----------\n" "SEMAPHORES\n" "----------\n"); sync_print(ib_stream); #ifdef WITH_FOREIGN_KEY /* Conceptually, srv_innodb_monitor_mutex has a very high latching order level in sync0sync.h, while dict_foreign_err_mutex has a very low level 135. Therefore we can reserve the latter mutex here without a danger of a deadlock of threads. */ mutex_enter(&dict_foreign_err_mutex); if (ftell(dict_foreign_err_file) != 0L) { ib_logger(ib_stream, "------------------------\n" "LATEST FOREIGN KEY ERROR\n" "------------------------\n"); ut_copy_file(ib_stream, dict_foreign_err_file); } mutex_exit(&dict_foreign_err_mutex); #endif /* WITH_FOREIGN_KEY */ lock_print_info_all_transactions(ib_stream); ib_logger(ib_stream, "--------\n" "FILE I/O\n" "--------\n"); os_aio_print(ib_stream); /* Only if lock_print_info_summary proceeds correctly, before we call the lock_print_info_all_transactions to print all the lock information. */ ret = lock_print_info_summary(ib_stream, nowait); if (ret) { if (trx_start) { long t = ftell(ib_stream); if (t < 0) { *trx_start = ULINT_UNDEFINED; } else { *trx_start = (ulint) t; } } lock_print_info_all_transactions(ib_stream); if (trx_end) { long t = ftell(ib_stream); if (t < 0) { *trx_end = ULINT_UNDEFINED; } else { *trx_end = (ulint) t; } } } ib_logger(ib_stream, "--------\n" "FILE I/O\n" "--------\n"); os_aio_print(ib_stream); ib_logger(ib_stream, "-------------------------------------\n" "INSERT BUFFER AND ADAPTIVE HASH INDEX\n" "-------------------------------------\n"); ibuf_print(ib_stream); ha_print_info(ib_stream, btr_search_sys->hash_index); ib_logger(ib_stream, "%.2f hash searches/s, %.2f non-hash searches/s\n", (btr_cur_n_sea - btr_cur_n_sea_old) / time_elapsed, (btr_cur_n_non_sea - btr_cur_n_non_sea_old) / time_elapsed); btr_cur_n_sea_old = btr_cur_n_sea; btr_cur_n_non_sea_old = btr_cur_n_non_sea; ib_logger(ib_stream, "---\n" "LOG\n" "---\n"); log_print(ib_stream); ib_logger(ib_stream, "----------------------\n" "BUFFER POOL AND MEMORY\n" "----------------------\n"); ib_logger(ib_stream, "Total memory allocated " ULINTPF "\n", ut_total_allocated_memory); ib_logger(ib_stream, "Dictionary memory allocated " ULINTPF "\n", dict_sys->size); buf_print_io(ib_stream); ib_logger(ib_stream, "--------------\n" "ROW OPERATIONS\n" "--------------\n"); ib_logger(ib_stream, "%lu queries in queue\n", (ulong) srv_conc_n_waiting_threads); ib_logger(ib_stream, "%lu read views open inside InnoDB\n", UT_LIST_GET_LEN(trx_sys->view_list)); n_reserved = fil_space_get_n_reserved_extents(0); if (n_reserved > 0) { ib_logger(ib_stream, "%lu tablespace extents now reserved for" " B-tree split operations\n", (ulong) n_reserved); } #ifdef UNIV_LINUX ib_logger(ib_stream, "Main thread process no. %lu, id %lu, state: %s\n", (ulong) srv_main_thread_process_no, (ulong) srv_main_thread_id, srv_main_thread_op_info); #else ib_logger(ib_stream, "Main thread id %lu, state: %s\n", (ulong) srv_main_thread_id, srv_main_thread_op_info); #endif ib_logger(ib_stream, "Number of rows inserted " ULINTPF ", updated " ULINTPF ", deleted " ULINTPF ", read " ULINTPF "\n", srv_n_rows_inserted, srv_n_rows_updated, srv_n_rows_deleted, srv_n_rows_read); ib_logger(ib_stream, "%.2f inserts/s, %.2f updates/s," " %.2f deletes/s, %.2f reads/s\n", (srv_n_rows_inserted - srv_n_rows_inserted_old) / time_elapsed, (srv_n_rows_updated - srv_n_rows_updated_old) / time_elapsed, (srv_n_rows_deleted - srv_n_rows_deleted_old) / time_elapsed, (srv_n_rows_read - srv_n_rows_read_old) / time_elapsed); srv_n_rows_inserted_old = srv_n_rows_inserted; srv_n_rows_updated_old = srv_n_rows_updated; srv_n_rows_deleted_old = srv_n_rows_deleted; srv_n_rows_read_old = srv_n_rows_read; ib_logger(ib_stream, "----------------------------\n" "END OF INNODB MONITOR OUTPUT\n" "============================\n"); mutex_exit(&srv_innodb_monitor_mutex); return(ret); } /******************************************************************//** Function to pass InnoDB status variables to the client. */ UNIV_INTERN void srv_export_innodb_status(void) /*==========================*/ { mutex_enter(&srv_innodb_monitor_mutex); export_vars.innodb_data_pending_reads = os_n_pending_reads; export_vars.innodb_data_pending_writes = os_n_pending_writes; export_vars.innodb_data_pending_fsyncs = fil_n_pending_log_flushes + fil_n_pending_tablespace_flushes; export_vars.innodb_data_fsyncs = os_n_fsyncs; export_vars.innodb_data_read = srv_data_read; export_vars.innodb_data_reads = os_n_file_reads; export_vars.innodb_data_writes = os_n_file_writes; export_vars.innodb_data_written = srv_data_written; export_vars.innodb_buffer_pool_read_requests = buf_pool->stat.n_page_gets; export_vars.innodb_buffer_pool_write_requests = srv_buf_pool_write_requests; export_vars.innodb_buffer_pool_wait_free = srv_buf_pool_wait_free; export_vars.innodb_buffer_pool_pages_flushed = srv_buf_pool_flushed; export_vars.innodb_buffer_pool_reads = srv_buf_pool_reads; export_vars.innodb_buffer_pool_read_ahead = buf_pool->stat.n_ra_pages_read; export_vars.innodb_buffer_pool_read_ahead_evicted = buf_pool->stat.n_ra_pages_evicted; export_vars.innodb_buffer_pool_pages_data = UT_LIST_GET_LEN(buf_pool->LRU); export_vars.innodb_buffer_pool_pages_dirty = UT_LIST_GET_LEN(buf_pool->flush_list); export_vars.innodb_buffer_pool_pages_free = UT_LIST_GET_LEN(buf_pool->free); #ifdef UNIV_DEBUG export_vars.innodb_buffer_pool_pages_latched = buf_get_latched_pages_number(); #endif /* UNIV_DEBUG */ export_vars.innodb_buffer_pool_pages_total = buf_pool->curr_size; export_vars.innodb_buffer_pool_pages_misc = buf_pool->curr_size - UT_LIST_GET_LEN(buf_pool->LRU) - UT_LIST_GET_LEN(buf_pool->free); #ifdef HAVE_ATOMIC_BUILTINS export_vars.innodb_have_atomic_builtins = 1; #else export_vars.innodb_have_atomic_builtins = 0; #endif export_vars.innodb_page_size = UNIV_PAGE_SIZE; export_vars.innodb_log_waits = srv_log_waits; export_vars.innodb_os_log_written = srv_os_log_written; export_vars.innodb_os_log_fsyncs = fil_n_log_flushes; export_vars.innodb_os_log_pending_fsyncs = fil_n_pending_log_flushes; export_vars.innodb_os_log_pending_writes = srv_os_log_pending_writes; export_vars.innodb_log_write_requests = srv_log_write_requests; export_vars.innodb_log_writes = srv_log_writes; export_vars.innodb_dblwr_pages_written = srv_dblwr_pages_written; export_vars.innodb_dblwr_writes = srv_dblwr_writes; export_vars.innodb_pages_created = buf_pool->stat.n_pages_created; export_vars.innodb_pages_read = buf_pool->stat.n_pages_read; export_vars.innodb_pages_written = buf_pool->stat.n_pages_written; export_vars.innodb_row_lock_waits = srv_n_lock_wait_count; export_vars.innodb_row_lock_current_waits = srv_n_lock_wait_current_count; export_vars.innodb_row_lock_time = srv_n_lock_wait_time / 1000; if (srv_n_lock_wait_count > 0) { export_vars.innodb_row_lock_time_avg = (ulint) (srv_n_lock_wait_time / 1000 / srv_n_lock_wait_count); } else { export_vars.innodb_row_lock_time_avg = 0; } export_vars.innodb_row_lock_time_max = srv_n_lock_max_wait_time / 1000; export_vars.innodb_rows_read = srv_n_rows_read; export_vars.innodb_rows_inserted = srv_n_rows_inserted; export_vars.innodb_rows_updated = srv_n_rows_updated; export_vars.innodb_rows_deleted = srv_n_rows_deleted; mutex_exit(&srv_innodb_monitor_mutex); } /*********************************************************************//** A thread which prints the info output by various InnoDB monitors. @return a dummy parameter */ UNIV_INTERN os_thread_ret_t srv_monitor_thread( /*===============*/ void* arg __attribute__((unused))) /*!< in: a dummy parameter required by os_thread_create */ { double time_elapsed; time_t current_time; time_t last_table_monitor_time; time_t last_tablespace_monitor_time; time_t last_monitor_time; ulint mutex_skipped; ibool last_srv_print_monitor; #ifdef UNIV_DEBUG_THREAD_CREATION ib_logger(ib_stream, "Lock timeout thread starts, id %lu\n", os_thread_pf(os_thread_get_curr_id())); #endif UT_NOT_USED(arg); srv_last_monitor_time = time(NULL); last_table_monitor_time = time(NULL); last_tablespace_monitor_time = time(NULL); last_monitor_time = time(NULL); mutex_skipped = 0; last_srv_print_monitor = srv_print_innodb_monitor; loop: srv_monitor_active = TRUE; /* Wake up every 5 seconds to see if we need to print monitor information. */ os_thread_sleep(5000000); current_time = time(NULL); time_elapsed = difftime(current_time, last_monitor_time); if (time_elapsed > 15) { last_monitor_time = time(NULL); if (srv_print_innodb_monitor) { /* Reset mutex_skipped counter everytime srv_print_innodb_monitor changes. This is to ensure we will not be blocked by kernel_mutex for short duration information printing, such as requested by sync_array_print_long_waits() */ if (!last_srv_print_monitor) { mutex_skipped = 0; last_srv_print_monitor = TRUE; } if (!srv_printf_innodb_monitor(ib_stream, MUTEX_NOWAIT(mutex_skipped), NULL, NULL)) { mutex_skipped++; } else { /* Reset the counter */ mutex_skipped = 0; } } else { last_srv_print_monitor = FALSE; } if (srv_innodb_status) { mutex_enter(&srv_monitor_file_mutex); if (!srv_printf_innodb_monitor(ib_stream, MUTEX_NOWAIT(mutex_skipped), NULL, NULL)) { mutex_skipped++; } else { mutex_skipped = 0; } mutex_exit(&srv_monitor_file_mutex); } if (srv_print_innodb_tablespace_monitor && difftime(current_time, last_tablespace_monitor_time) > 60) { last_tablespace_monitor_time = time(NULL); ib_logger(ib_stream, "========================" "========================\n"); ut_print_timestamp(ib_stream); ib_logger(ib_stream, " INNODB TABLESPACE MONITOR OUTPUT\n" "========================" "========================\n"); fsp_print(0); ib_logger(ib_stream, "Validating tablespace\n"); fsp_validate(0); ib_logger(ib_stream, "Validation ok\n" "---------------------------------------\n" "END OF INNODB TABLESPACE MONITOR OUTPUT\n" "=======================================\n"); } if (srv_print_innodb_table_monitor && difftime(current_time, last_table_monitor_time) > 60) { last_table_monitor_time = time(NULL); ib_logger(ib_stream, "===========================================" "\n"); ut_print_timestamp(ib_stream); ib_logger(ib_stream, " INNODB TABLE MONITOR OUTPUT\n" "===========================================" "\n"); dict_print(); ib_logger(ib_stream, "-----------------------------------\n" "END OF INNODB TABLE MONITOR OUTPUT\n" "==================================\n"); } } if (srv_shutdown_state >= SRV_SHUTDOWN_CLEANUP) { goto exit_func; } if (srv_print_innodb_monitor || srv_print_innodb_lock_monitor || srv_print_innodb_tablespace_monitor || srv_print_innodb_table_monitor) { goto loop; } srv_monitor_active = FALSE; goto loop; exit_func: srv_monitor_active = FALSE; /* We count the number of threads in os_thread_exit(). A created thread should always use that to exit and not use return() to exit. */ os_thread_exit(NULL); OS_THREAD_DUMMY_RETURN; } /*********************************************************************//** A thread which wakes up threads whose lock wait may have lasted too long. @return a dummy parameter */ UNIV_INTERN os_thread_ret_t srv_lock_timeout_thread( /*====================*/ void* arg __attribute__((unused))) /* in: a dummy parameter required by os_thread_create */ { srv_slot_t* slot; ibool some_waits; double wait_time; ulint i; loop: /* When someone is waiting for a lock, we wake up every second and check if a timeout has passed for a lock wait */ os_thread_sleep(1000000); srv_lock_timeout_active = TRUE; mutex_enter(&kernel_mutex); some_waits = FALSE; /* Check of all slots if a thread is waiting there, and if it has exceeded the time limit */ for (i = 0; i < OS_THREAD_MAX_N; i++) { slot = srv_client_table + i; if (slot->in_use) { trx_t* trx; ulong lock_wait_timeout; some_waits = TRUE; wait_time = ut_difftime(ut_time(), slot->suspend_time); trx = thr_get_trx(slot->thr); lock_wait_timeout = sess_lock_wait_timeout(trx); if (trx_is_interrupted(trx) || (lock_wait_timeout < 100000000 && (wait_time > (double) lock_wait_timeout || wait_time < 0))) { /* Timeout exceeded or a wrap-around in system time counter: cancel the lock request queued by the transaction and release possible other transactions waiting behind; it is possible that the lock has already been granted: in that case do nothing */ if (trx->wait_lock) { lock_cancel_waiting_and_release( trx->wait_lock); } } } } os_event_reset(srv_lock_timeout_thread_event); mutex_exit(&kernel_mutex); if (srv_shutdown_state >= SRV_SHUTDOWN_CLEANUP) { goto exit_func; } if (some_waits) { goto loop; } srv_lock_timeout_active = FALSE; #if 0 /* The following synchronisation is disabled, since the InnoDB monitor output is to be updated every 15 seconds. */ os_event_wait(srv_lock_timeout_thread_event); #endif goto loop; exit_func: srv_lock_timeout_active = FALSE; /* We count the number of threads in os_thread_exit(). A created thread should always use that to exit and not use return() to exit. */ os_thread_exit(NULL); OS_THREAD_DUMMY_RETURN; } /*********************************************************************//** A thread which prints warnings about semaphore waits which have lasted too long. These can be used to track bugs which cause hangs. @return a dummy parameter */ UNIV_INTERN os_thread_ret_t srv_error_monitor_thread( /*=====================*/ void* arg __attribute__((unused))) /*!< in: a dummy parameter required by os_thread_create */ { /* number of successive fatal timeouts observed */ ulint fatal_cnt = 0; ib_uint64_t old_lsn; ib_uint64_t new_lsn; old_lsn = srv_start_lsn; #ifdef UNIV_DEBUG_THREAD_CREATION ib_logger(ib_stream, "Error monitor thread starts, id %lu\n", os_thread_pf(os_thread_get_curr_id())); #endif loop: srv_error_monitor_active = TRUE; /* Try to track a strange bug reported by Harald Fuchs and others, where the lsn seems to decrease at times */ new_lsn = log_get_lsn(); if (new_lsn < old_lsn) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Error: old log sequence number %llu" " was greater\n" "InnoDB: than the new log sequence number %llu!\n" "InnoDB: Please submit a bug report, " "check the InnoDB website for details", old_lsn, new_lsn); } old_lsn = new_lsn; if (difftime(time(NULL), srv_last_monitor_time) > 60) { /* We referesh InnoDB Monitor values so that averages are printed from at most 60 last seconds */ srv_refresh_innodb_monitor_stats(); } /* Update the statistics collected for deciding LRU eviction policy. */ buf_LRU_stat_update(); /* Update the statistics collected for flush rate policy. */ buf_flush_stat_update(); /* In case mutex_exit is not a memory barrier, it is theoretically possible some threads are left waiting though the semaphore is already released. Wake up those threads: */ sync_arr_wake_threads_if_sema_free(); if (sync_array_print_long_waits()) { fatal_cnt++; if (fatal_cnt > 10) { ib_logger(ib_stream, "InnoDB: Error: semaphore wait has lasted" " > %lu seconds\n" "InnoDB: We intentionally crash the server," " because it appears to be hung.\n", (ulong) srv_fatal_semaphore_wait_threshold); ut_error; } } else { fatal_cnt = 0; } os_thread_sleep(1000000); if (srv_shutdown_state < SRV_SHUTDOWN_CLEANUP) { goto loop; } srv_error_monitor_active = FALSE; /* We count the number of threads in os_thread_exit(). A created thread should always use that to exit and not use return() to exit. */ os_thread_exit(NULL); OS_THREAD_DUMMY_RETURN; } /*******************************************************************//** Tells the InnoDB server that there has been activity in the database and wakes up the master thread if it is suspended (not sleeping). Used in the client interface. Note that there is a small chance that the master thread stays suspended (we do not protect our operation with the kernel mutex, for performace reasons). */ UNIV_INTERN void srv_active_wake_master_thread(void) /*===============================*/ { srv_activity_count++; if (srv_n_threads_active[SRV_MASTER] == 0) { mutex_enter(&kernel_mutex); srv_release_threads(SRV_MASTER, 1); mutex_exit(&kernel_mutex); } } /*******************************************************************//** Wakes up the master thread if it is suspended or being suspended. */ UNIV_INTERN void srv_wake_master_thread(void) /*========================*/ { srv_activity_count++; mutex_enter(&kernel_mutex); srv_release_threads(SRV_MASTER, 1); mutex_exit(&kernel_mutex); } /********************************************************************** The master thread is tasked to ensure that flush of log file happens once every second in the background. This is to ensure that not more than one second of trxs are lost in case of crash when innodb_flush_logs_at_trx_commit != 1 */ static void srv_sync_log_buffer_in_background(void) /*===================================*/ { time_t current_time = time(NULL); srv_main_thread_op_info = "flushing log"; if (difftime(current_time, srv_last_log_flush_time) >= 1) { log_buffer_sync_in_background(TRUE); srv_last_log_flush_time = current_time; srv_log_writes_and_flush++; } } /*********************************************************************//** The master thread controlling the server. @return a dummy parameter */ UNIV_INTERN os_thread_ret_t srv_master_thread( /*==============*/ void* arg __attribute__((unused))) /*!< in: a dummy parameter required by os_thread_create */ { os_event_t event; ulint old_activity_count; ulint n_pages_purged = 0; ulint n_bytes_merged; ulint n_pages_flushed; ulint n_bytes_archived; ulint n_tables_to_drop; ulint n_ios; ulint n_ios_old; ulint n_ios_very_old; ulint n_pend_ios; ibool skip_sleep = FALSE; ulint i; #ifdef UNIV_DEBUG_THREAD_CREATION ib_logger(ib_stream, "Master thread starts, id %lu\n", os_thread_pf(os_thread_get_curr_id())); #endif #ifdef UNIV_LINUX srv_main_thread_process_no = os_proc_get_number(); #endif /* UNIV_LINUX */ srv_main_thread_id = os_thread_pf(os_thread_get_curr_id()); srv_table_reserve_slot(SRV_MASTER); mutex_enter(&kernel_mutex); srv_n_threads_active[SRV_MASTER]++; mutex_exit(&kernel_mutex); loop: /*****************************************************************/ /* ---- When there is database activity by users, we cycle in this loop */ srv_main_thread_op_info = "reserving kernel mutex"; n_ios_very_old = log_sys->n_log_ios + buf_pool->stat.n_pages_read + buf_pool->stat.n_pages_written; mutex_enter(&kernel_mutex); /* Store the user activity counter at the start of this loop */ old_activity_count = srv_activity_count; mutex_exit(&kernel_mutex); if (srv_force_recovery >= IB_RECOVERY_NO_BACKGROUND) { goto suspend_thread; } /* ---- We run the following loop approximately once per second when there is database activity */ srv_last_log_flush_time = time(NULL); /* No need to sleep if user has signalled shutdown. */ skip_sleep = (srv_shutdown_state != SRV_SHUTDOWN_NONE); for (i = 0; i < 10; i++) { n_ios_old = log_sys->n_log_ios + buf_pool->stat.n_pages_read + buf_pool->stat.n_pages_written; srv_main_thread_op_info = "sleeping"; srv_main_1_second_loops++; if (!skip_sleep) { os_thread_sleep(1000000); srv_main_sleeps++; } /* No need to sleep if user has signalled shutdown. */ skip_sleep = (srv_shutdown_state != SRV_SHUTDOWN_NONE); /* No need to sleep if user has signalled shutdown. */ /* ALTER TABLE on Unix requires that the table handler can drop tables lazily after there no longer are SELECT queries to them. */ srv_main_thread_op_info = "doing background drop tables"; ddl_drop_tables_in_background(); srv_main_thread_op_info = ""; if (srv_fast_shutdown != IB_SHUTDOWN_NORMAL && srv_shutdown_state > SRV_SHUTDOWN_NONE) { goto background_loop; } /* Flush logs if needed */ srv_sync_log_buffer_in_background(); srv_main_thread_op_info = "making checkpoint"; log_free_check(); /* If i/os during one second sleep were less than 5% of capacity, we assume that there is free disk i/o capacity available, and it makes sense to do an insert buffer merge. */ n_pend_ios = buf_get_n_pending_ios() + log_sys->n_pending_writes; n_ios = log_sys->n_log_ios + buf_pool->stat.n_pages_read + buf_pool->stat.n_pages_written; if (n_pend_ios < SRV_PEND_IO_THRESHOLD && (n_ios - n_ios_old < SRV_RECENT_IO_ACTIVITY)) { srv_main_thread_op_info = "doing insert buffer merge"; ibuf_contract_for_n_pages(FALSE, PCT_IO(5)); /* Flush logs if needed */ srv_sync_log_buffer_in_background(); } if (UNIV_UNLIKELY(buf_get_modified_ratio_pct() > srv_max_buf_pool_modified_pct)) { /* Try to keep the number of modified pages in the buffer pool under the limit wished by the user */ srv_main_thread_op_info = "flushing buffer pool pages"; n_pages_flushed = buf_flush_batch(BUF_FLUSH_LIST, PCT_IO(100), IB_UINT64_T_MAX); /* If we had to do the flush, it may have taken even more than 1 second, and also, there may be more to flush. Do not sleep 1 second during the next iteration of this loop. */ skip_sleep = TRUE; } else if (srv_adaptive_flushing) { /* Try to keep the rate of flushing of dirty pages such that redo log generation does not produce bursts of IO at checkpoint time. */ ulint n_flush = buf_flush_get_desired_flush_rate(); if (n_flush) { srv_main_thread_op_info = "flushing buffer pool pages"; n_flush = ut_min(PCT_IO(100), n_flush); n_pages_flushed = buf_flush_batch( BUF_FLUSH_LIST, n_flush, IB_ULONGLONG_MAX); if (n_flush == PCT_IO(100)) { skip_sleep = TRUE; } } } if (srv_activity_count == old_activity_count) { /* There is no user activity at the moment, go to the background loop */ goto background_loop; } } /* If i/os during the 10 second period were less than 200% of capacity, we assume that there is free disk i/o capacity available, and it makes sense to flush srv_io_capacity pages. Note that this is done regardless of the fraction of dirty pages relative to the max requested by the user. The one second loop above requests writes for that case. The writes done here are not required, and may be disabled. */ n_pend_ios = buf_get_n_pending_ios() + log_sys->n_pending_writes; n_ios = log_sys->n_log_ios + buf_pool->stat.n_pages_read + buf_pool->stat.n_pages_written; srv_main_10_second_loops++; if (n_pend_ios < SRV_PEND_IO_THRESHOLD && (n_ios - n_ios_very_old < SRV_PAST_IO_ACTIVITY)) { srv_main_thread_op_info = "flushing buffer pool pages"; buf_flush_batch(BUF_FLUSH_LIST, PCT_IO(100), IB_ULONGLONG_MAX); /* Flush logs if needed */ srv_sync_log_buffer_in_background(); } /* We run a batch of insert buffer merge every 10 seconds, even if the server were active */ srv_main_thread_op_info = "doing insert buffer merge"; ibuf_contract_for_n_pages(FALSE, PCT_IO(5)); /* Flush logs if needed */ srv_sync_log_buffer_in_background(); /* We run a full purge every 10 seconds, even if the server were active */ do { if (srv_fast_shutdown != IB_SHUTDOWN_NORMAL && srv_shutdown_state > 0) { goto background_loop; } srv_main_thread_op_info = "purging"; n_pages_purged = trx_purge(); /* Flush logs if needed */ srv_sync_log_buffer_in_background(); } while (n_pages_purged); srv_main_thread_op_info = "flushing buffer pool pages"; /* Flush a few oldest pages to make a new checkpoint younger */ if (buf_get_modified_ratio_pct() > 70) { /* If there are lots of modified pages in the buffer pool (> 70 %), we assume we can afford reserving the disk(s) for the time it requires to flush 100 pages */ n_pages_flushed = buf_flush_batch(BUF_FLUSH_LIST, PCT_IO(100), IB_UINT64_T_MAX); } else { /* Otherwise, we only flush a small number of pages so that we do not unnecessarily use much disk i/o capacity from other work */ n_pages_flushed = buf_flush_batch(BUF_FLUSH_LIST, PCT_IO(10), IB_UINT64_T_MAX); } srv_main_thread_op_info = "making checkpoint"; /* Make a new checkpoint about once in 10 seconds */ log_checkpoint(TRUE, FALSE); srv_main_thread_op_info = "reserving kernel mutex"; mutex_enter(&kernel_mutex); /* ---- When there is database activity, we jump from here back to the start of loop */ if (srv_activity_count != old_activity_count) { mutex_exit(&kernel_mutex); goto loop; } mutex_exit(&kernel_mutex); /* If the database is quiet, we enter the background loop */ /*****************************************************************/ background_loop: /* ---- In this loop we run background operations when the server is quiet from user activity. Also in the case of a shutdown, we loop here, flushing the buffer pool to the data files. */ /* The server has been quiet for a while: start running background operations */ srv_main_background_loops++; srv_main_thread_op_info = "doing background drop tables"; n_tables_to_drop = ddl_drop_tables_in_background(); if (n_tables_to_drop > 0) { /* Do not monopolize the CPU even if there are tables waiting in the background drop queue. (It is essentially a bug if user tries to drop a table while there are still open handles to it and we had to put it to the background drop queue.) */ os_thread_sleep(100000); } srv_main_thread_op_info = "purging"; /* Run a full purge */ do { if (srv_fast_shutdown != IB_SHUTDOWN_NORMAL && srv_shutdown_state > 0) { break; } srv_main_thread_op_info = "purging"; n_pages_purged = trx_purge(); /* Flush logs if needed */ srv_sync_log_buffer_in_background(); } while (n_pages_purged); srv_main_thread_op_info = "reserving kernel mutex"; mutex_enter(&kernel_mutex); if (srv_activity_count != old_activity_count) { mutex_exit(&kernel_mutex); goto loop; } mutex_exit(&kernel_mutex); srv_main_thread_op_info = "doing insert buffer merge"; if (srv_fast_shutdown != IB_SHUTDOWN_NORMAL && srv_shutdown_state > 0) { n_bytes_merged = 0; } else { /* This should do an amount of IO similar to the number of dirty pages that will be flushed in the call to buf_flush_batch below. Otherwise, the system favors clean pages over cleanup throughput. */ n_bytes_merged = ibuf_contract_for_n_pages(FALSE, PCT_IO(100)); } srv_main_thread_op_info = "reserving kernel mutex"; mutex_enter(&kernel_mutex); if (srv_activity_count != old_activity_count) { mutex_exit(&kernel_mutex); goto loop; } mutex_exit(&kernel_mutex); flush_loop: srv_main_thread_op_info = "flushing buffer pool pages"; srv_main_flush_loops++; if (srv_fast_shutdown != IB_SHUTDOWN_NO_BUFPOOL_FLUSH) { n_pages_flushed = buf_flush_batch(BUF_FLUSH_LIST, PCT_IO(100), IB_UINT64_T_MAX); } else { /* In the fastest shutdown we do not flush the buffer pool to data files: we set n_pages_flushed to 0 artificially. */ n_pages_flushed = 0; } srv_main_thread_op_info = "reserving kernel mutex"; mutex_enter(&kernel_mutex); if (srv_activity_count != old_activity_count) { mutex_exit(&kernel_mutex); goto loop; } mutex_exit(&kernel_mutex); srv_main_thread_op_info = "waiting for buffer pool flush to end"; buf_flush_wait_batch_end(BUF_FLUSH_LIST); /* Flush logs if needed */ srv_sync_log_buffer_in_background(); srv_main_thread_op_info = "making checkpoint"; log_checkpoint(TRUE, FALSE); if (buf_get_modified_ratio_pct() > srv_max_buf_pool_modified_pct) { /* Try to keep the number of modified pages in the buffer pool under the limit wished by the user */ goto flush_loop; } srv_main_thread_op_info = "reserving kernel mutex"; mutex_enter(&kernel_mutex); if (srv_activity_count != old_activity_count) { mutex_exit(&kernel_mutex); goto loop; } mutex_exit(&kernel_mutex); /* srv_main_thread_op_info = "archiving log (if log archive is on)"; log_archive_do(FALSE, &n_bytes_archived); */ n_bytes_archived = 0; /* Keep looping in the background loop if still work to do */ if (srv_fast_shutdown != IB_SHUTDOWN_NORMAL && srv_shutdown_state > 0) { if (n_tables_to_drop + n_pages_flushed + n_bytes_archived != 0) { /* If we are doing a fast shutdown (= the default) we do not do purge or insert buffer merge. But we flush the buffer pool completely to disk. In a 'very fast' shutdown we do not flush the buffer pool to data files: we have set n_pages_flushed to 0 artificially. */ goto background_loop; } } else if (n_tables_to_drop + n_pages_purged + n_bytes_merged + n_pages_flushed + n_bytes_archived != 0) { /* In a 'slow' shutdown we run purge and the insert buffer merge to completion */ goto background_loop; } /* There is no work for background operations either: suspend master thread to wait for more server activity */ suspend_thread: srv_main_thread_op_info = "suspending"; mutex_enter(&kernel_mutex); if (ddl_get_background_drop_list_len_low() > 0) { mutex_exit(&kernel_mutex); goto loop; } event = srv_suspend_thread(); mutex_exit(&kernel_mutex); /* DO NOT CHANGE THIS STRING. innobase_start_or_create() waits for database activity to die down when converting < 4.1.x databases, and relies on this string being exactly as it is. InnoDB manual also mentions this string in several places. */ srv_main_thread_op_info = "waiting for server activity"; os_event_wait(event); if (srv_shutdown_state == SRV_SHUTDOWN_EXIT_THREADS) { /* This is only extra safety, the thread should exit already when the event wait ends */ os_thread_exit(NULL); } /* When there is user activity, InnoDB will set the event and the main thread goes back to loop. */ goto loop; OS_THREAD_DUMMY_RETURN; /* Not reached, avoid compiler warning */ } /**********************************************************************//** Enqueues a task to server task queue and releases a worker thread, if there is a suspended one. */ UNIV_INTERN void srv_que_task_enqueue_low( /*=====================*/ que_thr_t* thr) /*!< in: query thread */ { ut_ad(thr); ut_ad(mutex_own(&kernel_mutex)); UT_LIST_ADD_LAST(queue, srv_sys->tasks, thr); srv_release_threads(SRV_WORKER, 1); } void srv_panic(int panic_ib_error, char* fmt, ...) { va_list ap; srv_panic_status = panic_ib_error; va_start(ap, fmt); if (ib_panic) { ib_panic(ib_panic_data, panic_ib_error, fmt, ap); return; } else { ib_logger(ib_stream, "Database forced shutdown! " "(ib_err %d)", panic_ib_error); ib_logger(ib_stream, fmt, ap); exit(-1); } va_end(ap); } haildb-2.3.2/srv/srv0start.c0000644000175000017500000015463711513177357016611 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2010, Innobase Oy. All Rights Reserved. Copyright (c) 2008, Google Inc. Copyright (c) 2009, Percona Inc. Portions of this file contain modifications contributed and copyrighted by Google, Inc. Those modifications are gratefully acknowledged and are described briefly in the InnoDB documentation. The contributions by Google are incorporated with their permission, and subject to the conditions contained in the file COPYING.Google. Portions of this file contain modifications contributed and copyrighted by Percona Inc.. Those modifications are gratefully acknowledged and are described briefly in the InnoDB documentation. The contributions by Percona Inc. are incorporated with their permission, and subject to the conditions contained in the file COPYING.Percona. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /********************************************************************//** @file srv/srv0start.c Starts the InnoDB database server Created 2/16/1996 Heikki Tuuri *************************************************************************/ #include "ut0mem.h" #include "mem0mem.h" #include "data0data.h" #include "data0type.h" #include "dict0dict.h" #include "buf0buf.h" #include "os0file.h" #include "os0thread.h" #include "fil0fil.h" #include "fsp0fsp.h" #include "rem0rec.h" #include "mtr0mtr.h" #include "log0log.h" #include "log0recv.h" #include "page0page.h" #include "page0cur.h" #include "trx0trx.h" #include "trx0sys.h" #include "btr0btr.h" #include "btr0cur.h" #include "rem0rec.h" #include "ibuf0ibuf.h" #include "srv0start.h" #include "srv0srv.h" #ifndef UNIV_HOTBACKUP # include "os0proc.h" # include "sync0sync.h" # include "buf0flu.h" # include "buf0rea.h" # include "dict0boot.h" # include "dict0load.h" # include "que0que.h" # include "usr0sess.h" # include "lock0lock.h" # include "trx0roll.h" # include "trx0purge.h" # include "lock0lock.h" # include "pars0pars.h" # include "btr0sea.h" # include "rem0cmp.h" # include "dict0crea.h" # include "row0ins.h" # include "row0sel.h" # include "row0upd.h" # include "row0row.h" # include "btr0pcur.h" # include "thr0loc.h" # include "os0sync.h" /* for INNODB_RW_LOCKS_USE_ATOMICS */ #ifdef HAVE_ZIP # include "zlib.h" /* for ZLIB_VERSION */ #endif /* HAVE_ZIP */ #ifdef HAVE_UNISTD_H #include #endif #include /* Log sequence number immediately after startup */ UNIV_INTERN ib_uint64_t srv_start_lsn; /** Log sequence number at shutdown */ UNIV_INTERN ib_uint64_t srv_shutdown_lsn; #ifdef HAVE_DARWIN_THREADS # include /** TRUE if the F_FULLFSYNC option is available */ UNIV_INTERN ibool srv_have_fullfsync = FALSE; #endif /** TRUE if a raw partition is in use */ UNIV_INTERN ibool srv_start_raw_disk_in_use = FALSE; /** TRUE if the server is being started, before rolling back any incomplete transactions */ UNIV_INTERN ibool srv_startup_is_before_trx_rollback_phase = FALSE; /** TRUE if the server is being started */ UNIV_INTERN ibool srv_is_being_started = FALSE; /** TRUE if the server was successfully started */ UNIV_INTERN ibool srv_was_started = FALSE; /** TRUE if innobase_start_or_create_for_mysql() has been called */ static ibool srv_start_has_been_called = FALSE; /** At a shutdown this value climbs from SRV_SHUTDOWN_NONE to SRV_SHUTDOWN_CLEANUP and then to SRV_SHUTDOWN_LAST_PHASE, and so on */ UNIV_INTERN enum srv_shutdown_state srv_shutdown_state = SRV_SHUTDOWN_NONE; /** Files comprising the system tablespace */ static os_file_t files[1000]; /** Mutex protecting the ios count */ static mutex_t ios_mutex; /** Count of I/O operations in io_handler_thread() */ static ulint ios; /** io_handler_thread parameters for thread identification */ static ulint n[SRV_MAX_N_IO_THREADS + 6]; /** io_handler_thread identifiers */ static os_thread_id_t thread_ids[SRV_MAX_N_IO_THREADS + 6]; /* The value passed to srv_parse_data_file_paths_and_sizes() is copied to this variable. Since the function does a destructive read. */ static char* data_path_buf; /* The value passed to srv_parse_log_group_home_dirs() is copied to this variable. Since the function does a destructive read. */ static char* log_path_buf; /* We use this mutex to test the return value of pthread_mutex_trylock on successful locking. HP-UX does NOT return 0, though Linux et al do. */ static os_fast_mutex_t srv_os_test_mutex; /** The system data file names */ static char** srv_data_file_names = NULL; /** The system log file names */ static char** srv_log_group_home_dirs = NULL; #endif /* !UNIV_HOTBACKUP */ /****************************************************************//** All threads end up waiting for certain events. Put those events to the signaled state. Then the threads will exit themselves in os_thread_event_wait(). @return TRUE if all threads exited. */ static ibool srv_threads_shutdown(void); /*======================*/ /** */ #define SRV_N_PENDING_IOS_PER_THREAD OS_AIO_N_PENDING_IOS_PER_THREAD #define SRV_MAX_N_PENDING_SYNC_IOS 100 /*********************************************************************//** Convert a numeric string that optionally ends in G or M, to a number containing megabytes. @return next character in string */ static char* srv_parse_megabytes( /*================*/ char* str, /*!< in: string containing a quantity in bytes */ ulint* megs) /*!< out: the number in megabytes */ { char* endp; ulint size; size = strtoul(str, &endp, 10); str = endp; switch (*str) { case 'G': case 'g': size *= 1024; /* fall through */ case 'M': case 'm': str++; break; default: size /= 1024 * 1024; break; } *megs = size; return(str); } /*********************************************************************//** Adds a slash or a backslash to the end of a string if it is missing and the string is not empty. @return string which has the separator if the string is not empty */ static char* srv_add_path_separator_if_needed( /*=============================*/ char* str) /*!< in: null-terminated character string */ { char* out_str; ulint len = ut_strlen(str); out_str = malloc(len + 2); ut_strcpy(out_str, str); if (len > 0 && out_str[len - 1] != SRV_PATH_SEPARATOR) { out_str[len] = SRV_PATH_SEPARATOR; out_str[len + 1] = 0; } return(out_str); } /*********************************************************************//** Reads the data files and their sizes from a character string. @return TRUE if ok, FALSE on parse error */ UNIV_INTERN ibool srv_parse_data_file_paths_and_sizes( /*================================*/ const char* usr_str)/*!< in/out: the data file path string */ { char* str; char* path; ulint size; char* input_str; ulint i = 0; if (data_path_buf != NULL) { free(data_path_buf); data_path_buf = NULL; } data_path_buf = malloc(ut_strlen(usr_str) + 1); ut_strcpy(data_path_buf, usr_str); str = data_path_buf; srv_auto_extend_last_data_file = FALSE; srv_last_file_size_max = 0; if (srv_data_file_names != NULL) { free(srv_data_file_names); srv_data_file_names = NULL; } if (srv_data_file_sizes != NULL) { free(srv_data_file_sizes); srv_data_file_sizes = NULL; } if (srv_data_file_is_raw_partition != NULL) { free(srv_data_file_is_raw_partition); srv_data_file_is_raw_partition = NULL; } input_str = str; /* First calculate the number of data files and check syntax: path:size[M | G];path:size[M | G]... . Note that a Windows path may contain a drive name and a ':'. */ while (*str != '\0') { path = str; while ((*str != ':' && *str != '\0') || (*str == ':' && (*(str + 1) == '\\' || *(str + 1) == '/' || *(str + 1) == ':'))) { str++; } if (*str == '\0') { return(FALSE); } str++; str = srv_parse_megabytes(str, &size); if (0 == strncmp(str, ":autoextend", (sizeof ":autoextend") - 1)) { str += (sizeof ":autoextend") - 1; if (0 == strncmp(str, ":max:", (sizeof ":max:") - 1)) { str += (sizeof ":max:") - 1; str = srv_parse_megabytes(str, &size); } if (*str != '\0') { return(FALSE); } } if (strlen(str) >= 6 && *str == 'n' && *(str + 1) == 'e' && *(str + 2) == 'w') { str += 3; } if (*str == 'r' && *(str + 1) == 'a' && *(str + 2) == 'w') { str += 3; } if (size == 0) { return(FALSE); } i++; if (*str == ';') { str++; } else if (*str != '\0') { return(FALSE); } } if (i == 0) { /* If data_file_path was defined it must contain at least one data file definition */ return(FALSE); } ut_a(srv_data_file_names == NULL); srv_data_file_names = malloc(i * sizeof *srv_data_file_names); ut_a(srv_data_file_sizes == NULL); srv_data_file_sizes = malloc(i * sizeof *srv_data_file_sizes); ut_a(srv_data_file_is_raw_partition == NULL); srv_data_file_is_raw_partition = malloc( i * sizeof *srv_data_file_is_raw_partition); srv_n_data_files = i; /* Then store the actual values to our arrays */ str = input_str; i = 0; while (*str != '\0') { path = str; /* Note that we must step over the ':' in a Windows path; a Windows path normally looks like C:\ibdata\ibdata1:1G, but a Windows raw partition may have a specification like \\.\C::1Gnewraw or \\.\PHYSICALDRIVE2:1Gnewraw */ while ((*str != ':' && *str != '\0') || (*str == ':' && (*(str + 1) == '\\' || *(str + 1) == '/' || *(str + 1) == ':'))) { str++; } if (*str == ':') { /* Make path a null-terminated string */ *str = '\0'; str++; } str = srv_parse_megabytes(str, &size); srv_data_file_names[i] = path; srv_data_file_sizes[i] = size; if (0 == strncmp(str, ":autoextend", (sizeof ":autoextend") - 1)) { srv_auto_extend_last_data_file = TRUE; str += (sizeof ":autoextend") - 1; if (0 == strncmp(str, ":max:", (sizeof ":max:") - 1)) { str += (sizeof ":max:") - 1; str = srv_parse_megabytes( str, &srv_last_file_size_max); } if (*str != '\0') { return(FALSE); } } (srv_data_file_is_raw_partition)[i] = 0; if (strlen(str) >= 6 && *str == 'n' && *(str + 1) == 'e' && *(str + 2) == 'w') { str += 3; (srv_data_file_is_raw_partition)[i] = SRV_NEW_RAW; } if (*str == 'r' && *(str + 1) == 'a' && *(str + 2) == 'w') { str += 3; if ((srv_data_file_is_raw_partition)[i] == 0) { (srv_data_file_is_raw_partition)[i] = SRV_OLD_RAW; } } i++; if (*str == ';') { str++; } } return(TRUE); } /*********************************************************************//** Reads log group home directories from a character string. @return TRUE if ok, FALSE on parse error */ UNIV_INTERN ibool srv_parse_log_group_home_dirs( /*==========================*/ const char* usr_str)/*!< in: character string */ { ulint i; char* str; char* path; int n_bytes; char* input_str; if (log_path_buf != NULL) { free(log_path_buf); log_path_buf = NULL; } log_path_buf = malloc(ut_strlen(usr_str) + 1); ut_strcpy(log_path_buf, usr_str); str = log_path_buf; if (srv_log_group_home_dirs != NULL) { for (i = 0; srv_log_group_home_dirs[i] != NULL; ++i) { free(srv_log_group_home_dirs[i]); srv_log_group_home_dirs[i] = NULL; } free(srv_log_group_home_dirs); srv_log_group_home_dirs = NULL; } i = 0; input_str = str; /* First calculate the number of directories and check syntax: path;path;... */ while (*str != '\0') { path = str; while (*str != ';' && *str != '\0') { str++; } i++; if (*str == ';') { str++; } else if (*str != '\0') { return(FALSE); } } if (i != 1) { /* If log_group_home_dir was defined it must contain exactly one path definition. */ return(FALSE); } /* Add sentinel element to the array. */ n_bytes = (i + 1) * sizeof(*srv_log_group_home_dirs); srv_log_group_home_dirs = malloc(n_bytes); memset(srv_log_group_home_dirs, 0x0, n_bytes); /* Then store the actual values to our array */ str = input_str; i = 0; while (*str != '\0') { path = str; while (*str != ';' && *str != '\0') { str++; } if (*str == ';') { *str = '\0'; str++; } srv_normalize_path_for_win(path); /* Note that this memory is malloc() and so must be freed. */ srv_log_group_home_dirs[i] = srv_add_path_separator_if_needed(path); i++; } /* We rely on this sentinel value during free. */ ut_a(i > 0); ut_a(srv_log_group_home_dirs[i] == NULL); return(TRUE); } /*********************************************************************//** Frees the memory allocated by srv_parse_data_file_paths_and_sizes() and srv_parse_log_group_home_dirs(). */ UNIV_INTERN void srv_free_paths_and_sizes(void) /*==========================*/ { if (srv_data_file_names != NULL) { free(srv_data_file_names); srv_data_file_names = NULL; } if (srv_data_file_sizes != NULL) { free(srv_data_file_sizes); srv_data_file_sizes = NULL; } if (srv_data_file_is_raw_partition != NULL) { free(srv_data_file_is_raw_partition); srv_data_file_is_raw_partition = NULL; } if (srv_log_group_home_dirs != NULL) { ulint i; for (i = 0; srv_log_group_home_dirs[i] != NULL; ++i) { free(srv_log_group_home_dirs[i]); srv_log_group_home_dirs[i] = NULL; } free(srv_log_group_home_dirs); srv_log_group_home_dirs = NULL; } if (data_path_buf != NULL) { free(data_path_buf); data_path_buf = NULL; } if (log_path_buf != NULL) { free(log_path_buf); log_path_buf = NULL; } } #ifndef UNIV_HOTBACKUP /********************************************************************//** I/o-handler thread function. @return OS_THREAD_DUMMY_RETURN */ static os_thread_ret_t io_handler_thread( /*==============*/ void* arg) /*!< in: pointer to the number of the segment in the aio array */ { ulint segment; ulint i; segment = *((ulint*)arg); #ifdef UNIV_DEBUG_THREAD_CREATION ib_logger(ib_stream, "Io handler thread %lu starts, id %lu\n", segment, os_thread_pf(os_thread_get_curr_id())); #endif for (i = 0;; i++) { fil_aio_wait(segment); mutex_enter(&ios_mutex); ios++; mutex_exit(&ios_mutex); } thr_local_free(os_thread_get_curr_id()); /* We count the number of threads in os_thread_exit(). A created thread should always use that to exit and not use return() to exit. The thread actually never comes here because it is exited in an os_event_wait(). */ os_thread_exit(NULL); OS_THREAD_DUMMY_RETURN; } #endif /* !UNIV_HOTBACKUP */ /*********************************************************************//** Normalizes a directory path for Windows: converts slashes to backslashes. */ UNIV_INTERN void srv_normalize_path_for_win( /*=======================*/ char* str __attribute__((unused))) /*!< in/out: null-terminated character string */ { #ifdef __WIN__ for (; *str; str++) { if (*str == '/') { *str = '\\'; } } #endif } #ifndef UNIV_HOTBACKUP /*********************************************************************//** Calculates the low 32 bits when a file size which is given as a number database pages is converted to the number of bytes. @return low 32 bytes of file size when expressed in bytes */ static ulint srv_calc_low32( /*===========*/ ulint file_size) /*!< in: file size in database pages */ { return(0xFFFFFFFFUL & (file_size << UNIV_PAGE_SIZE_SHIFT)); } /*********************************************************************//** Calculates the high 32 bits when a file size which is given as a number database pages is converted to the number of bytes. @return high 32 bytes of file size when expressed in bytes */ static ulint srv_calc_high32( /*============*/ ulint file_size) /*!< in: file size in database pages */ { return(file_size >> (32 - UNIV_PAGE_SIZE_SHIFT)); } /*********************************************************************//** Creates or opens the log files and closes them. @return DB_SUCCESS or error code */ static ulint open_or_create_log_file( /*====================*/ ibool create_new_db, /*!< in: TRUE if we should create a new database */ ibool* log_file_created, /*!< out: TRUE if new log file created */ ibool log_file_has_been_opened,/*!< in: TRUE if a log file has been opened before: then it is an error to try to create another log file */ ulint k, /*!< in: log group number */ ulint i) /*!< in: log file number in group */ { ibool ret; ulint size; ulint size_high; char name[10000]; UT_NOT_USED(create_new_db); *log_file_created = FALSE; ut_a(ut_strlen(srv_log_group_home_dirs[k]) < (sizeof name) - 10 - sizeof "ib_logfile"); ut_snprintf(name, sizeof(name), "%s%s%lu", srv_log_group_home_dirs[k], "ib_logfile", (ulong) i); files[i] = os_file_create(name, OS_FILE_CREATE, OS_FILE_NORMAL, OS_LOG_FILE, &ret); if (ret == FALSE) { if (os_file_get_last_error(FALSE) != OS_FILE_ALREADY_EXISTS #ifdef UNIV_AIX /* AIX 5.1 after security patch ML7 may have errno set to 0 here, which causes our function to return 100; work around that AIX problem */ && os_file_get_last_error(FALSE) != 100 #endif ) { ib_logger(ib_stream, "InnoDB: Error in creating" " or opening %s\n", name); return(DB_ERROR); } files[i] = os_file_create(name, OS_FILE_OPEN, OS_FILE_AIO, OS_LOG_FILE, &ret); if (!ret) { ib_logger(ib_stream, "InnoDB: Error in opening %s\n", name); return(DB_ERROR); } ret = os_file_get_size(files[i], &size, &size_high); ut_a(ret); if (size != srv_calc_low32(srv_log_file_size) || size_high != srv_calc_high32(srv_log_file_size)) { ib_logger(ib_stream, "InnoDB: Error: log file %s is" " of different size %lu %lu bytes\n" "InnoDB: than the configured %lu %lu bytes!\n", name, (ulong) size_high, (ulong) size, (ulong) srv_calc_high32(srv_log_file_size), (ulong) srv_calc_low32(srv_log_file_size)); return(DB_ERROR); } } else { *log_file_created = TRUE; ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Log file %s did not exist:" " new to be created\n", name); if (log_file_has_been_opened) { return(DB_ERROR); } ib_logger(ib_stream, "InnoDB: Setting log file %s size to %lu MB\n", name, (ulong) srv_log_file_size >> (20 - UNIV_PAGE_SIZE_SHIFT)); ib_logger(ib_stream, "InnoDB: Database physically writes the file" " full: wait...\n"); ret = os_file_set_size(name, files[i], srv_calc_low32(srv_log_file_size), srv_calc_high32(srv_log_file_size)); if (!ret) { ib_logger(ib_stream, "InnoDB: Error in creating %s:" " probably out of disk space\n", name); return(DB_ERROR); } } ret = os_file_close(files[i]); ut_a(ret); if (i == 0) { /* Create in memory the file space object which is for this log group */ fil_space_create(name, 2 * k + SRV_LOG_SPACE_FIRST_ID, 0, FIL_LOG); } ut_a(fil_validate()); fil_node_create(name, srv_log_file_size, 2 * k + SRV_LOG_SPACE_FIRST_ID, FALSE); #ifdef UNIV_LOG_ARCHIVE /* If this is the first log group, create the file space object for archived logs. */ if (k == 0 && i == 0) { // FIXME: ARCHIVE: Where is this defined ? ulint arch_space_id; arch_space_id = 2 * k + 1 + SRV_LOG_SPACE_FIRST_ID; fil_space_create("arch_log_space", arch_space_id, 0, FIL_LOG); } else { // FIXME: ARCHIVE: Where is this defined ? //arch_space_id = ULINT_UNDEFINED; } #endif /* UNIV_LOG_ARCHIVE */ if (i == 0) { log_group_init(k, srv_n_log_files, srv_log_file_size * UNIV_PAGE_SIZE, 2 * k + SRV_LOG_SPACE_FIRST_ID, SRV_LOG_SPACE_FIRST_ID + 1); /* dummy arch space id */ } return(DB_SUCCESS); } /*********************************************************************//** Creates or opens database data files and closes them. @return DB_SUCCESS or error code */ static ulint open_or_create_data_files( /*======================*/ ibool* create_new_db, /*!< out: TRUE if new database should be created */ #ifdef UNIV_LOG_ARCHIVE ulint* min_arch_log_no,/*!< out: min of archived log numbers in data files */ ulint* max_arch_log_no,/*!< out: max of archived log numbers in data files */ #endif /* UNIV_LOG_ARCHIVE */ ib_uint64_t* min_flushed_lsn,/*!< out: min of flushed lsn values in data files */ ib_uint64_t* max_flushed_lsn,/*!< out: max of flushed lsn values in data files */ ulint* sum_of_new_sizes)/*!< out: sum of sizes of the new files added */ { ibool ret; ulint i; ibool one_opened = FALSE; ibool one_created = FALSE; ulint size; ulint size_high; ulint rounded_size_pages; char name[10000]; char home[10000]; if (srv_n_data_files >= 1000) { ib_logger(ib_stream, "InnoDB: can only have < 1000 data files\n" "InnoDB: you have defined %lu\n", (ulong) srv_n_data_files); return(DB_ERROR); } *sum_of_new_sizes = 0; *create_new_db = FALSE; /* Copy the path because we want to normalize it. */ ut_strcpy(home, srv_data_home); srv_normalize_path_for_win(home); /* We require that the user have a trailing '/' when setting the srv_data_home variable. */ for (i = 0; i < srv_n_data_files; i++) { ibool is_absolute = FALSE; const char* ptr = srv_data_file_names[i]; #ifdef __WIN__ /* We are not using isalpha() here because of locale dependent issues. */ if (((*ptr >= 'a' && *ptr <= 'z') || (*ptr >= 'A' && *ptr <= 'Z') && *(ptr + 1) == ':') || *ptr == '\\' || *ptr == '/') { is_absolute = TRUE; } #else /* We assume Unix file paths here. */ is_absolute = (*ptr == '/'); #endif srv_normalize_path_for_win(srv_data_file_names[i]); /* If the name is not absolute then the system files are created relative to home. */ if (!is_absolute) { ut_a(ut_strlen(home) + ut_strlen(srv_data_file_names[i]) < (sizeof name) - 1); ut_snprintf(name, sizeof(name), "%s%s", home, srv_data_file_names[i]); } else { ut_a(ut_strlen(home) < (sizeof name) - 1); ut_snprintf(name, sizeof(name), "%s", srv_data_file_names[i]); } if (srv_data_file_is_raw_partition[i] == 0) { /* First we try to create the file: if it already exists, ret will get value FALSE */ files[i] = os_file_create(name, OS_FILE_CREATE, OS_FILE_NORMAL, OS_DATA_FILE, &ret); if (ret == FALSE && os_file_get_last_error(FALSE) != OS_FILE_ALREADY_EXISTS #ifdef UNIV_AIX /* AIX 5.1 after security patch ML7 may have errno set to 0 here, which causes our function to return 100; work around that AIX problem */ && os_file_get_last_error(FALSE) != 100 #endif ) { ib_logger(ib_stream, "InnoDB: Error in creating" " or opening %s\n", name); return(DB_ERROR); } } else if (srv_data_file_is_raw_partition[i] == SRV_NEW_RAW) { /* The partition is opened, not created; then it is written over */ srv_start_raw_disk_in_use = TRUE; srv_created_new_raw = TRUE; files[i] = os_file_create(name, OS_FILE_OPEN_RAW, OS_FILE_NORMAL, OS_DATA_FILE, &ret); if (!ret) { ib_logger(ib_stream, "InnoDB: Error in opening %s\n", name); return(DB_ERROR); } } else if (srv_data_file_is_raw_partition[i] == SRV_OLD_RAW) { srv_start_raw_disk_in_use = TRUE; ret = FALSE; } else { ut_a(0); } if (ret == FALSE) { /* We open the data file */ if (one_created) { ib_logger(ib_stream, "InnoDB: Error: data files can only" " be added at the end\n"); ib_logger(ib_stream, "InnoDB: of a tablespace, but" " data file %s existed beforehand.\n", name); return(DB_ERROR); } if (srv_data_file_is_raw_partition[i] == SRV_OLD_RAW) { files[i] = os_file_create( name, OS_FILE_OPEN_RAW, OS_FILE_NORMAL, OS_DATA_FILE, &ret); } else if (i == 0) { files[i] = os_file_create( name, OS_FILE_OPEN_RETRY, OS_FILE_NORMAL, OS_DATA_FILE, &ret); } else { files[i] = os_file_create( name, OS_FILE_OPEN, OS_FILE_NORMAL, OS_DATA_FILE, &ret); } if (!ret) { ib_logger(ib_stream, "InnoDB: Error in opening %s\n", name); os_file_get_last_error(TRUE); return(DB_ERROR); } if (srv_data_file_is_raw_partition[i] == SRV_OLD_RAW) { goto skip_size_check; } ret = os_file_get_size(files[i], &size, &size_high); ut_a(ret); /* Round size downward to megabytes */ rounded_size_pages = (size / (1024 * 1024) + 4096 * size_high) << (20 - UNIV_PAGE_SIZE_SHIFT); if (i == srv_n_data_files - 1 && srv_auto_extend_last_data_file) { if (srv_data_file_sizes[i] > rounded_size_pages || (srv_last_file_size_max > 0 && srv_last_file_size_max < rounded_size_pages)) { ib_logger(ib_stream, "InnoDB: Error: auto-extending" " data file %s is" " of a different size\n" "InnoDB: %lu pages (rounded" " down to MB) than the " "configured\n" "InnoDB: initial %lu pages," " max %lu (relevant if" " non-zero) pages!\n", name, (ulong) rounded_size_pages, (ulong) srv_data_file_sizes[i], (ulong) srv_last_file_size_max); return(DB_ERROR); } srv_data_file_sizes[i] = rounded_size_pages; } if (rounded_size_pages != srv_data_file_sizes[i]) { ib_logger(ib_stream, "InnoDB: Error: data file %s" " is of a different size\n" "InnoDB: %lu pages" " (rounded down to MB)\n" "InnoDB: than the configured " "%lu pages!\n", name, (ulong) rounded_size_pages, (ulong) srv_data_file_sizes[i]); return(DB_ERROR); } skip_size_check: fil_read_flushed_lsn_and_arch_log_no( files[i], one_opened, #ifdef UNIV_LOG_ARCHIVE min_arch_log_no, max_arch_log_no, #endif /* UNIV_LOG_ARCHIVE */ min_flushed_lsn, max_flushed_lsn); one_opened = TRUE; } else { /* We created the data file and now write it full of zeros */ one_created = TRUE; if (i > 0) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Data file %s did not" " exist: new to be created\n", name); } else { ib_logger(ib_stream, "InnoDB: The first specified" " data file %s did not exist:\n" "InnoDB: a new database" " to be created!\n", name); *create_new_db = TRUE; } ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Setting file %s size to %lu MB\n", name, (ulong) (srv_data_file_sizes[i] >> (20 - UNIV_PAGE_SIZE_SHIFT))); ib_logger(ib_stream, "InnoDB: Database physically writes the" " file full: wait...\n"); ret = os_file_set_size( name, files[i], srv_calc_low32(srv_data_file_sizes[i]), srv_calc_high32(srv_data_file_sizes[i])); if (!ret) { ib_logger(ib_stream, "InnoDB: Error in creating %s:" " probably out of disk space\n", name); return(DB_ERROR); } *sum_of_new_sizes = *sum_of_new_sizes + srv_data_file_sizes[i]; } ret = os_file_close(files[i]); ut_a(ret); if (i == 0) { fil_space_create(name, 0, 0, FIL_TABLESPACE); } ut_a(fil_validate()); fil_node_create(name, srv_data_file_sizes[i], 0, srv_data_file_is_raw_partition[i] != 0); } ios = 0; mutex_create(&ios_mutex, SYNC_NO_ORDER_CHECK); return(DB_SUCCESS); } /****************************************************************//* Abort the startup process and shutdown the minimum set of sub-systems required to create files and. */ static void srv_startup_abort( /*==============*/ enum db_err err) /* in: Current error code */ { /* This is currently required to inform the master thread only. Once we have contexts we can get rid of this global. */ srv_fast_shutdown = IB_SHUTDOWN_NORMAL; /* For fatal errors we want to avoid writing to the data files. */ if (err != DB_FATAL) { logs_empty_and_mark_files_at_shutdown( srv_force_recovery, srv_fast_shutdown); fil_close_all_files(); } srv_threads_shutdown(); log_shutdown(); lock_sys_close(); buf_close(); fil_close(); os_aio_close(); log_mem_free(); buf_mem_free(); } /****************************************************************//** Starts InnoDB and creates a new database if database files are not found and the user wants. @return DB_SUCCESS or error code */ UNIV_INTERN ib_err_t innobase_start_or_create(void) /*===========================*/ { buf_pool_t* ret; ibool create_new_db; ibool log_file_created; ibool log_created = FALSE; ibool log_opened = FALSE; ib_uint64_t min_flushed_lsn; ib_uint64_t max_flushed_lsn; #ifdef UNIV_LOG_ARCHIVE ulint min_arch_log_no; ulint max_arch_log_no; #endif /* UNIV_LOG_ARCHIVE */ ulint sum_of_new_sizes; ulint sum_of_data_file_sizes; ulint tablespace_size_in_header; ulint err; ulint i; ulint io_limit; mtr_t mtr; ibool srv_file_per_table_original_value; srv_file_per_table_original_value = srv_file_per_table; #ifdef HAVE_DARWIN_THREADS # ifdef F_FULLFSYNC /* This executable has been compiled on Mac OS X 10.3 or later. Assume that F_FULLFSYNC is available at run-time. */ srv_have_fullfsync = TRUE; # else /* F_FULLFSYNC */ /* This executable has been compiled on Mac OS X 10.2 or earlier. Determine if the executable is running on Mac OS X 10.3 or later. */ struct utsname utsname; if (uname(&utsname)) { ib_logger(ib_stream, "InnoDB: cannot determine Mac OS X version!\n"); } else { srv_have_fullfsync = strcmp(utsname.release, "7.") >= 0; } if (!srv_have_fullfsync) { ib_logger(ib_stream, "InnoDB: On Mac OS X, fsync() may be" " broken on internal drives,\n" "InnoDB: making transactions unsafe!\n"); } # endif /* F_FULLFSYNC */ #endif /* HAVE_DARWIN_THREADS */ if (sizeof(ulint) != sizeof(void*)) { ib_logger(ib_stream, "InnoDB: Error: size of InnoDB's ulint is %lu," " but size of void* is %lu.\n" "InnoDB: The sizes should be the same" " so that on a 64-bit platform you can\n" "InnoDB: allocate more than 4 GB of memory.", (ulong)sizeof(ulint), (ulong)sizeof(void*)); } /* System tables are created in tablespace 0. Thus, we must temporarily clear srv_file_per_table. This is ok, because the server will not accept connections (which could modify file_per_table) until this function has returned. */ srv_file_per_table = FALSE; #ifdef UNIV_DEBUG ib_logger(ib_stream, "InnoDB: !!!!!!!! UNIV_DEBUG switched on !!!!!!!!!\n"); #endif #ifdef UNIV_IBUF_DEBUG ib_logger(ib_stream, "InnoDB: !!!!!!!! UNIV_IBUF_DEBUG switched on !!!!!!!!!\n" # ifdef UNIV_IBUF_COUNT_DEBUG "InnoDB: !!!!!!!! UNIV_IBUF_COUNT_DEBUG switched on !!!!!!!!!\n" "InnoDB: Crash recovery will fail with UNIV_IBUF_COUNT_DEBUG\n" # endif ); #endif #ifdef UNIV_SYNC_DEBUG ib_logger(ib_stream, "InnoDB: !!!!!!!! UNIV_SYNC_DEBUG switched on !!!!!!!!!\n"); #endif #ifdef UNIV_SEARCH_DEBUG ib_logger(ib_stream, "InnoDB: !!!!!!!! UNIV_SEARCH_DEBUG switched on !!!!!!!!!\n"); #endif #ifdef UNIV_LOG_LSN_DEBUG ib_logger(ib_stream, "InnoDB: !!!!!!!! UNIV_LOG_LSN_DEBUG switched on !!!!!!!!!\n"); #endif /* UNIV_LOG_LSN_DEBUG */ #ifdef UNIV_MEM_DEBUG ib_logger(ib_stream, "InnoDB: !!!!!!!! UNIV_MEM_DEBUG switched on !!!!!!!!!\n"); #endif if (UNIV_LIKELY(srv_use_sys_malloc)) { ib_logger(ib_stream, "InnoDB: The InnoDB memory heap is disabled\n"); } ib_logger(ib_stream, "InnoDB: " IB_ATOMICS_STARTUP_MSG #ifdef HAVE_ZIP "\nInnoDB: Compressed tables use zlib " ZLIB_VERSION # ifdef UNIV_ZIP_DEBUG " with validation" # endif /* UNIV_ZIP_DEBUG */ # ifdef UNIV_ZIP_COPY " and extra copying" # endif /* UNIV_ZIP_COPY */ #endif /* HAVE_ZIP */ "\n"); /* Print an error message if someone tries to start up InnoDB a second time while it's already in state running. */ if (srv_was_started && srv_start_has_been_called) { ib_logger(ib_stream, "InnoDB: Error: startup called second time" " during the process lifetime.\n" "InnoDB: more than once during" " the process lifetime.\n"); } srv_start_has_been_called = TRUE; #ifdef UNIV_DEBUG log_do_write = TRUE; #endif /* UNIV_DEBUG */ /* yydebug = TRUE; */ srv_is_being_started = TRUE; srv_startup_is_before_trx_rollback_phase = TRUE; /* Note that the call srv_boot() also changes the values of some variables to the units used by InnoDB internally */ /* Set the maximum number of threads which can wait for a semaphore inside InnoDB: this is the 'sync wait array' size, as well as the maximum number of threads that can wait in the 'srv_conc array' for their time to enter InnoDB. */ #if defined(__NETWARE__) /* Create less event semaphores because Win 98/ME had difficulty creating 40000 event semaphores. Comment from Novell, Inc.: also, these just take a lot of memory on NetWare. */ srv_max_n_threads = 1000; #else if (srv_buf_pool_size >= 1000 * 1024 * 1024) { /* If buffer pool is less than 1000 MB, assume fewer threads. */ srv_max_n_threads = 50000; } else if (srv_buf_pool_size >= 8 * 1024 * 1024) { srv_max_n_threads = 10000; } else { srv_max_n_threads = 1000; /* saves several MB of memory, especially in 64-bit computers */ } #endif err = srv_boot(); if (err != DB_SUCCESS) { return(err); } /* file_io_threads used to be user settable, now it's just a sum of read_io_threads and write_io_threads */ srv_n_file_io_threads = 2 + srv_n_read_io_threads + srv_n_write_io_threads; ut_a(srv_n_file_io_threads <= SRV_MAX_N_IO_THREADS); /* TODO: Investigate if SRV_N_PENDING_IOS_PER_THREAD (32) limit still applies to windows. */ if (!os_aio_use_native_aio) { io_limit = 8 * SRV_N_PENDING_IOS_PER_THREAD; } else { io_limit = SRV_N_PENDING_IOS_PER_THREAD; } #ifdef UNIV_DEBUG /* We have observed deadlocks with a 5MB buffer pool but the actual lower limit could very well be a little higher. */ if (srv_buf_pool_size <= 5 * 1024 * 1024) { ib_logger(ib_stream, "InnoDB: Warning: Small buffer pool size " "(%luM), the flst_validate() debug function " "can cause a deadlock if the buffer pool fills up.\n", srv_buf_pool_size / 1024 / 1024); } #endif #ifdef UNIV_LOG_ARCHIVE if (0 != ut_strcmp(srv_log_group_home_dirs[0], srv_arch_dir)) { ib_logger(ib_stream, "InnoDB: Error: you must set the log group" " home dir same as log arch dir.\n"); return(DB_ERROR); } #endif /* UNIV_LOG_ARCHIVE */ if (srv_n_log_files * srv_log_file_size >= 262144) { ib_logger(ib_stream, "InnoDB: Error: combined size of log files" " must be < 4 GB\n"); return(DB_ERROR); } sum_of_new_sizes = 0; for (i = 0; i < srv_n_data_files; i++) { #ifndef __WIN__ if (sizeof(off_t) < 5 && srv_data_file_sizes[i] >= 262144) { ib_logger(ib_stream, "InnoDB: Error: file size must be < 4 GB" " with this binary\n" "InnoDB: and operating system combination," " in some OS's < 2 GB\n"); return(DB_ERROR); } #endif sum_of_new_sizes += srv_data_file_sizes[i]; } if (sum_of_new_sizes < 10485760 / UNIV_PAGE_SIZE) { ib_logger(ib_stream, "InnoDB: Error: tablespace size must be" " at least 10 MB\n"); return(DB_ERROR); } os_aio_init( io_limit, srv_n_read_io_threads, srv_n_write_io_threads, SRV_MAX_N_PENDING_SYNC_IOS); fil_init(srv_file_per_table ? 50000 : 5000, srv_max_n_open_files); ret = buf_pool_init(); if (ret == NULL) { /* Shutdown all sub-systems that have been initialized. */ fil_close(); os_aio_close(); ib_logger(ib_stream, "InnoDB: Fatal error: cannot allocate the memory" " for the buffer pool\n"); return(DB_ERROR); } fsp_init(); innobase_log_init(); lock_sys_create(srv_lock_table_size); /* Create i/o-handler threads: */ for (i = 0; i < srv_n_file_io_threads; i++) { n[i] = i; os_thread_create(io_handler_thread, n + i, thread_ids + i); } err = open_or_create_data_files(&create_new_db, #ifdef UNIV_LOG_ARCHIVE &min_arch_log_no, &max_arch_log_no, #endif /* UNIV_LOG_ARCHIVE */ &min_flushed_lsn, &max_flushed_lsn, &sum_of_new_sizes); if (err != DB_SUCCESS) { ib_logger(ib_stream, "InnoDB: Could not open or create data files.\n" "InnoDB: If you tried to add new data files," " and it failed here,\n" "InnoDB: you should now set data_file_path" " back\n" "InnoDB: to what it was, and remove the" " new ibdata files InnoDB created\n" "InnoDB: in this failed attempt. InnoDB only wrote" " those files full of\n" "InnoDB: zeros, but did not yet use them in any way." " But be careful: do not\n" "InnoDB: remove old data files" " which contain your precious data!\n"); srv_startup_abort(err); return(err); } #ifdef UNIV_LOG_ARCHIVE srv_normalize_path_for_win(srv_arch_dir); srv_arch_dir = srv_add_path_separator_if_needed(srv_arch_dir); #endif /* UNIV_LOG_ARCHIVE */ for (i = 0; i < srv_n_log_files; i++) { err = open_or_create_log_file( create_new_db, &log_file_created, log_opened, 0, i); if (err != DB_SUCCESS) { srv_startup_abort(err); return(err); } else if (log_file_created) { log_created = TRUE; } else { log_opened = TRUE; } if ((log_opened && create_new_db) || (log_opened && log_created)) { ib_logger(ib_stream, "InnoDB: Error: all log files must be" " created at the same time.\n" "InnoDB: All log files must be" " created also in database creation.\n" "InnoDB: If you want bigger or smaller" " log files, shut down the\n" "InnoDB: database and make sure there" " were no errors in shutdown.\n" "InnoDB: Then delete the existing log files." " Reconfigure InnoDB\n" "InnoDB: and start the database again.\n"); srv_startup_abort(DB_ERROR); return(DB_ERROR); } } /* Open all log files and data files in the system tablespace: we keep them open until database shutdown */ fil_open_log_and_system_tablespace_files(); if (log_created && !create_new_db #ifdef UNIV_LOG_ARCHIVE && !srv_archive_recovery #endif /* UNIV_LOG_ARCHIVE */ ) { if (max_flushed_lsn != min_flushed_lsn #ifdef UNIV_LOG_ARCHIVE || max_arch_log_no != min_arch_log_no #endif /* UNIV_LOG_ARCHIVE */ ) { ib_logger(ib_stream, "InnoDB: Cannot initialize created" " log files because\n" "InnoDB: data files were not in sync" " with each other\n" "InnoDB: or the data files are corrupt.\n"); srv_startup_abort(DB_ERROR); return(DB_ERROR); } if (max_flushed_lsn < (ib_uint64_t) 1000) { ib_logger(ib_stream, "InnoDB: Cannot initialize created" " log files because\n" "InnoDB: data files are corrupt," " or new data files were\n" "InnoDB: created when the database" " was started previous\n" "InnoDB: time but the database" " was not shut down\n" "InnoDB: normally after that.\n"); srv_startup_abort(DB_ERROR); return(DB_ERROR); } mutex_enter(&(log_sys->mutex)); #ifdef UNIV_LOG_ARCHIVE /* Do not + 1 arch_log_no because we do not use log archiving */ recv_reset_logs(max_flushed_lsn, max_arch_log_no, TRUE); #else recv_reset_logs(max_flushed_lsn, TRUE); #endif /* UNIV_LOG_ARCHIVE */ mutex_exit(&(log_sys->mutex)); } trx_sys_file_format_init(); if (create_new_db) { mtr_start(&mtr); fsp_header_init(0, sum_of_new_sizes, &mtr); mtr_commit(&mtr); trx_sys_create(srv_force_recovery); dict_create(); srv_startup_is_before_trx_rollback_phase = FALSE; #ifdef UNIV_LOG_ARCHIVE } else if (srv_archive_recovery) { ib_logger(ib_stream, "InnoDB: Starting archive" " recovery from a backup...\n"); err = recv_recovery_from_archive_start( min_flushed_lsn, srv_archive_recovery_limit_lsn, min_arch_log_no); if (err != DB_SUCCESS) { return(DB_ERROR); } /* Since ibuf init is in dict_boot, and ibuf is needed in any disk i/o, first call dict_boot */ dict_boot(); trx_sys_init_at_db_start(srv_force_recovery); srv_startup_is_before_trx_rollback_phase = FALSE; /* Initialize the fsp free limit global variable in the log system */ fsp_header_get_free_limit(); recv_recovery_from_archive_finish(); #endif /* UNIV_LOG_ARCHIVE */ } else { /* Check if we support the max format that is stamped on the system tablespace. Note: We are NOT allowed to make any modifications to the TRX_SYS_PAGE_NO page before recovery because this page also contains the max_trx_id etc. important system variables that are required for recovery. We need to ensure that we return the system to a state where normal recovery is guaranteed to work. We do this by invalidating the buffer cache, this will force the reread of the page and restoration to its last known consistent state, this is REQUIRED for the recovery process to work. */ err = trx_sys_file_format_max_check( srv_check_file_format_at_startup); if (err != DB_SUCCESS) { srv_startup_abort(err); return(err); } /* We always try to do a recovery, even if the database had been shut down normally: this is the normal startup path */ err = recv_recovery_from_checkpoint_start( srv_force_recovery, LOG_CHECKPOINT, IB_UINT64_T_MAX, min_flushed_lsn, max_flushed_lsn); if (err != DB_SUCCESS) { srv_startup_abort(err); return(DB_ERROR); } /* Since the insert buffer init is in dict_boot, and the insert buffer is needed in any disk i/o, first we call dict_boot(). Note that trx_sys_init_at_db_start() only needs to access space 0, and the insert buffer at this stage already works for space 0. */ dict_boot(); trx_sys_init_at_db_start(srv_force_recovery); /* Initialize the fsp free limit global variable in the log system */ fsp_header_get_free_limit(); /* recv_recovery_from_checkpoint_finish needs trx lists which are initialized in trx_sys_init_at_db_start(). */ recv_recovery_from_checkpoint_finish(srv_force_recovery); if (srv_force_recovery < IB_RECOVERY_NO_IBUF_MERGE) { /* The following call is necessary for the insert buffer to work with multiple tablespaces. We must know the mapping between space id's and .ibd file names. In a crash recovery, we check that the info in data dictionary is consistent with what we already know about space id's from the call of fil_load_single_table_tablespaces(). In a normal startup, we create the space objects for every table in the InnoDB data dictionary that has an .ibd file. We also determine the maximum tablespace id used. */ dict_check_tablespaces_and_store_max_id( recv_needed_recovery); } srv_startup_is_before_trx_rollback_phase = FALSE; recv_recovery_rollback_active(); /* It is possible that file_format tag has never been set. In this case we initialize it to minimum value. Important to note that we can do it ONLY after we have finished the recovery process so that the image of TRX_SYS_PAGE_NO is not stale. */ trx_sys_file_format_tag_init(); } if (!create_new_db && sum_of_new_sizes > 0) { /* New data file(s) were added */ mtr_start(&mtr); fsp_header_inc_size(0, sum_of_new_sizes, &mtr); mtr_commit(&mtr); /* Immediately write the log record about increased tablespace size to disk, so that it is durable even if we crash quickly */ log_buffer_flush_to_disk(); } #ifdef UNIV_LOG_ARCHIVE if (!srv_log_archive_on) { ut_a(DB_SUCCESS == log_archive_noarchivelog()); } else { // FIXME: ARCHIVE: Where is this defined ? ibool start_archive; log_acquire(); start_archive = FALSE; if (log_sys->archiving_state == LOG_ARCH_OFF) { start_archive = TRUE; } log_release(); if (start_archive) { ut_a(DB_SUCCESS == log_archive_archivelog()); } } #endif /* UNIV_LOG_ARCHIVE */ /* ib_logger(ib_stream, "Max allowed record size %lu\n", page_get_free_space_of_empty() / 2); */ /* Create the thread which watches the timeouts for lock waits */ os_thread_create(&srv_lock_timeout_thread, NULL, thread_ids + 2 + SRV_MAX_N_IO_THREADS); /* Create the thread which warns of long semaphore waits */ os_thread_create(&srv_error_monitor_thread, NULL, thread_ids + 3 + SRV_MAX_N_IO_THREADS); /* Create the thread which prints InnoDB monitor info */ os_thread_create(&srv_monitor_thread, NULL, thread_ids + 4 + SRV_MAX_N_IO_THREADS); srv_is_being_started = FALSE; if (trx_doublewrite == NULL) { /* Create the doublewrite buffer to a new tablespace */ err = trx_sys_create_doublewrite_buf(); } if (err == DB_SUCCESS) { err = dict_create_or_check_foreign_constraint_tables(); } if (err != DB_SUCCESS) { srv_startup_abort(err); return(DB_ERROR); } /* Create the master thread which does purge and other utility operations */ os_thread_create(&srv_master_thread, NULL, thread_ids + (1 + SRV_MAX_N_IO_THREADS)); #ifdef UNIV_DEBUG /* buf_debug_prints = TRUE; */ #endif /* UNIV_DEBUG */ sum_of_data_file_sizes = 0; for (i = 0; i < srv_n_data_files; i++) { sum_of_data_file_sizes += srv_data_file_sizes[i]; } tablespace_size_in_header = fsp_header_get_tablespace_size(); if (!srv_auto_extend_last_data_file && sum_of_data_file_sizes != tablespace_size_in_header) { ib_logger(ib_stream, "InnoDB: Error: tablespace size" " stored in header is %lu pages, but\n" "InnoDB: the sum of data file sizes is %lu pages\n", (ulong) tablespace_size_in_header, (ulong) sum_of_data_file_sizes); if (srv_force_recovery == IB_RECOVERY_DEFAULT && sum_of_data_file_sizes < tablespace_size_in_header) { /* This is a fatal error, the tail of a tablespace is missing */ ib_logger(ib_stream, "InnoDB: Cannot start InnoDB." " The tail of the system tablespace is\n" "InnoDB: missing. Have you set the" " data_file_path in an\n" "InnoDB: inappropriate way, removing" " ibdata files from there?\n" "InnoDB: You can set force_recovery=1" " to force\n" "InnoDB: a startup if you are trying" " to recover a badly corrupt database.\n"); srv_startup_abort(DB_ERROR); return(DB_ERROR); } } if (srv_auto_extend_last_data_file && sum_of_data_file_sizes < tablespace_size_in_header) { ib_logger(ib_stream, "InnoDB: Error: tablespace size stored in header" " is %lu pages, but\n" "InnoDB: the sum of data file sizes" " is only %lu pages\n", (ulong) tablespace_size_in_header, (ulong) sum_of_data_file_sizes); if (srv_force_recovery == IB_RECOVERY_DEFAULT) { ib_logger(ib_stream, "InnoDB: Cannot start InnoDB. The tail of" " the system tablespace is\n" "InnoDB: missing. Have you set " " data_file_path in an\n" "InnoDB: inappropriate way, removing" " ibdata files from there?\n" "InnoDB: You can set force_recovery=1" " in to force\n" "InnoDB: a startup if you are trying to" " recover a badly corrupt database.\n"); srv_startup_abort(DB_ERROR); return(DB_ERROR); } } /* Check that os_fast_mutexes work as expected */ os_fast_mutex_init(&srv_os_test_mutex); if (0 != os_fast_mutex_trylock(&srv_os_test_mutex)) { ib_logger(ib_stream, "InnoDB: Error: pthread_mutex_trylock returns" " an unexpected value on\n" "InnoDB: success! Cannot continue.\n"); srv_startup_abort(DB_ERROR); return(DB_ERROR); } os_fast_mutex_unlock(&srv_os_test_mutex); os_fast_mutex_lock(&srv_os_test_mutex); os_fast_mutex_unlock(&srv_os_test_mutex); os_fast_mutex_free(&srv_os_test_mutex); if (srv_print_verbose_log) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " HailDB %s started; " "log sequence number %llu\n", VERSION, srv_start_lsn); } if (srv_force_recovery != IB_RECOVERY_DEFAULT) { ib_logger(ib_stream, "InnoDB: !!! force_recovery is set to %lu !!!\n", (ulong) srv_force_recovery); } if (trx_doublewrite_must_reset_space_ids) { /* Actually, we did not change the undo log format between 4.0 and 4.1.1, and we would not need to run purge to completion. Note also that the purge algorithm in 4.1.1 can process the history list again even after a full purge, because our algorithm does not cut the end of the history list in all cases so that it would become empty after a full purge. That mean that we may purge 4.0 type undo log even after this phase. The insert buffer record format changed between 4.0 and 4.1.1. It is essential that the insert buffer is emptied here! */ ib_logger(ib_stream, "InnoDB: You are upgrading to an" " InnoDB version which allows multiple\n" "InnoDB: tablespaces. Wait that purge" " and insert buffer merge run to\n" "InnoDB: completion...\n"); for (;;) { os_thread_sleep(1000000); if (0 == strcmp(srv_main_thread_op_info, "waiting for server activity")) { ut_a(ibuf_is_empty()); break; } } ib_logger(ib_stream, "InnoDB: Full purge and insert buffer merge" " completed.\n"); trx_sys_mark_upgraded_to_multiple_tablespaces(); ib_logger(ib_stream, "InnoDB: You have now successfully upgraded" " to the multiple tablespaces\n" "InnoDB: format. You should NOT DOWNGRADE" " to an earlier version of\n" "InnoDB: InnoDB! But if you absolutely need to" " downgrade, check\n" "InnoDB: the InnoDB website for details\n" "InnoDB: for instructions.\n"); } if (srv_force_recovery == IB_RECOVERY_DEFAULT) { /* In the insert buffer we may have even bigger tablespace id's, because we may have dropped those tablespaces, but insert buffer merge has not had time to clean the records from the ibuf tree. */ ibuf_update_max_tablespace_id(); } srv_file_per_table = srv_file_per_table_original_value; srv_was_started = TRUE; return(DB_SUCCESS); } /****************************************************************//** Try to shutdown the InnoDB threads. @return TRUE if all threads exited. */ static ibool srv_threads_try_shutdown( /*=====================*/ os_event_t lock_timeout_thread_event) { /* Let the lock timeout thread exit */ os_event_set(lock_timeout_thread_event); /* srv error monitor thread exits automatically, no need to do anything here */ /* We wake the master thread so that it exits */ srv_wake_master_thread(); /* Exit the i/o threads */ os_aio_wake_all_threads_at_shutdown(); os_mutex_enter(os_sync_mutex); if (os_thread_count == 0) { /* All the threads have exited or are just exiting; NOTE that the threads may not have completed their exit yet. Should we use pthread_join() to make sure they have exited? Now we just sleep 0.1 seconds and hope that is enough! */ os_mutex_exit(os_sync_mutex); os_thread_sleep(100000); return(TRUE); } os_mutex_exit(os_sync_mutex); os_thread_sleep(100000); return(FALSE); } /****************************************************************//** All threads end up waiting for certain events. Put those events to the signaled state. Then the threads will exit themselves in os_thread_event_wait(). @return TRUE if all threads exited. */ static ibool srv_threads_shutdown(void) /*======================*/ { ulint i; srv_shutdown_state = SRV_SHUTDOWN_EXIT_THREADS; for (i = 0; i < 1000; i++) { if (srv_threads_try_shutdown(srv_lock_timeout_thread_event)) { return(TRUE); } } ib_logger(ib_stream, "InnoDB: Warning: %lu threads created by InnoDB" " had not exited at shutdown!\n", (ulong) os_thread_count); return(FALSE); } /****************************************************************//** Shuts down the InnoDB database. @return DB_SUCCESS or error code */ UNIV_INTERN enum db_err innobase_shutdown( /*==============*/ ib_shutdown_t shutdown) /*!< in: shutdown flag */ { #ifdef __NETWARE__ extern ibool panic_shutdown; #endif if (!srv_was_started) { if (srv_is_being_started) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Warning: shutting down" " a not properly started\n" "InnoDB: or created database!\n"); } ut_free_all_mem(); return(DB_SUCCESS); } /* This is currently required to inform the master thread only. Once we have contexts we can get rid of this global. */ srv_fast_shutdown = shutdown; /* 1. Flush the buffer pool to disk, write the current lsn to the tablespace header(s), and copy all log data to archive. The step 1 is the real InnoDB shutdown. The remaining steps 2 - ... just free data structures after the shutdown. */ if (shutdown == IB_SHUTDOWN_NO_BUFPOOL_FLUSH) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: User has requested a very fast shutdown" " without flushing " "the InnoDB buffer pool to data files." " At the next startup " "InnoDB will do a crash recovery!\n"); } #ifdef __NETWARE__ if (!panic_shutdown) #endif logs_empty_and_mark_files_at_shutdown( srv_force_recovery, shutdown); /* In a 'very fast' shutdown, we do not need to wait for these threads to die; all which counts is that we flushed the log; a 'very fast' shutdown is essentially a crash. */ if (shutdown == IB_SHUTDOWN_NO_BUFPOOL_FLUSH) { return(DB_SUCCESS); } srv_threads_shutdown(); /* This must be disabled before closing the buffer pool and closing the data dictionary. */ btr_search_disable(); ibuf_close(); log_shutdown(); lock_sys_close(); thr_local_close(); trx_sys_file_format_close(); trx_sys_close(); dict_close(); /* Must be called before buf_close(). */ buf_close(); fil_close(); os_aio_close(); srv_free(); /* 3. Free all InnoDB's own mutexes and the os_fast_mutexes inside them */ sync_close(); /* 4. Free the os_conc_mutex and all os_events and os_mutexes */ os_sync_free(); /* 5. Free all allocated memory */ pars_close(); log_mem_free(); buf_mem_free(); /* This variable should come from the user and should not be malloced by InnoDB. */ srv_data_home = NULL; #ifdef UNIV_LOG_ARCHIVE free(srv_arch_dir); srv_arch_dir = NULL; #endif /* UNIV_LOG_ARCHIVE */ /* This variable should come from the user and should not be malloced by InnoDB. */ pars_lexer_var_init(); ut_free_all_mem(); if (os_thread_count != 0 || os_event_count != 0 || os_mutex_count != 0 || os_fast_mutex_count != 0) { ib_logger(ib_stream, "InnoDB: Warning: some resources were not" " cleaned up in shutdown:\n" "InnoDB: threads %lu, events %lu," " os_mutexes %lu, os_fast_mutexes %lu\n", (ulong) os_thread_count, (ulong) os_event_count, (ulong) os_mutex_count, (ulong) os_fast_mutex_count); } if (lock_latest_err_stream) { fclose(lock_latest_err_stream); } if (srv_print_verbose_log) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Shutdown completed;" " log sequence number %llu\n", srv_shutdown_lsn); } srv_was_started = FALSE; srv_start_has_been_called = FALSE; srv_modules_var_init(); srv_var_init(); srv_data_file_names = NULL; srv_log_group_home_dirs = NULL; return(DB_SUCCESS); } #ifdef __NETWARE__ void set_panic_flag_for_netware() { extern ibool panic_shutdown; panic_shutdown = TRUE; } #endif /* __NETWARE__ */ #endif /* !UNIV_HOTBACKUP */ haildb-2.3.2/tests/0000755000175000017500000000000011513177437015004 5ustar00pcrewspcrews00000000000000haildb-2.3.2/tests/ib_mt_stress.c0000644000175000017500000006663711513177357017670 0ustar00pcrewspcrews00000000000000/*********************************************************************** Copyright (c) 2009 Innobase Oy. All rights reserved. Copyright (c) 2009 Oracle. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ************************************************************************/ /* Multi-threaded test that is equivalent of ibtest3. It roughly does the following: Create a database CREATE TABLE blobt3( A INT, D INT, B BLOB, C TEXT, PRIMARY KEY(B(10), A, D), INDEX(D), INDEX(A), INDEX(C(255), B(255)), INDEX(B(5), C(10), A)); Insert into the table. Create four type of worker threads (total threads being NUM_THREADS) 1) Insert worker thread that does the following: INSERT INTO blobt3 VALUES( RANDOM(INT), 5, RANDOM(BLOB), RANDOM(TEXT)) Insert workers insert rows in batches and then commit or rollback the transaction based on rollback_percent. 2) Update worker thread that does the following: UPDATE blobt3 SET B = WHERE A = ; Update workers update rows in batches and then commit or rollback the transaction based on rollback_percent. 3) Delete workers (no-op for now) 4) Select workers (no-op for now) The test will create all the relevant sub-directories in the current working directory. */ #include #include #include #include #include #include #include #include "test0aux.h" #ifdef UNIV_DEBUG_VALGRIND #include #endif /* Test parameters. For now hard coded. */ #define DATABASE "test" #define TABLE "blobt3" /* The page size for compressed tables, if this value is > 0 then we create compressed tables. It's set via the command line parameter --page-size INT */ static int page_size = 0; /* Total number of worker threads for SEL, UPD, INS and DEL */ #define NUM_THREADS 64 static pthread_t tid[NUM_THREADS]; /* Initial number of rows. */ static const int num_rows = 2000; /* batch size for DML. Commit after that many rows are worked upon */ static const int batch_size = 10; /* %age transactions to be rolled back */ static const int rollback_percent = 10; /* isolation level for transactions */ static const int isolation_level = IB_TRX_REPEATABLE_READ; /* blob/text column field length */ #define BLOB_LEN 34000 /* array to hold random data strings */ static char** rnd_str; /* array to hold length of strings in rnd_str */ static int* rnd_str_len; /* array to hold pre-defined prefixes for blob */ static char pre_str[][128] = { "kjgclgrtfuylfluyfyufyulfulfyyulofuyolfyufyufuyfyufyufyufyui" "fyyufujhfghd", "khd", "kh"}; /* Test duration in seconds */ static int test_time = 1800; /* Flag used by worker threads to finish the run */ static ib_bool_t test_running = IB_FALSE; /* to hold statistics of a particular type of DML */ typedef struct dml_op_struct { int n_ops; /* Total ops performed */ int n_errs; /* Total errors */ int errs[DB_DATA_MISMATCH]; /* This is taken from db_err.h and it is going to be a very sparse array but we can live with it for testing. */ pthread_mutex_t mutex; /*mutex protecting this struct. */ } dml_op_t; static dml_op_t sel_stats; static dml_op_t del_stats; static dml_op_t upd_stats; static dml_op_t ins_stats; enum op_type { DML_OP_TYPE_SELECT = 0, DML_OP_TYPE_INSERT, DML_OP_TYPE_UPDATE, DML_OP_TYPE_DELETE, }; /* Update statistics for any given dml_op_struct */ #define UPDATE_ERR_STATS(x, y) \ do { \ pthread_mutex_lock(&((x).mutex)); \ (x).n_ops++; \ if ((y) != DB_SUCCESS ) { \ (x).n_errs++; \ (x).errs[(y)]++; \ } \ pthread_mutex_unlock(&((x).mutex)); \ } while(0) /********************************************************************** Populate rnd_str with random strings having one of the prefixes from the three hardcoded prefixes from pre_str. */ static void gen_random_data(void) /*=================*/ { int i; assert(num_rows > 0); rnd_str = (char**) malloc(sizeof(*rnd_str) * num_rows); assert(rnd_str); for (i = 0; i < num_rows; ++i) { rnd_str[i] = (char*) malloc(BLOB_LEN); assert(rnd_str[i]); } rnd_str_len = (int*) malloc(sizeof(int) * num_rows); assert(rnd_str_len); /* Now generate the random text strings */ for (i = 0; i < num_rows; ++i) { char* ptr; int len; strcpy(rnd_str[i], pre_str[random() % 3]); len = strlen(rnd_str[i]); ptr = rnd_str[i] + len; len += gen_rand_text(ptr, BLOB_LEN - 128); rnd_str_len[i] = len; } } #if 0 /********************************************************************** Print character array of give size or upto 256 chars */ static void print_char_array( /*=============*/ FILE* stream, /*!< in: stream to print to */ const char* array, /*!< in: char array */ int len) /*!< in: length of data */ { int j; const char* ptr = array; for (j = 0; j < 256 && j < len; ++j) { fprintf(stream, "%c", *(ptr + j)); } } /********************************************************************** Print the random strings generated by gen_random_data(). Just for debugging to check we are generating good data. */ static void print_random_data(void) /*===================*/ { int i; for (i = 0; i < num_rows; ++i) { fprintf(stderr, "%d:", rnd_str_len[i]); print_char_array(stderr, rnd_str[i], rnd_str_len[i]); fprintf(stderr, "\n"); } } #endif /********************************************************************* Create an InnoDB database (sub-directory). */ static ib_err_t create_database( /*============*/ const char* name) { ib_bool_t err; err = ib_database_create(name); assert(err == IB_TRUE); return(DB_SUCCESS); } /********************************************************************* Drop the database. */ static ib_err_t drop_database( /*==========*/ const char* dbname) /*!< in: db to drop */ { ib_err_t err; err = ib_database_drop(dbname); return(err); } /********************************************************************* CREATE TABLE blobt3( A INT, D INT, B BLOB, C BLOB, PRIMARY KEY(B(10), A, D), INDEX(D), INDEX(A), INDEX(C(255), B(255)), INDEX(B(5), C(10), A)); */ static ib_err_t create_table( /*=========*/ const char* dbname, /*!< in: database name */ const char* name) /*!< in: table name */ { ib_trx_t ib_trx; ib_id_t table_id = 0; ib_err_t err = DB_SUCCESS; ib_tbl_sch_t ib_tbl_sch = NULL; ib_idx_sch_t idx_sch = NULL; ib_tbl_fmt_t tbl_fmt = IB_TBL_COMPACT; char table_name[IB_MAX_TABLE_NAME_LEN]; snprintf(table_name, sizeof(table_name), "%s/%s", dbname, name); if (page_size > 0) { tbl_fmt = IB_TBL_COMPRESSED; printf("Creating compressed table with page size %d\n", page_size); } /* Pass a table page size of 0, ie., use default page size. */ err = ib_table_schema_create( table_name, &ib_tbl_sch, tbl_fmt, page_size); assert(err == DB_SUCCESS); /* Define the table columns */ err = ib_tbl_sch_add_u32_col(ib_tbl_sch, "A"); assert(err == DB_SUCCESS); err = ib_tbl_sch_add_u32_col(ib_tbl_sch, "D"); assert(err == DB_SUCCESS); err = ib_tbl_sch_add_blob_col(ib_tbl_sch, "B"); assert(err == DB_SUCCESS); //err = ib_tbl_sch_add_text_col(ib_tbl_sch, "C"); err = ib_tbl_sch_add_blob_col(ib_tbl_sch, "C"); assert(err == DB_SUCCESS); /* Add primary key */ err = ib_table_schema_add_index(ib_tbl_sch, "PRIMARY", &idx_sch); assert(err == DB_SUCCESS); err = ib_index_schema_add_col(idx_sch, "B", 10); assert(err == DB_SUCCESS); err = ib_index_schema_add_col(idx_sch, "A", 0); assert(err == DB_SUCCESS); err = ib_index_schema_add_col(idx_sch, "D", 0); assert(err == DB_SUCCESS); err = ib_index_schema_set_clustered(idx_sch); assert(err == DB_SUCCESS); err = ib_index_schema_set_unique(idx_sch); assert(err == DB_SUCCESS); /* Add secondary indexes */ err = ib_table_schema_add_index(ib_tbl_sch, "SEC_0", &idx_sch); assert(err == DB_SUCCESS); err = ib_index_schema_add_col(idx_sch, "D", 0); assert(err == DB_SUCCESS); err = ib_table_schema_add_index(ib_tbl_sch, "SEC_1", &idx_sch); assert(err == DB_SUCCESS); err = ib_index_schema_add_col(idx_sch, "A", 0); assert(err == DB_SUCCESS); err = ib_table_schema_add_index(ib_tbl_sch, "SEC_2", &idx_sch); assert(err == DB_SUCCESS); err = ib_index_schema_add_col(idx_sch, "C", 255); assert(err == DB_SUCCESS); err = ib_index_schema_add_col(idx_sch, "B", 255); assert(err == DB_SUCCESS); err = ib_table_schema_add_index(ib_tbl_sch, "SEC_3", &idx_sch); assert(err == DB_SUCCESS); err = ib_index_schema_add_col(idx_sch, "B", 5); assert(err == DB_SUCCESS); err = ib_index_schema_add_col(idx_sch, "C", 10); assert(err == DB_SUCCESS); err = ib_index_schema_add_col(idx_sch, "A", 0); assert(err == DB_SUCCESS); /* create table */ ib_trx = ib_trx_begin(IB_TRX_REPEATABLE_READ); err = ib_schema_lock_exclusive(ib_trx); assert(err == DB_SUCCESS); err = ib_table_create(ib_trx, ib_tbl_sch, &table_id); assert(err == DB_SUCCESS); err = ib_trx_commit(ib_trx); assert(err == DB_SUCCESS); if (ib_tbl_sch != NULL) { ib_table_schema_delete(ib_tbl_sch); } return(err); } /********************************************************************* Open a table and return a cursor for the table. */ static ib_err_t open_table( /*=======*/ const char* dbname, /*!< in: database name */ const char* name, /*!< in: table name */ ib_trx_t ib_trx, /*!< in: transaction */ ib_crsr_t* crsr) /*!< out: innodb cursor */ { ib_err_t err = DB_SUCCESS; char table_name[IB_MAX_TABLE_NAME_LEN]; snprintf(table_name, sizeof(table_name), "%s/%s", dbname, name); err = ib_cursor_open_table(table_name, ib_trx, crsr); assert(err == DB_SUCCESS); return(err); } /********************************************************************** Depending on rollback_percent decides whether to commit or rollback a given transaction */ static void commit_or_rollback( /*===============*/ ib_trx_t trx, /*!< in: trx to commit or rollback */ int cnt) /*!< in: trx counter */ { ib_err_t err; if (ib_trx_state(trx) == IB_TRX_NOT_STARTED) { err = ib_trx_release(trx); assert(err == DB_SUCCESS); } else if (cnt % (100 / rollback_percent)) { //printf("Commit transaction\n"); err = ib_trx_commit(trx); assert(err == DB_SUCCESS); } else { //printf("Rollback transaction\n"); err = ib_trx_rollback(trx); assert(err == DB_SUCCESS); } } /********************************************************************** Inserts one row into the table. @return error from insert row */ static ib_err_t insert_one_row( /*===========*/ ib_crsr_t crsr, /*!< in, out: cursor to use for write */ ib_tpl_t tpl) /*!< in: tpl to work on */ { int j; ib_err_t err; err = ib_tuple_write_u32(tpl, 0, random() % num_rows); assert(err == DB_SUCCESS); err = ib_tuple_write_u32(tpl, 1, 5); assert(err == DB_SUCCESS); j = random() % num_rows; err = ib_col_set_value(tpl, 2, rnd_str[j], rnd_str_len[j]); assert(err == DB_SUCCESS); j = random() % num_rows; err = ib_col_set_value(tpl, 3, rnd_str[j], rnd_str_len[j]); assert(err == DB_SUCCESS); //print_tuple(stderr, tpl); err = ib_cursor_insert_row(crsr, tpl); return(err); } /********************************************************************** Insert one batch of rows which constitute one transactions. @return number of row inserted we return on first error. */ static int insert_row_batch( /*=============*/ ib_crsr_t crsr, /*!< in, out: cursor to use */ int n_rows, /*!< in: num rows to insert */ ib_err_t* err) /*!< out: error code */ { int i; int cnt = 0; ib_tpl_t tpl; assert(n_rows <= num_rows); tpl = ib_clust_read_tuple_create(crsr); if (tpl == NULL) { *err = DB_OUT_OF_MEMORY; return(0); } *err = DB_SUCCESS; for (i = 0; i < n_rows; ++i) { *err = insert_one_row(crsr, tpl); UPDATE_ERR_STATS(ins_stats, *err); if (*err == DB_SUCCESS) { ++cnt; } else if (*err == DB_DEADLOCK || *err == DB_LOCK_WAIT_TIMEOUT) { break; } else { tpl = ib_tuple_clear(tpl); break; } tpl = ib_tuple_clear(tpl); if (tpl == NULL) { break; } } if (tpl != NULL) { ib_tuple_delete(tpl); } else { *err = DB_OUT_OF_MEMORY; } return(cnt); } /********************************************************************* Insert worker thread. Will do the following in batches: INSERT INTO blobt3 VALUES( RANDOM(INT), 5, RANDOM(BLOB), RANDOM(BLOB)) */ static void* ins_worker( /*=============*/ void* dummy) /*!< in: unused */ { int cnt = 0; (void) dummy; printf("ins_worker up\n"); do { ib_trx_t trx; ib_crsr_t crsr; ib_err_t err; ib_err_t ins_err = DB_SUCCESS; ++cnt; //printf("Begin INSERT transaction\n"); trx = ib_trx_begin(isolation_level); assert(trx != NULL); err = open_table(DATABASE, TABLE, trx, &crsr); assert(err == DB_SUCCESS); if (ib_cursor_lock(crsr, IB_LOCK_IX) == DB_SUCCESS) { insert_row_batch(crsr, batch_size, &ins_err); } err = ib_cursor_close(crsr); assert(err == DB_SUCCESS); crsr = NULL; if (ins_err == DB_DEADLOCK || ins_err == DB_LOCK_WAIT_TIMEOUT) { err = ib_trx_release(trx); assert(err == DB_SUCCESS); } else { commit_or_rollback(trx, cnt); } } while (test_running); return(NULL); } /********************************************************************** Update one row in the table. @return error returned from ib_crsr_moveto() or ib_cursor_update_row() */ static ib_err_t update_one_row( /*===========*/ ib_crsr_t crsr) /*!< in: cursor on the table or the secondary index */ { int j; ib_err_t err; ib_tpl_t old_tpl; ib_tpl_t new_tpl; ib_ulint_t data_len; ib_col_meta_t col_meta; /* Create the tuple instance that we will use to update the table. old_tpl is used for reading the existing row and new_tpl will contain the update row data. */ old_tpl = ib_clust_read_tuple_create(crsr); assert(old_tpl != NULL); new_tpl = ib_clust_read_tuple_create(crsr); assert(new_tpl != NULL); err = ib_cursor_read_row(crsr, old_tpl); assert(err == DB_SUCCESS); /* Copy the old contents to the new tuple. */ err = ib_tuple_copy(new_tpl, old_tpl); /* Update the B column in the new tuple. */ data_len = ib_col_get_meta(old_tpl, 2, &col_meta); assert(data_len != IB_SQL_NULL); j = random() % num_rows; err = ib_col_set_value(new_tpl, 2, rnd_str[j], rnd_str_len[j]); assert(err == DB_SUCCESS); err = ib_cursor_update_row(crsr, old_tpl, new_tpl); /* delete the old and new tuple instances. */ if (old_tpl != NULL) { ib_tuple_delete(old_tpl); } if (new_tpl != NULL) { ib_tuple_delete(new_tpl); } return(err); } /********************************************************************** Update or delete a batch of rows that constitute one transaction. */ static void process_row_batch( /*==============*/ ib_crsr_t crsr, /*!< in, out: cursor to use for write */ enum op_type type, /*!< in: DML_OP_TYPE_UPDATE or DML_OP_TYPE_DELETE */ int n_rows) /*!< in: rows to update */ { int i; int key; int res = ~0; ib_crsr_t index_crsr; ib_err_t err = DB_SUCCESS; assert(n_rows <= num_rows); /* Open the secondary index. */ err = ib_cursor_open_index_using_name( crsr, "SEC_1", &index_crsr); assert(err == DB_SUCCESS); err = ib_cursor_set_lock_mode(index_crsr, IB_LOCK_X); assert(err == DB_SUCCESS); ib_cursor_set_cluster_access(index_crsr); for (i = 0; i < n_rows && err != DB_DEADLOCK && err != DB_LOCK_WAIT_TIMEOUT; ++i) { ib_tpl_t sec_key_tpl; /* Create a tuple for searching the secondary index. */ sec_key_tpl = ib_sec_search_tuple_create(index_crsr); assert(sec_key_tpl != NULL); /* Set the value to look for. */ key = random() % num_rows; err = ib_col_set_value( sec_key_tpl, 0, &key, sizeof(key)); assert(err == DB_SUCCESS); /* Search for the key using the secondary index "SEC_1" */ err = ib_cursor_moveto( index_crsr, sec_key_tpl, IB_CUR_GE, &res); assert(err == DB_SUCCESS || err == DB_DEADLOCK || err == DB_END_OF_INDEX || err == DB_LOCK_WAIT_TIMEOUT || err == DB_RECORD_NOT_FOUND); if (sec_key_tpl != NULL) { ib_tuple_delete(sec_key_tpl); sec_key_tpl = NULL; } /* Match found in secondary index "SEC_1" */ if (err == DB_SUCCESS && res == 0) { if (type == DML_OP_TYPE_UPDATE) { /* update the row in the table */ err = update_one_row(index_crsr); UPDATE_ERR_STATS(upd_stats, err); } else { /* Now delete cluster the cluster index row using the secondary index. */ err = ib_cursor_delete_row(index_crsr); assert(err == DB_SUCCESS); UPDATE_ERR_STATS(del_stats, err); } } } err = ib_cursor_close(index_crsr); assert(err == DB_SUCCESS); } /********************************************************************** UPDATE worker thread that does the following: UPDATE blobt3 SET B = WHERE A = ; TODO: It may be that multiple rows match the where clause. Currently we just update the first row that matches the where clause. */ static void* upd_worker( /*=============*/ void* dummy) /*!< in: unused */ { ib_trx_t trx; ib_crsr_t crsr; ib_err_t err; int cnt = 0; (void) dummy; printf("upd_worker up\n"); while (1) { ++cnt; //printf("Begin UPDATE transaction\n"); trx = ib_trx_begin(isolation_level); assert(trx != NULL); err = open_table(DATABASE, TABLE, trx, &crsr); assert(err == DB_SUCCESS); if (ib_cursor_lock(crsr, IB_LOCK_IX) == DB_SUCCESS) { process_row_batch(crsr, DML_OP_TYPE_UPDATE, batch_size); } err = ib_cursor_close(crsr); assert(err == DB_SUCCESS); crsr = NULL; commit_or_rollback(trx, cnt); if (test_running == IB_FALSE) { break; } } return(NULL); } /********************************************************************** DELETE worker thread that does the following: DELETE FROM blobt3 WHERE A = ; TODO: It may be that multiple rows match the where clause. Currently we just update the first row that matches the where clause. */ static void* del_worker( /*=============*/ void* dummy) /*!< in: unused */ { int cnt = 0; (void)dummy; printf("del_worker up\n"); do { ib_trx_t trx; ib_crsr_t crsr; ib_err_t err; ++cnt; //printf("Begin DELETE transaction\n"); trx = ib_trx_begin(isolation_level); assert(trx != NULL); err = open_table(DATABASE, TABLE, trx, &crsr); assert(err == DB_SUCCESS); if (ib_cursor_lock(crsr, IB_LOCK_IX) == DB_SUCCESS) { process_row_batch(crsr, DML_OP_TYPE_DELETE, batch_size); } err = ib_cursor_close(crsr); assert(err == DB_SUCCESS); crsr = NULL; commit_or_rollback(trx, cnt); } while (test_running != IB_FALSE); return(NULL); } /********************************************************************* SELECT * FROM blobt3; */ static ib_err_t do_query( /*=====*/ ib_crsr_t crsr) { int cnt; ib_err_t err; ib_tpl_t tpl; tpl = ib_clust_read_tuple_create(crsr); assert(tpl != NULL); err = ib_cursor_first(crsr); assert(err == DB_SUCCESS || err == DB_END_OF_INDEX); cnt = 0; while (err == DB_SUCCESS) { err = ib_cursor_read_row(crsr, tpl); ++cnt; //print_tuple(stderr, tpl); assert(err == DB_SUCCESS || err == DB_END_OF_INDEX || err == DB_RECORD_NOT_FOUND); if (err == DB_RECORD_NOT_FOUND || err == DB_END_OF_INDEX) { break; } err = ib_cursor_next(crsr); assert(err == DB_SUCCESS || err == DB_END_OF_INDEX || err == DB_RECORD_NOT_FOUND); UPDATE_ERR_STATS(sel_stats, err); tpl = ib_tuple_clear(tpl); assert(tpl != NULL); } if (tpl != NULL) { ib_tuple_delete(tpl); } if (err == DB_RECORD_NOT_FOUND || err == DB_END_OF_INDEX) { err = DB_SUCCESS; } //fprintf(stderr, "rows read = %d\n", cnt); return(err); } /********************************************************************** SELECT * FROM blobt3; */ static void* sel_worker( /*=============*/ void* dummy) /*!< in: unused */ { ib_crsr_t crsr; ib_err_t err; (void) dummy; printf("sel_worker up\n"); do { ib_trx_t trx; //printf("Begin SELECT transaction\n"); trx = ib_trx_begin(isolation_level); assert(trx != NULL); err = open_table(DATABASE, TABLE, trx, &crsr); assert(err == DB_SUCCESS); do_query(crsr); err = ib_cursor_close(crsr); assert(err == DB_SUCCESS); crsr = NULL; if (ib_trx_state(trx) == IB_TRX_NOT_STARTED) { err = ib_trx_release(trx); assert(err == DB_SUCCESS); } else { err = ib_trx_commit(trx); assert(err == DB_SUCCESS); } } while (test_running); return(NULL); } #if 0 /********************************************************************** dummy worker */ static void* dummy_worker( /*===============*/ void* dummy) /*!< in: unused */ { while (1) { usleep(100000); if (test_running == IB_FALSE) { return(NULL); } } return(NULL); } #endif /********************************************************************** Create worker threads of one type. @return number of threads created */ static int create_dml_threads( /*===============*/ int ind, /*!< in: index in tid array */ void* (*fn)(void*)) /*!< in: worker thread function */ { int rc; int count = 0; int i; for (i = 0; i < NUM_THREADS / 4; ++i) { rc = pthread_create(&tid[ind + i], NULL, fn, NULL); assert(!rc); ++count; } return(count); } /********************************************************************** Create worker threads. */ static void create_worker_threads(void) /*=======================*/ { int i = 0; /* comment any of these and uncomment same number of dummy workers below if you want to run a specific workload. For example, if you want to run just select and updates then comment ins_worker and del_worker and uncomment two lines of dummy_worker. If you want to change the number of each type of threads then change NUM_THREADS. */ i += create_dml_threads(i, ins_worker); i += create_dml_threads(i, upd_worker); i += create_dml_threads(i, del_worker); i += create_dml_threads(i, sel_worker); //i += create_dml_threads(i, dummy_worker); //i += create_dml_threads(i, dummy_worker); //i += create_dml_threads(i, dummy_worker); //i += create_dml_threads(i, dummy_worker); assert(i == NUM_THREADS); } /********************************************************************** Initialize the structure to hold error statistics */ static void init_dml_op_struct( /*===============*/ dml_op_t* st) /*!< in/out: struct to initialize */ { int rc; memset(st, 0x00, sizeof(*st)); rc = pthread_mutex_init(&st->mutex, NULL); assert(!rc); } /********************************************************************** Initialize statistic structures. */ static void init_stat_structs(void) /*===================*/ { init_dml_op_struct(&sel_stats); init_dml_op_struct(&upd_stats); init_dml_op_struct(&ins_stats); init_dml_op_struct(&del_stats); } /********************************************************************** Free up the resources used in the test */ static void clean_up(void) /*==========*/ { int i; assert(rnd_str); assert(rnd_str_len); for (i = 0; i < num_rows; ++i) { assert(rnd_str[i]); free(rnd_str[i]); } free(rnd_str); free(rnd_str_len); pthread_mutex_destroy(&sel_stats.mutex); pthread_mutex_destroy(&del_stats.mutex); pthread_mutex_destroy(&upd_stats.mutex); pthread_mutex_destroy(&ins_stats.mutex); } /********************************************************************** Print statistics at the end of the test */ static void print_one_struct( /*=============*/ dml_op_t* st) { int i; fprintf(stderr, "n_ops = %d n_err = %d\n", st->n_ops, st->n_errs); fprintf(stderr, "err freq\n"); fprintf(stderr, "=========\n"); for (i = 0; i < DB_DATA_MISMATCH; ++i) { if (st->errs[i] != 0) { fprintf(stderr, "%d %d\n", i, st->errs[i]); } } fprintf(stderr, "=========\n"); } /********************************************************************** Print statistics at the end of the test */ static void print_results(void) /*===============*/ { fprintf(stderr, "SELECT: "); print_one_struct(&sel_stats); fprintf(stderr, "DELETE: "); print_one_struct(&del_stats); fprintf(stderr, "UPDATE: "); print_one_struct(&upd_stats); fprintf(stderr, "INSERT: "); print_one_struct(&ins_stats); } #ifndef __WIN__ /********************************************************************* Set the runtime global options. */ static void set_options( /*========*/ int argc, char* argv[]) { int opt; int size = 0; struct option* longopts; int count = 0; /* Count the number of InnoDB system options. */ while (ib_longopts[count].name) { ++count; } /* Add one of our options and a spot for the sentinel. */ size = sizeof(struct option) * (count + 2); longopts = (struct option*) malloc(size); memset(longopts, 0x0, size); memcpy(longopts, ib_longopts, sizeof(struct option) * count); /* Add the local parameter (page-size). */ longopts[count].name = "page-size"; longopts[count].has_arg = required_argument; longopts[count].flag = NULL; longopts[count].val = USER_OPT + 1; ++count; while ((opt = getopt_long(argc, argv, "", longopts, NULL)) != -1) { switch(opt) { case USER_OPT + 1: page_size = strtoul(optarg, NULL, 10); break; default: /* If it's an InnoDB parameter, then we let the auxillary function handle it. */ if (set_global_option(opt, optarg) != DB_SUCCESS) { print_usage(argv[0]); exit(EXIT_FAILURE); } } /* switch */ } free(longopts); } #endif /* __WIN__ */ int main(int argc, char* argv[]) { int i; int cnt; void* res; ib_err_t err; ib_crsr_t crsr; ib_trx_t ib_trx; time_t start_time; (void) argc; srandom(time(NULL)); err = ib_init(); assert(err == DB_SUCCESS); test_configure(); #ifndef __WIN__ set_options(argc, argv); #endif /* __WIN__ */ gen_random_data(); //print_random_data(); init_stat_structs(); err = ib_startup("barracuda"); assert(err == DB_SUCCESS); err = create_database(DATABASE); assert(err == DB_SUCCESS); err = create_table(DATABASE, TABLE); assert(err == DB_SUCCESS); cnt = 0; for (i = 0; i < num_rows; ++i) { /* Insert initial rows */ //printf("Begin transaction\n"); ib_trx = ib_trx_begin(isolation_level); assert(ib_trx != NULL); err = open_table(DATABASE, TABLE, ib_trx, &crsr); assert(err == DB_SUCCESS); if (ib_cursor_lock(crsr, IB_LOCK_IX) == DB_SUCCESS) { cnt += insert_row_batch(crsr, 1, &err); } err = ib_cursor_close(crsr); assert(err == DB_SUCCESS); crsr = NULL; //printf("Commit transaction\n"); if (ib_trx_state(ib_trx) == IB_TRX_NOT_STARTED) { err = ib_trx_release(ib_trx); assert(err == DB_SUCCESS); } else { err = ib_trx_commit(ib_trx); assert(err == DB_SUCCESS); } } fprintf(stderr, "initial insertions = %d\n", cnt); /* start the test. */ test_running = IB_TRUE; create_worker_threads(); start_time = time(NULL); /* Sleep can be interrupted by a signal. */ do { /* sleep for test duration */ if (sleep(test_time) != 0) { test_time -= (int) time(NULL) - start_time; } else { break; } } while (test_time > 0); /* stop test and let workers exit */ test_running = IB_FALSE; for (i = 0; i < NUM_THREADS; ++i) { pthread_join(tid[i], &res); } err = drop_table(DATABASE, TABLE); assert(err == DB_SUCCESS); err = drop_database(DATABASE); assert(err == DB_SUCCESS); err = ib_shutdown(IB_SHUTDOWN_NORMAL); assert(err == DB_SUCCESS); print_results(); clean_up(); #ifdef UNIV_DEBUG_VALGRIND VALGRIND_DO_LEAK_CHECK; #endif return(EXIT_SUCCESS); } haildb-2.3.2/tests/ib_deadlock.c0000644000175000017500000003035711513177357017401 0ustar00pcrewspcrews00000000000000/*********************************************************************** Copyright (c) 2009 Innobase Oy. All rights reserved. Copyright (c) 2009 Oracle. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ************************************************************************/ /* Create the conditions for a deadlock. Create a database CREATE TABLE T1(c1 INT, c2 INT, PK(c1)); CREATE TABLE T2(c1 INT, c2 INT, PK(c1)); In multiple threads: BEGIN; INSERT INTO Tx VALUES(1, 1); INSERT INTO Ty VALUES(N, N); -- sleep 60 seconds COMMIT; */ #include #include #include #include #include #include #include #include #include /* For getopt_long() */ #include "test0aux.h" #ifdef UNIV_DEBUG_VALGRIND #include #endif #define DATABASE "test" static ib_u32_t n_rows = 100; static ib_u32_t n_threads = 2; /* The page size for compressed tables, if this value is > 0 then we create compressed tables. It's set via the command line parameter --page-size INT */ static int page_size = 0; /* Barrier to synchronize all threads */ static pthread_barrier_t barrier; /********************************************************************* Create an InnoDB database (sub-directory). */ static ib_err_t create_database( /*============*/ const char* name) { ib_bool_t err; err = ib_database_create(name); assert(err == IB_TRUE); return(DB_SUCCESS); } /********************************************************************* CREATE TABLE T (c1 INT, c2 INT, PRIMARY KEY(c1)); */ static ib_err_t create_table( /*=========*/ const char* dbname, /*!< in: database name */ const char* name) /*!< in: table name */ { ib_trx_t ib_trx; ib_id_t table_id = 0; ib_err_t err = DB_SUCCESS; ib_tbl_sch_t ib_tbl_sch = NULL; ib_idx_sch_t ib_idx_sch = NULL; ib_tbl_fmt_t tbl_fmt = IB_TBL_COMPACT; char table_name[IB_MAX_TABLE_NAME_LEN]; #ifdef __WIN__ sprintf(table_name, "%s/%s", dbname, name); #else snprintf(table_name, sizeof(table_name), "%s/%s", dbname, name); #endif if (page_size > 0) { tbl_fmt = IB_TBL_COMPRESSED; printf("Creating compressed table with page size %d\n", page_size); } /* Pass a table page size of 0, ie., use default page size. */ err = ib_table_schema_create( table_name, &ib_tbl_sch, tbl_fmt, page_size); assert(err == DB_SUCCESS); err = ib_table_schema_add_col( ib_tbl_sch, "c1", IB_INT, IB_COL_UNSIGNED, 0, sizeof(ib_u32_t)); assert(err == DB_SUCCESS); err = ib_table_schema_add_col( ib_tbl_sch, "c2", IB_INT, IB_COL_UNSIGNED, 0, sizeof(ib_u32_t)); assert(err == DB_SUCCESS); err = ib_table_schema_add_index(ib_tbl_sch, "PRIMARY", &ib_idx_sch); assert(err == DB_SUCCESS); /* Set prefix length to 0. */ err = ib_index_schema_add_col( ib_idx_sch, "c1", 0); assert(err == DB_SUCCESS); err = ib_index_schema_set_clustered(ib_idx_sch); assert(err == DB_SUCCESS); /* create table */ ib_trx = ib_trx_begin(IB_TRX_REPEATABLE_READ); err = ib_schema_lock_exclusive(ib_trx); assert(err == DB_SUCCESS); err = ib_table_create(ib_trx, ib_tbl_sch, &table_id); assert(err == DB_SUCCESS); err = ib_trx_commit(ib_trx); assert(err == DB_SUCCESS); if (ib_tbl_sch != NULL) { ib_table_schema_delete(ib_tbl_sch); } return(err); } /********************************************************************* Open a table and return a cursor for the table. */ static ib_err_t open_table( /*=======*/ const char* dbname, /*!< in: database name */ const char* name, /*!< in: table name */ ib_trx_t ib_trx, /*!< in: transaction */ ib_crsr_t* crsr) /*!< out: innodb cursor */ { ib_err_t err = DB_SUCCESS; char table_name[IB_MAX_TABLE_NAME_LEN]; #ifdef __WIN__ sprintf(table_name, "%s/%s", dbname, name); #else snprintf(table_name, sizeof(table_name), "%s/%s", dbname, name); #endif err = ib_cursor_open_table(table_name, ib_trx, crsr); assert(err == DB_SUCCESS); return(err); } /********************************************************************* INSERT INTO T VALUE(i, i); */ static ib_err_t insert_rows( /*========*/ ib_crsr_t crsr, /*!< in, out: cursor to use for write */ ib_u32_t start, /*!< in: start of column value */ ib_u32_t n_values, /*!< in: no. of values to insert */ int thread_id) /*!< in: id of thread doing insert */ { ib_u32_t i; ib_tpl_t tpl; ib_err_t err = DB_SUCCESS; tpl = ib_clust_read_tuple_create(crsr); assert(tpl != NULL); for (i = start; i < start + n_values; ++i) { err = ib_tuple_write_u32(tpl, 0, i); assert(err == DB_SUCCESS); err = ib_tuple_write_u32(tpl, 1, (ib_u32_t) thread_id); assert(err == DB_SUCCESS); err = ib_cursor_insert_row(crsr, tpl); if (err != DB_SUCCESS) { break; } /* Since we are writing fixed length columns (all INTs), there is no need to reset the tuple. */ } ib_tuple_delete(tpl); return(err); } /********************************************************************* Run the test. */ static void* worker_thread( /*==========*/ void* arg) { int ret; ib_err_t err; ib_trx_t ib_trx; ib_crsr_t crsr1 = NULL; ib_crsr_t crsr2 = NULL; ib_bool_t deadlock = IB_FALSE; int thread_id = *(int*) arg; free(arg); err = open_table(DATABASE, "T1", NULL, &crsr1); assert(err == DB_SUCCESS); err = open_table(DATABASE, "T2", NULL, &crsr2); assert(err == DB_SUCCESS); ret = pthread_barrier_wait(&barrier); assert(ret == 0 || ret == PTHREAD_BARRIER_SERIAL_THREAD); if (ret == PTHREAD_BARRIER_SERIAL_THREAD) { printf("Start insert...\n"); } ib_trx = ib_trx_begin(IB_TRX_REPEATABLE_READ); assert(ib_trx != NULL); ib_cursor_attach_trx(crsr1, ib_trx); ib_cursor_attach_trx(crsr2, ib_trx); if (!(thread_id % 2)) { err = ib_cursor_lock(crsr1, IB_LOCK_IX); assert(err == DB_SUCCESS); err = ib_cursor_lock(crsr2, IB_LOCK_IX); assert(err == DB_SUCCESS); err = insert_rows(crsr1, 0, n_rows, thread_id); assert(err == DB_SUCCESS || err == DB_DEADLOCK || err == DB_LOCK_WAIT_TIMEOUT); if (err == DB_SUCCESS) { sleep(3); err = insert_rows(crsr2, 0, n_rows, thread_id); assert(err == DB_SUCCESS || err == DB_DEADLOCK || err == DB_LOCK_WAIT_TIMEOUT); if (err == DB_SUCCESS) { sleep(3); } } } else { err = ib_cursor_lock(crsr2, IB_LOCK_IX); assert(err == DB_SUCCESS); err = ib_cursor_lock(crsr1, IB_LOCK_IX); assert(err == DB_SUCCESS); err = insert_rows(crsr2, 0, n_rows, thread_id); assert(err == DB_SUCCESS || err == DB_DEADLOCK || err == DB_LOCK_WAIT_TIMEOUT); if (err == DB_SUCCESS) { sleep(3); err = insert_rows(crsr1, 0, n_rows, thread_id); assert(err == DB_SUCCESS || err == DB_DEADLOCK || err == DB_LOCK_WAIT_TIMEOUT); if (err == DB_SUCCESS) { sleep(3); } } } if (err != DB_SUCCESS) { deadlock = IB_TRUE; } err = ib_cursor_reset(crsr1); assert(err == DB_SUCCESS); err = ib_cursor_reset(crsr2); assert(err == DB_SUCCESS); if (!deadlock) { /* If all went well then the transaction should still be active, we need to commit it. */ assert(ib_trx_state(ib_trx) == IB_TRX_ACTIVE); err = ib_trx_commit(ib_trx); assert(err == DB_SUCCESS); printf("Thread#%d - trx committed.\n", thread_id); } else { /* The transaction should have been rolled back by InnoDB, we can only release the handle now. */ assert(ib_trx_state(ib_trx) != IB_TRX_ACTIVE); err = ib_trx_release(ib_trx); assert(err == DB_SUCCESS); printf("Thread#%d - deadlock, trx rolled back.\n", thread_id); } if (crsr1) { err = ib_cursor_close(crsr1); assert(err == DB_SUCCESS); crsr1 = NULL; } if (crsr2) { err = ib_cursor_close(crsr2); assert(err == DB_SUCCESS); crsr2 = NULL; } pthread_exit(0); } #ifndef __WIN__ /********************************************************************* Set the runtime global options. */ static void set_options( /*========*/ int argc, char* argv[]) { int opt; int optind; int size = 0; struct option* longopts; int count = 0; /* Count the number of InnoDB system options. */ while (ib_longopts[count].name) { ++count; } /* Add two of our options and a spot for the sentinel. */ size = sizeof(struct option) * (count + 4); longopts = (struct option*) malloc(size); memset(longopts, 0x0, size); memcpy(longopts, ib_longopts, sizeof(struct option) * count); /* Add the local parameters (threads, rows and page_size). */ longopts[count].name = "threads"; longopts[count].has_arg = required_argument; longopts[count].flag = NULL; longopts[count].val = USER_OPT + 1; ++count; longopts[count].name = "rows"; longopts[count].has_arg = required_argument; longopts[count].flag = NULL; longopts[count].val = USER_OPT + 2; ++count; longopts[count].name = "page_size"; longopts[count].has_arg = required_argument; longopts[count].flag = NULL; longopts[count].val = USER_OPT + 3; while ((opt = getopt_long(argc, argv, "", longopts, &optind)) != -1) { switch(opt) { case USER_OPT + 1: n_threads = strtoul(optarg, NULL, 10); break; case USER_OPT + 2: n_rows = strtoul(optarg, NULL, 10); break; case USER_OPT + 3: page_size = strtoul(optarg, NULL, 10); break; default: /* If it's an InnoDB parameter, then we let the auxillary function handle it. */ if (set_global_option(opt, optarg) != DB_SUCCESS) { print_usage(argv[0]); exit(EXIT_FAILURE); } } /* switch */ } free(longopts); } #endif /* __WIN__ */ /********************************************************************* Create the tables required for the test @return DB_SUCCESS if all went well.*/ static ib_err_t create_tables(void) /*===============*/ { ib_err_t err; err = create_table(DATABASE, "T1"); assert(err == DB_SUCCESS); err = create_table(DATABASE, "T2"); assert(err == DB_SUCCESS); return(err); } /********************************************************************* Drop the tables required for the test @return DB_SUCCESS if all went well.*/ static ib_err_t drop_tables(void) /*=============*/ { ib_err_t err; err = drop_table(DATABASE, "T1"); assert(err == DB_SUCCESS); err = drop_table(DATABASE, "T2"); assert(err == DB_SUCCESS); return(err); } int main(int argc, char* argv[]) { int i; int ret; ib_err_t err; pthread_t* pthreads; ib_init(); test_configure(); #ifndef __WIN__ set_options(argc, argv); #endif /* __WIN__ */ err = ib_cfg_set_int("open_files", 8192); assert(err == DB_SUCCESS); /* Reduce the timeout to trigger lock timeout quickly. */ err = ib_cfg_set_int("lock_wait_timeout", 3); assert(err == DB_SUCCESS); err = ib_startup("barracuda"); assert(err == DB_SUCCESS); err = create_database(DATABASE); assert(err == DB_SUCCESS); err = create_tables(); assert(err == DB_SUCCESS); ret = pthread_barrier_init(&barrier, NULL, n_threads); assert(ret == 0); pthreads = (pthread_t*) malloc(sizeof(*pthreads) * n_threads); memset(pthreads, 0, sizeof(*pthreads) * n_threads); printf("About to spawn %d threads ", n_threads); for (i = 0; i < n_threads; ++i) { int retval; int* ptr = malloc(sizeof(int)); assert(ptr != NULL); *ptr = i; /* worker_thread owns the argument and is responsible for freeing it. */ retval = pthread_create(&pthreads[i], NULL, worker_thread, ptr); if (retval != 0) { fprintf(stderr, "Error spawning thread %d, " "pthread_create() returned %d\n", i, retval); exit(EXIT_FAILURE); } printf("."); } printf("\nWaiting for threads to finish ...\n"); for (i = 0; i < n_threads; ++i) { pthread_join(pthreads[i], NULL); } free(pthreads); pthreads = NULL; ret = pthread_barrier_destroy(&barrier); assert(ret == 0); err = drop_tables(); assert(err == DB_SUCCESS); err = ib_shutdown(IB_SHUTDOWN_NORMAL); assert(err == DB_SUCCESS); #ifdef UNIV_DEBUG_VALGRIND VALGRIND_DO_LEAK_CHECK; #endif return(EXIT_SUCCESS); } haildb-2.3.2/tests/test0aux.h0000644000175000017500000000670211513177357016740 0ustar00pcrewspcrews00000000000000#ifndef _TEST0AUX_H #define TEST0AUX_H #include #include #include "haildb.h" #define USER_OPT 1000 #define OK(expr) \ do { \ ib_err_t ok_ib_err; \ ok_ib_err = expr; \ if (ok_ib_err != DB_SUCCESS) { \ fprintf(stderr, "%s: %s\n", #expr, \ ib_strerror(ok_ib_err)); \ exit(EXIT_FAILURE); \ } \ } while (0) #ifndef __WIN__ #include /* For getopt_long() and struct option. */ /* InnoDB command line parameters */ extern struct option ib_longopts[]; #endif /* __WIN__ */ /* Arbitrary string */ typedef struct ib_string { ib_byte_t* ptr; int len; } ib_string_t; /* Config variable name=value pair. */ typedef struct ib_var { ib_string_t name; ib_string_t value; } ib_var_t; /* Memory representation of the config file. */ typedef struct ib_config { ib_var_t* elems; ib_ulint_t n_elems; ib_ulint_t n_count; } ib_config_t; /********************************************************************* Read a value from an integer column in an InnoDB tuple. @return column value */ ib_u64_t read_int_from_tuple( /*================*/ ib_tpl_t tpl, /*!< in: InnoDB tuple */ const ib_col_meta_t* col_meta, /*!< in: col meta data */ int i); /*!< in: column number */ /********************************************************************* Print all columns in a tuple. */ void print_tuple( /*========*/ FILE* stream, /*!< in: Output stream */ const ib_tpl_t tpl); /*!< in: Tuple to print */ /********************************************************************* Setup the InnoDB configuration parameters. */ void test_configure(void); /*================*/ /********************************************************************* Generate random text upto max size. */ int gen_rand_text( /*==========*/ char* ptr, /*!< in,out: output text */ int max_size); /*!< in: max size of ptr */ /********************************************************************* Set the runtime global options. @return DB_SUCCESS on success. */ ib_err_t set_global_option( /*==============*/ int opt, /*!< in: option index */ const char* arg); /*!< in: option value */ /********************************************************************* Print usage. */ void print_usage( /*========*/ const char* progname); /*!< in: name of application */ /********************************************************************* Print API version to stdout. */ void print_version(void); /*===============*/ /********************************************************************* Free the the elements. */ void config_free( /*=========*/ ib_config_t* config); /*!< in, own: config values */ /********************************************************************* Print the elements. */ void config_print( /*=========*/ const ib_config_t* config); /*!< in: config values */ /********************************************************************* Parse a config file, the file has a very simply format: Lines beginning with '#' are ignored. Characters after '#' (inclusive) are also ignored. Empty lines are also ignored. Variable syntax is: \s*var_name\s*=\s*value\s*\n */ int config_parse_file( /*==============*/ const char* filename, /*!< in: config file name */ ib_config_t* config); /*!< out: config values */ /********************************************************************* Drop the table. */ ib_err_t drop_table( /*=======*/ const char* dbname, /*!< in: database name */ const char* name); /*!< in: table to drop */ #endif /* _TEST0AUX_H */ haildb-2.3.2/tests/ib_shutdown.c0000644000175000017500000000504211513177357017477 0ustar00pcrewspcrews00000000000000/*********************************************************************** Copyright (c) 2008 Innobase Oy. All rights reserved. Copyright (c) 2008 Oracle. All rights reserved. Copyright (c) 2009 Oracle. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ************************************************************************/ #include #include #include #ifdef WIN32 #include #else #include /* For sleep() */ #include /* For getopt_long() */ #endif #include "test0aux.h" #ifdef UNIV_DEBUG_VALGRIND #include #endif #ifndef WIN32 static int use_sys_malloc = 0; /********************************************************************* Get the runtime options. */ static void get_options( /*========*/ int argc, char* argv[]) { int opt; struct option longopts[] = { {"use-sys-malloc", required_argument, NULL, 1}, {NULL, 0, NULL, 0}}; while ((opt = getopt_long(argc, argv, "", longopts, NULL)) != -1) { switch(opt) { case 1: use_sys_malloc = 1; break; default: fprintf(stderr, "usage: %s [--use-sys-malloc ]\n", argv[0]); exit(EXIT_FAILURE); } } } #endif int main(int argc, char* argv[]) { int i; #ifndef WIN32 get_options(argc, argv); #endif for (i = 0; i < 10; ++i) { ib_ulint_t err; printf(" *** STARTING INNODB *** \n"); err = ib_init(); assert(err == DB_SUCCESS); test_configure(); #ifdef WIN32 Sleep(2); #else if (use_sys_malloc) { printf("Using system malloc\n"); err = ib_cfg_set_bool_on("use_sys_malloc"); } else { err = ib_cfg_set_bool_off("use_sys_malloc"); } assert(err == DB_SUCCESS); sleep(2); #endif err = ib_shutdown(IB_SHUTDOWN_NORMAL); assert(err == DB_SUCCESS); printf(" *** SHUTDOWN OF INNODB COMPLETE *** \n"); /* Note: We check for whether variables are reset to their default values externally. */ #ifdef UNIV_DEBUG_VALGRIND VALGRIND_DO_LEAK_CHECK; #endif } return(EXIT_SUCCESS); } haildb-2.3.2/tests/run.sh0000755000175000017500000000215211513177357016150 0ustar00pcrewspcrews00000000000000#!/bin/sh # # Drizzle Client Library # # Copyright (C) 2008 Eric Day (eday@oddments.org) # All rights reserved. # # Use and distribution licensed under the BSD license. See # the COPYING file in this directory for full text. # # Get filename we want to run without path name=`echo $1 | sed 's/.*\/\([^\/]*\)$/\1/'` ext=`echo $name | sed 's/.*\.\([^.]*$\)/\1/'` if [ "x$ext" = "x$name" ] then ext="" fi if [ ! "x$ext" = "xsh" ] then libtool_prefix="libtool --mode=execute" fi # Set prefix if it was given through environment if [ -n "$LIBDRIZZLE_TEST_PREFIX" ] then if [ -n "$LIBDRIZZLE_TEST_FILTER" ] then # If filter variable is set, only apply prefix to those that match for x in $LIBDRIZZLE_TEST_FILTER do if [ "x$x" = "x$name" ] then prefix="$libtool_prefix $LIBDRIZZLE_TEST_PREFIX" with=" (with prefix after filter)" break fi done else prefix="$libtool_prefix $LIBDRIZZLE_TEST_PREFIX" with=" (with prefix)" fi fi # Set this to fix broken libtool test ECHO=`which echo` export ECHO echo echo "RUN: $name$with" (cd tests && $prefix ./$name) haildb-2.3.2/tests/ib_test2.c0000644000175000017500000002555011513177357016673 0ustar00pcrewspcrews00000000000000/*********************************************************************** Copyright (c) 2008 Innobase Oy. All rights reserved. Copyright (c) 2008 Oracle. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ************************************************************************/ /* Single threaded test that does the equivalent of: Create a database FOR 1 TO 10 CREATE TABLE T(c1 VARCHAR(128), c2 BLOB, c3 INT, PK(c1)); ... FOR 1 TO 10 BEGIN; FOR 1 TO 100 INSERT INTO T VALUES(RANDOM(STRING), RANDOM(STRING), 0); END UPDATE T SET c1 = RANDOM(string), c3 = c3 + 1 WHERE c1 = RANDOM(STRING); COMMIT; END DROP TABLE T; END The test will create all the relevant sub-directories in the current working directory. */ #include #include #include #include #include #include "test0aux.h" #ifdef UNIV_DEBUG_VALGRIND #include #endif #define DATABASE "test" #define TABLE "ib_test2" /* The page size for compressed tables, if this value is > 0 then we create compressed tables. It's set via the command line parameter --page-size INT */ static int page_size = 0; /********************************************************************* Create an InnoDB database (sub-directory). */ static ib_err_t create_database( /*============*/ const char* name) { ib_bool_t err; err = ib_database_create(name); assert(err == IB_TRUE); return(DB_SUCCESS); } /********************************************************************* CREATE TABLE T( vchar VARCHAR(128), blob VARCHAR(n), count INT, PRIMARY KEY(vchar); */ static ib_err_t create_table( /*=========*/ const char* dbname, /*!< in: database name */ const char* name) /*!< in: table name */ { ib_trx_t ib_trx; ib_id_t table_id = 0; ib_err_t err = DB_SUCCESS; ib_tbl_sch_t ib_tbl_sch = NULL; ib_idx_sch_t ib_idx_sch = NULL; ib_tbl_fmt_t tbl_fmt = IB_TBL_COMPACT; char table_name[IB_MAX_TABLE_NAME_LEN]; #ifdef __WIN__ sprintf(table_name, "%s/%s", dbname, name); #else snprintf(table_name, sizeof(table_name), "%s/%s", dbname, name); #endif if (page_size > 0) { tbl_fmt = IB_TBL_COMPRESSED; printf("Creating compressed table with page size %d\n", page_size); } err = ib_table_schema_create( table_name, &ib_tbl_sch, tbl_fmt, page_size); assert(err == DB_SUCCESS); err = ib_table_schema_add_col( ib_tbl_sch, "vchar", IB_VARCHAR, IB_COL_NONE, 0, 128); assert(err == DB_SUCCESS); err = ib_table_schema_add_col( ib_tbl_sch, "blob", IB_BLOB, IB_COL_NONE, 0, 0); assert(err == DB_SUCCESS); err = ib_table_schema_add_col( ib_tbl_sch, "count", IB_INT, IB_COL_UNSIGNED, 0, 4); assert(err == DB_SUCCESS); err = ib_table_schema_add_index(ib_tbl_sch, "PRIMARY", &ib_idx_sch); assert(err == DB_SUCCESS); /* Set prefix length to 0. */ err = ib_index_schema_add_col( ib_idx_sch, "vchar", 0); assert(err == DB_SUCCESS); err = ib_index_schema_set_clustered(ib_idx_sch); assert(err == DB_SUCCESS); err = ib_index_schema_set_unique(ib_idx_sch); assert(err == DB_SUCCESS); /* create table */ ib_trx = ib_trx_begin(IB_TRX_REPEATABLE_READ); err = ib_schema_lock_exclusive(ib_trx); assert(err == DB_SUCCESS); err = ib_table_create(ib_trx, ib_tbl_sch, &table_id); if (err != DB_SUCCESS) { fprintf(stderr, "Warning: table create failed: %s\n", ib_strerror(err)); err = ib_trx_rollback(ib_trx); } else { err = ib_trx_commit(ib_trx); } assert(err == DB_SUCCESS); if (ib_tbl_sch != NULL) { ib_table_schema_delete(ib_tbl_sch); } return(err); } /********************************************************************* Open a table and return a cursor for the table. */ static ib_err_t open_table( /*=======*/ const char* dbname, /*!< in: database name */ const char* name, /*!< in: table name */ ib_trx_t ib_trx, /*!< in: transaction */ ib_crsr_t* crsr) /*!< out: innodb cursor */ { ib_err_t err = DB_SUCCESS; char table_name[IB_MAX_TABLE_NAME_LEN]; #ifdef __WIN__ sprintf(table_name, "%s/%s", dbname, name); #else snprintf(table_name, sizeof(table_name), "%s/%s", dbname, name); #endif err = ib_cursor_open_table(table_name, ib_trx, crsr); assert(err == DB_SUCCESS); return(err); } /********************************************************************* INSERT INTO T VALUE(RANDOM(TEXT), RANDOM(TEXT), 0); */ static ib_err_t insert_random_rows( /*===============*/ ib_crsr_t crsr) /*!< in, out: cursor to use for write */ { int i; ib_err_t err; ib_tpl_t tpl = NULL; char* ptr = malloc(8192); tpl = ib_clust_read_tuple_create(crsr); assert(tpl != NULL); for (i = 0; i < 100; ++i) { int l; l = gen_rand_text(ptr, 128); err = ib_col_set_value(tpl, 0, ptr, l); assert(err == DB_SUCCESS); l = gen_rand_text(ptr, 8192); err = ib_col_set_value(tpl, 1, ptr, l); assert(err == DB_SUCCESS); err = ib_tuple_write_u32(tpl, 2, 0); assert(err == DB_SUCCESS); err = ib_cursor_insert_row(crsr, tpl); assert(err == DB_SUCCESS || err == DB_DUPLICATE_KEY); tpl = ib_tuple_clear(tpl); assert(tpl != NULL); } if (tpl != NULL) { ib_tuple_delete(tpl); } free(ptr); return(err); } /********************************************************************* UPDATE T SET score = score + 100 WHERE first = 'a'; */ static ib_err_t update_random_rows( /*===============*/ ib_crsr_t crsr) { ib_err_t err; int l; char* ptr; int res = ~0; ib_tpl_t key_tpl; ib_tpl_t old_tpl = NULL; ib_tpl_t new_tpl = NULL; /* Create a tuple for searching an index. */ key_tpl = ib_sec_search_tuple_create(crsr); assert(key_tpl != NULL); ptr = (char*) malloc(8192); l = gen_rand_text(ptr, 128); /* Set the value to look for. */ err = ib_col_set_value(key_tpl, 0, ptr, l); assert(err == DB_SUCCESS); /* Search for the key using the cluster index (PK) */ err = ib_cursor_moveto(crsr, key_tpl, IB_CUR_GE, &res); assert(err == DB_SUCCESS || err == DB_END_OF_INDEX || err == DB_RECORD_NOT_FOUND); if (key_tpl != NULL) { ib_tuple_delete(key_tpl); } /* Match found */ if (res == 0) { ib_u32_t score; const char* first; ib_ulint_t data_len; ib_ulint_t first_len; ib_col_meta_t col_meta; /* Create the tuple instance that we will use to update the table. old_tpl is used for reading the existing row and new_tpl will contain the update row data. */ old_tpl = ib_clust_read_tuple_create(crsr); assert(old_tpl != NULL); new_tpl = ib_clust_read_tuple_create(crsr); assert(new_tpl != NULL); err = ib_cursor_read_row(crsr, old_tpl); assert(err == DB_SUCCESS); /* Get the first column value. */ first = ib_col_get_value(old_tpl, 0); first_len = ib_col_get_meta(old_tpl, 0, &col_meta); /* There are no SQL_NULL values in our test data. */ assert(first != NULL); /* Copy the old contents to the new tuple. */ err = ib_tuple_copy(new_tpl, old_tpl); /* Update the score column in the new tuple. */ data_len = ib_col_get_meta(old_tpl, 2, &col_meta); assert(data_len != IB_SQL_NULL); err = ib_tuple_read_u32(old_tpl, 2, &score); assert(err == DB_SUCCESS); ++score; /* Get the new text to insert. */ l = gen_rand_text(ptr, 128); /* Set the new key value in the new tuple. */ err = ib_col_set_value(new_tpl, 0, ptr, l); assert(err == DB_SUCCESS); first_len = ib_col_get_len(new_tpl, 0); assert(first_len == IB_SQL_NULL || first_len <= 128); /* Get the new text to insert. */ l = gen_rand_text(ptr, 8192); /* Set the blob value in the new tuple. */ err = ib_col_set_value(new_tpl, 1, ptr, l); assert(err == DB_SUCCESS); /* Set the updated score value in the new tuple. */ err = ib_tuple_write_u32(new_tpl, 2, score); assert(err == DB_SUCCESS); err = ib_cursor_update_row(crsr, old_tpl, new_tpl); assert(err == DB_SUCCESS || err == DB_DUPLICATE_KEY); } if (old_tpl != NULL) { ib_tuple_delete(old_tpl); } if (new_tpl != NULL) { ib_tuple_delete(new_tpl); } free(ptr); return(err); } #ifndef __WIN__ /********************************************************************* Set the runtime global options. */ static void set_options( /*========*/ int argc, char* argv[]) { int opt; int size = 0; struct option* longopts; int count = 0; /* Count the number of InnoDB system options. */ while (ib_longopts[count].name) { ++count; } /* Add one of our options and a spot for the sentinel. */ size = sizeof(struct option) * (count + 2); longopts = (struct option*) malloc(size); memset(longopts, 0x0, size); memcpy(longopts, ib_longopts, sizeof(struct option) * count); /* Add the local parameter (page-size). */ longopts[count].name = "page-size"; longopts[count].has_arg = required_argument; longopts[count].flag = NULL; longopts[count].val = USER_OPT + 1; ++count; while ((opt = getopt_long(argc, argv, "", longopts, NULL)) != -1) { switch(opt) { case USER_OPT + 1: page_size = strtoul(optarg, NULL, 10); break; default: /* If it's an InnoDB parameter, then we let the auxillary function handle it. */ if (set_global_option(opt, optarg) != DB_SUCCESS) { print_usage(argv[0]); exit(EXIT_FAILURE); } } /* switch */ } free(longopts); } #endif /* __WIN__ */ int main(int argc, char* argv[]) { int i; ib_err_t err; ib_crsr_t crsr; ib_trx_t ib_trx; #ifdef __WIN__ srand((int) time(NULL)); #else srandom(time(NULL)); #endif err = ib_init(); assert(err == DB_SUCCESS); test_configure(); #ifndef __WIN__ set_options(argc, argv); #endif /* __WIN__ */ err = ib_startup("barracuda"); assert(err == DB_SUCCESS); err = create_database(DATABASE); assert(err == DB_SUCCESS); for (i = 0; i < 10; ++i) { int j; printf("Create table\n"); err = create_table(DATABASE, TABLE); assert(err == DB_SUCCESS); for (j = 0; j < 10; ++j) { ib_trx = ib_trx_begin(IB_TRX_REPEATABLE_READ); assert(ib_trx != NULL); err = open_table(DATABASE, TABLE, ib_trx, &crsr); assert(err == DB_SUCCESS); err = ib_cursor_lock(crsr, IB_LOCK_IX); assert(err == DB_SUCCESS); insert_random_rows(crsr); update_random_rows(crsr); err = ib_cursor_close(crsr); assert(err == DB_SUCCESS); crsr = NULL; err = ib_trx_commit(ib_trx); assert(err == DB_SUCCESS); } printf("Drop table\n"); err = drop_table(DATABASE, TABLE); assert(err == DB_SUCCESS); } err = ib_shutdown(IB_SHUTDOWN_NORMAL); assert(err == DB_SUCCESS); #ifdef UNIV_DEBUG_VALGRIND VALGRIND_DO_LEAK_CHECK; #endif return(EXIT_SUCCESS); } haildb-2.3.2/tests/ib_drop.c0000644000175000017500000001110111513177357016561 0ustar00pcrewspcrews00000000000000/*********************************************************************** Copyright (c) 2008 Innobase Oy. All rights reserved. Copyright (c) 2009 Oracle. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ************************************************************************/ /* Simple single threaded test that does the equivalent of: Create a database CREATE TABLE D.T1(c1 INT); ... CREATE TABLE D.Tn(c1 INT); DROP DATABASE D; InnoDB should drop all tables and remove the underlying directory. The test will create all the relevant sub-directories in the current working directory. */ #include #include #include #include #include "test0aux.h" #ifdef UNIV_DEBUG_VALGRIND #include #endif #define DATABASE "drop_test" #define TABLE "t" /********************************************************************* Create an InnoDB database (sub-directory). */ static ib_err_t create_database( /*============*/ const char* name) { ib_bool_t err; err = ib_database_create(name); assert(err == IB_TRUE); return(DB_SUCCESS); } /********************************************************************* CREATE TABLE D.Tn(C1 INT); */ static ib_err_t create_table( /*=========*/ const char* dbname, /*!< in: database name */ const char* name, /*!< in: table name */ int n) /*!< in: table suffix */ { ib_trx_t ib_trx; ib_id_t table_id = 0; ib_err_t err = DB_SUCCESS; ib_tbl_sch_t ib_tbl_sch = NULL; char table_name[IB_MAX_TABLE_NAME_LEN]; #ifdef __WIN__ sprintf(table_name, "%s/%s%d", dbname, name, n); #else snprintf(table_name, sizeof(table_name), "%s/%s%d", dbname, name, n); #endif /* Pass a table page size of 0, ie., use default page size. */ err = ib_table_schema_create( table_name, &ib_tbl_sch, IB_TBL_COMPACT, 0); assert(err == DB_SUCCESS); err = ib_table_schema_add_col( ib_tbl_sch, "c1", IB_INT, IB_COL_UNSIGNED, 0, sizeof(ib_u32_t)); assert(err == DB_SUCCESS); /* create table */ ib_trx = ib_trx_begin(IB_TRX_REPEATABLE_READ); err = ib_schema_lock_exclusive(ib_trx); assert(err == DB_SUCCESS); err = ib_table_create(ib_trx, ib_tbl_sch, &table_id); assert(err == DB_SUCCESS); err = ib_trx_commit(ib_trx); assert(err == DB_SUCCESS); if (ib_tbl_sch != NULL) { ib_table_schema_delete(ib_tbl_sch); } return(err); } /********************************************************************* Open a table and return a cursor for the table. */ static ib_err_t open_table( /*=======*/ const char* dbname, /*!< in: database name */ const char* name, /*!< in: table name */ int n, /*!< in: table suffix */ ib_trx_t ib_trx, /*!< in: transaction */ ib_crsr_t* crsr) /*!< out: innodb cursor */ { ib_err_t err = DB_SUCCESS; char table_name[IB_MAX_TABLE_NAME_LEN]; #ifdef __WIN__ sprintf(table_name, "%s/%s%d", dbname, name, n); #else snprintf(table_name, sizeof(table_name), "%s/%s%d", dbname, name, n); #endif err = ib_cursor_open_table(table_name, ib_trx, crsr); assert(err == DB_SUCCESS); return(err); } int main(int argc, char* argv[]) { int i; ib_err_t err; ib_crsr_t crsr; ib_trx_t ib_trx; (void) argc; (void) argv; err = ib_init(); assert(err == DB_SUCCESS); test_configure(); err = ib_startup("barracuda"); assert(err == DB_SUCCESS); err = create_database(DATABASE); assert(err == DB_SUCCESS); /* Create the tables. */ for (i = 0; i < 10; i++) { err = create_table(DATABASE, TABLE, i); assert(err == DB_SUCCESS); } ib_trx = ib_trx_begin(IB_TRX_REPEATABLE_READ); assert(ib_trx != NULL); /* Open and close the cursor. */ for (i = 0; i < 10; i++) { err = open_table(DATABASE, TABLE, i, ib_trx, &crsr); assert(err == DB_SUCCESS); err = ib_cursor_close(crsr); assert(err == DB_SUCCESS); crsr = NULL; } err = ib_trx_commit(ib_trx); assert(err == DB_SUCCESS); err = ib_database_drop(DATABASE); assert(err == DB_SUCCESS); err = ib_shutdown(IB_SHUTDOWN_NORMAL); assert(err == DB_SUCCESS); #ifdef UNIV_DEBUG_VALGRIND VALGRIND_DO_LEAK_CHECK; #endif return(EXIT_SUCCESS); } haildb-2.3.2/tests/ib_duplicate_key_name.c0000644000175000017500000001604211513177357021450 0ustar00pcrewspcrews00000000000000/*********************************************************************** Copyright (c) 2009 Innobase Oy. All rights reserved. Copyright (c) 2009 Oracle. All rights reserved. Copyright (c) 2010 Stewart Smith This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ************************************************************************/ /* Tests getting the name of the index that caused a duplicate key error */ #include #include #include "haildb.h" #include "test0aux.h" #include #include #define DATABASE "test" #define TABLE "ib_duplicate_key_name" #define TABLE_PATH DATABASE "/" TABLE #define PRIMARY_INDEX_NAME "PRIMARY" #define SEC_INDEX_NAME "c2_idx" /********************************************************************* Create an InnoDB database (sub-directory). */ static ib_err_t create_database( /*============*/ const char* name) { ib_bool_t err; err = ib_database_create(name); assert(err == IB_TRUE); return(DB_SUCCESS); } /********************************************************************* CREATE TABLE T(C1 INT, C2 INT, PK(C1), UNIQUE(C2)); */ static ib_err_t create_table( /*=========*/ const char* dbname, /*!< in: database name */ const char* name) /*!< in: table name */ { ib_trx_t ib_trx; ib_id_t table_id = 0; ib_err_t err = DB_SUCCESS; ib_tbl_sch_t ib_tbl_sch = NULL; ib_idx_sch_t ib_idx_sch = NULL; char table_name[IB_MAX_TABLE_NAME_LEN]; #ifdef __WIN__ sprintf(table_name, "%s/%s", dbname, name); #else snprintf(table_name, sizeof(table_name), "%s/%s", dbname, name); #endif /* Pass a table page size of 0, ie., use default page size. */ err = ib_table_schema_create( table_name, &ib_tbl_sch, IB_TBL_COMPACT, 0); assert(err == DB_SUCCESS); err = ib_table_schema_add_col( ib_tbl_sch, "c1", IB_INT, IB_COL_NONE, 0, sizeof(ib_i32_t)); assert(err == DB_SUCCESS); err = ib_table_schema_add_col( ib_tbl_sch, "c2", IB_INT, IB_COL_NONE, 0, sizeof(ib_i32_t)); assert(err == DB_SUCCESS); err = ib_table_schema_add_index(ib_tbl_sch, PRIMARY_INDEX_NAME, &ib_idx_sch); assert(err == DB_SUCCESS); /* Set prefix length to 0. */ err = ib_index_schema_add_col( ib_idx_sch, "c1", 0); assert(err == DB_SUCCESS); err = ib_index_schema_set_clustered(ib_idx_sch); assert(err == DB_SUCCESS); err = ib_table_schema_add_index(ib_tbl_sch, SEC_INDEX_NAME, &ib_idx_sch); assert(err == DB_SUCCESS); /* Set prefix length to 0. */ err = ib_index_schema_add_col( ib_idx_sch, "c2", 0); assert(err == DB_SUCCESS); err = ib_index_schema_set_unique(ib_idx_sch); assert(err == DB_SUCCESS); /* create table */ ib_trx = ib_trx_begin(IB_TRX_REPEATABLE_READ); err = ib_schema_lock_exclusive(ib_trx); assert(err == DB_SUCCESS); err = ib_table_create(ib_trx, ib_tbl_sch, &table_id); if (err == DB_SUCCESS) { err = ib_trx_commit(ib_trx); } else { fprintf(stderr, "Table: %s create failed: %s\n", table_name, ib_strerror(err)); err = ib_trx_rollback(ib_trx); } assert(err == DB_SUCCESS); if (ib_tbl_sch != NULL) { ib_table_schema_delete(ib_tbl_sch); } return(err); } /********************************************************************* Open a table and return a cursor for the table. */ static ib_err_t open_table( /*=======*/ const char* dbname, /*!< in: database name */ const char* name, /*!< in: table name */ ib_trx_t ib_trx, /*!< in: transaction */ ib_crsr_t* crsr) /*!< out: innodb cursor */ { ib_err_t err = DB_SUCCESS; char table_name[IB_MAX_TABLE_NAME_LEN]; #ifdef __WIN__ sprintf(table_name, "%s/%s", dbname, name); #else snprintf(table_name, sizeof(table_name), "%s/%s", dbname, name); #endif err = ib_cursor_open_table(table_name, ib_trx, crsr); assert(err == DB_SUCCESS); return(err); } /********************************************************************* INSERT INTO T VALUE(0, RANDOM(TEXT), RANDOM(TEXT)); ... 100 */ static ib_err_t insert_rows( /*===============*/ ib_trx_t trx, ib_crsr_t crsr) { ib_err_t err; ib_tpl_t tpl; tpl = ib_clust_read_tuple_create(crsr); assert(tpl != NULL); /* INSERT (1, 1) */ err = ib_tuple_write_i32(tpl, 0, 1); assert(err == DB_SUCCESS); err = ib_tuple_write_i32(tpl, 1, 1); assert(err == DB_SUCCESS); err = ib_cursor_insert_row(crsr, tpl); assert(err == DB_SUCCESS); tpl = ib_tuple_clear(tpl); assert(tpl != NULL); /* INSERT (2, 2) */ err = ib_tuple_write_i32(tpl, 0, 2); assert(err == DB_SUCCESS); err = ib_tuple_write_i32(tpl, 1, 2); assert(err == DB_SUCCESS); err = ib_cursor_insert_row(crsr, tpl); assert(err == DB_SUCCESS); tpl = ib_tuple_clear(tpl); assert(tpl != NULL); /* INSERT (1,3) <- fail on pkey */ err = ib_tuple_write_i32(tpl, 0, 1); assert(err == DB_SUCCESS); err = ib_tuple_write_i32(tpl, 1, 3); assert(err == DB_SUCCESS); err = ib_cursor_insert_row(crsr, tpl); assert(err == DB_DUPLICATE_KEY); char *d_table, *d_index; err = ib_get_duplicate_key(trx, &d_table, &d_index); assert(err == DB_SUCCESS); printf("DB_DUPLICATE_KEY: TABLE: %s INDEX: %s\n", d_table, d_index); assert(strcmp(d_table, TABLE_PATH) == 0); assert(strcmp(d_index, PRIMARY_INDEX_NAME) == 0); tpl = ib_tuple_clear(tpl); assert(tpl != NULL); /* insert (3,1) <- fail on secondary */ err = ib_tuple_write_i32(tpl, 0, 3); assert(err == DB_SUCCESS); err = ib_tuple_write_i32(tpl, 1, 1); assert(err == DB_SUCCESS); err = ib_cursor_insert_row(crsr, tpl); assert(err == DB_DUPLICATE_KEY); err = ib_get_duplicate_key(trx, &d_table, &d_index); assert(err == DB_SUCCESS); printf("DB_DUPLICATE_KEY: TABLE: %s INDEX: %s\n", d_table, d_index); assert(strcmp(d_table, TABLE_PATH) == 0); assert(strcmp(d_index, SEC_INDEX_NAME) == 0); tpl = ib_tuple_clear(tpl); assert(tpl != NULL); if (tpl != NULL) { ib_tuple_delete(tpl); } return(DB_SUCCESS); } int main(int argc, char** argv) { ib_err_t err; ib_crsr_t crsr; ib_trx_t ib_trx; (void)argc; (void)argv; err = ib_init(); assert(err == DB_SUCCESS); test_configure(); err = ib_startup("barracuda"); assert(err == DB_SUCCESS); err = create_database(DATABASE); assert(err == DB_SUCCESS); err = create_table(DATABASE, TABLE); assert(err == DB_SUCCESS); ib_trx = ib_trx_begin(IB_TRX_REPEATABLE_READ); assert(ib_trx != NULL); err = open_table(DATABASE, TABLE, ib_trx, &crsr); assert(err == DB_SUCCESS); err = insert_rows(ib_trx, crsr); assert(err == DB_SUCCESS); err = ib_cursor_close(crsr); assert(err == DB_SUCCESS); crsr = NULL; err = ib_trx_commit(ib_trx); assert(err == DB_SUCCESS); err = ib_shutdown(IB_SHUTDOWN_NORMAL); assert(err == DB_SUCCESS); return(0); } haildb-2.3.2/tests/ib_search.c0000644000175000017500000003530511513177357017076 0ustar00pcrewspcrews00000000000000/*********************************************************************** Copyright (c) 2010 Innobase Oy. All rights reserved. Copyright (c) 2010 Oracle. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ************************************************************************/ /* Simple single threaded test that does the equivalent of: Create a database CREATE TABLE T(c1 VARCHAR(n), c2 VARCHAR(n), c3 INT, PRIMARY KEY(c1, c2)); INSERT INTO T VALUES('abc', 'def', 1); INSERT INTO T VALUES('abc', 'zzz', 1); INSERT INTO T VALUES('ghi', 'jkl', 2); INSERT INTO T VALUES('mno', 'pqr', 3); INSERT INTO T VALUES('mno', 'xxx', 3); INSERT INTO T VALUES('stu', 'vwx', 4); SELECT * FROM T WHERE c1 = 'abc' AND c2 = 'def'; SELECT * FROM T WHERE c1 = 'abc'; SELECT * FROM T WHERE c1 >= 'g%'; SELECT * FROM T WHERE c1 = 'mno' AND c2 >= 'x%'; SELECT * FROM T WHERE c1 = 'mno' AND c2 >= 'z%'; DROP TABLE T; The test will create all the relevant sub-directories in the current working directory. */ #include #include #include #include #include "test0aux.h" #ifdef UNIV_DEBUG_VALGRIND #include #endif #define DATABASE "test" #define TABLE "t" /* A row from our test table. */ typedef struct row_t { char c1[32]; char c2[32]; ib_u32_t c3; } row_t; static row_t in_rows[] = { {"abc", "def", 1}, {"abc", "zzz", 1}, {"ghi", "jkl", 2}, {"mno", "pqr", 3}, {"mno", "xxx", 3}, {"stu", "vwx", 4}, {"", "", 0}}; #define COL_LEN(n) (sizeof(((row_t*)0)->n)) /********************************************************************* Create an InnoDB database (sub-directory). */ static ib_err_t create_database( /*============*/ const char* name) { ib_bool_t err; err = ib_database_create(name); assert(err == IB_TRUE); return(DB_SUCCESS); } /********************************************************************* CREATE TABLE T( c1 VARCHAR(n), c2 VARCHAR(n), c3 INT, PRIMARY KEY(c1, c2); */ static ib_err_t create_table( /*=========*/ const char* dbname, /*!< in: database name */ const char* name) /*!< in: table name */ { ib_trx_t ib_trx; ib_id_t table_id = 0; ib_err_t err = DB_SUCCESS; ib_tbl_sch_t ib_tbl_sch = NULL; ib_idx_sch_t ib_idx_sch = NULL; char table_name[IB_MAX_TABLE_NAME_LEN]; #ifdef __WIN__ sprintf(table_name, "%s/%s", dbname, name); #else snprintf(table_name, sizeof(table_name), "%s/%s", dbname, name); #endif /* Pass a table page size of 0, ie., use default page size. */ err = ib_table_schema_create( table_name, &ib_tbl_sch, IB_TBL_COMPACT, 0); assert(err == DB_SUCCESS); err = ib_table_schema_add_col( ib_tbl_sch, "c1", IB_VARCHAR, IB_COL_NONE, 0, COL_LEN(c1)-1); assert(err == DB_SUCCESS); err = ib_table_schema_add_col( ib_tbl_sch, "c2", IB_VARCHAR, IB_COL_NONE, 0, COL_LEN(c2)-1); assert(err == DB_SUCCESS); err = ib_table_schema_add_col( ib_tbl_sch, "c3", IB_INT, IB_COL_UNSIGNED, 0, COL_LEN(c3)); assert(err == DB_SUCCESS); err = ib_table_schema_add_index(ib_tbl_sch, "PRIMARY_KEY", &ib_idx_sch); assert(err == DB_SUCCESS); /* Set prefix length to 0. */ err = ib_index_schema_add_col( ib_idx_sch, "c1", 0); assert(err == DB_SUCCESS); /* Set prefix length to 0. */ err = ib_index_schema_add_col( ib_idx_sch, "c2", 0); assert(err == DB_SUCCESS); err = ib_index_schema_set_clustered(ib_idx_sch); assert(err == DB_SUCCESS); /* create table */ ib_trx = ib_trx_begin(IB_TRX_REPEATABLE_READ); err = ib_schema_lock_exclusive(ib_trx); assert(err == DB_SUCCESS); err = ib_table_create(ib_trx, ib_tbl_sch, &table_id); assert(err == DB_SUCCESS); err = ib_trx_commit(ib_trx); assert(err == DB_SUCCESS); if (ib_tbl_sch != NULL) { ib_table_schema_delete(ib_tbl_sch); } return(err); } /********************************************************************* Open a table and return a cursor for the table. */ static ib_err_t open_table( /*=======*/ const char* dbname, /*!< in: database name */ const char* name, /*!< in: table name */ ib_trx_t ib_trx, /*!< in: transaction */ ib_crsr_t* crsr) /*!< out: innodb cursor */ { ib_err_t err = DB_SUCCESS; char table_name[IB_MAX_TABLE_NAME_LEN]; #ifdef __WIN__ sprintf(table_name, "%s/%s", dbname, name); #else snprintf(table_name, sizeof(table_name), "%s/%s", dbname, name); #endif err = ib_cursor_open_table(table_name, ib_trx, crsr); assert(err == DB_SUCCESS); return(err); } /********************************************************************* INSERT INTO T VALUE('c1', 'c2', c3); */ static ib_err_t insert_rows( /*========*/ ib_crsr_t crsr) /*!< in, out: cursor to use for write */ { row_t* row; ib_tpl_t tpl = NULL; ib_err_t err = DB_ERROR; tpl = ib_clust_read_tuple_create(crsr); assert(tpl != NULL); for (row = in_rows; *row->c1; ++row) { err = ib_col_set_value(tpl, 0, row->c1, strlen(row->c1)); assert(err == DB_SUCCESS); err = ib_col_set_value(tpl, 1, row->c2, strlen(row->c2)); assert(err == DB_SUCCESS); err = ib_col_set_value(tpl, 2, &row->c3, sizeof(row->c3)); assert(err == DB_SUCCESS); err = ib_cursor_insert_row(crsr, tpl); assert(err == DB_SUCCESS); } assert(tpl != NULL); ib_tuple_delete(tpl); return(err); } /********************************************************************* SELECT * FROM T WHERE c1 = 'abc' AND c2 = 'def'; */ static ib_err_t do_moveto1( /*=======*/ ib_crsr_t crsr) { ib_err_t err; ib_tpl_t key_tpl; int res = ~0; printf("SELECT * FROM T WHERE c1 = 'abc' AND c2 = 'def';\n"); /* Create a tuple for searching an index. */ key_tpl = ib_sec_search_tuple_create(crsr); assert(key_tpl != NULL); /* Set the value to move to. */ err = ib_col_set_value(key_tpl, 0, "abc", 3); assert(err == DB_SUCCESS); err = ib_col_set_value(key_tpl, 1, "def", 3); assert(err == DB_SUCCESS); /* The InnoDB search function will not cache the next N records when this search mode is set. We should not try and do a cursor next/prev after this search. */ ib_cursor_set_match_mode(crsr, IB_EXACT_MATCH); /* Search for the key using the cluster index (PK) */ err = ib_cursor_moveto(crsr, key_tpl, IB_CUR_GE, &res); /* Should be no errors reported. */ assert(err == DB_SUCCESS); /* Must be positioned on the record, since we've specified an exact match. */ assert(res == 0); return(err); } /********************************************************************* SELECT * FROM T WHERE c1 = 'abc' AND c2 = 'def'; */ static ib_bool_t do_select1( /*=======*/ ib_tpl_t tpl) { print_tuple(stdout, tpl); return(IB_FALSE); } /********************************************************************* SELECT * FROM T WHERE c1 = 'abc'; */ static ib_err_t do_moveto2( /*=======*/ ib_crsr_t crsr) { ib_err_t err; ib_tpl_t key_tpl; int res = ~0; printf("SELECT * FROM T WHERE c1 = 'abc';\n"); /* Create a tuple for searching an index. */ key_tpl = ib_sec_search_tuple_create(crsr); assert(key_tpl != NULL); /* Set the value to move to. */ err = ib_col_set_value(key_tpl, 0, "abc", 3); assert(err == DB_SUCCESS); /* The InnoDB search function will cache the next N records when this search mode is set. */ ib_cursor_set_match_mode(crsr, IB_CLOSEST_MATCH); /* Search for the key using the cluster index (PK) */ err = ib_cursor_moveto(crsr, key_tpl, IB_CUR_GE, &res); /* Should be no errors reported. */ assert(err == DB_SUCCESS); /* Since we supplied an incomplete key it should be -1. */ assert(res == -1); return(err); } /********************************************************************* SELECT * FROM T WHERE c1 = 'abc'; */ static ib_bool_t do_select2( /*=======*/ ib_tpl_t tpl) { const char* c1; c1 = ib_col_get_value(tpl, 0); /* There are no SQL_NULL values in our test data. */ assert(c1 != NULL); if (strncmp(c1, "abc", 3) == 0) { print_tuple(stdout, tpl); return(IB_TRUE); } return(IB_FALSE); } /********************************************************************* SELECT * FROM T WHERE c1 >= 'g%'; */ static ib_err_t do_moveto3( /*=======*/ ib_crsr_t crsr) { ib_err_t err; ib_tpl_t key_tpl; int res = ~0; printf("SELECT * FROM T WHERE c1 >= 'g%%';\n"); /* Create a tuple for searching an index. */ key_tpl = ib_sec_search_tuple_create(crsr); assert(key_tpl != NULL); /* Set the value to move to. */ err = ib_col_set_value(key_tpl, 0, "g", 1); assert(err == DB_SUCCESS); /* The InnoDB search function will cache the next N records when this search mode is set. */ ib_cursor_set_match_mode(crsr, IB_CLOSEST_MATCH); /* Search for the key using the cluster index (PK) */ err = ib_cursor_moveto(crsr, key_tpl, IB_CUR_GE, &res); /* Should be no errors reported. */ assert(err == DB_SUCCESS); /* Since we supplied an incomplete key it should be -1. */ assert(res == -1); return(err); } /********************************************************************* SELECT * FROM T WHERE c1 >= 'g%'; */ static ib_bool_t do_select3( /*=======*/ ib_tpl_t tpl) { const char* c1; c1 = ib_col_get_value(tpl, 0); /* There are no SQL_NULL values in our test data. */ assert(c1 != NULL); if (strncmp(c1, "g", 1) >= 0) { print_tuple(stdout, tpl); return(IB_TRUE); } return(IB_FALSE); } /********************************************************************* SELECT * FROM T WHERE c1 = 'mno' AND c2 >= 'x%'; */ static ib_err_t do_moveto4( /*=======*/ ib_crsr_t crsr) { ib_err_t err; ib_tpl_t key_tpl; int res = ~0; printf("SELECT * FROM T WHERE c1 = 'mno' AND c2 >= 'x%%';\n"); /* Create a tuple for searching an index. */ key_tpl = ib_sec_search_tuple_create(crsr); assert(key_tpl != NULL); /* Set the value to move to. */ err = ib_col_set_value(key_tpl, 0, "mno", 3); assert(err == DB_SUCCESS); /* Set the value to move to. */ err = ib_col_set_value(key_tpl, 1, "x", 1); assert(err == DB_SUCCESS); /* The InnoDB search function will cache the next N records when this search mode is set. */ ib_cursor_set_match_mode(crsr, IB_EXACT_PREFIX); /* Search for the key using the cluster index (PK) */ err = ib_cursor_moveto(crsr, key_tpl, IB_CUR_GE, &res); /* Should be no errors reported. */ assert(err == DB_SUCCESS); /* Since we supplied an incomplete key it should be -1. */ assert(res == -1); return(err); } /********************************************************************* SELECT * FROM T WHERE c1 = 'mno' AND c2 >= 'x%'; */ static ib_bool_t do_select4( /*=======*/ ib_tpl_t tpl) { const char* c1; const char* c2; c1 = ib_col_get_value(tpl, 0); c2 = ib_col_get_value(tpl, 1); /* There are no SQL_NULL values in our test data. */ assert(c1 != NULL); assert(c2 != NULL); if (strncmp(c1, "mno", 3) == 0 && strncmp(c2, "x", 1) >= 0) { print_tuple(stdout, tpl); return(IB_TRUE); } return(IB_FALSE); } /********************************************************************* SELECT * FROM T WHERE c1 = 'mno' AND c2 >= 'z%'; */ static ib_err_t do_moveto5( /*=======*/ ib_crsr_t crsr) { ib_err_t err; ib_tpl_t key_tpl; int res = ~0; printf("SELECT * FROM T WHERE c1 = 'mno' AND c2 >= 'z%%';\n"); /* Create a tuple for searching an index. */ key_tpl = ib_sec_search_tuple_create(crsr); assert(key_tpl != NULL); /* Set the value to move to. */ err = ib_col_set_value(key_tpl, 0, "mno", 3); assert(err == DB_SUCCESS); /* Set the value to move to. */ err = ib_col_set_value(key_tpl, 1, "z", 1); assert(err == DB_SUCCESS); /* The InnoDB search function will cache the next N records when this search mode is set. */ ib_cursor_set_match_mode(crsr, IB_EXACT_PREFIX); /* Search for the key using the cluster index (PK) */ err = ib_cursor_moveto(crsr, key_tpl, IB_CUR_GE, &res); /* Record with given prefix doesn't exist in our test data. */ assert(err == DB_RECORD_NOT_FOUND); return(err); } /********************************************************************* SELECT * FROM T ; */ static ib_err_t do_query( /*=====*/ ib_crsr_t crsr, ib_err_t (*moveto)(ib_crsr_t), ib_bool_t (*select_func)(ib_tpl_t)) { ib_err_t err; ib_tpl_t tpl = NULL; err = moveto(crsr); if (err == DB_SUCCESS) { tpl = ib_clust_read_tuple_create(crsr); assert(tpl != NULL); } while (err == DB_SUCCESS) { err = ib_cursor_read_row(crsr, tpl); assert(err == DB_SUCCESS || err == DB_END_OF_INDEX || err == DB_RECORD_NOT_FOUND); if (err == DB_RECORD_NOT_FOUND || err == DB_END_OF_INDEX) { break; } if (!select_func(tpl)) { break; } err = ib_cursor_next(crsr); assert(err == DB_SUCCESS || err == DB_END_OF_INDEX || err == DB_RECORD_NOT_FOUND); tpl = ib_tuple_clear(tpl); assert(tpl != NULL); } if (tpl != NULL) { ib_tuple_delete(tpl); } if (err == DB_RECORD_NOT_FOUND || err == DB_END_OF_INDEX) { err = DB_SUCCESS; } return(err); } int main(int argc, char* argv[]) { ib_err_t err; ib_crsr_t crsr; ib_trx_t ib_trx; ib_u64_t version; (void)argc; (void)argv; version = ib_api_version(); printf("API: %d.%d.%d\n", (int) (version >> 32), /* Current version */ (int) ((version >> 16)) & 0xffff, /* Revisiion */ (int) (version & 0xffff)); /* Age */ err = ib_init(); assert(err == DB_SUCCESS); test_configure(); err = ib_startup("barracuda"); assert(err == DB_SUCCESS); err = create_database(DATABASE); assert(err == DB_SUCCESS); err = create_table(DATABASE, TABLE); assert(err == DB_SUCCESS); ib_trx = ib_trx_begin(IB_TRX_REPEATABLE_READ); assert(ib_trx != NULL); err = open_table(DATABASE, TABLE, ib_trx, &crsr); assert(err == DB_SUCCESS); err = ib_cursor_lock(crsr, IB_LOCK_IX); assert(err == DB_SUCCESS); err = insert_rows(crsr); assert(err == DB_SUCCESS); err = do_query(crsr, do_moveto1, do_select1); assert(err == DB_SUCCESS); err = do_query(crsr, do_moveto2, do_select2); assert(err == DB_SUCCESS); err = do_query(crsr, do_moveto3, do_select3); assert(err == DB_SUCCESS); err = do_query(crsr, do_moveto4, do_select4); assert(err == DB_SUCCESS); /* There should be no records to select, pass NULL. */ err = do_query(crsr, do_moveto5, NULL); assert(err == DB_SUCCESS); err = ib_cursor_close(crsr); assert(err == DB_SUCCESS); crsr = NULL; err = ib_trx_commit(ib_trx); assert(err == DB_SUCCESS); err = drop_table(DATABASE, TABLE); assert(err == DB_SUCCESS); err = ib_shutdown(IB_SHUTDOWN_NORMAL); assert(err == DB_SUCCESS); #ifdef UNIV_DEBUG_VALGRIND VALGRIND_DO_LEAK_CHECK; #endif return(EXIT_SUCCESS); } haildb-2.3.2/tests/ib_mt_t1.c0000644000175000017500000002716411513177357016661 0ustar00pcrewspcrews00000000000000/*********************************************************************** Copyright (c) 2009 Innobase Oy. All rights reserved. Copyright (c) 2009 Oracle. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ************************************************************************/ /********************************************************************** This is a derived table class that can be plugged into the mt_drv test suite. This particular table class overrides following DML and DDL functions: 1) UPDATE 2) INSERT 3) DELETE 4) CREATE 5) ALTER Following functions are used from generic base table class which is defined in mt_base.c: 1) SELECT 2) DROP 3) TRUNCATE The table definition is: CREATE TABLE t1 (first VARCHAR(128), last VARCHAR(128), score INT, ins_run INT, upd_run INT, PRIMARY KEY(first, last)); ***********************************************************************/ #include #include #include #include #include #include "test0aux.h" #include "ib_mt_drv.h" #include "ib_mt_base.h" #ifdef UNIV_DEBUG_VALGRIND #include #endif /********************************************************************** CREATE TABLE t1... @return DB_SUCCESS or error code */ static ib_err_t create_t1( /*======*/ void* arg) /*!< in: arguments for callback */ { ib_trx_t ib_trx; ib_id_t table_id = 0; ib_err_t err = DB_SUCCESS; ib_err_t err2 = DB_SUCCESS; ib_tbl_sch_t ib_tbl_sch = NULL; ib_idx_sch_t ib_idx_sch = NULL; char table_name[IB_MAX_TABLE_NAME_LEN]; cb_args_t* cb_arg = (cb_args_t *)arg; tbl_class_t* tbl = cb_arg->tbl; snprintf(table_name, sizeof(table_name), "%s/%s", tbl->db_name, tbl->name); /* Pass a table page size of 0, ie., use default page size. */ err = ib_table_schema_create( table_name, &ib_tbl_sch, tbl->format, tbl->page_size); assert(err == DB_SUCCESS); err = ib_table_schema_add_col( ib_tbl_sch, "first", IB_VARCHAR, IB_COL_NONE, 0, 128); assert(err == DB_SUCCESS); err = ib_table_schema_add_col( ib_tbl_sch, "last", IB_VARCHAR, IB_COL_NONE, 0, 128); assert(err == DB_SUCCESS); err = ib_table_schema_add_col( ib_tbl_sch, "score", IB_INT, IB_COL_UNSIGNED, 0, 4); assert(err == DB_SUCCESS); /* These two columns are where we put the value of run_number when doing INSERT or UPDATE */ err = ib_table_schema_add_col( ib_tbl_sch, "ins_run", IB_INT, IB_COL_UNSIGNED, 0, 4); assert(err == DB_SUCCESS); err = ib_table_schema_add_col( ib_tbl_sch, "upd_run", IB_INT, IB_COL_UNSIGNED, 0, 4); assert(err == DB_SUCCESS); err = ib_table_schema_add_index(ib_tbl_sch, "first_last", &ib_idx_sch); assert(err == DB_SUCCESS); /* Set prefix length to 0. */ err = ib_index_schema_add_col( ib_idx_sch, "first", 0); assert(err == DB_SUCCESS); /* Set prefix length to 0. */ err = ib_index_schema_add_col( ib_idx_sch, "last", 0); assert(err == DB_SUCCESS); err = ib_index_schema_set_clustered(ib_idx_sch); assert(err == DB_SUCCESS); /* create table */ ib_trx = ib_trx_begin(cb_arg->isolation_level); err = ib_schema_lock_exclusive(ib_trx); assert(err == DB_SUCCESS); err = ib_table_create(ib_trx, ib_tbl_sch, &table_id); err2 = ib_trx_commit(ib_trx); assert(err2 == DB_SUCCESS); if (ib_tbl_sch != NULL) { ib_table_schema_delete(ib_tbl_sch); } update_err_stats(cb_arg->err_st, err); return(err); } /********************************************************************** ALTER TABLE ... TODO: This should have FIC stuff. No-op for now. @return DB_SUCCESS or error code */ static ib_err_t alter_t1( /*=====*/ void* arg) /*!< in: arguments for callback */ { (void)arg; //fprintf(stderr, "t1: ALTER\n"); return(DB_SUCCESS); } /********************************************************************** INSERT INTO t1 VALUES (, , 0, run_number, 0) @return DB_SUCCESS or error code */ static ib_err_t insert_t1( /*======*/ void* arg) /*!< in: arguments for callback */ { int i; ib_err_t err; int val = 0; int zero = 0; ib_crsr_t crsr = NULL; ib_tpl_t tpl = NULL; char* ptr = malloc(8192); cb_args_t* cb_arg = (cb_args_t *)arg; tbl_class_t* tbl = cb_arg->tbl; //fprintf(stderr, "t1: INSERT\n"); assert(sizeof(val) == 4); err = open_table(tbl->db_name, tbl->name, cb_arg->trx, &crsr); if (err == DB_SUCCESS) { err = ib_cursor_lock(crsr, IB_LOCK_IX); } if (err == DB_SUCCESS) { err = ib_cursor_set_lock_mode(crsr, IB_LOCK_X); } if (err == DB_SUCCESS) { tpl = ib_clust_read_tuple_create(crsr); if (tpl == NULL) { err = DB_OUT_OF_MEMORY; } } for (i = 0; i < cb_arg->batch_size && err == DB_SUCCESS; ++i) { int l; l = gen_rand_text(ptr, 2); err = ib_col_set_value(tpl, 0, ptr, l); assert(err == DB_SUCCESS); l = gen_rand_text(ptr, 2); err = ib_col_set_value(tpl, 1, ptr, l); assert(err == DB_SUCCESS); err = ib_col_set_value(tpl, 2, &zero, 4); assert(err == DB_SUCCESS); err = ib_col_set_value(tpl, 3, &cb_arg->run_number, 4); assert(err == DB_SUCCESS); err = ib_col_set_value(tpl, 4, &zero, 4); assert(err == DB_SUCCESS); err = ib_cursor_insert_row(crsr, tpl); if (err == DB_SUCCESS) { update_err_stats(cb_arg->err_st, err); tpl = ib_tuple_clear(tpl); assert(tpl != NULL); } } if (err != DB_SUCCESS) { update_err_stats(cb_arg->err_st, err); } if (tpl != NULL) { ib_tuple_delete(tpl); } if (crsr != NULL) { ib_err_t cur_err; cur_err = ib_cursor_close(crsr); assert(cur_err == DB_SUCCESS); crsr = NULL; } free(ptr); return(err); } /********************************************************************** UPDATE t1 SET score = score + 100 AND upd_run = run_number WHERE first == 'a' @return DB_SUCCESS or error code */ static ib_err_t update_t1( /*======*/ void* arg) /*!< in: arguments for callback */ { ib_err_t err; int res = ~0; ib_tpl_t key_tpl = NULL; ib_tpl_t old_tpl = NULL; ib_tpl_t new_tpl = NULL; ib_crsr_t crsr = NULL; cb_args_t* cb_arg = (cb_args_t *)arg; tbl_class_t* tbl = cb_arg->tbl; //fprintf(stderr, "t1: UPDATE\n"); err = open_table(tbl->db_name, tbl->name, cb_arg->trx, &crsr); if (err != DB_SUCCESS) { goto err_exit; } err = ib_cursor_lock(crsr, IB_LOCK_IX); if (err != DB_SUCCESS) { goto err_exit; } err = ib_cursor_set_lock_mode(crsr, IB_LOCK_X); if (err != DB_SUCCESS) { goto err_exit; } /* Create a tuple for searching an index. */ key_tpl = ib_sec_search_tuple_create(crsr); assert(key_tpl != NULL); /* Set the value to look for. */ err = ib_col_set_value(key_tpl, 0, "a", 1); assert(err == DB_SUCCESS); /* Search for the key using the cluster index (PK) */ err = ib_cursor_moveto(crsr, key_tpl, IB_CUR_GE, &res); ib_tuple_delete(key_tpl); if (err != DB_SUCCESS) { goto err_exit; } /* Must be positioned on a record that's greater than search key. */ assert(res == -1); /* Create the tuple instance that we will use to update the table. old_tpl is used for reading the existing row and new_tpl will contain the update row data. */ old_tpl = ib_clust_read_tuple_create(crsr); assert(old_tpl != NULL); new_tpl = ib_clust_read_tuple_create(crsr); assert(new_tpl != NULL); /* Iterate over the records while the first column matches "a". */ while (1) { ib_u32_t score; const char* first; ib_ulint_t data_len; ib_col_meta_t col_meta; err = ib_cursor_read_row(crsr, old_tpl); assert(err == DB_SUCCESS); /* Get the first column value. */ first = ib_col_get_value(old_tpl, 0); ib_col_get_meta(old_tpl, 0, &col_meta); /* There are no SQL_NULL values in our test data. */ assert(first != NULL); /* Only update first names that are == "a". */ if (strncmp(first, "a", 1) != 0) { goto clean_exit; } /* Copy the old contents to the new tuple. */ err = ib_tuple_copy(new_tpl, old_tpl); /* Update the score column in the new tuple. */ data_len = ib_col_get_meta(old_tpl, 2, &col_meta); assert(data_len != IB_SQL_NULL); err = ib_tuple_read_u32(old_tpl, 2, &score); assert(err == DB_SUCCESS); score += 100; /* Set the updated value in the new tuple. */ err = ib_tuple_write_u32(new_tpl, 2, score); assert(err == DB_SUCCESS); /* Set the updated value in the new tuple. */ err = ib_tuple_write_u32(new_tpl, 4, cb_arg->run_number); assert(err == DB_SUCCESS); err = ib_cursor_update_row(crsr, old_tpl, new_tpl); if (err != DB_SUCCESS) { goto err_exit; } update_err_stats(cb_arg->err_st, err); /* Move to the next record to update. */ err = ib_cursor_next(crsr); if (err != DB_SUCCESS) { goto err_exit; } /* Reset the old and new tuple instances. */ old_tpl = ib_tuple_clear(old_tpl); assert(old_tpl != NULL); new_tpl = ib_tuple_clear(new_tpl); assert(new_tpl != NULL); } err_exit: update_err_stats(cb_arg->err_st, err); clean_exit: if (old_tpl != NULL) { ib_tuple_delete(old_tpl); } if (new_tpl != NULL) { ib_tuple_delete(new_tpl); } if (crsr != NULL) { ib_err_t err2; err2 = ib_cursor_close(crsr); assert(err2 == DB_SUCCESS); crsr = NULL; } return(err); } /********************************************************************** DELETE FROM t1 WHERE first == 'x' AND last == 'z' @return DB_SUCCESS or error code */ static ib_err_t delete_t1( /*======*/ void* arg) /*!< in: arguments for callback */ { ib_err_t err; int res = ~0; ib_tpl_t key_tpl = NULL; ib_crsr_t crsr = NULL; cb_args_t* cb_arg = (cb_args_t *)arg; tbl_class_t* tbl = cb_arg->tbl; //fprintf(stderr, "t1: DELETE\n"); err = open_table(tbl->db_name, tbl->name, cb_arg->trx, &crsr); if (err != DB_SUCCESS) { goto err_exit; } err = ib_cursor_lock(crsr, IB_LOCK_IX); if (err != DB_SUCCESS) { goto err_exit; } err = ib_cursor_set_lock_mode(crsr, IB_LOCK_X); if (err != DB_SUCCESS) { goto err_exit; } /* Create a tuple for searching an index. */ key_tpl = ib_sec_search_tuple_create(crsr); assert(key_tpl != NULL); /* Set the value to delete. */ err = ib_col_set_value(key_tpl, 0, "x", 1); assert(err == DB_SUCCESS); err = ib_col_set_value(key_tpl, 1, "z", 1); assert(err == DB_SUCCESS); /* Search for the key using the cluster index (PK) */ err = ib_cursor_moveto(crsr, key_tpl, IB_CUR_GE, &res); if (err != DB_SUCCESS) { goto err_exit; } if (res != 0) { goto clean_exit; } /* InnoDB handles the updating of all secondary indexes. */ err = ib_cursor_delete_row(crsr); if (err != DB_SUCCESS) { goto err_exit; } update_err_stats(cb_arg->err_st, err); goto clean_exit; err_exit: update_err_stats(cb_arg->err_st, err); clean_exit: if (key_tpl != NULL) { ib_tuple_delete(key_tpl); } if (crsr != NULL) { ib_err_t err2; err2 = ib_cursor_close(crsr); assert(err2 == DB_SUCCESS); crsr = NULL; } return(err); } /********************************************************************** Function to register this table class with mt_drv test suite */ void register_t1_table( /*==============*/ tbl_class_t* tbl) /*!< in/out: table class to register */ { assert(tbl != NULL); strcpy((char *)tbl->name, "t1"); tbl->dml_fn[DML_OP_TYPE_INSERT] = insert_t1; tbl->dml_fn[DML_OP_TYPE_UPDATE] = update_t1; tbl->dml_fn[DML_OP_TYPE_DELETE] = delete_t1; tbl->ddl_fn[DDL_OP_TYPE_CREATE] = create_t1; tbl->ddl_fn[DDL_OP_TYPE_ALTER] = alter_t1; } haildb-2.3.2/tests/ib_logger.c0000644000175000017500000000350211513177357017102 0ustar00pcrewspcrews00000000000000/*********************************************************************** Copyright (c) 2008 Innobase Oy. All rights reserved. Copyright (c) 2009 Oracle. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ************************************************************************/ /* Create a log function that doesn't print anything. Set it as the default InnoDB message logger. This function should not print any messages to stderr. It should simply startup and shutdown InnoDB. */ #include #include #include #include #include "test0aux.h" #ifdef UNIV_DEBUG_VALGRIND #include #endif #define DATABASE "test" #define TABLE "t" /********************************************************************* Just ignore all messages. */ static int null_logger( /*========*/ FILE* stream, const char* fmt, ...) { (void) stream; (void) fmt; return(0); } int main(int argc, char* argv[]) { ib_err_t err; (void) argc; (void) argv; err = ib_init(); assert(err == DB_SUCCESS); test_configure(); ib_logger_set(null_logger, NULL); err = ib_startup("barracuda"); assert(err == DB_SUCCESS); err = ib_shutdown(IB_SHUTDOWN_NORMAL); assert(err == DB_SUCCESS); return(EXIT_SUCCESS); } haildb-2.3.2/tests/ib_recover.c0000644000175000017500000001737311513177357017303 0ustar00pcrewspcrews00000000000000/*********************************************************************** Copyright (c) 2009 Innobase Oy. All rights reserved. Copyright (c) 2009 Oracle. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ************************************************************************/ /* Simple single threaded test that does the equivalent of: Create a database CREATE TABLE T(c1 INT, c2 VARCHAR(n), c3 BLOB, PK(c1)); INSERT INTO T VALUES(n, RANDOM(n), RANDOM(n)); ... The test will create all the relevant sub-directories in the current working directory. */ #include #include #include #include #ifdef __WIN__ # include #else /* __WIN__ */ # include #endif /* __WIN__ */ #include "test0aux.h" #ifdef UNIV_DEBUG_VALGRIND #include #endif #define DATABASE "test" #define TABLE "t" static const int N_TRX = 10; static const int N_RECS = 100; static const int C2_MAX_LEN = 256; static const int C3_MAX_LEN = 8192; /********************************************************************* Create an InnoDB database (sub-directory). */ static ib_err_t create_database( /*============*/ const char* name) { ib_bool_t err; err = ib_database_create(name); assert(err == IB_TRUE); return(DB_SUCCESS); } /********************************************************************* CREATE TABLE T(c1 INT, c2 VARCHAR(n), c3 BLOB, PK(c1)); */ static ib_err_t create_table( /*=========*/ const char* dbname, /*!< in: database name */ const char* name) /*!< in: table name */ { ib_trx_t ib_trx; ib_id_t table_id = 0; ib_err_t err = DB_SUCCESS; ib_tbl_sch_t ib_tbl_sch = NULL; ib_idx_sch_t ib_idx_sch = NULL; char table_name[IB_MAX_TABLE_NAME_LEN]; #ifdef __WIN__ sprintf(table_name, "%s/%s", dbname, name); #else snprintf(table_name, sizeof(table_name), "%s/%s", dbname, name); #endif /* Pass a table page size of 0, ie., use default page size. */ err = ib_table_schema_create( table_name, &ib_tbl_sch, IB_TBL_COMPACT, 0); assert(err == DB_SUCCESS); err = ib_table_schema_add_col( ib_tbl_sch, "c1", IB_INT, IB_COL_NONE, 0, 4); assert(err == DB_SUCCESS); err = ib_table_schema_add_col( ib_tbl_sch, "c2", IB_VARCHAR, IB_COL_NONE, 0, C2_MAX_LEN); assert(err == DB_SUCCESS); err = ib_table_schema_add_col( ib_tbl_sch, "c3", IB_BLOB, IB_COL_NONE, 0, 0); assert(err == DB_SUCCESS); err = ib_table_schema_add_index(ib_tbl_sch, "c1", &ib_idx_sch); assert(err == DB_SUCCESS); /* Set prefix length to 0. */ err = ib_index_schema_add_col( ib_idx_sch, "c1", 0); assert(err == DB_SUCCESS); err = ib_index_schema_set_clustered(ib_idx_sch); assert(err == DB_SUCCESS); /* create table */ ib_trx = ib_trx_begin(IB_TRX_REPEATABLE_READ); err = ib_schema_lock_exclusive(ib_trx); assert(err == DB_SUCCESS); err = ib_table_create(ib_trx, ib_tbl_sch, &table_id); assert(err == DB_SUCCESS || err == DB_TABLE_IS_BEING_USED); err = ib_trx_commit(ib_trx); assert(err == DB_SUCCESS); if (ib_tbl_sch != NULL) { ib_table_schema_delete(ib_tbl_sch); } return(err); } /********************************************************************* Open a table and return a cursor for the table. */ static ib_err_t open_table( /*=======*/ const char* dbname, /*!< in: database name */ const char* name, /*!< in: table name */ ib_trx_t ib_trx, /*!< in: transaction */ ib_crsr_t* crsr) /*!< out: innodb cursor */ { ib_err_t err = DB_SUCCESS; char table_name[IB_MAX_TABLE_NAME_LEN]; #ifdef __WIN__ sprintf(table_name, "%s/%s", dbname, name); #else snprintf(table_name, sizeof(table_name), "%s/%s", dbname, name); #endif err = ib_cursor_open_table(table_name, ib_trx, crsr); assert(err == DB_SUCCESS); return(err); } /********************************************************************* INSERT INTO T VALUE(N, RANDOM(n), RANDOM(n)); */ static ib_err_t insert_rows( /*========*/ ib_crsr_t crsr, /*!< in, out: cursor to use for write */ int start, /*!< in: in: start of c1 value */ int count) /*!< in: number of rows to insert */ { int i; int dups = 0; ib_tpl_t tpl = NULL; ib_err_t err = DB_ERROR; char* ptr = malloc(8192); assert(ptr != NULL); tpl = ib_clust_read_tuple_create(crsr); assert(tpl != NULL); for (i = start; i < start + count; ++i) { int l; err = ib_tuple_write_i32(tpl, 0, i); assert(err == DB_SUCCESS); l = gen_rand_text(ptr, C2_MAX_LEN); err = ib_col_set_value(tpl, 1, ptr, l); assert(err == DB_SUCCESS); l = gen_rand_text(ptr, C3_MAX_LEN); err = ib_col_set_value(tpl, 2, ptr, l); assert(err == DB_SUCCESS); err = ib_cursor_insert_row(crsr, tpl); assert(err == DB_SUCCESS || err == DB_DUPLICATE_KEY); if (err == DB_DUPLICATE_KEY) { err = DB_SUCCESS; ++dups; } } if (tpl != NULL) { ib_tuple_delete(tpl); } free(ptr); if (err == DB_SUCCESS) { if (dups == count) { err = DB_DUPLICATE_KEY; printf("Recover OK\n"); } else { assert(dups == 0); printf("Insert OK\n"); } } return(err); } #ifndef __WIN__ /********************************************************************* Set the runtime global options. */ static void set_options( /*========*/ int argc, char* argv[]) { int opt; while ((opt = getopt_long( argc, argv, "", ib_longopts, NULL)) != -1) { /* If it's an InnoDB parameter, then we let the auxillary function handle it. */ if (set_global_option(opt, optarg) != DB_SUCCESS) { print_usage(argv[0]); exit(EXIT_FAILURE); } } } #endif /* __WIN__ */ static ib_err_t test_phase_I(void) /*==============*/ { int i; ib_err_t err; int dups = 0; err = create_database(DATABASE); assert(err == DB_SUCCESS); err = create_table(DATABASE, TABLE); assert(err == DB_SUCCESS); for (i = 0; i < N_TRX; ++i) { ib_crsr_t crsr; ib_trx_t ib_trx; ib_trx = ib_trx_begin(IB_TRX_REPEATABLE_READ); assert(ib_trx != NULL); err = open_table(DATABASE, TABLE, ib_trx, &crsr); assert(err == DB_SUCCESS); err = ib_cursor_lock(crsr, IB_LOCK_IX); assert(err == DB_SUCCESS); err = insert_rows(crsr, i * N_RECS, N_RECS); assert(err == DB_SUCCESS || err == DB_DUPLICATE_KEY); if (err == DB_DUPLICATE_KEY) { ++dups; } err = ib_cursor_close(crsr); assert(err == DB_SUCCESS); crsr = NULL; err = ib_trx_commit(ib_trx); assert(err == DB_SUCCESS); } assert(dups == 0 || dups == N_TRX); return(dups == N_TRX ? DB_DUPLICATE_KEY : DB_SUCCESS); } /********************************************************************* Restart the process with the same args. */ static void restart( /*====*/ int argc, char* argv[]) { (void) argc; #ifdef __WIN__ _execvp(argv[0], argv); #else execvp(argv[0], argv); #endif perror("execvp"); abort(); } int main(int argc, char* argv[]) { ib_err_t err; print_version(); err = ib_init(); assert(err == DB_SUCCESS); test_configure(); #ifndef __WIN__ set_options(argc, argv); #endif err = ib_startup("barracuda"); assert(err == DB_SUCCESS); err = test_phase_I(); if (err == DB_SUCCESS) { restart(argc, argv); /* Shouldn't get here. */ abort(); } else { /* Recovery was successful. */ assert(err == DB_DUPLICATE_KEY); err = drop_table(DATABASE, TABLE); assert(err == DB_SUCCESS); err = ib_shutdown(IB_SHUTDOWN_NORMAL); assert(err == DB_SUCCESS); } return(EXIT_SUCCESS); } haildb-2.3.2/tests/ib_zip.c0000644000175000017500000001356211513177357016434 0ustar00pcrewspcrews00000000000000/*********************************************************************** Copyright (c) 2009 Innobase Oy. All rights reserved. Copyright (c) 2009 Oracle. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ************************************************************************/ /* Simple single threaded test that does the equivalent of: Create a database if not exists CREATE TABLE T(c1 VARCHAR(n), c2 INT, PK(c1)) if not exists; INSERT INTO T VALUES('x', 1); ... The test will create all the relevant sub-directories in the current working directory. */ #include #include #include #include #include "test0aux.h" #ifdef UNIV_DEBUG_VALGRIND #include #endif #define DATABASE "test" #define TABLE "t" /********************************************************************* Create an InnoDB database (sub-directory). */ static ib_err_t create_database( /*============*/ const char* name) { ib_bool_t err; err = ib_database_create(name); assert(err == IB_TRUE); return(DB_SUCCESS); } /********************************************************************* CREATE TABLE T( first VARCHAR(n), last VARCHAR(n), score INT, PRIMARY KEY(first, last); */ static ib_err_t create_table( /*=========*/ const char* dbname, /*!< in: database name */ const char* name, /*!< in: table name */ int page_size) /*!< in: page size */ { ib_trx_t ib_trx; ib_id_t table_id = 0; ib_err_t err = DB_SUCCESS; ib_tbl_sch_t ib_tbl_sch = NULL; ib_idx_sch_t ib_idx_sch = NULL; ib_tbl_fmt_t tbl_fmt = IB_TBL_COMPACT; char table_name[IB_MAX_TABLE_NAME_LEN]; #ifdef __WIN__ sprintf(table_name, "%s/%s", dbname, name); #else snprintf(table_name, sizeof(table_name), "%s/%s", dbname, name); #endif if (page_size > 0) { tbl_fmt = IB_TBL_COMPRESSED; printf("Creating compressed table with page size %d\n", page_size); } /* Pass a table page size of 0, ie., use default page size. */ err = ib_table_schema_create( table_name, &ib_tbl_sch, tbl_fmt, page_size); if (err == DB_UNSUPPORTED) { return(DB_SUCCESS); } assert(err == DB_SUCCESS); err = ib_table_schema_add_col( ib_tbl_sch, "c1", IB_VARCHAR, IB_COL_NONE, 0, 10); assert(err == DB_SUCCESS); err = ib_table_schema_add_col( ib_tbl_sch, "c2", IB_INT, 0, 0, sizeof(ib_i32_t)); assert(err == DB_SUCCESS); err = ib_table_schema_add_index(ib_tbl_sch, "c1", &ib_idx_sch); assert(err == DB_SUCCESS); /* Set prefix length to 0. */ err = ib_index_schema_add_col( ib_idx_sch, "c1", 0); assert(err == DB_SUCCESS); err = ib_index_schema_set_clustered(ib_idx_sch); assert(err == DB_SUCCESS); /* create table */ ib_trx = ib_trx_begin(IB_TRX_REPEATABLE_READ); err = ib_schema_lock_exclusive(ib_trx); assert(err == DB_SUCCESS); err = ib_table_create(ib_trx, ib_tbl_sch, &table_id); assert(err == DB_SUCCESS || err == DB_TABLE_IS_BEING_USED); err = ib_trx_commit(ib_trx); assert(err == DB_SUCCESS); if (ib_tbl_sch != NULL) { ib_table_schema_delete(ib_tbl_sch); } return(err); } /********************************************************************* Open a table and return a cursor for the table. */ static ib_err_t open_table( /*=======*/ const char* dbname, /*!< in: database name */ const char* name, /*!< in: table name */ ib_trx_t ib_trx, /*!< in: transaction */ ib_crsr_t* crsr) /*!< out: innodb cursor */ { ib_err_t err = DB_SUCCESS; char table_name[IB_MAX_TABLE_NAME_LEN]; #ifdef __WIN__ sprintf(table_name, "%s/%s", dbname, name); #else snprintf(table_name, sizeof(table_name), "%s/%s", dbname, name); #endif err = ib_cursor_open_table(table_name, ib_trx, crsr); assert(err == DB_SUCCESS); return(err); } /********************************************************************* INSERT INTO T VALUE('x', 1); */ static void insert_row( /*=======*/ ib_crsr_t crsr) /*!< in, out: cursor to use for write */ { ib_tpl_t tpl = NULL; ib_err_t err = DB_ERROR; tpl = ib_clust_read_tuple_create(crsr); assert(tpl != NULL); err = ib_col_set_value(tpl, 0, "x", 1); assert(err == DB_SUCCESS); err = ib_tuple_write_i32(tpl, 1, 1); assert(err == DB_SUCCESS); err = ib_cursor_insert_row(crsr, tpl); assert(err == DB_SUCCESS || err == DB_DUPLICATE_KEY); ib_tuple_delete(tpl); } int main(int argc, char* argv[]) { ib_err_t err; ib_crsr_t crsr; ib_trx_t ib_trx; ib_u64_t version; (void)argc; (void)argv; version = ib_api_version(); printf("API: %d.%d.%d\n", (int) (version >> 32), /* Current version */ (int) ((version >> 16)) & 0xffff, /* Revisiion */ (int) (version & 0xffff)); /* Age */ err = ib_init(); assert(err == DB_SUCCESS); test_configure(); err = ib_startup("barracuda"); assert(err == DB_SUCCESS); err = create_database(DATABASE); assert(err == DB_SUCCESS); err = create_table(DATABASE, TABLE, 4); assert(err == DB_SUCCESS); ib_trx = ib_trx_begin(IB_TRX_REPEATABLE_READ); assert(ib_trx != NULL); err = open_table(DATABASE, TABLE, ib_trx, &crsr); assert(err == DB_SUCCESS); err = ib_cursor_lock(crsr, IB_LOCK_IX); assert(err == DB_SUCCESS); insert_row(crsr); err = ib_cursor_close(crsr); assert(err == DB_SUCCESS); crsr = NULL; err = ib_trx_commit(ib_trx); assert(err == DB_SUCCESS); err = ib_shutdown(IB_SHUTDOWN_NORMAL); assert(err == DB_SUCCESS); #ifdef UNIV_DEBUG_VALGRIND VALGRIND_DO_LEAK_CHECK; #endif return(EXIT_SUCCESS); } haildb-2.3.2/tests/ib_ddl.c0000644000175000017500000002606211513177357016374 0ustar00pcrewspcrews00000000000000/*********************************************************************** Copyright (c) 2009 Innobase Oy. All rights reserved. Copyright (c) 2009 Oracle. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ************************************************************************/ /* Simple single threaded test that does the equivalent of: Create a database CREATE TABLE T(c1 INT, c2 VARCHAR(10), c3 BLOB); INSERT INTO T VALUES(1, '1'); ... CREATE INDEX T_C1 ON T(c1); CREATE INDEX T_C2 ON T(c2); CREATE INDEX T_C2 ON T(c3(10)); DROP TABLE T; The test will create all the relevant sub-directories in the current working directory. */ #include #include #include #include #include "test0aux.h" #ifdef UNIV_DEBUG_VALGRIND #include #endif #define DATABASE "test" #define TABLE "ib_ddl" /********************************************************************* Create an InnoDB database (sub-directory). */ static ib_err_t create_database( /*============*/ const char* name) { ib_bool_t err; err = ib_database_create(name); assert(err == IB_TRUE); return(DB_SUCCESS); } /********************************************************************* CREATE TABLE T(C1 INT, C2 VARCHAR(10), C3 BLOB); */ static ib_err_t create_table( /*=========*/ const char* dbname, /*!< in: database name */ const char* name) /*!< in: table name */ { ib_trx_t ib_trx; ib_id_t table_id = 0; ib_err_t err = DB_SUCCESS; ib_tbl_sch_t ib_tbl_sch = NULL; char table_name[IB_MAX_TABLE_NAME_LEN]; #ifdef __WIN__ sprintf(table_name, "%s/%s", dbname, name); #else snprintf(table_name, sizeof(table_name), "%s/%s", dbname, name); #endif /* Pass a table page size of 0, ie., use default page size. */ err = ib_table_schema_create( table_name, &ib_tbl_sch, IB_TBL_COMPACT, 0); assert(err == DB_SUCCESS); err = ib_table_schema_add_col( ib_tbl_sch, "c1", IB_INT, IB_COL_NONE, 0, sizeof(ib_i32_t)); assert(err == DB_SUCCESS); err = ib_tbl_sch_add_varchar_col(ib_tbl_sch, "c2", 10); assert(err == DB_SUCCESS); err = ib_tbl_sch_add_blob_col(ib_tbl_sch, "c3"); assert(err == DB_SUCCESS); /* create table */ ib_trx = ib_trx_begin(IB_TRX_REPEATABLE_READ); err = ib_schema_lock_exclusive(ib_trx); assert(err == DB_SUCCESS); err = ib_table_create(ib_trx, ib_tbl_sch, &table_id); if (err == DB_SUCCESS) { err = ib_trx_commit(ib_trx); } else { fprintf(stderr, "Table: %s create failed: %s\n", table_name, ib_strerror(err)); err = ib_trx_rollback(ib_trx); } assert(err == DB_SUCCESS); if (ib_tbl_sch != NULL) { ib_table_schema_delete(ib_tbl_sch); } return(err); } /********************************************************************* Open a table and return a cursor for the table. */ static ib_err_t open_table( /*=======*/ const char* dbname, /*!< in: database name */ const char* name, /*!< in: table name */ ib_trx_t ib_trx, /*!< in: transaction */ ib_crsr_t* crsr) /*!< out: innodb cursor */ { ib_err_t err = DB_SUCCESS; char table_name[IB_MAX_TABLE_NAME_LEN]; #ifdef __WIN__ sprintf(table_name, "%s/%s", dbname, name); #else snprintf(table_name, sizeof(table_name), "%s/%s", dbname, name); #endif err = ib_cursor_open_table(table_name, ib_trx, crsr); assert(err == DB_SUCCESS); return(err); } /********************************************************************* INSERT INTO T VALUE(0, RANDOM(TEXT), RANDOM(TEXT)); ... 100 */ static ib_err_t insert_random_rows( /*===============*/ ib_crsr_t crsr) /*!< in, out: cursor to use for write */ { ib_i32_t i; ib_err_t err; ib_tpl_t tpl; char* ptr = malloc(8192); tpl = ib_clust_read_tuple_create(crsr); assert(tpl != NULL); for (i = 0; i < 100; ++i) { int l; err = ib_tuple_write_i32(tpl, 0, i % 10); assert(err == DB_SUCCESS); l = gen_rand_text(ptr, 10); err = ib_col_set_value(tpl, 1, ptr, l); assert(err == DB_SUCCESS); l = gen_rand_text(ptr, 8192); err = ib_col_set_value(tpl, 2, ptr, l); assert(err == DB_SUCCESS); err = ib_cursor_insert_row(crsr, tpl); assert(err == DB_SUCCESS); tpl = ib_tuple_clear(tpl); assert(tpl != NULL); } if (tpl != NULL) { ib_tuple_delete(tpl); } free(ptr); return(err); } /********************************************************************* Create a secondary indexes on a table. @return DB_SUCCESS or error code */ static ib_err_t create_sec_index( /*=============*/ const char* table_name, /*!< in: table name */ const char* col_name, /*!< in: column name */ int prefix_len) /*!< in: prefix index length */ { ib_err_t err; ib_trx_t ib_trx; ib_id_t index_id = 0; ib_idx_sch_t ib_idx_sch = NULL; char index_name[IB_MAX_TABLE_NAME_LEN]; ib_trx = ib_trx_begin(IB_TRX_REPEATABLE_READ); err = ib_schema_lock_exclusive(ib_trx); assert(err == DB_SUCCESS); #ifdef __WIN__ sprintf(index_name, "%s_%s", table_name, col_name); #else snprintf(index_name, sizeof(index_name), "%s_%s", table_name, col_name); #endif err = ib_index_schema_create( ib_trx, index_name, table_name, &ib_idx_sch); assert(err == DB_SUCCESS); err = ib_index_schema_add_col(ib_idx_sch, col_name, prefix_len); assert(err == DB_SUCCESS); err = ib_index_create(ib_idx_sch, &index_id); if (ib_idx_sch != NULL) { ib_index_schema_delete(ib_idx_sch); ib_idx_sch = NULL; } if (err == DB_SUCCESS) { err = ib_trx_commit(ib_trx); } else { err = ib_trx_rollback(ib_trx); } assert(err == DB_SUCCESS); return(err); } /********************************************************************* Create secondary indexes on T(C1), T(C2), T(C3). */ static ib_err_t create_sec_index_1( /*===============*/ const char* dbname, /*!< in: database name */ const char* name) /*!< in: table to drop */ { ib_err_t err; char table_name[IB_MAX_TABLE_NAME_LEN]; #ifdef __WIN__ sprintf(table_name, "%s/%s", dbname, name); #else snprintf(table_name, sizeof(table_name), "%s/%s", dbname, name); #endif err = create_sec_index(table_name, "c1", 0); if (err == DB_SUCCESS) { err = create_sec_index(table_name, "c2", 0); } if (err == DB_SUCCESS) { err = create_sec_index(table_name, "c3", 10); } return(err); } /********************************************************************* Open the secondary index. */ static ib_err_t open_sec_index( /*===========*/ ib_crsr_t crsr, /*!< in: table cusor */ const char* index_name) /*!< in: sec. index to open */ { ib_err_t err; ib_crsr_t idx_crsr; err = ib_cursor_open_index_using_name(crsr, index_name, &idx_crsr); assert(err == DB_SUCCESS); err = ib_cursor_close(idx_crsr); assert(err == DB_SUCCESS); return(err); } /********************************************************************* Open the secondary indexes on T(C1), T(C2), T(C3). */ static ib_err_t open_sec_index_1( /*=============*/ const char* dbname, /*!< in: database name */ const char* name) /*!< in: table name */ { ib_crsr_t crsr; ib_trx_t ib_trx; ib_err_t err = DB_SUCCESS; char index_name[IB_MAX_TABLE_NAME_LEN]; char table_name[IB_MAX_TABLE_NAME_LEN]; #ifdef __WIN__ sprintf(table_name, "%s/%s", dbname, name); #else snprintf(table_name, sizeof(table_name), "%s/%s", dbname, name); #endif ib_trx = ib_trx_begin(IB_TRX_REPEATABLE_READ); err = ib_cursor_open_table(table_name, ib_trx, &crsr); assert(err == DB_SUCCESS); #ifdef __WIN__ sprintf(index_name, "%s_%s", table_name, "c1"); #else snprintf(index_name, sizeof(index_name), "%s_%s", table_name, "c1"); #endif err = open_sec_index(crsr, index_name); assert(err == DB_SUCCESS); #ifdef __WIN__ sprintf(index_name, "%s_%s", table_name, "c2"); #else snprintf(index_name, sizeof(index_name), "%s_%s", table_name, "c2"); #endif err = open_sec_index(crsr, index_name); assert(err == DB_SUCCESS); #ifdef __WIN__ sprintf(index_name, "%s_%s", table_name, "c3"); #else snprintf(index_name, sizeof(index_name), "%s_%s", table_name, "c3"); #endif err = open_sec_index(crsr, index_name); assert(err == DB_SUCCESS); err = ib_cursor_close(crsr); assert(err == DB_SUCCESS); err = ib_trx_commit(ib_trx); assert(err == DB_SUCCESS); return(err); } #define TEMP_INDEX_PREFIX '\377' /* from ut0ut.h */ /********************************************************************* Tests creating a index with a name that InnoDB uses for temporary indexs @return DB_SUCCESS or error code */ static ib_err_t test_create_temp_index( /*=============*/ const char* dbname, const char* name, /*!< in: table name */ const char* col_name) /*!< in: column name */ { ib_err_t err; ib_trx_t ib_trx; ib_id_t index_id = 0; ib_idx_sch_t ib_idx_sch = NULL; char index_name[IB_MAX_TABLE_NAME_LEN]; char table_name[IB_MAX_TABLE_NAME_LEN]; #ifdef __WIN__ sprintf(table_name, "%s/%s", dbname, name); #else snprintf(table_name, sizeof(table_name), "%s/%s", dbname, name); #endif ib_trx = ib_trx_begin(IB_TRX_REPEATABLE_READ); err = ib_schema_lock_exclusive(ib_trx); assert(err == DB_SUCCESS); #ifdef __WIN__ sprintf(index_name, "%c%s_%s", TEMP_INDEX_PREFIX, table_name, col_name); #else snprintf(index_name, sizeof(index_name), "%c%s_%s", TEMP_INDEX_PREFIX, table_name, col_name); #endif err = ib_index_schema_create( ib_trx, index_name, table_name, &ib_idx_sch); assert(err == DB_INVALID_INPUT); if (ib_idx_sch != NULL) { ib_index_schema_delete(ib_idx_sch); ib_idx_sch = NULL; } if (err == DB_SUCCESS) { err = ib_trx_commit(ib_trx); } else { err = ib_trx_rollback(ib_trx); } assert(err == DB_SUCCESS); return(err); } int main(int argc, char* argv[]) { ib_err_t err; ib_crsr_t crsr; ib_trx_t ib_trx; (void) argc; (void) argv; err = ib_init(); assert(err == DB_SUCCESS); test_configure(); err = ib_startup("barracuda"); assert(err == DB_SUCCESS); err = create_database(DATABASE); assert(err == DB_SUCCESS); err = create_table(DATABASE, TABLE); assert(err == DB_SUCCESS); ib_trx = ib_trx_begin(IB_TRX_REPEATABLE_READ); assert(ib_trx != NULL); err = open_table(DATABASE, TABLE, ib_trx, &crsr); assert(err == DB_SUCCESS); err = ib_cursor_lock(crsr, IB_LOCK_IX); assert(err == DB_SUCCESS); err = insert_random_rows(crsr); assert(err == DB_SUCCESS); err = ib_cursor_close(crsr); assert(err == DB_SUCCESS); crsr = NULL; err = ib_trx_commit(ib_trx); assert(err == DB_SUCCESS); err = create_sec_index_1(DATABASE, TABLE); assert(err == DB_SUCCESS); err = test_create_temp_index(DATABASE, TABLE, "c2"); err = open_sec_index_1(DATABASE, TABLE); assert(err == DB_SUCCESS); err = drop_table(DATABASE, TABLE); assert(err == DB_SUCCESS); err = ib_shutdown(IB_SHUTDOWN_NORMAL); assert(err == DB_SUCCESS); #ifdef UNIV_DEBUG_VALGRIND VALGRIND_DO_LEAK_CHECK; #endif return(EXIT_SUCCESS); } haildb-2.3.2/tests/ib_types.c0000644000175000017500000001735211513177357016777 0ustar00pcrewspcrews00000000000000/*********************************************************************** Copyright (c) 2008 Innobase Oy. All rights reserved. Copyright (c) 2008 Oracle. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ************************************************************************/ /* Simple single threaded test that does the equivalent of: Create a database CREATE TABLE T( c1 VARCHAR(n), c2 INT NOT NULL, c3 FLOAT, c4 DOUBLE, c5 BLOB, c6 DECIMAL, PK(c1)); INSERT INTO T VALUES('x', 1, 2.0, 3.0, 'xxx'); ... SELECT * FROM T; DROP TABLE T; The test will create all the relevant sub-directories in the current working directory. */ #include #include #include #include #include "test0aux.h" #ifdef UNIV_DEBUG_VALGRIND #include #endif #define DATABASE "test" #define TABLE "t" /********************************************************************* Create an InnoDB database (sub-directory). */ static ib_err_t create_database( /*============*/ const char* name) { ib_bool_t err; err = ib_database_create(name); assert(err == IB_TRUE); return(DB_SUCCESS); } /********************************************************************* CREATE TABLE T( c1 VARCHAR(n), c2 INT NOT NULL, c3 FLOAT, c4 DOUBLE, c5 BLOB, c6 DECIMAL, PK(c1)); */ static ib_err_t create_table( /*=========*/ const char* dbname, /*!< in: database name */ const char* name) /*!< in: table name */ { ib_trx_t ib_trx; ib_id_t table_id = 0; ib_err_t err = DB_SUCCESS; ib_tbl_sch_t ib_tbl_sch = NULL; ib_idx_sch_t ib_idx_sch = NULL; char table_name[IB_MAX_TABLE_NAME_LEN]; #ifdef __WIN__ sprintf(table_name, "%s/%s", dbname, name); #else snprintf(table_name, sizeof(table_name), "%s/%s", dbname, name); #endif /* Pass a table page size of 0, ie., use default page size. */ err = ib_table_schema_create( table_name, &ib_tbl_sch, IB_TBL_COMPACT, 0); assert(err == DB_SUCCESS); err = ib_table_schema_add_col( ib_tbl_sch, "c1", IB_VARCHAR, IB_COL_NONE, 0, 10); assert(err == DB_SUCCESS); err = ib_table_schema_add_col( ib_tbl_sch, "c2", IB_INT, IB_COL_NOT_NULL, 0, sizeof(ib_u32_t)); assert(err == DB_SUCCESS); err = ib_table_schema_add_col( ib_tbl_sch, "c3", IB_FLOAT, IB_COL_NONE, 0, sizeof(float)); assert(err == DB_SUCCESS); err = ib_table_schema_add_col( ib_tbl_sch, "c4", IB_DOUBLE, IB_COL_NONE, 0, sizeof(double)); assert(err == DB_SUCCESS); err = ib_table_schema_add_col( ib_tbl_sch, "c5", IB_BLOB, IB_COL_NONE, 0, 0); assert(err == DB_SUCCESS); err = ib_table_schema_add_col( ib_tbl_sch, "c6", IB_DECIMAL, IB_COL_NONE, 0, 0); assert(err == DB_SUCCESS); err = ib_table_schema_add_index(ib_tbl_sch, "PRIMARY", &ib_idx_sch); assert(err == DB_SUCCESS); /* Set prefix length to 0. */ err = ib_index_schema_add_col( ib_idx_sch, "c1", 0); assert(err == DB_SUCCESS); err = ib_index_schema_set_clustered(ib_idx_sch); assert(err == DB_SUCCESS); /* create table */ ib_trx = ib_trx_begin(IB_TRX_REPEATABLE_READ); err = ib_schema_lock_exclusive(ib_trx); assert(err == DB_SUCCESS); err = ib_table_create(ib_trx, ib_tbl_sch, &table_id); assert(err == DB_SUCCESS); err = ib_trx_commit(ib_trx); assert(err == DB_SUCCESS); if (ib_tbl_sch != NULL) { ib_table_schema_delete(ib_tbl_sch); } return(err); } /********************************************************************* Open a table and return a cursor for the table. */ static ib_err_t open_table( /*=======*/ const char* dbname, /*!< in: database name */ const char* name, /*!< in: table name */ ib_trx_t ib_trx, /*!< in: transaction */ ib_crsr_t* crsr) /*!< out: innodb cursor */ { ib_err_t err = DB_SUCCESS; char table_name[IB_MAX_TABLE_NAME_LEN]; #ifdef __WIN__ sprintf(table_name, "%s/%s", dbname, name); #else snprintf(table_name, sizeof(table_name), "%s/%s", dbname, name); #endif err = ib_cursor_open_table(table_name, ib_trx, crsr); assert(err == DB_SUCCESS); return(err); } /********************************************************************* INSERT INTO T VALUE(c1, c2, c3, c4, c5, c6); */ static ib_err_t insert_rows( /*========*/ ib_crsr_t crsr) /*!< in, out: cursor to use for write */ { ib_err_t err; ib_tpl_t tpl = NULL; tpl = ib_clust_read_tuple_create(crsr); assert(tpl != NULL); err = ib_col_set_value(tpl, 0, "abcdefghij", 10); assert(err == DB_SUCCESS); err = ib_tuple_write_float(tpl, 2, 2.0); assert(err == DB_SUCCESS); err = ib_tuple_write_double(tpl, 3, 3.0); assert(err == DB_SUCCESS); err = ib_col_set_value(tpl, 4, "BLOB", 4); assert(err == DB_SUCCESS); err = ib_col_set_value(tpl, 5, "1.23", 4); assert(err == DB_SUCCESS); /* Check for NULL constraint violation. */ err = ib_cursor_insert_row(crsr, tpl); assert(err == DB_DATA_MISMATCH); /* Set column C2 value and retry operation. */ err = ib_tuple_write_u32(tpl, 1, 1); assert(err == DB_SUCCESS); err = ib_cursor_insert_row(crsr, tpl); assert(err == DB_SUCCESS); ib_tuple_delete(tpl); return(DB_SUCCESS); } /********************************************************************* SELECT * FROM T; */ static ib_err_t do_query( /*=====*/ ib_crsr_t crsr) { ib_err_t err; ib_tpl_t tpl; tpl = ib_clust_read_tuple_create(crsr); assert(tpl != NULL); err = ib_cursor_first(crsr); assert(err == DB_SUCCESS); while (err == DB_SUCCESS) { err = ib_cursor_read_row(crsr, tpl); assert(err == DB_SUCCESS || err == DB_END_OF_INDEX || err == DB_RECORD_NOT_FOUND); if (err == DB_RECORD_NOT_FOUND || err == DB_END_OF_INDEX) { break; } print_tuple(stdout, tpl); err = ib_cursor_next(crsr); assert(err == DB_SUCCESS || err == DB_END_OF_INDEX || err == DB_RECORD_NOT_FOUND); tpl = ib_tuple_clear(tpl); assert(tpl != NULL); } if (tpl != NULL) { ib_tuple_delete(tpl); } if (err == DB_RECORD_NOT_FOUND || err == DB_END_OF_INDEX) { err = DB_SUCCESS; } return(err); } int main(int argc, char* argv[]) { ib_err_t err; ib_crsr_t crsr; ib_trx_t ib_trx; (void)argc; (void)argv; err = ib_init(); assert(err == DB_SUCCESS); test_configure(); err = ib_startup("barracuda"); assert(err == DB_SUCCESS); err = create_database(DATABASE); assert(err == DB_SUCCESS); printf("Create table\n"); err = create_table(DATABASE, TABLE); assert(err == DB_SUCCESS); printf("Begin transaction\n"); ib_trx = ib_trx_begin(IB_TRX_REPEATABLE_READ); assert(ib_trx != NULL); printf("Open cursor\n"); err = open_table(DATABASE, TABLE, ib_trx, &crsr); assert(err == DB_SUCCESS); printf("Lock table in IX\n"); err = ib_cursor_lock(crsr, IB_LOCK_IX); assert(err == DB_SUCCESS); printf("Insert rows\n"); err = insert_rows(crsr); assert(err == DB_SUCCESS); printf("Query table\n"); err = do_query(crsr); assert(err == DB_SUCCESS); printf("Close cursor\n"); err = ib_cursor_close(crsr); assert(err == DB_SUCCESS); crsr = NULL; printf("Commit transaction\n"); err = ib_trx_commit(ib_trx); assert(err == DB_SUCCESS); printf("Drop table\n"); err = drop_table(DATABASE, TABLE); assert(err == DB_SUCCESS); err = ib_shutdown(IB_SHUTDOWN_NORMAL); assert(err == DB_SUCCESS); #ifdef UNIV_DEBUG_VALGRIND VALGRIND_DO_LEAK_CHECK; #endif return(EXIT_SUCCESS); } haildb-2.3.2/tests/ib_table_statistics.c0000644000175000017500000002105511513177357021167 0ustar00pcrewspcrews00000000000000/*********************************************************************** Copyright (c) 2008 Innobase Oy. All rights reserved. Copyright (c) 2008 Oracle. All rights reserved. Copyright (c) 2010 Stewart Smith This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ************************************************************************/ /* Test table statistics API*/ #include #include #include #include #include #include "test0aux.h" #include #ifdef UNIV_DEBUG_VALGRIND #include #endif #define DATABASE "test" #define TABLE "ib_table_statistics" /********************************************************************* Create an InnoDB database (sub-directory). */ static ib_err_t create_database( /*============*/ const char* name) { ib_bool_t err; err = ib_database_create(name); assert(err == IB_TRUE); return(DB_SUCCESS); } /********************************************************************* CREATE TABLE T( pk INT, a INT, b INT, PRIMARY KEY(pk), INDEX (a), INDEX (b), ; */ static ib_err_t create_table_t2( /*=========*/ const char* dbname, /*!< in: database name */ const char* name) /*!< in: table name */ { ib_trx_t ib_trx; ib_id_t table_id = 0; ib_err_t err = DB_SUCCESS; ib_tbl_sch_t ib_tbl_sch = NULL; ib_idx_sch_t ib_idx_sch = NULL; ib_idx_sch_t ib_idx_sch_a = NULL; ib_idx_sch_t ib_idx_sch_b = NULL; ib_tbl_fmt_t tbl_fmt = IB_TBL_COMPACT; char table_name[IB_MAX_TABLE_NAME_LEN]; #ifdef __WIN__ sprintf(table_name, "%s/%s", dbname, name); #else snprintf(table_name, sizeof(table_name), "%s/%s", dbname, name); #endif err = ib_table_schema_create( table_name, &ib_tbl_sch, tbl_fmt, 0); assert(err == DB_SUCCESS); err = ib_table_schema_add_col( ib_tbl_sch, "pk", IB_INT, IB_COL_NONE, 0, 4); assert(err == DB_SUCCESS); err = ib_table_schema_add_col( ib_tbl_sch, "a", IB_INT, IB_COL_NONE, 0, 4); assert(err == DB_SUCCESS); err = ib_table_schema_add_col( ib_tbl_sch, "b", IB_INT, IB_COL_NONE, 0, 4); assert(err == DB_SUCCESS); assert(err == DB_SUCCESS); err = ib_table_schema_add_index(ib_tbl_sch, "PRIMARY", &ib_idx_sch); assert(err == DB_SUCCESS); /* Set prefix length to 0. */ err = ib_index_schema_add_col( ib_idx_sch, "pk", 0); assert(err == DB_SUCCESS); err = ib_index_schema_set_clustered(ib_idx_sch); assert(err == DB_SUCCESS); err = ib_index_schema_set_unique(ib_idx_sch); assert(err == DB_SUCCESS); err = ib_table_schema_add_index(ib_tbl_sch, "a", &ib_idx_sch_a); assert(err == DB_SUCCESS); /* Set prefix length to 0. */ err = ib_index_schema_add_col( ib_idx_sch_a, "a", 0); assert(err == DB_SUCCESS); err = ib_table_schema_add_index(ib_tbl_sch, "b", &ib_idx_sch_b); assert(err == DB_SUCCESS); /* Set prefix length to 0. */ err = ib_index_schema_add_col( ib_idx_sch_b, "b", 0); assert(err == DB_SUCCESS); /* create table */ ib_trx = ib_trx_begin(IB_TRX_REPEATABLE_READ); err = ib_schema_lock_exclusive(ib_trx); assert(err == DB_SUCCESS); err = ib_table_create(ib_trx, ib_tbl_sch, &table_id); if (err != DB_SUCCESS) { fprintf(stderr, "Warning: table create failed: %s\n", ib_strerror(err)); err = ib_trx_rollback(ib_trx); } else { err = ib_trx_commit(ib_trx); } assert(err == DB_SUCCESS); if (ib_tbl_sch != NULL) { ib_table_schema_delete(ib_tbl_sch); } return(err); } /********************************************************************* Open a table and return a cursor for the table. */ static ib_err_t open_table( /*=======*/ const char* dbname, /*!< in: database name */ const char* name, /*!< in: table name */ ib_trx_t ib_trx, /*!< in: transaction */ ib_crsr_t* crsr) /*!< out: innodb cursor */ { ib_err_t err = DB_SUCCESS; char table_name[IB_MAX_TABLE_NAME_LEN]; #ifdef __WIN__ sprintf(table_name, "%s/%s", dbname, name); #else snprintf(table_name, sizeof(table_name), "%s/%s", dbname, name); #endif err = ib_cursor_open_table(table_name, ib_trx, crsr); assert(err == DB_SUCCESS); return(err); } /********************************************************************* INSERT INTO T VALUE(int); */ static ib_err_t insert_rows( /*===============*/ ib_crsr_t crsr, int start, int count) { int i; ib_err_t err; ib_tpl_t tpl = NULL; char* ptr = malloc(8192); tpl = ib_clust_read_tuple_create(crsr); assert(tpl != NULL); for (i = start; i < start+count; ++i) { err = ib_tuple_write_u32(tpl, 0, i); assert(err == DB_SUCCESS); err = ib_tuple_write_u32(tpl, 1, i%10); assert(err == DB_SUCCESS); err = ib_tuple_write_u32(tpl, 1, rand()); assert(err == DB_SUCCESS); err = ib_cursor_insert_row(crsr, tpl); assert(err == DB_SUCCESS); tpl = ib_tuple_clear(tpl); assert(tpl != NULL); } if (tpl != NULL) { ib_tuple_delete(tpl); } free(ptr); return(err); } static void display_table_stats(ib_crsr_t crsr) { ib_err_t err; ib_table_stats_t tstats; err = ib_get_table_statistics(crsr, &tstats, sizeof(tstats)); assert(err == DB_SUCCESS); printf("Table Statistics\n"); printf("----------------\n"); printf("Rows:\t%" PRIi64 "\n", tstats.stat_n_rows); printf("Clustered Index Size:\t%" PRIu64 "\n", tstats.stat_clustered_index_size); printf("Other Index Size:\t%" PRIu64 "\n", tstats.stat_sum_of_other_index_sizes); printf("Modified Counter:\t%" PRIu64 "\n", tstats.stat_modified_counter); printf("\n"); printf("\n\nINSERT 10,000 ROWS\n\n"); printf("\n\n"); printf("INDEX Statistics: n_diff_key_vals\n"); printf("---------------------------------\n"); uint64_t ncols; int64_t *n_diff; unsigned int col; printf("PRIMARY:\t"); err= ib_get_index_stat_n_diff_key_vals(crsr, "PRIMARY",&ncols, &n_diff); for (col=0; col #include #include "haildb.h" #include "test0aux.h" #include #include #define DATABASE "test" #define TABLE "ib_ddl" /********************************************************************* Create an InnoDB database (sub-directory). */ static ib_err_t create_database( /*============*/ const char* name) { ib_bool_t err; err = ib_database_create(name); assert(err == IB_TRUE); return(DB_SUCCESS); } /********************************************************************* CREATE TABLE T(C1 INT, C2 VARCHAR(10), C3 BLOB); */ static ib_err_t create_table( /*=========*/ const char* dbname, /*!< in: database name */ const char* name) /*!< in: table name */ { ib_trx_t ib_trx; ib_id_t table_id = 0; ib_err_t err = DB_SUCCESS; ib_tbl_sch_t ib_tbl_sch = NULL; char table_name[IB_MAX_TABLE_NAME_LEN]; #ifdef __WIN__ sprintf(table_name, "%s/%s", dbname, name); #else snprintf(table_name, sizeof(table_name), "%s/%s", dbname, name); #endif /* Pass a table page size of 0, ie., use default page size. */ err = ib_table_schema_create( table_name, &ib_tbl_sch, IB_TBL_COMPACT, 0); assert(err == DB_SUCCESS); err = ib_table_schema_add_col( ib_tbl_sch, "c1", IB_INT, IB_COL_NONE, 0, sizeof(ib_i32_t)); assert(err == DB_SUCCESS); err = ib_tbl_sch_add_varchar_col(ib_tbl_sch, "c2", 10); assert(err == DB_SUCCESS); err = ib_tbl_sch_add_blob_col(ib_tbl_sch, "c3"); assert(err == DB_SUCCESS); /* create table */ ib_trx = ib_trx_begin(IB_TRX_REPEATABLE_READ); ib_trx_set_client_data(ib_trx, "create_table"); err = ib_schema_lock_exclusive(ib_trx); assert(err == DB_SUCCESS); err = ib_table_create(ib_trx, ib_tbl_sch, &table_id); if (err == DB_SUCCESS) { err = ib_trx_commit(ib_trx); } else { fprintf(stderr, "Table: %s create failed: %s\n", table_name, ib_strerror(err)); err = ib_trx_rollback(ib_trx); } assert(err == DB_SUCCESS); if (ib_tbl_sch != NULL) { ib_table_schema_delete(ib_tbl_sch); } return(err); } /********************************************************************* Open a table and return a cursor for the table. */ static ib_err_t open_table( /*=======*/ const char* dbname, /*!< in: database name */ const char* name, /*!< in: table name */ ib_trx_t ib_trx, /*!< in: transaction */ ib_crsr_t* crsr) /*!< out: innodb cursor */ { ib_err_t err = DB_SUCCESS; char table_name[IB_MAX_TABLE_NAME_LEN]; #ifdef __WIN__ sprintf(table_name, "%s/%s", dbname, name); #else snprintf(table_name, sizeof(table_name), "%s/%s", dbname, name); #endif err = ib_cursor_open_table(table_name, ib_trx, crsr); assert(err == DB_SUCCESS); return(err); } /********************************************************************* INSERT INTO T VALUE(0, RANDOM(TEXT), RANDOM(TEXT)); ... 100 */ static ib_err_t insert_random_rows( /*===============*/ ib_crsr_t crsr) /*!< in, out: cursor to use for write */ { ib_i32_t i; ib_err_t err; ib_tpl_t tpl; char* ptr = malloc(8192); tpl = ib_clust_read_tuple_create(crsr); assert(tpl != NULL); for (i = 0; i < 100; ++i) { int l; err = ib_tuple_write_i32(tpl, 0, i % 10); assert(err == DB_SUCCESS); l = gen_rand_text(ptr, 10); err = ib_col_set_value(tpl, 1, ptr, l); assert(err == DB_SUCCESS); l = gen_rand_text(ptr, 8192); err = ib_col_set_value(tpl, 2, ptr, l); assert(err == DB_SUCCESS); err = ib_cursor_insert_row(crsr, tpl); assert(err == DB_SUCCESS); tpl = ib_tuple_clear(tpl); assert(tpl != NULL); } if (tpl != NULL) { ib_tuple_delete(tpl); } free(ptr); return(err); } /********************************************************************* Create a secondary indexes on a table. @return DB_SUCCESS or error code */ static ib_err_t create_sec_index( /*=============*/ const char* table_name, /*!< in: table name */ const char* col_name, /*!< in: column name */ int prefix_len) /*!< in: prefix index length */ { ib_err_t err; ib_trx_t ib_trx; ib_id_t index_id = 0; ib_idx_sch_t ib_idx_sch = NULL; char index_name[IB_MAX_TABLE_NAME_LEN]; ib_trx = ib_trx_begin(IB_TRX_REPEATABLE_READ); /* also test when we don't set client_data */ if (strcmp(col_name, "c1") != 0) ib_trx_set_client_data(ib_trx, col_name); err = ib_schema_lock_exclusive(ib_trx); assert(err == DB_SUCCESS); #ifdef __WIN__ sprintf(index_name, "%s_%s", table_name, col_name); #else snprintf(index_name, sizeof(index_name), "%s_%s", table_name, col_name); #endif err = ib_index_schema_create( ib_trx, index_name, table_name, &ib_idx_sch); assert(err == DB_SUCCESS); err = ib_index_schema_add_col(ib_idx_sch, col_name, prefix_len); assert(err == DB_SUCCESS); err = ib_index_create(ib_idx_sch, &index_id); if (ib_idx_sch != NULL) { ib_index_schema_delete(ib_idx_sch); ib_idx_sch = NULL; } if (err == DB_SUCCESS) { err = ib_trx_commit(ib_trx); } else { err = ib_trx_rollback(ib_trx); } assert(err == DB_SUCCESS); return(err); } /********************************************************************* Create secondary indexes on T(C1), T(C2), T(C3). */ static ib_err_t create_sec_index_1( /*===============*/ const char* dbname, /*!< in: database name */ const char* name) /*!< in: table to drop */ { ib_err_t err; char table_name[IB_MAX_TABLE_NAME_LEN]; #ifdef __WIN__ sprintf(table_name, "%s/%s", dbname, name); #else snprintf(table_name, sizeof(table_name), "%s/%s", dbname, name); #endif err = create_sec_index(table_name, "c1", 0); if (err == DB_SUCCESS) { err = create_sec_index(table_name, "c2", 0); } if (err == DB_SUCCESS) { err = create_sec_index(table_name, "c3", 10); } return(err); } /********************************************************************* Open the secondary index. */ static ib_err_t open_sec_index( /*===========*/ ib_crsr_t crsr, /*!< in: table cusor */ const char* index_name) /*!< in: sec. index to open */ { ib_err_t err; ib_crsr_t idx_crsr; err = ib_cursor_open_index_using_name(crsr, index_name, &idx_crsr); assert(err == DB_SUCCESS); err = ib_cursor_close(idx_crsr); assert(err == DB_SUCCESS); return(err); } /********************************************************************* Open the secondary indexes on T(C1), T(C2), T(C3). */ static ib_err_t open_sec_index_1( /*=============*/ const char* dbname, /*!< in: database name */ const char* name) /*!< in: table name */ { ib_crsr_t crsr; ib_trx_t ib_trx; ib_err_t err = DB_SUCCESS; char index_name[IB_MAX_TABLE_NAME_LEN]; char table_name[IB_MAX_TABLE_NAME_LEN]; #ifdef __WIN__ sprintf(table_name, "%s/%s", dbname, name); #else snprintf(table_name, sizeof(table_name), "%s/%s", dbname, name); #endif ib_trx = ib_trx_begin(IB_TRX_REPEATABLE_READ); ib_trx_set_client_data(ib_trx, "open_sec_index_1"); err = ib_cursor_open_table(table_name, ib_trx, &crsr); assert(err == DB_SUCCESS); #ifdef __WIN__ sprintf(index_name, "%s_%s", table_name, "c1"); #else snprintf(index_name, sizeof(index_name), "%s_%s", table_name, "c1"); #endif err = open_sec_index(crsr, index_name); assert(err == DB_SUCCESS); #ifdef __WIN__ sprintf(index_name, "%s_%s", table_name, "c2"); #else snprintf(index_name, sizeof(index_name), "%s_%s", table_name, "c2"); #endif err = open_sec_index(crsr, index_name); assert(err == DB_SUCCESS); #ifdef __WIN__ sprintf(index_name, "%s_%s", table_name, "c3"); #else snprintf(index_name, sizeof(index_name), "%s_%s", table_name, "c3"); #endif err = open_sec_index(crsr, index_name); assert(err == DB_SUCCESS); err = ib_cursor_close(crsr); assert(err == DB_SUCCESS); err = ib_trx_commit(ib_trx); assert(err == DB_SUCCESS); return(err); } int killed_lock_wait= 0; ib_err_t test_kill_during_lock_wait( const char* dbname, /*!< in: database name */ const char* name) /*!< in: table name */ { ib_trx_t locking_trx, kill_trx; ib_crsr_t locking_cursor, kill_cursor; ib_err_t err; ib_tpl_t locking_tpl, kill_tpl; char table_name[IB_MAX_TABLE_NAME_LEN]; #ifdef __WIN__ sprintf(table_name, "%s/%s", dbname, name); #else snprintf(table_name, sizeof(table_name), "%s/%s", dbname, name); #endif locking_trx = ib_trx_begin(IB_TRX_REPEATABLE_READ); assert(locking_trx); err = ib_cursor_open_table(table_name, locking_trx, &locking_cursor); assert(err == DB_SUCCESS); err = ib_cursor_set_lock_mode(locking_cursor, IB_LOCK_X); assert(err == DB_SUCCESS); locking_tpl = ib_clust_read_tuple_create(locking_cursor); assert(err == DB_SUCCESS); ib_cursor_first(locking_cursor); int rows=0; while(err == DB_SUCCESS) { err = ib_cursor_read_row(locking_cursor, locking_tpl); ib_tuple_clear(locking_tpl); ib_cursor_next(locking_cursor); rows++; } printf("Read %d rows from table with IB_LOCK_X\n", rows-1); ib_tuple_delete(locking_tpl); /* All rows in the table are now locked */ /* We now start a new transaction and attempt to do the same We should end up being aborted due to chk_thd_killed() saying that this trx has been killed */ kill_trx = ib_trx_begin(IB_TRX_REPEATABLE_READ); assert(kill_trx); ib_trx_set_client_data(kill_trx, "kill_in_lock_wait"); err = ib_cursor_open_table(table_name, kill_trx, &kill_cursor); assert(err == DB_SUCCESS); err = ib_cursor_set_lock_mode(kill_cursor, IB_LOCK_X); assert(err == DB_SUCCESS); kill_tpl = ib_clust_read_tuple_create(kill_cursor); assert(err == DB_SUCCESS); err = ib_cursor_first(kill_cursor); assert(err == DB_LOCK_WAIT_TIMEOUT); assert(killed_lock_wait == 1); ib_tuple_delete(kill_tpl); /* since we have DB_LOCK_WAIT_TIMEOUT, we release the already rolled back transaction */ err = ib_cursor_close(kill_cursor); assert(err == DB_SUCCESS); err = ib_trx_release(kill_trx); assert(err == DB_SUCCESS); /* cleanup */ err = ib_cursor_close(locking_cursor); assert(err == DB_SUCCESS); err = ib_trx_commit(locking_trx); assert(err == DB_SUCCESS); return DB_SUCCESS; } int check_thd_killed(void* data); int not_killed_thds= 0; int killed_thds= 0; int check_thd_killed(void* data) { char* thd_name= (char*)data; printf("Checking if THD is KILLED: %s\n", thd_name); assert(strcmp(thd_name, "c1") != 0); if (strcmp(thd_name, "c3") == 0) { killed_thds++; return 1; } if (strcmp(thd_name, "kill_in_lock_wait") == 0) { killed_lock_wait= 1; killed_thds++; return 1; } not_killed_thds++; return 0; } int main(int argc, char** argv) { ib_err_t err; ib_crsr_t crsr; ib_trx_t ib_trx; ib_u64_t version; (void) argc; (void) argv; version = ib_api_version(); printf("API: %d.%d.%d\n", (int) (version >> 32), /* Current version */ (int) ((version >> 16)) & 0xffff, /* Revisiion */ (int) (version & 0xffff)); /* Age */ err = ib_init(); assert(err == DB_SUCCESS); test_configure(); err = ib_startup("barracuda"); assert(err == DB_SUCCESS); fprintf(stderr, "Set trx_is_interrupted handler\n"); ib_set_trx_is_interrupted_handler(check_thd_killed); err = create_database(DATABASE); assert(err == DB_SUCCESS); err = create_table(DATABASE, TABLE); assert(err == DB_SUCCESS); ib_trx = ib_trx_begin(IB_TRX_REPEATABLE_READ); ib_trx_set_client_data(ib_trx, "main"); assert(ib_trx != NULL); err = open_table(DATABASE, TABLE, ib_trx, &crsr); assert(err == DB_SUCCESS); err = ib_cursor_lock(crsr, IB_LOCK_IX); assert(err == DB_SUCCESS); err = insert_random_rows(crsr); assert(err == DB_SUCCESS); err = ib_cursor_close(crsr); assert(err == DB_SUCCESS); crsr = NULL; err = ib_trx_commit(ib_trx); assert(err == DB_SUCCESS); err = create_sec_index_1(DATABASE, TABLE); assert(err == DB_SUCCESS); err = open_sec_index_1(DATABASE, TABLE); assert(err == DB_SUCCESS); err = test_kill_during_lock_wait(DATABASE, TABLE); assert(err == DB_SUCCESS); err = drop_table(DATABASE, TABLE); assert(err == DB_SUCCESS); err = ib_shutdown(IB_SHUTDOWN_NORMAL); assert(err == DB_SUCCESS); assert(killed_thds > 0); assert(not_killed_thds > 0); assert(killed_thds == 3); /* index creation *and* 2 for lock wait */ printf("Killed %d and didn't kill %d\n", killed_thds, not_killed_thds); return(0); } haildb-2.3.2/tests/test0aux.c0000644000175000017500000004476511513177357016746 0ustar00pcrewspcrews00000000000000/*********************************************************************** Copyright (c) 2008 Innobase Oy. All rights reserved. Copyright (c) 2008 Oracle. All rights reserved. Copyright (c) 2009 Oracle. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ************************************************************************/ #include #include #include #include #include #ifdef __WIN__ #include #else #include #include #include #include /* For getopt_long() */ #endif #include "test0aux.h" #define COMMENT '#' /* Runtime config */ static const char log_group_home_dir[] = "log"; static const char data_file_path[] = "ibdata1:32M:autoextend"; static void create_directory( /*=============*/ const char* path) { #ifdef __WIN__ BOOL ret; ret = CreateDirectory((LPCTSTR) path, NULL); if (ret == 0 && GetLastError() != ERROR_ALREADY_EXISTS) { fprintf(stderr, "CreateDirectory failed for: %s\n", path); exit(EXIT_FAILURE); } #else int ret; /* Try and create the log sub-directory */ ret = mkdir(path, S_IRWXU); /* Note: This doesn't catch all errors. EEXIST can also refer to dangling symlinks. */ if (ret == -1 && errno != EEXIST) { perror(path); exit(EXIT_FAILURE); } #endif } /***************************************************************** Read a value from an integer column in an InnoDB tuple. @return column value */ ib_u64_t read_int_from_tuple( /*================*/ ib_tpl_t tpl, /*!< in: InnoDB tuple */ const ib_col_meta_t* col_meta, /*!< in: col meta data */ int i) /*!< in: column number */ { ib_u64_t ival = 0; switch (col_meta->type_len) { case 1: { ib_u8_t v; ib_col_copy_value(tpl, i, &v, sizeof(v)); ival = (col_meta->attr & IB_COL_UNSIGNED) ? v : (ib_i8_t) v; break; } case 2: { ib_u16_t v; ib_col_copy_value(tpl, i, &v, sizeof(v)); ival = (col_meta->attr & IB_COL_UNSIGNED) ? v : (ib_i16_t) v; break; } case 4: { ib_u32_t v; ib_col_copy_value(tpl, i, &v, sizeof(v)); ival = (col_meta->attr & IB_COL_UNSIGNED) ? v : (ib_i32_t) v; break; } case 8: { ib_col_copy_value(tpl, i, &ival, sizeof(ival)); break; } default: assert(IB_FALSE); } return(ival); } /********************************************************************* Print all columns in a tuple. */ void print_int_col( /*==========*/ FILE* stream, const ib_tpl_t tpl, int i, ib_col_meta_t* col_meta) { ib_err_t err = DB_SUCCESS; switch (col_meta->type_len) { case 1: { if (col_meta->attr & IB_COL_UNSIGNED) { ib_u8_t u8; err = ib_tuple_read_u8(tpl, i, &u8); fprintf(stream, "%u", u8); } else { ib_i8_t i8; err = ib_tuple_read_i8(tpl, i, &i8); fprintf(stream, "%d", i8); } break; } case 2: { if (col_meta->attr & IB_COL_UNSIGNED) { ib_u16_t u16; err = ib_tuple_read_u16(tpl, i, &u16); fprintf(stream, "%u", u16); } else { ib_i16_t i16; err = ib_tuple_read_i16(tpl, i, &i16); fprintf(stream, "%d", i16); } break; } case 4: { if (col_meta->attr & IB_COL_UNSIGNED) { ib_u32_t u32; err = ib_tuple_read_u32(tpl, i, &u32); fprintf(stream, "%u", u32); } else { ib_i32_t i32; err = ib_tuple_read_i32(tpl, i, &i32); fprintf(stream, "%d", i32); } break; } case 8: { if (col_meta->attr & IB_COL_UNSIGNED) { ib_u64_t u64; err = ib_tuple_read_u64(tpl, i, &u64); fprintf(stream, "%llu", (unsigned long long) u64); } else { ib_i64_t i64; err = ib_tuple_read_i64(tpl, i, &i64); fprintf(stream, "%lld", (long long) i64); } break; } default: assert(0); break; } assert(err == DB_SUCCESS); } /********************************************************************** Print character array of give size or upto 256 chars */ void print_char_array( /*=============*/ FILE* stream, /*!< in: stream to print to */ const char* array, /*!< in: char array */ int len) /*!< in: length of data */ { int i; const char* ptr = array; for (i = 0; i < len; ++i) { fprintf(stream, "%c", *(ptr + i)); } } /********************************************************************* Print all columns in a tuple. */ void print_tuple( /*========*/ FILE* stream, const ib_tpl_t tpl) { int i; int n_cols = ib_tuple_get_n_cols(tpl); for (i = 0; i < n_cols; ++i) { ib_ulint_t data_len; ib_col_meta_t col_meta; data_len = ib_col_get_meta(tpl, i, &col_meta); /* Skip system columns. */ if (col_meta.type == IB_SYS) { continue; /* Nothing to print. */ } else if (data_len == IB_SQL_NULL) { fprintf(stream, "|"); continue; } else { switch (col_meta.type) { case IB_INT: { print_int_col(stream, tpl, i, &col_meta); break; } case IB_FLOAT: { float v; ib_err_t err; err = ib_tuple_read_float(tpl, i, &v); assert(err == DB_SUCCESS); fprintf(stream, "%f", v); break; } case IB_DOUBLE: { double v; ib_err_t err; err = ib_tuple_read_double(tpl, i, &v); assert(err == DB_SUCCESS); fprintf(stream, "%lf", v); break; } case IB_CHAR: case IB_BLOB: case IB_DECIMAL: case IB_VARCHAR: case IB_VARCHAR_ANYCHARSET: case IB_CHAR_ANYCHARSET: { const char* ptr; ptr = ib_col_get_value(tpl, i); fprintf(stream, "%d:", (int) data_len); print_char_array(stream, ptr, (int) data_len); break; } default: assert(IB_FALSE); break; } } fprintf(stream, "|"); } fprintf(stream, "\n"); } /********************************************************************* Setup the InnoDB configuration parameters. */ void test_configure(void) /*================*/ { ib_err_t err; create_directory(log_group_home_dir); #ifndef __WIN__ err = ib_cfg_set_text("flush_method", "O_DIRECT"); assert(err == DB_SUCCESS); #else err = ib_cfg_set_text("flush_method", "async_unbuffered"); assert(err == DB_SUCCESS); #endif err = ib_cfg_set_int("log_files_in_group", 2); assert(err == DB_SUCCESS); err = ib_cfg_set_int("log_file_size", 32 * 1024 * 1024); assert(err == DB_SUCCESS); err = ib_cfg_set_int("log_buffer_size", 24 * 16384); assert(err == DB_SUCCESS); err = ib_cfg_set_int("buffer_pool_size", 5 * 1024 * 1024); assert(err == DB_SUCCESS); err = ib_cfg_set_int("additional_mem_pool_size", 4 * 1024 * 1024); assert(err == DB_SUCCESS); err = ib_cfg_set_int("flush_log_at_trx_commit", 1); assert(err == DB_SUCCESS); err = ib_cfg_set_int("file_io_threads", 4); assert(err == DB_READONLY); err = ib_cfg_set_int("lock_wait_timeout", 60); assert(err == DB_SUCCESS); err = ib_cfg_set_int("open_files", 300); assert(err == DB_SUCCESS); err = ib_cfg_set_bool_on("doublewrite"); assert(err == DB_SUCCESS); err = ib_cfg_set_bool_on("checksums"); assert(err == DB_SUCCESS); err = ib_cfg_set_bool_on("rollback_on_timeout"); assert(err == DB_SUCCESS); err = ib_cfg_set_bool_on("print_verbose_log"); assert(err == DB_SUCCESS); err = ib_cfg_set_bool_on("file_per_table"); assert(err == DB_SUCCESS); err = ib_cfg_set_text("data_home_dir", "./"); assert(err == DB_SUCCESS); err = ib_cfg_set_text("log_group_home_dir", log_group_home_dir); if (err != DB_SUCCESS) { fprintf(stderr, "syntax error in log_group_home_dir, or a " "wrong number of mirrored log groups\n"); exit(1); } err = ib_cfg_set_text("data_file_path", data_file_path); if (err != DB_SUCCESS) { fprintf(stderr, "InnoDB: syntax error in data_file_path\n"); exit(1); } } /********************************************************************* Generate random text upto max size. */ int gen_rand_text( /*==========*/ char* ptr, /*!< in,out: text written here */ int max_size) /*!< in: max size of ptr */ { int i; int len = 0; static char txt[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789"; do { #ifdef __WIN__ len = rand() % max_size; #else len = random() % max_size; #endif } while (len == 0); for (i = 0; i < len; ++i, ++ptr) { #ifdef __WIN__ *ptr = txt[rand() % (sizeof(txt) - 1)]; #else *ptr = txt[random() % (sizeof(txt) - 1)]; #endif } return(len); } #ifndef __WIN__ struct option ib_longopts[] = { {"ib-buffer-pool-size", required_argument, NULL, 1}, {"ib-log-file-size", required_argument, NULL, 2}, {"ib-disable-ahi", no_argument, NULL, 3}, {"ib-io-capacity", required_argument, NULL, 4}, {"ib-use-sys-malloc", required_argument, NULL, 5}, {"ib-lru-old-ratio", required_argument, NULL, 6}, {"ib-lru-access-threshold",required_argument, NULL, 7}, {"ib-force-recovery", required_argument, NULL, 8}, {"ib-log-dir", required_argument, NULL, 9}, {"ib-data-dir", required_argument, NULL, 10}, {"ib-data-file-path", required_argument, NULL, 11}, {"ib-disble-dblwr", no_argument, NULL, 12}, {"ib-disble-checksum", no_argument, NULL, 13}, {"ib-disble-file-per-table",no_argument, NULL, 14}, {"ib-flush-log-at-trx-commit",required_argument,NULL, 15}, {"ib-flush-method", required_argument, NULL, 16}, {"ib-read-threads", required_argument, NULL, 17}, {"ib-write-threads", required_argument, NULL, 18}, {"ib-max-open-files", required_argument, NULL, 19}, {"ib-lock-wait-timeout",required_argument, NULL, 20}, {NULL, 0, NULL, 0}}; #endif /* __WIN__ */ /********************************************************************* Print usage. */ void print_usage( /*========*/ const char* progname) { fprintf(stderr, "usage: %s " "[--ib-buffer-pool-size size in mb]\n" "[--ib-log-file-size size in mb]\n" "[--ib-disable-ahi]\n" "[--ib-io-capacity number of records]\n" "[--ib-use-sys-malloc]\n" "[--ib-lru-old-ratio as %% e.g. 38]\n" "[--ib-lru-access-threshold in ms]\n" "[--ib-force-recovery 1-6]\n" "[--ib-log-dir path]\n" "[--ib-data-dir path]\n" "[--ib-data-file-path string]\n" "[--ib-disble-dblwr]\n" "[--ib-disble-checksum]\n" "[--ib-disble-file-per-table]\n" "[--ib-flush-log-at-trx-commit 1-3]\n" "[--ib-flush-method method]\n" "[--ib-read-threads count]\n" "[--ib-write-threads count]\n" "[--ib-max-open-files count]\n" "[--ib-lock-wait-timeout seconds]\n", progname); } /********************************************************************* Set the runtime global options. */ ib_err_t set_global_option( /*==============*/ int opt, const char* arg) { ib_err_t err = DB_ERROR; /* FIXME: Change numbers to enums or #defines */ switch(opt) { case 1: { ib_ulint_t size; size = abs(strtoul(arg, NULL, 10)); size *= 1024UL * 1024UL; err = ib_cfg_set_int("buffer_pool_size", size); assert(err == DB_SUCCESS); break; } case 2: { ib_ulint_t size; size = abs(strtoul(arg, NULL, 10)); size *= 1024UL * 1024UL; err = ib_cfg_set_int("log_file_size", size); assert(err == DB_SUCCESS); break; } case 3: err = ib_cfg_set_bool_off("adaptive_hash_index"); assert(err == DB_SUCCESS); break; case 4: { ib_ulint_t size; size = abs(strtoul(arg, NULL, 10)); err = ib_cfg_set_int("io_capacity", size); assert(err == DB_SUCCESS); break; } case 5: err = ib_cfg_set_bool_on("use_sys_malloc"); assert(err == DB_SUCCESS); break; case 6: { ib_ulint_t pct; pct = abs(strtoul(arg, NULL, 10)); err = ib_cfg_set_int("lru_old_blocks_pct", pct); assert(err == DB_SUCCESS); break; } case 7: { ib_ulint_t pct; pct = abs(strtoul(arg, NULL, 10)); err = ib_cfg_set_int("lru_block_access_recency", pct); assert(err == DB_SUCCESS); break; } case 8: { ib_ulint_t level; level = abs(strtoul(arg, NULL, 10)); err = ib_cfg_set_int("force_recovery", level); assert(err == DB_SUCCESS); break; } case 9: { err = ib_cfg_set_text("log_group_home_dir", arg); assert(err == DB_SUCCESS); break; } case 10: { err = ib_cfg_set_text("data_home_dir", arg); assert(err == DB_SUCCESS); break; } case 11: { err = ib_cfg_set_text("data_file_path", arg); assert(err == DB_SUCCESS); break; } case 12: { err = ib_cfg_set_bool_off("doublewrite"); assert(err == DB_SUCCESS); break; } case 13: { err = ib_cfg_set_bool_off("checksum"); assert(err == DB_SUCCESS); break; } case 14: { err = ib_cfg_set_bool_off("file_per_table"); assert(err == DB_SUCCESS); break; } case 15: { ib_ulint_t level; level = abs(strtoul(arg, NULL, 10)); err = ib_cfg_set_int("flush_log_at_trx_commit", level); assert(err == DB_SUCCESS); break; } case 16: { err = ib_cfg_set_int("flush_method", arg); assert(err == DB_SUCCESS); break; } case 17: { ib_ulint_t threads; threads = abs(strtoul(arg, NULL, 10)); err = ib_cfg_set_int("read_io_threads", threads); assert(err == DB_SUCCESS); break; } case 18: { ib_ulint_t threads; threads = abs(strtoul(arg, NULL, 10)); err = ib_cfg_set_int("write_io_threads", threads); assert(err == DB_SUCCESS); break; } case 19: { ib_ulint_t n; n = abs(strtoul(arg, NULL, 10)); err = ib_cfg_set_int("open_files", n); assert(err == DB_SUCCESS); break; } case 20: { ib_ulint_t secs; secs = abs(strtoul(arg, NULL, 10)); err = ib_cfg_set_int("lock_wait_timeout", secs); assert(err == DB_SUCCESS); break; } default: err = DB_ERROR; break; } /* switch */ return(err); } /********************************************************************* Print API version to stdout. */ void print_version(void) /*===============*/ { ib_u64_t version; version = ib_api_version(); printf("API: %d.%d.%d\n", (int) (version >> 32), /* Current version */ (int) ((version >> 16)) & 0xffff, /* Revisiion */ (int) (version & 0xffff)); /* Age */ } /********************************************************************* Skip line. */ static int config_skip_line( /*=============*/ FILE* fp) { int ch; while ((ch = getc(fp)) != -1 && ch != '\n') ; return(ch); } /********************************************************************* Add an element to the config. */ static void config_add_elem( /*============*/ ib_config_t* config, const ib_string_t* key, const ib_string_t* val) { ib_var_t* var; if (config->n_count == 0) { int size; config->n_count = 4; size = config->n_count * sizeof(ib_var_t); config->elems = (ib_var_t*) malloc(size); } else if (config->n_elems == config->n_count - 1) { int size; config->n_count *= 2; size = config->n_count * sizeof(ib_var_t); config->elems = (ib_var_t*) realloc(config->elems, size); } assert(config->n_elems < config->n_count); var = &config->elems[config->n_elems]; assert(key->len > 0); assert(key->ptr != NULL); var->name.len = key->len; var->name.ptr = (ib_byte_t*) malloc(var->name.len); memcpy(var->name.ptr, key->ptr, var->name.len); /* Value can be NULL for boolean variables */ if (val->len > 0) { var->value.len = val->len; var->value.ptr = (ib_byte_t*) malloc(var->value.len); memcpy(var->value.ptr, val->ptr, var->value.len); } ++config->n_elems; } /********************************************************************* Parse an InnoDB config file, the file has a very simply format: Lines beginning with '#' are ignored. Characters after '#' (inclusive) are also ignored. Empty lines are also ignored. Variable syntax is: \s*var_name\s*=\s*value\s*\n */ int config_parse_file( /*==============*/ const char* filename, ib_config_t* config) { int ch; FILE* fp; ib_string_t key; ib_string_t val; ib_string_t* str; ib_byte_t name[BUFSIZ]; ib_byte_t value[BUFSIZ]; fp = fopen(filename, "r"); if (fp == NULL) { return(-1); } memset(&key, 0x0, sizeof(key)); memset(&val, 0x0, sizeof(val)); key.ptr = name; val.ptr = value; str = &key; while ((ch = getc(fp)) != -1) { /* Skip comments. */ if (ch == COMMENT) { if (config_skip_line(fp) == -1) { break; } continue; } /* Skip white space. */ while (ch != '\n' && isspace(ch)) { if ((ch = getc(fp)) == -1) { break; } } /* Skip comments. */ if (ch == COMMENT) { if (config_skip_line(fp) == -1) { break; } continue; } if (ch == -1) { break; } switch (ch) { case '\r': break; case '\n': key.ptr = name; val.ptr = value; if (key.len > 0) { config_add_elem(config, &key, &val); } memset(&key, 0x0, sizeof(key)); memset(&val, 0x0, sizeof(val)); /* Anything by default is a variable name */ str = &key; str->ptr = name; break; case '=': /* Anything that follows an '=' is a value. */ str = &val; str->ptr = value; break; default: ++str->len; *str->ptr++ = ch; } assert(str->len < sizeof(name)); } if (str->len > 0) { key.ptr = name; val.ptr = value; config_add_elem(config, &key, &val); } fclose(fp); return(0); } /********************************************************************* Print the elements. */ void config_print( /*=========*/ const ib_config_t* config) { ib_ulint_t i; for (i = 0; i < config->n_elems; ++i) { if (config->elems[i].value.ptr != NULL) { printf("%.*s=%.*s\n", config->elems[i].name.len, config->elems[i].name.ptr, config->elems[i].value.len, config->elems[i].value.ptr); } else { printf("%.*s\n", config->elems[i].name.len, config->elems[i].name.ptr); } } } /********************************************************************* Free the the elements. */ void config_free( /*=========*/ ib_config_t* config) { ib_ulint_t i; for (i = 0; i < config->n_elems; ++i) { free(config->elems[i].name.ptr); free(config->elems[i].value.ptr); } free(config->elems); memset(config, 0x0, sizeof(*config)); } /********************************************************************* Drop the table. */ ib_err_t drop_table( /*=======*/ const char* dbname, /*!< in: database name */ const char* name) /*!< in: table to drop */ { ib_err_t err; ib_trx_t ib_trx; char table_name[IB_MAX_TABLE_NAME_LEN]; #ifdef __WIN__ sprintf(table_name, "%s/%s", dbname, name); #else snprintf(table_name, sizeof(table_name), "%s/%s", dbname, name); #endif ib_trx = ib_trx_begin(IB_TRX_REPEATABLE_READ); assert(ib_trx != NULL); err = ib_schema_lock_exclusive(ib_trx); assert(err == DB_SUCCESS); err = ib_table_drop(ib_trx, table_name); assert(err == DB_SUCCESS); err = ib_trx_commit(ib_trx); assert(err == DB_SUCCESS); return(err); } haildb-2.3.2/tests/ib_perf1.c0000644000175000017500000005047711513177357016655 0ustar00pcrewspcrews00000000000000/*********************************************************************** Copyright (c) 2008 Innobase Oy. All rights reserved. Copyright (c) 2008 Oracle. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ************************************************************************/ /* Simple multi-threaded test that does the equivalent of: Create a database CREATE TABLE Tn1(c1 INT AUTOINCREMENT, c2 INT, PK(c1)); CREATE TABLE Tn2(c1 INT AUTOINCREMENT, c2 INT, PK(c1)); INSERT N million rows into Tn1; Since the API doesn't support autoincrement, the attribute will be ignored. The aim of this test is to: 1. How long does it take to copy N million rows from Tn1 to an empty Tn2 ? INSERT INTO Tn2 SELECT * FROM Tn1; 2. How long does the query : SELECT COUNT(*) FROM Tn1, Tn2 WHERE Tn1.c1 = Tn2.c1; which joins n million rows take? The above tests measure the internal speed of the database engine. We startup N threads each running the above test but on a self contained set of tables independent of each other. The objective of the test is to measure any unnecessary locking issues/contention for resources. */ #include #include #include #include #include #include #include #include #include /* For getopt_long() */ #include "test0aux.h" #ifdef UNIV_DEBUG_VALGRIND #include #endif #define DATABASE "test" #define MILLION 1000000 typedef enum ib_op_t { INSERT, COPY, JOIN } ib_op_t; typedef struct ib_op_time { time_t* time; int n_elems; } ib_op_time_t; typedef struct ib_op_stats { pthread_mutex_t mutex; ib_op_time_t copy; ib_op_time_t join; ib_op_time_t insert; } ib_op_stats_t; static ib_u32_t n_threads = 1; static ib_u32_t n_rows = MILLION; static const ib_u32_t BATCH_SIZE = 10000; /* The page size for compressed tables, if this value is > 0 then we create compressed tables. It's set via the command line parameter --page-size INT */ static int page_size = 0; static ib_op_stats_t ib_op_stats; /* Barrier to synchronize all threads */ static pthread_barrier_t barrier; /********************************************************************* Allocate memory. */ static void ib_op_time_alloc( /*=============*/ ib_op_time_t* ib_op_time, int n_threads) { memset(ib_op_time, 0x0, sizeof(*ib_op_time)); ib_op_time->time = (time_t*) malloc(sizeof(time_t) * n_threads); } /********************************************************************* Allocate memory for the global stats collector. */ static void ib_op_stats_alloc( /*==============*/ int n_threads) { int ret; memset(&ib_op_stats, 0x0, sizeof(ib_op_stats)); ib_op_time_alloc(&ib_op_stats.copy, n_threads); ib_op_time_alloc(&ib_op_stats.join, n_threads); ib_op_time_alloc(&ib_op_stats.insert, n_threads); ret = pthread_mutex_init(&ib_op_stats.mutex, NULL); assert(ret == 0); } /********************************************************************* Inverse of ib_op_time_alloc() */ static void ib_op_time_free( /*=============*/ ib_op_time_t* ib_op_time) { free(ib_op_time->time); memset(ib_op_time, 0x0, sizeof(*ib_op_time)); } /********************************************************************* Free the global stats collector. */ static void ib_op_stats_free(void) /*==================*/ { int ret; ib_op_time_free(&ib_op_stats.copy); ib_op_time_free(&ib_op_stats.join); ib_op_time_free(&ib_op_stats.insert); ret = pthread_mutex_destroy(&ib_op_stats.mutex); assert(ret == 0); } /********************************************************************* Add timing info for operation. */ static void ib_stats_collect( /*=============*/ ib_op_t op, time_t elapsed_time) { int ret; ib_op_time_t* collect = NULL; ret = pthread_mutex_lock(&ib_op_stats.mutex); assert(ret == 0); switch (op) { case INSERT: collect = &ib_op_stats.insert; break; case COPY: collect = &ib_op_stats.copy; break; case JOIN: collect = &ib_op_stats.join; break; default: assert(0); } assert(collect->n_elems < n_threads); collect->time[collect->n_elems++] = elapsed_time; ret = pthread_mutex_unlock(&ib_op_stats.mutex); assert(ret == 0); } /********************************************************************* Create an InnoDB database (sub-directory). */ static ib_err_t create_database( /*============*/ const char* name) { ib_bool_t err; err = ib_database_create(name); assert(err == IB_TRUE); return(DB_SUCCESS); } /********************************************************************* CREATE TABLE T (c1 INT, c2 INT, PRIMARY KEY(c1)); */ static ib_err_t create_table( /*=========*/ const char* dbname, /*!< in: database name */ const char* name) /*!< in: table name */ { ib_trx_t ib_trx; ib_id_t table_id = 0; ib_err_t err = DB_SUCCESS; ib_tbl_sch_t ib_tbl_sch = NULL; ib_idx_sch_t ib_idx_sch = NULL; ib_tbl_fmt_t tbl_fmt = IB_TBL_COMPACT; char table_name[IB_MAX_TABLE_NAME_LEN]; #ifdef __WIN__ sprintf(table_name, "%s/%s", dbname, name); #else snprintf(table_name, sizeof(table_name), "%s/%s", dbname, name); #endif if (page_size > 0) { tbl_fmt = IB_TBL_COMPRESSED; printf("Creating compressed table with page size %d\n", page_size); } /* Pass a table page size of 0, ie., use default page size. */ err = ib_table_schema_create( table_name, &ib_tbl_sch, tbl_fmt, page_size); assert(err == DB_SUCCESS); err = ib_table_schema_add_col( ib_tbl_sch, "c1", IB_INT, IB_COL_UNSIGNED, 0, sizeof(ib_u32_t)); assert(err == DB_SUCCESS); err = ib_table_schema_add_col( ib_tbl_sch, "c2", IB_INT, IB_COL_UNSIGNED, 0, sizeof(ib_u32_t)); assert(err == DB_SUCCESS); err = ib_table_schema_add_index(ib_tbl_sch, "PRIMARY", &ib_idx_sch); assert(err == DB_SUCCESS); /* Set prefix length to 0. */ err = ib_index_schema_add_col( ib_idx_sch, "c1", 0); assert(err == DB_SUCCESS); err = ib_index_schema_set_clustered(ib_idx_sch); assert(err == DB_SUCCESS); /* create table */ ib_trx = ib_trx_begin(IB_TRX_REPEATABLE_READ); err = ib_schema_lock_exclusive(ib_trx); assert(err == DB_SUCCESS); err = ib_table_create(ib_trx, ib_tbl_sch, &table_id); assert(err == DB_SUCCESS); err = ib_trx_commit(ib_trx); assert(err == DB_SUCCESS); if (ib_tbl_sch != NULL) { ib_table_schema_delete(ib_tbl_sch); } return(err); } /********************************************************************* Open a table and return a cursor for the table. */ static ib_err_t open_table( /*=======*/ const char* dbname, /*!< in: database name */ const char* name, /*!< in: table name */ ib_trx_t ib_trx, /*!< in: transaction */ ib_crsr_t* crsr) /*!< out: innodb cursor */ { ib_err_t err = DB_SUCCESS; char table_name[IB_MAX_TABLE_NAME_LEN]; #ifdef __WIN__ sprintf(table_name, "%s/%s", dbname, name); #else snprintf(table_name, sizeof(table_name), "%s/%s", dbname, name); #endif err = ib_cursor_open_table(table_name, ib_trx, crsr); assert(err == DB_SUCCESS); return(err); } /********************************************************************* INSERT INTO T VALUE(i, i); */ static ib_err_t insert_rows( /*========*/ ib_crsr_t crsr, /*!< in, out: cursor to use for write */ ib_u32_t start, /*!< in: start of column value */ ib_u32_t n_values) /*!< in: no. of values to insert */ { ib_u32_t i; ib_tpl_t tpl = NULL; ib_err_t err = DB_SUCCESS; tpl = ib_clust_read_tuple_create(crsr); assert(tpl != NULL); for (i = start; i < start + n_values; ++i) { err = ib_col_set_value(tpl, 0, &i, sizeof(i)); assert(err == DB_SUCCESS); err = ib_col_set_value(tpl, 1, &i, sizeof(i)); assert(err == DB_SUCCESS); err = ib_cursor_insert_row(crsr, tpl); assert(err == DB_SUCCESS); /* Since we are writing fixed length columns (all INTs), there is no need to reset the tuple. */ } if (tpl != NULL) { ib_tuple_delete(tpl); } return(err); } /********************************************************************* INSERT INTO T2 SELECT * FROM T2; */ static ib_err_t copy_table( /*=======*/ ib_crsr_t dst_crsr, /*!< in, out: dest table */ ib_crsr_t src_crsr, /*!< in, out: source table */ ib_u32_t n_values) /*!< in: no. of rows to copy in a batch */ { ib_u32_t i; ib_tpl_t src_tpl = NULL; ib_tpl_t dst_tpl = NULL; ib_err_t err = DB_SUCCESS; src_tpl = ib_clust_read_tuple_create(src_crsr); assert(src_tpl != NULL); dst_tpl = ib_clust_read_tuple_create(dst_crsr); assert(dst_tpl != NULL); for (i = 0; i < n_values; ++i) { ib_u32_t v; err = ib_cursor_read_row(src_crsr, src_tpl); assert(err == DB_SUCCESS); /* Since we know that both the tables are identical, we simply copy corresponding columns. */ /* Get and set the c1 column value. */ err = ib_tuple_read_u32(src_tpl, 0, &v); assert(err == DB_SUCCESS); err = ib_tuple_write_u32(dst_tpl, 0, v); assert(err == DB_SUCCESS); /* Get and set the c2 column value. */ err = ib_tuple_read_u32(src_tpl, 1, &v); assert(err == DB_SUCCESS); err = ib_tuple_write_u32(dst_tpl, 1, v); assert(err == DB_SUCCESS); err = ib_cursor_insert_row(dst_crsr, dst_tpl); assert(err == DB_SUCCESS); /* Since we are writing fixed length columns (all INTs), there is no need to reset the tuple. */ if ((i % 100) == 0) { /* The source tuple makes a copy of the record therefore it needs to be reset. */ src_tpl = ib_tuple_clear(src_tpl); assert(dst_tpl != NULL); } err = ib_cursor_next(src_crsr); /* We don't expect any other kind of error. */ if (err == DB_END_OF_INDEX) { break; } assert(err == DB_SUCCESS); } if (src_tpl != NULL) { ib_tuple_delete(src_tpl); } if (dst_tpl != NULL) { ib_tuple_delete(dst_tpl); } return(err); } /********************************************************************* SELECT COUNT(*) FROM T1, T2 WHERE T1.c1 = T2.c1; */ static ib_err_t join_on_c1( /*=======*/ ib_crsr_t t1_crsr, /*!< in: table */ ib_crsr_t t2_crsr, /*!< in: table */ ib_u32_t* count) /*!< in: no. of rows that matched */ { ib_err_t t2_err; ib_err_t t1_err; ib_trx_t ib_trx; ib_tpl_t t1_tpl = NULL; ib_tpl_t t2_tpl = NULL; //printf("Begin transaction\n"); ib_trx = ib_trx_begin(IB_TRX_REPEATABLE_READ); assert(ib_trx != NULL); ib_cursor_attach_trx(t1_crsr, ib_trx); ib_cursor_attach_trx(t2_crsr, ib_trx); t1_tpl = ib_clust_read_tuple_create(t1_crsr); assert(t1_tpl != NULL); t2_tpl = ib_clust_read_tuple_create(t2_crsr); assert(t2_tpl != NULL); t1_err = ib_cursor_first(t1_crsr); assert(t1_err == DB_SUCCESS); t2_err = ib_cursor_first(t2_crsr); assert(t2_err == DB_SUCCESS); /* Since we know that both tables have the same number of rows we iterate over the tables together. */ while (t1_err == DB_SUCCESS && t2_err == DB_SUCCESS) { ib_u32_t t1_c1; t1_err = ib_cursor_read_row(t1_crsr, t1_tpl); assert(t1_err == DB_SUCCESS); t1_err = ib_tuple_read_u32(t1_tpl, 0, &t1_c1); assert(t1_err == DB_SUCCESS); while (t2_err == DB_SUCCESS) { ib_u32_t t2_c1; t2_err = ib_cursor_read_row(t2_crsr, t2_tpl); assert(t2_err == DB_SUCCESS); t2_err = ib_tuple_read_u32(t2_tpl, 0, &t2_c1); assert(t2_err == DB_SUCCESS); if ((*count % 100) == 0) { t2_tpl = ib_tuple_clear(t2_tpl); assert(t2_tpl != NULL); } if (t1_c1 == t2_c1) { ++*count; t2_err = ib_cursor_next(t2_crsr); /* We don't expect any other kind of error. */ if (t2_err == DB_END_OF_INDEX) { break; } } else { break; } assert(t2_err == DB_SUCCESS); } if ((*count % 100) == 0) { t1_tpl = ib_tuple_clear(t1_tpl); assert(t1_tpl != NULL); } t1_err = ib_cursor_next(t1_crsr); /* We don't expect any other kind of error. */ if (t1_err == DB_END_OF_INDEX) { break; } assert(t1_err == DB_SUCCESS); } assert(t1_err == DB_END_OF_INDEX); if (t1_tpl != NULL) { ib_tuple_delete(t1_tpl); } if (t2_tpl != NULL) { ib_tuple_delete(t2_tpl); } t1_err = ib_cursor_reset(t1_crsr); assert(t1_err == DB_SUCCESS); t2_err = ib_cursor_reset(t2_crsr); assert(t2_err == DB_SUCCESS); //printf("Commit transaction\n"); t1_err = ib_trx_commit(ib_trx); assert(t1_err == DB_SUCCESS); return(t1_err); } /********************************************************************* Run the test. */ static void* worker_thread( /*==========*/ void* arg) { int i; int ret; ib_err_t err; time_t end; time_t start; ib_u32_t count = 0; char table1[BUFSIZ]; char table2[BUFSIZ]; ib_crsr_t src_crsr = NULL; ib_crsr_t dst_crsr = NULL; int* table_id = (int*) arg; ib_bool_t positioned = IB_FALSE; snprintf(table1, sizeof(table1), "T%d", *table_id); snprintf(table2, sizeof(table2), "T%d", *table_id + 1); /* We are done with the arg. */ free(arg); table_id = NULL; err = create_table(DATABASE, table1); assert(err == DB_SUCCESS); err = create_table(DATABASE, table2); assert(err == DB_SUCCESS); err = open_table(DATABASE, table1, NULL, &src_crsr); assert(err == DB_SUCCESS); ret = pthread_barrier_wait(&barrier); assert(ret == 0 || ret == PTHREAD_BARRIER_SERIAL_THREAD); if (ret == PTHREAD_BARRIER_SERIAL_THREAD) { printf("Start insert...\n"); } start = time(NULL); for (i = 0; i < n_rows; i += BATCH_SIZE) { ib_trx_t ib_trx; ib_trx = ib_trx_begin(IB_TRX_REPEATABLE_READ); assert(ib_trx != NULL); ib_cursor_attach_trx(src_crsr, ib_trx); err = ib_cursor_lock(src_crsr, IB_LOCK_IX); assert(err == DB_SUCCESS); err = insert_rows(src_crsr, i, BATCH_SIZE); assert(err == DB_SUCCESS); err = ib_cursor_reset(src_crsr); assert(err == DB_SUCCESS); err = ib_trx_commit(ib_trx); assert(err == DB_SUCCESS); } end = time(NULL); ib_stats_collect(INSERT, end - start); err = open_table(DATABASE, table2, NULL, &dst_crsr); assert(err == DB_SUCCESS); ret = pthread_barrier_wait(&barrier); assert(ret == 0 || ret == PTHREAD_BARRIER_SERIAL_THREAD); if (ret == PTHREAD_BARRIER_SERIAL_THREAD) { printf("Start copy...\n"); } start = time(NULL); for (i = 0; i < n_rows && err == DB_SUCCESS; i += BATCH_SIZE) { ib_trx_t ib_trx; ib_trx = ib_trx_begin(IB_TRX_REPEATABLE_READ); assert(ib_trx != NULL); ib_cursor_attach_trx(src_crsr, ib_trx); ib_cursor_attach_trx(dst_crsr, ib_trx); err = ib_cursor_lock(src_crsr, IB_LOCK_IS); assert(err == DB_SUCCESS); err = ib_cursor_lock(dst_crsr, IB_LOCK_IX); assert(err == DB_SUCCESS); if (!positioned) { err = ib_cursor_first(src_crsr); assert(err == DB_SUCCESS); positioned = IB_TRUE; } err = copy_table(dst_crsr, src_crsr, BATCH_SIZE); assert(err == DB_SUCCESS || err == DB_END_OF_INDEX); err = ib_cursor_reset(src_crsr); assert(err == DB_SUCCESS); err = ib_cursor_reset(dst_crsr); assert(err == DB_SUCCESS); err = ib_trx_commit(ib_trx); assert(err == DB_SUCCESS); } end = time(NULL); ib_stats_collect(COPY, end - start); ret = pthread_barrier_wait(&barrier); assert(ret == 0 || ret == PTHREAD_BARRIER_SERIAL_THREAD); if (ret == PTHREAD_BARRIER_SERIAL_THREAD) { printf("Start join ...\n"); } start = time(NULL); err = join_on_c1(src_crsr, dst_crsr, &count); assert(err == DB_SUCCESS); end = time(NULL); assert(count == n_rows); ib_stats_collect(JOIN, end - start); if (src_crsr) { err = ib_cursor_close(src_crsr); assert(err == DB_SUCCESS); src_crsr = NULL; } if (dst_crsr) { err = ib_cursor_close(dst_crsr); assert(err == DB_SUCCESS); dst_crsr = NULL; } err = drop_table(DATABASE, table1); assert(err == DB_SUCCESS); err = drop_table(DATABASE, table2); assert(err == DB_SUCCESS); pthread_exit(0); } /********************************************************************* Callback for qsort(3). */ static int op_time_compare( /*============*/ const void* p1, const void* p2) { return(((int) (*(time_t*) p1 - *(time_t*) p2))); } /********************************************************************* Sort the elapsed time data and print to stdout. */ static void print_data( /*=======*/ ib_op_time_t* ib_op_time, const char* title) { int i; double avg = 0.0; double stdev = 0.0; time_t* elapsed_time = ib_op_time->time; qsort(elapsed_time, ib_op_time->n_elems, sizeof(*elapsed_time), op_time_compare); printf("%s\n", title); for (i = 0; i < ib_op_time->n_elems; ++i) { avg += elapsed_time[i]; printf("%d%c", (int) elapsed_time[i], i < ib_op_time->n_elems - 1 ? ' ' : '\n'); } avg /= ib_op_time->n_elems; for (i = 0; i < ib_op_time->n_elems; ++i) { stdev += pow(avg - elapsed_time[i], 2); } stdev = sqrt(stdev / ib_op_time->n_elems); printf("avg: %5.2lfs stddev: %5.2lf low: %d high: %d\n", avg, stdev, (int) elapsed_time[0], (int) elapsed_time[ib_op_time->n_elems - 1]); } #ifndef __WIN__ /********************************************************************* Set the runtime global options. */ static void set_options( /*========*/ int argc, char* argv[]) { int opt; int optind; int size = 0; struct option* longopts; int count = 0; /* Count the number of InnoDB system options. */ while (ib_longopts[count].name) { ++count; } /* Add two of our options and a spot for the sentinel. */ size = sizeof(struct option) * (count + 4); longopts = (struct option*) malloc(size); memset(longopts, 0x0, size); memcpy(longopts, ib_longopts, sizeof(struct option) * count); /* Add the local parameters (threads, rows and page_size). */ longopts[count].name = "threads"; longopts[count].has_arg = required_argument; longopts[count].flag = NULL; longopts[count].val = USER_OPT + 1; ++count; longopts[count].name = "rows"; longopts[count].has_arg = required_argument; longopts[count].flag = NULL; longopts[count].val = USER_OPT + 2; ++count; longopts[count].name = "page_size"; longopts[count].has_arg = required_argument; longopts[count].flag = NULL; longopts[count].val = USER_OPT + 3; while ((opt = getopt_long(argc, argv, "", longopts, &optind)) != -1) { switch(opt) { case USER_OPT + 1: n_threads = strtoul(optarg, NULL, 10); break; case USER_OPT + 2: n_rows = strtoul(optarg, NULL, 10); break; case USER_OPT + 3: page_size = strtoul(optarg, NULL, 10); break; default: /* If it's an InnoDB parameter, then we let the auxillary function handle it. */ if (set_global_option(opt, optarg) != DB_SUCCESS) { print_usage(argv[0]); exit(EXIT_FAILURE); } } /* switch */ } free(longopts); } #endif /* __WIN__ */ /********************************************************************* Print the statistics. */ static void print_stats(void) /*=============*/ { print_data(&ib_op_stats.insert, "op: insert"); print_data(&ib_op_stats.copy, "op: copy"); print_data(&ib_op_stats.join, "op: join"); } int main(int argc, char* argv[]) { int i; int ret; ib_err_t err; pthread_t* pthreads; ib_init(); test_configure(); #ifndef __WIN__ set_options(argc, argv); #endif /* __WIN__ */ err = ib_cfg_set_int("open_files", 8192); assert(err == DB_SUCCESS); err = ib_startup("barracuda"); assert(err == DB_SUCCESS); err = create_database(DATABASE); assert(err == DB_SUCCESS); ret = pthread_barrier_init(&barrier, NULL, n_threads); assert(ret == 0); pthreads = (pthread_t*) malloc(sizeof(*pthreads) * n_threads); memset(pthreads, 0, sizeof(*pthreads) * n_threads); ib_op_stats_alloc(n_threads); printf("About to spawn %d threads ", n_threads); for (i = 0; i < n_threads; ++i) { int retval; int* ptr = malloc(sizeof(int)); assert(ptr != NULL); *ptr = i * 2; /* worker_thread owns the argument and is responsible for freeing it. */ retval = pthread_create(&pthreads[i], NULL, worker_thread, ptr); if (retval != 0) { fprintf(stderr, "Error spawning thread %d, " "pthread_create() returned %d\n", i, retval); exit(EXIT_FAILURE); } printf("."); } printf("\nWaiting for threads to finish ...\n"); for (i = 0; i < n_threads; ++i) { pthread_join(pthreads[i], NULL); } free(pthreads); pthreads = NULL; ret = pthread_barrier_destroy(&barrier); assert(ret == 0); err = ib_shutdown(IB_SHUTDOWN_NORMAL); assert(err == DB_SUCCESS); print_stats(); ib_op_stats_free(); #ifdef UNIV_DEBUG_VALGRIND VALGRIND_DO_LEAK_CHECK; #endif return(EXIT_SUCCESS); } haildb-2.3.2/tests/ib_cfg.c0000644000175000017500000000776511513177357016401 0ustar00pcrewspcrews00000000000000/*********************************************************************** Copyright (c) 2009 Innobase Oy. All rights reserved. Copyright (c) 2009 Oracle. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ************************************************************************/ #include #include #include #include #include "test0aux.h" static void get_all(void) { static const char* var_names[] = { "adaptive_hash_index", "additional_mem_pool_size", "autoextend_increment", "buffer_pool_size", "checksums", "data_file_path", "data_home_dir", "doublewrite", "file_format", "file_io_threads", "file_per_table", "flush_log_at_trx_commit", "flush_method", "force_recovery", "lock_wait_timeout", "log_buffer_size", "log_file_size", "log_files_in_group", "log_group_home_dir", "max_dirty_pages_pct", "max_purge_lag", "lru_old_blocks_pct", "lru_block_access_recency", "open_files", "pre_rollback_hook", "print_verbose_log", "rollback_on_timeout", "stats_sample_pages", "status_file", "sync_spin_loops", "version", NULL }; const char** ptr; for (ptr = var_names; *ptr; ++ptr) { void* val; ib_err_t err; err = ib_cfg_get(*ptr, &val); assert(err == DB_SUCCESS); } } /********************************************************************* Function to test our simple config file parser. */ static void test_config_parser(void) /*====================*/ { ib_config_t config; const char* filename = "test.conf"; memset(&config, 0x0, sizeof(config)); if (config_parse_file(filename, &config) == 0) { config_print(&config); } else { perror(filename); } config_free(&config); } static void test_ib_cfg_get_all(void) { const char** names; ib_u32_t names_num; char buf[8]; /* long enough to store any type */ ib_u32_t i; /* can be called before ib_init() */ OK(ib_cfg_get_all(&names, &names_num)); for (i = 0; i < names_num; i++) { /* must be called after ib_init() */ OK(ib_cfg_get(names[i], buf)); /* the type of the variable can be retrieved with ib_cfg_var_get_type() */ } free(names); } int main(int argc, char** argv) { ib_err_t err; char* ptr; ib_ulint_t val; unsigned int i; (void)argc; (void)argv; test_config_parser(); err = ib_init(); assert(err == DB_SUCCESS); test_ib_cfg_get_all(); get_all(); /* the value should end in / */ err = ib_cfg_set("data_home_dir", "/some/path"); assert(err == DB_INVALID_INPUT); err = ib_cfg_set("data_home_dir", "/some/path/"); assert(err == DB_SUCCESS); err = ib_cfg_get("data_home_dir", &ptr); assert(err == DB_SUCCESS); assert(strcmp("/some/path/", ptr) == 0); err = ib_cfg_set("buffer_pool_size", 0xFFFFFFFFUL - 5); assert(err == DB_SUCCESS); err = ib_cfg_set("flush_method", "fdatasync"); assert(err == DB_INVALID_INPUT); for (i = 0; i <= 100; i++) { err = ib_cfg_set("lru_old_blocks_pct", i); if (5 <= i && i <= 95) { assert(err == DB_SUCCESS); err = ib_cfg_get("lru_old_blocks_pct", &val); assert(err == DB_SUCCESS); assert(i == val); } else { assert(err == DB_INVALID_INPUT); } } err = ib_cfg_set("lru_block_access_recency", 123); assert(err == DB_SUCCESS); err = ib_cfg_get("lru_block_access_recency", &val); assert(err == DB_SUCCESS); assert(val == 123); err = ib_cfg_set("open_files", 123); assert(err == DB_SUCCESS); get_all(); err = ib_shutdown(IB_SHUTDOWN_NORMAL); assert(err == DB_SUCCESS); return(0); } haildb-2.3.2/tests/ib_dict.c0000644000175000017500000003215011513177357016547 0ustar00pcrewspcrews00000000000000/*********************************************************************** Copyright (c) 2008, 2009 Innobase Oy. All rights reserved. Copyright (c) 2008, 2009 Oracle. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ************************************************************************/ /* Test to check if we can list all tables in the InnoDB data dictionary including the system tables. Create a database CREATE TABLE t1(C1 INT, C2 UNSIGNED INT, c3 VARCHAR(10) NOT NULL, PK(C1, C2)); ... CREATE TABLE tn(C1 INT, C2 UNSIGNED INT, c3 VARCHAR(10) NOT NULL, PK(C1, C2)); Print the schema using the API */ #include #include #include #include #include #include "test0aux.h" #ifdef UNIV_DEBUG_VALGRIND #include #endif #define DATABASE "dict_test" #define TABLE "t" typedef struct visitor_arg { FILE* fp; ib_trx_t ib_trx; const char* table_name; int cur_col; int n_indexes; int n_table_cols; int n_index_cols; } visitor_arg_t; /********************************************************************* Read a table's schema and print it. */ static int visit_tables( /*=========*/ void* arg, /*!< in: callback argument */ const char* name, /*!< in: table name */ int len); /*!< in: table name length */ /********************************************************************* Callback functions to list table nams. */ static int visit_table_list( /*=============*/ void* arg, /*!< User callback arg */ const char* name, /*!< Table name */ ib_tbl_fmt_t tbl_fmt, /*!< Table type */ ib_ulint_t page_size, /*!< Table page size */ int n_cols, /*!< No. of cols defined */ int n_indexes) /*!< No. of indexes defined */ { (void) arg; (void) name; (void) page_size; (void) tbl_fmt; (void) n_cols; (void) n_indexes; return(0); } /********************************************************************* Callback functions to traverse a table's schema. */ static int visit_table( /*========*/ void* arg, /*!< User callback arg */ const char* name, /*!< Table name */ ib_tbl_fmt_t tbl_fmt, /*!< Table type */ ib_ulint_t page_size, /*!< Table page size */ int n_cols, /*!< No. of cols defined */ int n_indexes) /*!< No. of indexes defined */ { const char* p; visitor_arg_t* visitor_arg = (visitor_arg_t*) arg; FILE* fp = (FILE*) visitor_arg->fp; (void) page_size; (void) tbl_fmt; p = strchr(name, '/'); if (p == NULL) { p = name; } else { ++p; } fprintf(fp, "CREATE TABLE %s(\n", p); visitor_arg->cur_col = 0; visitor_arg->table_name = name; visitor_arg->n_table_cols = n_cols; visitor_arg->n_indexes = n_indexes; return(0); } /********************************************************************* Callback functions to traverse a table's schema. */ static int visit_table_col( /*============*/ void* arg, /*!< User callback arg */ const char* name, /*!< Column name */ ib_col_type_t col_type, /*!< Column type */ ib_ulint_t len, /*!< Column len */ ib_col_attr_t attr) /*!< Column attributes */ { visitor_arg_t* visitor_arg = (visitor_arg_t*) arg; FILE* fp = (FILE*) visitor_arg->fp; if (visitor_arg->cur_col > 0 && col_type != IB_SYS) { fprintf(fp, ",\n"); } ++visitor_arg->cur_col; /* Ignore system columns. */ if (col_type != IB_SYS) { fprintf(fp, "\t%-16s\t", name); } else { if (visitor_arg->cur_col == visitor_arg->n_table_cols) { fprintf(fp, ");\n"); } return(0); } switch (col_type) { case IB_VARCHAR: case IB_VARCHAR_ANYCHARSET: if (len > 0) { fprintf(fp, "VARCHAR(%d)", (int) len); } else { fprintf(fp, "TEXT"); } break; case IB_CHAR: case IB_CHAR_ANYCHARSET: fprintf(fp, "CHAR(%d)", (int) len); break; case IB_BINARY: fprintf(fp, "BINARY(%d)", (int) len); break; case IB_VARBINARY: if (len > 0) { fprintf(fp, "VARBINARY(%d)", (int) len); } else { fprintf(fp, "VARBINARY"); } break; case IB_BLOB: fprintf(fp, "BLOB"); break; case IB_INT: if (attr & IB_COL_UNSIGNED) { fprintf(fp, "UNSIGNED "); } switch (len) { case 1: fprintf(fp, "TINYINT"); break; case 2: fprintf(fp, "SMALLINT"); break; case 4: fprintf(fp, "INT"); break; case 8: fprintf(fp, "BIGINT"); break; default: abort(); } break; case IB_FLOAT: fprintf(fp, "FLOAT"); break; case IB_DOUBLE: fprintf(fp, "DOUBLE"); break; case IB_DECIMAL: fprintf(fp, "DECIMAL"); break; default: fprintf(fp, "UNKNOWN"); break; } if (attr & IB_COL_NOT_NULL) { fprintf(fp, " NOT NULL"); } return(0); } /********************************************************************* Callback functions to traverse a table's schema. */ static int visit_index( /*========*/ void* arg, /*!< User callback arg */ const char* name, /*!< Index name */ ib_bool_t clustered, /*!< True if clustered */ ib_bool_t unique, /*!< True if unique */ int n_cols) /*!< No. of cols defined */ { const char* p; visitor_arg_t* visitor_arg = (visitor_arg_t*) arg; FILE* fp = (FILE*) visitor_arg->fp; (void) clustered; visitor_arg->cur_col = 0; visitor_arg->n_index_cols = n_cols; p = strchr(visitor_arg->table_name, '/'); if (p == NULL) { p = visitor_arg->table_name; } else { ++p; } fprintf(fp, "CREATE %s INDEX %s ON %s(", (unique ? "UNIQUE" : ""), name, p); return(0); } /********************************************************************* Callback functions to traverse a table's schema. */ int visit_index_col( /*============*/ void* arg, /*!< User callback arg */ const char* name, /*!< Column name */ ib_ulint_t prefix_len) /*!< Prefix length */ { visitor_arg_t* visitor_arg = (visitor_arg_t*) arg; FILE* fp = (FILE*) visitor_arg->fp; ++visitor_arg->cur_col; fprintf(fp, "%s", name); if (visitor_arg->cur_col < visitor_arg->n_index_cols) { fprintf(fp, ","); } else { fprintf(fp, ");\n"); } return(0); } /********************************************************************* Create an InnoDB database (sub-directory). */ static ib_err_t create_database( /*============*/ const char* name) { ib_bool_t err; err = ib_database_create(name); assert(err == IB_TRUE); return(DB_SUCCESS); } /********************************************************************* Create a table using InnoDB's internal SQL parser. */ static ib_err_t create_table( /*=========*/ const char* dbname, /*!< in: database name */ const char* name, /*!< in: table name */ int n) /*!< in: table suffix */ { ib_trx_t ib_trx; ib_id_t table_id = 0; ib_err_t err = DB_SUCCESS; ib_tbl_sch_t ib_tbl_sch = NULL; ib_idx_sch_t ib_idx_sch = NULL; char table_name[IB_MAX_TABLE_NAME_LEN]; #ifdef __WIN__ sprintf(table_name, "%s/%s%d", dbname, name, n); #else snprintf(table_name, sizeof(table_name), "%s/%s%d", dbname, name, n); #endif /* Pass a table page size of 0, ie., use default page size. */ err = ib_table_schema_create( table_name, &ib_tbl_sch, IB_TBL_COMPACT, 0); assert(err == DB_SUCCESS); err = ib_table_schema_add_col( ib_tbl_sch, "C1", IB_VARCHAR, IB_COL_NONE, 0, 10); assert(err == DB_SUCCESS); err = ib_table_schema_add_col( ib_tbl_sch, "C2", IB_VARCHAR, IB_COL_NONE, 0, 10); assert(err == DB_SUCCESS); err = ib_table_schema_add_col( ib_tbl_sch, "C3", IB_INT, IB_COL_UNSIGNED, 0, sizeof(ib_u32_t)); assert(err == DB_SUCCESS); err = ib_table_schema_add_index(ib_tbl_sch, "PRIMARY", &ib_idx_sch); assert(err == DB_SUCCESS); /* Set prefix length to 0. */ err = ib_index_schema_add_col( ib_idx_sch, "C1", 0); assert(err == DB_SUCCESS); /* Set prefix length to 0. */ err = ib_index_schema_add_col( ib_idx_sch, "C2", 0); assert(err == DB_SUCCESS); err = ib_index_schema_set_clustered(ib_idx_sch); assert(err == DB_SUCCESS); /* create table */ ib_trx = ib_trx_begin(IB_TRX_REPEATABLE_READ); err = ib_schema_lock_exclusive(ib_trx); assert(err == DB_SUCCESS); err = ib_table_create(ib_trx, ib_tbl_sch, &table_id); assert(err == DB_SUCCESS); err = ib_trx_commit(ib_trx); assert(err == DB_SUCCESS); if (ib_tbl_sch != NULL) { ib_table_schema_delete(ib_tbl_sch); } return(err); } /* Check whether NULL callbacks work. */ static const ib_schema_visitor_t table_visitor = { IB_SCHEMA_VISITOR_TABLE_AND_INDEX_COL, visit_table_list, NULL, NULL, NULL }; /********************************************************************* Read table names only. */ static int visit_tables_list( /*==============*/ void* arg, const char* name, int len) /*!< out: 0 on success, nonzero on error */ { ib_err_t err; char* ptr = malloc(len + 1); visitor_arg_t* visitor_arg = (visitor_arg_t*) arg; memset(ptr, 0x0, len + 1); strncpy(ptr, name, len); err = ib_table_schema_visit( visitor_arg->ib_trx, ptr, &table_visitor, arg); free(ptr); return(err == DB_SUCCESS ? 0 : -1); } static const ib_schema_visitor_t visitor = { IB_SCHEMA_VISITOR_TABLE_AND_INDEX_COL, visit_table, visit_table_col, visit_index, visit_index_col }; /********************************************************************* Read a table's schema and print it. */ static int visit_tables( /*=========*/ void* arg, const char* name, int len) /*!< out: 0 on success, nonzero on error */ { ib_err_t err; char* ptr = malloc(len + 1); visitor_arg_t* visitor_arg = (visitor_arg_t*) arg; memset(ptr, 0x0, len + 1); strncpy(ptr, name, len); err = ib_table_schema_visit(visitor_arg->ib_trx, ptr, &visitor, arg); free(ptr); return(err == DB_SUCCESS ? 0 : -1); } /********************************************************************* Read the system tables schema and print it. @return 0 on success, nonzero on error */ static int visit_sys_tables(void) /*==================*/ { ib_err_t err; visitor_arg_t arg; ib_trx_t ib_trx; ib_trx = ib_trx_begin(IB_TRX_SERIALIZABLE); memset(&arg, 0x0, sizeof(arg)); arg.fp = stdout; arg.ib_trx = ib_trx; err = ib_table_schema_visit(ib_trx, "SYS_TABLES", &visitor, &arg); assert(err == DB_SCHEMA_NOT_LOCKED); err = ib_schema_lock_exclusive(ib_trx); assert(err == DB_SUCCESS); err = ib_table_schema_visit(ib_trx, "SYS_TABLES", &visitor, &arg); assert(err == DB_SUCCESS); err = ib_table_schema_visit(ib_trx, "SYS_COLUMNS", &visitor, &arg); assert(err == DB_SUCCESS); err = ib_table_schema_visit(ib_trx, "SYS_TABLES", &visitor, &arg); assert(err == DB_SUCCESS); err = ib_table_schema_visit(ib_trx, "SYS_INDEXES", &visitor, &arg); assert(err == DB_SUCCESS); err = ib_trx_commit(ib_trx); return(err == DB_SUCCESS ? 0 : -1); } /********************************************************************* Iterate over all the tables in the InnoDB data dictionary and print their schemas. @return DB_SUCCESS or error code */ static ib_err_t print_entire_schema(void) /*=====================*/ { ib_err_t err; visitor_arg_t arg; memset(&arg, 0x0, sizeof(arg)); arg.fp = stdout; arg.ib_trx = ib_trx_begin(IB_TRX_SERIALIZABLE); err = ib_schema_lock_exclusive(arg.ib_trx); assert(err == DB_SUCCESS); err = ib_schema_tables_iterate(arg.ib_trx, visit_tables_list, &arg); assert(err == DB_SUCCESS); err = ib_schema_tables_iterate(arg.ib_trx, visit_tables, &arg); assert(err == DB_SUCCESS); err = ib_trx_commit(arg.ib_trx); assert(err == DB_SUCCESS); return(err); } /********************************************************************* Drop the table. @return DB_SUCCESS or error code */ static ib_err_t drop_table_n( /*=========*/ const char* dbname, /*!< in: database name */ const char* name, /*!< in: table to drop */ int n) /*!< in: count */ { ib_trx_t ib_trx; ib_err_t err = DB_SUCCESS; char table_name[IB_MAX_TABLE_NAME_LEN]; #ifdef __WIN__ sprintf(table_name, "%s/%s%d", dbname, name, n); #else snprintf(table_name, sizeof(table_name), "%s/%s%d", dbname, name, n); #endif ib_trx = ib_trx_begin(IB_TRX_REPEATABLE_READ); assert(ib_trx != NULL); err = ib_schema_lock_exclusive(ib_trx); assert(err == DB_SUCCESS); err = ib_table_drop(ib_trx, table_name); assert(err == DB_SUCCESS); err = ib_trx_commit(ib_trx); assert(err == DB_SUCCESS); return(err); } int main(int argc, char* argv[]) { int i; ib_err_t err; (void) argc; (void) argv; err = ib_init(); assert(err == DB_SUCCESS); test_configure(); err = ib_startup("barracuda"); assert(err == DB_SUCCESS); visit_sys_tables(); err = create_database(DATABASE); assert(err == DB_SUCCESS); for (i = 0; i < 10; i++) { err = create_table(DATABASE, TABLE, i); assert(err == DB_SUCCESS); } err = print_entire_schema(); assert(err == DB_SUCCESS); for (i = 0; i < 10; ++i) { err = drop_table_n(DATABASE, TABLE, i); assert(err == DB_SUCCESS); } err = ib_shutdown(IB_SHUTDOWN_NORMAL); assert(err == DB_SUCCESS); #ifdef UNIV_DEBUG_VALGRIND VALGRIND_DO_LEAK_CHECK; #endif return(EXIT_SUCCESS); } haildb-2.3.2/tests/ib_client_compare.c0000644000175000017500000003052111513177357020610 0ustar00pcrewspcrews00000000000000/*********************************************************************** Copyright (c) 2008 Innobase Oy. All rights reserved. Copyright (c) 2008 Oracle. All rights reserved. Copyright (c) 2010 Stewart Smith. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ************************************************************************/ #include #include #include #include #include "test0aux.h" #ifdef UNIV_DEBUG_VALGRIND #include #endif #define DATABASE "test" #define TABLE "t" /* A row from our test table. */ typedef struct row_t { char c1[32]; char c2[32]; ib_u32_t c3; } row_t; static row_t in_rows[] = { {"a", "t", 1}, {"b", "u", 2}, {"c", "b", 3}, {"d", "n", 4}, {"e", "s", 5}, {"e", "j", 6}, {"d", "f", 7}, {"c", "n", 8}, {"b", "z", 9}, {"a", "i", 10}, {"", "", 0}}; #define COL_LEN(n) (sizeof(((row_t*)0)->n)) /********************************************************************* Create an InnoDB database (sub-directory). */ static ib_err_t create_database( /*============*/ const char* name) { ib_bool_t err; err = ib_database_create(name); assert(err == IB_TRUE); return(DB_SUCCESS); } /********************************************************************* CREATE TABLE T( c1 VARCHAR(n), c2 VARCHAR(n), c3 INT, PRIMARY KEY(c1, c2); */ static ib_err_t create_table( /*=========*/ const char* dbname, /*!< in: database name */ const char* name) /*!< in: table name */ { ib_trx_t ib_trx; ib_id_t table_id = 0; ib_err_t err = DB_SUCCESS; ib_tbl_sch_t ib_tbl_sch = NULL; ib_idx_sch_t ib_idx_sch = NULL; char table_name[IB_MAX_TABLE_NAME_LEN]; #ifdef __WIN__ sprintf(table_name, "%s/%s", dbname, name); #else snprintf(table_name, sizeof(table_name), "%s/%s", dbname, name); #endif /* Pass a table page size of 0, ie., use default page size. */ err = ib_table_schema_create( table_name, &ib_tbl_sch, IB_TBL_COMPACT, 0); assert(err == DB_SUCCESS); err = ib_table_schema_add_col( ib_tbl_sch, "c1", IB_VARCHAR_ANYCHARSET, IB_COL_NONE, 0, COL_LEN(c1)-1); assert(err == DB_SUCCESS); err = ib_table_schema_add_col( ib_tbl_sch, "c2", IB_VARCHAR, IB_COL_NONE, 0, COL_LEN(c2)-1); assert(err == DB_SUCCESS); err = ib_table_schema_add_col( ib_tbl_sch, "c3", IB_INT, IB_COL_UNSIGNED, 0, COL_LEN(c3)); assert(err == DB_SUCCESS); err = ib_table_schema_add_index(ib_tbl_sch, "c1_c2", &ib_idx_sch); assert(err == DB_SUCCESS); /* Set prefix length to 0. */ err = ib_index_schema_add_col( ib_idx_sch, "c1", 0); assert(err == DB_SUCCESS); /* Set prefix length to 0. */ err = ib_index_schema_add_col( ib_idx_sch, "c2", 0); assert(err == DB_SUCCESS); err = ib_index_schema_set_clustered(ib_idx_sch); assert(err == DB_SUCCESS); /* create table */ ib_trx = ib_trx_begin(IB_TRX_REPEATABLE_READ); err = ib_schema_lock_exclusive(ib_trx); assert(err == DB_SUCCESS); err = ib_table_create(ib_trx, ib_tbl_sch, &table_id); assert(err == DB_SUCCESS); err = ib_trx_commit(ib_trx); assert(err == DB_SUCCESS); if (ib_tbl_sch != NULL) { ib_table_schema_delete(ib_tbl_sch); } return(err); } /********************************************************************* Open a table and return a cursor for the table. */ static ib_err_t open_table( /*=======*/ const char* dbname, /*!< in: database name */ const char* name, /*!< in: table name */ ib_trx_t ib_trx, /*!< in: transaction */ ib_crsr_t* crsr) /*!< out: innodb cursor */ { ib_err_t err = DB_SUCCESS; char table_name[IB_MAX_TABLE_NAME_LEN]; #ifdef __WIN__ sprintf(table_name, "%s/%s", dbname, name); #else snprintf(table_name, sizeof(table_name), "%s/%s", dbname, name); #endif err = ib_cursor_open_table(table_name, ib_trx, crsr); assert(err == DB_SUCCESS); return(err); } /********************************************************************* INSERT INTO T VALUE('c1', 'c2', c3); */ static ib_err_t insert_rows( /*========*/ ib_crsr_t crsr) /*!< in, out: cursor to use for write */ { row_t* row; ib_tpl_t tpl = NULL; ib_err_t err = DB_ERROR; tpl = ib_clust_read_tuple_create(crsr); assert(tpl != NULL); for (row = in_rows; *row->c1; ++row) { err = ib_col_set_value(tpl, 0, row->c1, strlen(row->c1)); assert(err == DB_SUCCESS); err = ib_col_set_value(tpl, 1, row->c2, strlen(row->c2)); assert(err == DB_SUCCESS); err = ib_col_set_value(tpl, 2, &row->c3, sizeof(row->c3)); assert(err == DB_SUCCESS); err = ib_cursor_insert_row(crsr, tpl); assert(err == DB_SUCCESS); tpl = ib_tuple_clear(tpl); assert(tpl != NULL); } if (tpl != NULL) { ib_tuple_delete(tpl); } return(err); } /********************************************************************* UPDATE T SET c3 = c3 + 100 WHERE c1 = 'a'; */ static ib_err_t update_a_row( /*=========*/ ib_crsr_t crsr) { ib_err_t err; int res = ~0; ib_tpl_t key_tpl; ib_tpl_t old_tpl = NULL; ib_tpl_t new_tpl = NULL; /* Create a tuple for searching an index. */ key_tpl = ib_sec_search_tuple_create(crsr); assert(key_tpl != NULL); /* Set the value to look for. */ err = ib_col_set_value(key_tpl, 0, "a", 1); assert(err == DB_SUCCESS); /* Search for the key using the cluster index (PK) */ err = ib_cursor_moveto(crsr, key_tpl, IB_CUR_GE, &res); assert(err == DB_SUCCESS); /* Must be positioned on a record that's greater than search key. */ assert(res == -1); if (key_tpl != NULL) { ib_tuple_delete(key_tpl); } /* Create the tuple instance that we will use to update the table. old_tpl is used for reading the existing row and new_tpl will contain the update row data. */ old_tpl = ib_clust_read_tuple_create(crsr); assert(old_tpl != NULL); new_tpl = ib_clust_read_tuple_create(crsr); assert(new_tpl != NULL); /* Iterate over the records while the c1 column matches "a". */ while (err == DB_SUCCESS) { const char* c1; ib_u32_t c3; ib_ulint_t c1_len; ib_ulint_t data_len; ib_col_meta_t col_meta; err = ib_cursor_read_row(crsr, old_tpl); assert(err == DB_SUCCESS); /* Get the c1 column value. */ c1 = ib_col_get_value(old_tpl, 0); c1_len = ib_col_get_meta(old_tpl, 0, &col_meta); /* There are no SQL_NULL values in our test data. */ assert(c1 != NULL); /* Only update c1 values that are == "a". */ if (strncmp(c1, "a", 1) != 0) { break; } /* Copy the old contents to the new tuple. */ err = ib_tuple_copy(new_tpl, old_tpl); /* Update the c3 column in the new tuple. */ data_len = ib_col_get_meta(old_tpl, 2, &col_meta); assert(data_len != IB_SQL_NULL); err = ib_tuple_read_u32(old_tpl, 2, &c3); assert(err == DB_SUCCESS); c3 += 100; /* Set the updated value in the new tuple. */ err = ib_tuple_write_u32(new_tpl, 2, c3); assert(err == DB_SUCCESS); err = ib_cursor_update_row(crsr, old_tpl, new_tpl); assert(err == DB_SUCCESS); /* NOTE: we go in *REVERSE* order since we are using custom compare function that does in reverse binary order. i.e. 'a' is *LAST*, not first */ /* Move to the next record to update. */ err = ib_cursor_prev(crsr); /* Since we are searching for "a" it must always succeed. */ assert(err == DB_SUCCESS); /* Reset the old and new tuple instances. */ old_tpl = ib_tuple_clear(old_tpl); assert(old_tpl != NULL); new_tpl = ib_tuple_clear(new_tpl); assert(new_tpl != NULL); } if (old_tpl != NULL) { ib_tuple_delete(old_tpl); } if (new_tpl != NULL) { ib_tuple_delete(new_tpl); } return(err); } /********************************************************************* DELETE RFOM T WHERE c1 = 'b' and c2 = 'z'; */ static ib_err_t delete_a_row( /*=========*/ ib_crsr_t crsr) { ib_err_t err; int res = ~0; ib_tpl_t key_tpl; /* Create a tuple for searching an index. */ key_tpl = ib_sec_search_tuple_create(crsr); assert(key_tpl != NULL); /* Set the value to delete. */ err = ib_col_set_value(key_tpl, 0, "b", 1); assert(err == DB_SUCCESS); err = ib_col_set_value(key_tpl, 1, "z", 1); assert(err == DB_SUCCESS); /* Search for the key using the cluster index (PK) */ err = ib_cursor_moveto(crsr, key_tpl, IB_CUR_GE, &res); assert(err == DB_SUCCESS); /* Must be positioned on the record to delete, since we've specified an exact prefix match. */ assert(res == 0); if (key_tpl != NULL) { ib_tuple_delete(key_tpl); } /* InnoDB handles the updating of all secondary indexes. */ err = ib_cursor_delete_row(crsr); assert(err == DB_SUCCESS); return(err); } /********************************************************************* SELECT * FROM T; */ static ib_err_t do_query( /*=====*/ ib_crsr_t crsr) { ib_err_t err; ib_tpl_t tpl; tpl = ib_clust_read_tuple_create(crsr); assert(tpl != NULL); err = ib_cursor_first(crsr); assert(err == DB_SUCCESS); char last_char='z'; while (err == DB_SUCCESS) { err = ib_cursor_read_row(crsr, tpl); assert(err == DB_SUCCESS || err == DB_END_OF_INDEX || err == DB_RECORD_NOT_FOUND); if (err == DB_RECORD_NOT_FOUND || err == DB_END_OF_INDEX) { break; } const char* ptr; ptr = ib_col_get_value(tpl, 0); fprintf(stdout, "COL 0:%s\tlast %c this %c", ptr, last_char, ptr[0]); assert(ptr[0] <= last_char); last_char= ptr[0]; print_tuple(stdout, tpl); err = ib_cursor_next(crsr); assert(err == DB_SUCCESS || err == DB_END_OF_INDEX || err == DB_RECORD_NOT_FOUND); tpl = ib_tuple_clear(tpl); assert(tpl != NULL); } if (tpl != NULL) { ib_tuple_delete(tpl); } if (err == DB_RECORD_NOT_FOUND || err == DB_END_OF_INDEX) { err = DB_SUCCESS; } return(err); } #define min(x, y) (x < y ? x : y) /* the same as default_compare, but in reverse! */ int reverse_compare( /*!< out: 1, 0, -1, if a is greater, equal, less than b, respectively */ const ib_col_meta_t* col_meta, /*!< in: column meta data */ const ib_byte_t*p1, /*!< in: key */ ib_ulint_t p1_len, /*!< in: key length */ const ib_byte_t*p2, /*!< in: key */ ib_ulint_t p2_len) /*!< in: key length */ { int ret; (void)col_meta; ret = memcmp(p1, p2, min(p1_len, p2_len)); if (ret == 0) { ret = p1_len - p2_len; } return(ret < 0 ? 1 : ((ret > 0) ? -1 : 0)); } int main(int argc, char* argv[]) { ib_err_t err; ib_crsr_t crsr; ib_trx_t ib_trx; ib_u64_t version; (void)argc; (void)argv; version = ib_api_version(); printf("API: %d.%d.%d\n", (int) (version >> 32), /* Current version */ (int) ((version >> 16)) & 0xffff, /* Revisiion */ (int) (version & 0xffff)); /* Age */ err = ib_init(); assert(err == DB_SUCCESS); test_configure(); ib_set_client_compare(reverse_compare); err = ib_startup("barracuda"); assert(err == DB_SUCCESS); err = create_database(DATABASE); assert(err == DB_SUCCESS); printf("Create table\n"); err = create_table(DATABASE, TABLE); assert(err == DB_SUCCESS); printf("Begin transaction\n"); ib_trx = ib_trx_begin(IB_TRX_REPEATABLE_READ); assert(ib_trx != NULL); printf("Open cursor\n"); err = open_table(DATABASE, TABLE, ib_trx, &crsr); assert(err == DB_SUCCESS); printf("Lock table in IX\n"); err = ib_cursor_lock(crsr, IB_LOCK_IX); assert(err == DB_SUCCESS); printf("Insert rows\n"); err = insert_rows(crsr); assert(err == DB_SUCCESS); printf("Query table\n"); err = do_query(crsr); assert(err == DB_SUCCESS); printf("Update a row\n"); err = update_a_row(crsr); assert(err == DB_SUCCESS); printf("Query table\n"); err = do_query(crsr); assert(err == DB_SUCCESS); printf("Delete a row\n"); err = delete_a_row(crsr); assert(err == DB_SUCCESS); printf("Query table\n"); err = do_query(crsr); assert(err == DB_SUCCESS); printf("Close cursor\n"); err = ib_cursor_close(crsr); assert(err == DB_SUCCESS); crsr = NULL; printf("Commit transaction\n"); err = ib_trx_commit(ib_trx); assert(err == DB_SUCCESS); printf("Drop table\n"); err = drop_table(DATABASE, TABLE); assert(err == DB_SUCCESS); err = ib_shutdown(IB_SHUTDOWN_NORMAL); assert(err == DB_SUCCESS); #ifdef UNIV_DEBUG_VALGRIND VALGRIND_DO_LEAK_CHECK; #endif return(EXIT_SUCCESS); } haildb-2.3.2/tests/ib_mt_drv.c0000644000175000017500000004076311513177357017130 0ustar00pcrewspcrews00000000000000/*********************************************************************** Copyright (c) 2009 Innobase Oy. All rights reserved. Copyright (c) 2009 Oracle. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ************************************************************************/ /********************************************************************** NOTE: This test is currently Unix centric and will not compile on Windows. This module is a driver framework for running arbitrary DML and DDL statements on arbitrary number of tables using configurable number of threads. The DML statements that can be triggered are: SELECT INSERT UPDATE DELETE The DDL statements that can be triggered are: CREATE TABLE ALTER TABLE DROP TABLE TRUNCATE TABLE How it works: ============= This is just a driver. You have to implement the DML and DDL functionality for each table (see mt_base.c and mt_t1.c for examples) Once a derived table class is implemented it can be plugged into this driver by calling the register function in register_test_tables(). This driver goes through following sequence: 1) Create database test 2) Register all tables 3) Create each registered table 4) Seed each table with n_rows 5) Create n_ddl_threads and n_dml_threads 6) Each DDL thread does following: a) Choose one of the table randomly b) Choose a DDL operation randomly c) Execute the DDL operation and update the stats struct d) Wait for ddl_interval before going back to (a) 7) Each DML thread does following: a) Choose one of the table randomly b) Choose a DML operation randomly c) Execute the DDL operation and update the stats struct d) Return to (a) 8) The test continues for test_time 9) Waits for all worker threads to finish 10) Check all tables (for now checking mean only doing a select *) 11) Drop all tables and the database 12) Print the statistics 13) Clean up **********************************************************************/ #include #include #include #include #include #include #include #include "test0aux.h" #include "ib_mt_drv.h" #ifdef UNIV_DEBUG_VALGRIND #include #endif #define DATABASE "test" /* Number of worker threads*/ static int n_ddl_thr = 4; static int n_dml_thr = 16; static pthread_t* ddl_tid; static pthread_t* dml_tid; /* Initial number of rows. */ static const int n_rows = 1000; /* batch size for DML. Commit after that many rows are worked upon */ static const int batch_size = 1; /* %age transactions to be rolled back */ static const int rollback_percent = 10; /* isolation level for transactions */ static const int isolation_level = IB_TRX_REPEATABLE_READ; /* Test duration in seconds */ static int test_time = 100; /* Interval between DDL statements in seconds */ static int ddl_interval = 1; /* To be incremented every time we do a DROP or TRUNCATE. This is for debug purposes so that we know that the old data actually vanished from the table after a DROP or TRUNCATE */ static int run_number = 1; #define get_cur_run_number() (run_number) #define incr_cur_run_number() (++run_number) /* Flag used by worker threads to finish the run */ static ib_bool_t test_running = IB_FALSE; /* These functions are defined in the corresponding modules where table classes are defined. Base class is defined in mt_base.c */ extern void register_base_table(tbl_class_t*); /* Register new tables by providing initialaztion function def here and calling it in register_test_tables() */ extern void register_t1_table(tbl_class_t*); extern void register_t2_table(tbl_class_t*); /* Maximum number of tables that this test suite can work with */ #define NUM_TBLS 64 static tbl_class_t tbl_array[NUM_TBLS]; /* How many tables we have registered currently */ static int num_tables = 0; /* These are going to be configurable parameters for table creation */ static ib_tbl_fmt_t tbl_format = IB_TBL_COMPACT; static ib_ulint_t page_size = 0; /* Datastructure to hold error statistics for DML and DDL operations */ static op_err_t dml_op_errs[DML_OP_TYPE_MAX]; static op_err_t ddl_op_errs[DDL_OP_TYPE_MAX]; /********************************************************************** Update the error stats */ void update_err_stats( /*=============*/ op_err_t* e, /*!< in: error stat struct */ ib_err_t err) /*!< in: error code */ { pthread_mutex_lock(&e->mutex); e->n_ops++; if (err != DB_SUCCESS ) { e->n_errs++; e->errs[err]++; } pthread_mutex_unlock(&e->mutex); } /********************************************************************* Create an InnoDB database (sub-directory). */ static ib_err_t create_database( /*============*/ const char* name) { ib_bool_t err; err = ib_database_create(name); assert(err == IB_TRUE); return(DB_SUCCESS); } /********************************************************************* Drop the database. */ static ib_err_t drop_database( /*==========*/ const char* dbname) /*!< in: db to drop */ { ib_err_t err; err = ib_database_drop(dbname); return(err); } /********************************************************************** Create test tables at startup */ static void create_test_table( /*==============*/ tbl_class_t* tbl) /*!< in: table to create */ { ib_err_t err; cb_args_t args; args.trx= NULL; args.tbl = tbl; args.isolation_level = isolation_level; args.run_number = get_cur_run_number(); args.err_st = &ddl_op_errs[DDL_OP_TYPE_CREATE]; err = tbl->ddl_fn[DDL_OP_TYPE_CREATE](&args); assert(err == DB_SUCCESS); } /********************************************************************** Seed test table with initial data */ static void seed_test_table( /*===========*/ tbl_class_t* tbl) /*!< in: table to populate */ { ib_err_t err; ib_trx_t trx; cb_args_t args; int i; args.trx = NULL; args.isolation_level = isolation_level; args.batch_size = 1; args.tbl = tbl; for (i = 0; i < n_rows; ++i) { trx = ib_trx_begin(isolation_level); assert(trx != NULL); args.trx = trx; args.run_number = get_cur_run_number(); args.err_st = &dml_op_errs[DML_OP_TYPE_INSERT]; err = tbl->dml_fn[DML_OP_TYPE_INSERT](&args); err = ib_trx_commit(trx); assert(err == DB_SUCCESS); } } /********************************************************************** Create test tables and populate them with n_rows of data */ static void init_test_tables(void) /*==================*/ { int i; for (i = 0; i < num_tables; ++i) { create_test_table(&tbl_array[i]); } for (i = 0; i < num_tables; ++i) { seed_test_table(&tbl_array[i]); } } /********************************************************************** Drop test tables */ static void drop_test_tables(void) /*==================*/ { ib_err_t err; int i; for (i = 0; i < num_tables; ++i) { cb_args_t args; tbl_class_t* tbl = &tbl_array[i]; args.trx = NULL; args.tbl = tbl; args.isolation_level = isolation_level; args.err_st = &ddl_op_errs[DDL_OP_TYPE_DROP]; err = tbl->ddl_fn[DDL_OP_TYPE_DROP](&args); } } /********************************************************************** Depending on rollback_percent decides whether to commit or rollback a given transaction */ static void commit_or_rollback( /*===============*/ ib_trx_t trx, /*!< in: trx to commit or rollback */ int cnt, /*!< in: trx counter */ ib_err_t err) /*!< in: err from last operation */ { /* We have set rollback_on_timeout therefore we need not to commit a trx that returns with this error. */ if (err == DB_LOCK_WAIT_TIMEOUT) { ; } else if ((!rollback_percent || cnt % (100 / rollback_percent)) && err == DB_SUCCESS) { //printf("Commit transaction\n"); err = ib_trx_commit(trx); assert(err == DB_SUCCESS); } else { //printf("Rollback transaction\n"); err = ib_trx_rollback(trx); assert(err == DB_SUCCESS); } } /********************************************************************** A DML worker thread that performs a randomly chosen DML operation on a randomly chosen table */ static void* dml_worker( /*=======*/ void* dummy) /*!< in: unused */ { ib_err_t err; ib_trx_t trx; tbl_class_t* tbl; cb_args_t args; int j; int cnt = 0; (void)dummy; fprintf(stderr, "dml_worker up!\n"); args.isolation_level = isolation_level; args.batch_size = batch_size; do { trx = ib_trx_begin(isolation_level); assert(trx != NULL); /* choose a table randomly */ tbl = &tbl_array[random() % num_tables]; args.tbl = tbl; args.trx = trx; args.run_number = get_cur_run_number(); /* choose a DML operation randomly */ j = random() % DML_OP_TYPE_MAX; ++cnt; args.err_st = &dml_op_errs[j]; err = tbl->dml_fn[j](&args); /* If it was an insert or update and the run_number has changed we unconditionally rollback in order to maintain consistency in the values that are inserted in the columns */ if (args.run_number != get_cur_run_number() && (j == DML_OP_TYPE_UPDATE || j == DML_OP_TYPE_INSERT)) { err = ib_trx_rollback(trx); assert(err == DB_SUCCESS); } else { commit_or_rollback(trx, cnt, err); } } while (test_running); fprintf(stderr, "dml_worker done!\n"); return(NULL); } /********************************************************************** A DDL worker thread that performs a randomly chosen DDL operation on a randomly chosen table */ static void* ddl_worker( /*=======*/ void* dummy) /*!< in: unused */ { cb_args_t args; (void)dummy; fprintf(stderr, "ddl_worker up!\n"); args.isolation_level = isolation_level; do { int j; ib_err_t err; tbl_class_t* tbl; /* wait for DDL interval */ sleep(ddl_interval); /* choose a table randomly */ tbl = &tbl_array[random() % num_tables]; args.tbl = tbl; /* choose a DDL operation randomly */ j = random() % DDL_OP_TYPE_MAX; /* When doing DROP or TRUNCATE we increment the run_number */ if (j == DDL_OP_TYPE_DROP || j == DDL_OP_TYPE_TRUNCATE) { incr_cur_run_number(); } args.err_st = &ddl_op_errs[j]; err = tbl->ddl_fn[j](&args); /* If it was a DROP TABLE or if it returned DB_TABLE_NOT_FOUND then we should make an attempt to create the table right away */ if ((err == DB_SUCCESS && j == DDL_OP_TYPE_DROP) || (err == DB_TABLE_NOT_FOUND)) { args.err_st = &ddl_op_errs[DDL_OP_TYPE_CREATE]; err = tbl->ddl_fn[DDL_OP_TYPE_CREATE](&args); } } while (test_running); fprintf(stderr, "ddl_worker done!\n"); return(NULL); } /********************************************************************** Create worker threads */ static void create_worker_threads(void) /*=======================*/ { int rc; int i; dml_tid = (pthread_t*)malloc(sizeof(pthread_t) * n_dml_thr); assert(dml_tid != NULL); for (i = 0; i < n_dml_thr; ++i) { rc = pthread_create(&dml_tid[i], NULL, dml_worker, NULL); assert(!rc); } ddl_tid = (pthread_t*)malloc(sizeof(pthread_t) * n_ddl_thr); assert(ddl_tid != NULL); for (i = 0; i < n_ddl_thr; ++i) { rc = pthread_create(&ddl_tid[i], NULL, ddl_worker, NULL); assert(!rc); } } /********************************************************************** Initialize the structure to hold error statistics */ static void init_err_op_struct( /*===============*/ op_err_t* st) /*!< in/out: struct to initialize */ { int rc; memset(st, 0x00, sizeof(*st)); rc = pthread_mutex_init(&st->mutex, NULL); assert(!rc); } /********************************************************************** Initialize statistic structures. */ static void init_stat_structs(void) /*===================*/ { int i; for (i = 0; i < DML_OP_TYPE_MAX; ++i) { init_err_op_struct(&dml_op_errs[i]); } for (i = 0; i < DDL_OP_TYPE_MAX; ++i) { init_err_op_struct(&ddl_op_errs[i]); } } /********************************************************************** Free up the resources used in the test */ static void clean_up(void) /*==========*/ { int i; for (i = 0; i < DML_OP_TYPE_MAX; ++i) { pthread_mutex_destroy(&dml_op_errs[i].mutex); } for (i = 0; i < DDL_OP_TYPE_MAX; ++i) { pthread_mutex_destroy(&ddl_op_errs[i].mutex); } free(dml_tid); free(ddl_tid); } /********************************************************************** Print statistics at the end of the test */ static void print_one_struct( /*=============*/ op_err_t* st) { int i; fprintf(stderr, "n_ops = %d n_err = %d\n", st->n_ops, st->n_errs); fprintf(stderr, "err freq\n"); fprintf(stderr, "=========\n"); for (i = 0; i < DB_DATA_MISMATCH; ++i) { if (st->errs[i] != 0) { fprintf(stderr, "%d %d\n", i, st->errs[i]); } } fprintf(stderr, "=========\n"); } /********************************************************************** Print statistics at the end of the test */ static void print_results(void) /*===============*/ { fprintf(stderr, "SELECT: "); print_one_struct(&dml_op_errs[DML_OP_TYPE_SELECT]); fprintf(stderr, "DELETE: "); print_one_struct(&dml_op_errs[DML_OP_TYPE_DELETE]); fprintf(stderr, "UPDATE: "); print_one_struct(&dml_op_errs[DML_OP_TYPE_UPDATE]); fprintf(stderr, "INSERT: "); print_one_struct(&dml_op_errs[DML_OP_TYPE_INSERT]); fprintf(stderr, "CREATE: "); print_one_struct(&ddl_op_errs[DDL_OP_TYPE_CREATE]); fprintf(stderr, "DROP: "); print_one_struct(&ddl_op_errs[DDL_OP_TYPE_DROP]); fprintf(stderr, "ALTER: "); print_one_struct(&ddl_op_errs[DDL_OP_TYPE_ALTER]); fprintf(stderr, "TRUNCATE: "); print_one_struct(&ddl_op_errs[DDL_OP_TYPE_TRUNCATE]); } /********************************************************************** Register a table to be part of this driver. This is a bit of polymorphism using function pointers. Each table is first initialized to the base table which provides the generic functionality for some operations. The derived classes can decide to override this if they want to. See mt_base.c and mt_t1.c on how to add new tables to this driver. */ static void register_test_tables(void) /*======================*/ { int i; /* Initialize all table classes as base table */ for (i = 0; i < NUM_TBLS; ++i) { register_base_table(&tbl_array[i]); strcpy((char *)&tbl_array[i].db_name, DATABASE); tbl_array[i].format = tbl_format; tbl_array[i].page_size = page_size; } /* Individually register different table types here */ register_t1_table(&tbl_array[num_tables++]); register_t2_table(&tbl_array[num_tables++]); assert(num_tables < NUM_TBLS); } /********************************************************************** Whatever checks we want to run after the test after all the worker threads have finished. For now I just call the select function to print the contents of the table */ static void check_test_tables(void) /*======================*/ { int i; cb_args_t args; /* Initialize all table classes as dummy table */ for (i = 0; i < num_tables; ++i) { tbl_class_t* tbl; ib_trx_t trx; ib_err_t err; trx = ib_trx_begin(isolation_level); assert(trx != NULL); tbl = &tbl_array[i]; args.tbl = tbl; args.trx = trx; args.isolation_level = isolation_level; args.print_res = IB_TRUE; args.run_number = get_cur_run_number(); args.err_st = &dml_op_errs[DML_OP_TYPE_SELECT]; tbl->dml_fn[DML_OP_TYPE_SELECT](&args); err = ib_trx_commit(trx); assert(err == DB_SUCCESS); } } int main(int argc, char* argv[]) { int i; void* res; ib_err_t err; (void)argc; (void)argv; srandom(time(NULL)); err = ib_init(); assert(err == DB_SUCCESS); test_configure(); init_stat_structs(); err = ib_startup("barracuda"); assert(err == DB_SUCCESS); err = create_database(DATABASE); assert(err == DB_SUCCESS); register_test_tables(); init_test_tables(); /* start the test. */ test_running = IB_TRUE; create_worker_threads(); /* sleep for test duration */ sleep(test_time); /* stop test and let workers exit */ test_running = IB_FALSE; for (i = 0; i < n_ddl_thr; ++i) { pthread_join(ddl_tid[i], &res); } for (i = 0; i < n_dml_thr; ++i) { pthread_join(dml_tid[i], &res); } /* Let innodb finish off any background drop table requests */ sleep(1); check_test_tables(); drop_test_tables(); err = drop_database(DATABASE); assert(err == DB_SUCCESS); err = ib_shutdown(IB_SHUTDOWN_NORMAL); assert(err == DB_SUCCESS); print_results(); clean_up(); #ifdef UNIV_DEBUG_VALGRIND VALGRIND_DO_LEAK_CHECK; #endif return(EXIT_SUCCESS); } haildb-2.3.2/tests/ib_mt_t2.c0000644000175000017500000002612311513177357016654 0ustar00pcrewspcrews00000000000000/*********************************************************************** Copyright (c) 2009 Innobase Oy. All rights reserved. Copyright (c) 2009 Oracle. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ************************************************************************/ /********************************************************************** This is a derived table class that can be plugged into the mt_drv test suite. This particular table class overrides following DML and DDL functions: 1) UPDATE 2) INSERT 3) DELETE 4) CREATE 5) ALTER Following functions are used from generic base table class which is defined in mt_base.c: 1) SELECT 2) DROP 3) TRUNCATE The table definition is: CREATE TABLE t2 (c1 INT PRIMARY KEY, score INT, ins_run INT, upd_run INT) ***********************************************************************/ #include #include #include #include #include #include "test0aux.h" #include "ib_mt_drv.h" #include "ib_mt_base.h" #ifdef UNIV_DEBUG_VALGRIND #include #endif /* Limit for PK values. Delibrately kept low to trigger DUPLICATE_KEY errors. */ static const int key_range = 100; /********************************************************************** CREATE TABLE t2... @return DB_SUCCESS or error code */ static ib_err_t create_t2( /*======*/ void* arg) /*!< in: arguments for callback */ { ib_trx_t ib_trx; ib_id_t table_id = 0; ib_err_t err = DB_SUCCESS; ib_err_t err2 = DB_SUCCESS; ib_tbl_sch_t ib_tbl_sch = NULL; ib_idx_sch_t ib_idx_sch = NULL; char table_name[IB_MAX_TABLE_NAME_LEN]; cb_args_t* cb_arg = (cb_args_t *)arg; tbl_class_t* tbl = cb_arg->tbl; snprintf(table_name, sizeof(table_name), "%s/%s", tbl->db_name, tbl->name); /* Pass a table page size of 0, ie., use default page size. */ err = ib_table_schema_create( table_name, &ib_tbl_sch, tbl->format, tbl->page_size); assert(err == DB_SUCCESS); err = ib_table_schema_add_col( ib_tbl_sch, "c1", IB_INT, IB_COL_UNSIGNED, 0, 4); assert(err == DB_SUCCESS); err = ib_table_schema_add_col( ib_tbl_sch, "score", IB_INT, IB_COL_UNSIGNED, 0, 4); assert(err == DB_SUCCESS); /* These two columns are where we put the value of run_number when doing INSERT or UPDATE */ err = ib_table_schema_add_col( ib_tbl_sch, "ins_run", IB_INT, IB_COL_UNSIGNED, 0, 4); assert(err == DB_SUCCESS); err = ib_table_schema_add_col( ib_tbl_sch, "upd_run", IB_INT, IB_COL_UNSIGNED, 0, 4); assert(err == DB_SUCCESS); err = ib_table_schema_add_index(ib_tbl_sch, "PK_index", &ib_idx_sch); assert(err == DB_SUCCESS); /* Set prefix length to 0. */ err = ib_index_schema_add_col( ib_idx_sch, "c1", 0); assert(err == DB_SUCCESS); err = ib_index_schema_set_clustered(ib_idx_sch); assert(err == DB_SUCCESS); /* create table */ ib_trx = ib_trx_begin(cb_arg->isolation_level); err = ib_schema_lock_exclusive(ib_trx); assert(err == DB_SUCCESS); err = ib_table_create(ib_trx, ib_tbl_sch, &table_id); err2 = ib_trx_commit(ib_trx); assert(err2 == DB_SUCCESS); if (ib_tbl_sch != NULL) { ib_table_schema_delete(ib_tbl_sch); } update_err_stats(cb_arg->err_st, err); return(err); } /********************************************************************** ALTER TABLE ... TODO: This should have FIC stuff. No-op for now. @return DB_SUCCESS or error code */ static ib_err_t alter_t2( /*=====*/ void* arg) /*!< in: arguments for callback */ { //fprintf(stderr, "t2: ALTER\n"); return(DB_SUCCESS); } /********************************************************************** INSERT INTO t2 VALUES (, 0, run_number, 0) @return DB_SUCCESS or error code */ static ib_err_t insert_t2( /*======*/ void* arg) /*!< in: arguments for callback */ { int i; ib_err_t err; ib_crsr_t crsr = NULL; ib_tpl_t tpl = NULL; cb_args_t* cb_arg = (cb_args_t *)arg; tbl_class_t* tbl = cb_arg->tbl; //fprintf(stderr, "t2: INSERT\n"); err = open_table(tbl->db_name, tbl->name, cb_arg->trx, &crsr); if (err != DB_SUCCESS) { goto err_exit; } err = ib_cursor_lock(crsr, IB_LOCK_IX); if (err != DB_SUCCESS) { goto err_exit; } err = ib_cursor_set_lock_mode(crsr, IB_LOCK_X); if (err != DB_SUCCESS) { goto err_exit; } tpl = ib_clust_read_tuple_create(crsr); assert(tpl != NULL); for (i = 0; i < cb_arg->batch_size; ++i) { int val = 0; int zero = 0; val = random() % key_range; err = ib_col_set_value(tpl, 0, &val, 4); assert(err == DB_SUCCESS); err = ib_col_set_value(tpl, 1, &zero, 4); assert(err == DB_SUCCESS); err = ib_col_set_value(tpl, 2, &cb_arg->run_number, 4); assert(err == DB_SUCCESS); err = ib_col_set_value(tpl, 3, &zero, 4); assert(err == DB_SUCCESS); err = ib_cursor_insert_row(crsr, tpl); if (err != DB_SUCCESS) { goto err_exit; } update_err_stats(cb_arg->err_st, err); tpl = ib_tuple_clear(tpl); assert(tpl != NULL); } goto clean_exit; err_exit: update_err_stats(cb_arg->err_st, err); clean_exit: if (tpl != NULL) { ib_tuple_delete(tpl); } if (crsr != NULL) { ib_err_t err2; err2 = ib_cursor_close(crsr); assert(err2 == DB_SUCCESS); crsr = NULL; } return(err); } /********************************************************************** UPDATE t2 SET score = score + 100 AND upd_run = run_number WHERE c1 == 5 @return DB_SUCCESS or error code */ static ib_err_t update_t2( /*======*/ void* arg) /*!< in: arguments for callback */ { ib_err_t err; int res = ~0; int five = 5; ib_tpl_t key_tpl = NULL; ib_tpl_t old_tpl = NULL; ib_tpl_t new_tpl = NULL; ib_crsr_t crsr = NULL; cb_args_t* cb_arg = (cb_args_t *)arg; tbl_class_t* tbl = cb_arg->tbl; //fprintf(stderr, "t2: UPDATE\n"); err = open_table(tbl->db_name, tbl->name, cb_arg->trx, &crsr); if (err != DB_SUCCESS) { goto err_exit; } err = ib_cursor_lock(crsr, IB_LOCK_IX); if (err != DB_SUCCESS) { goto err_exit; } err = ib_cursor_set_lock_mode(crsr, IB_LOCK_X); if (err != DB_SUCCESS) { goto err_exit; } err = ib_cursor_set_lock_mode(crsr, IB_LOCK_X); assert(err == DB_SUCCESS); /* Create a tuple for searching an index. */ key_tpl = ib_sec_search_tuple_create(crsr); assert(key_tpl != NULL); /* Set the value to look for. */ err = ib_col_set_value(key_tpl, 0, &five, 4); assert(err == DB_SUCCESS); /* Search for the key using the cluster index (PK) */ err = ib_cursor_moveto(crsr, key_tpl, IB_CUR_GE, &res); ib_tuple_delete(key_tpl); if (res != 0) { goto clean_exit; } else if (err != DB_SUCCESS) { goto err_exit; } /* Create the tuple instance that we will use to update the table. old_tpl is used for reading the existing row and new_tpl will contain the update row data. */ old_tpl = ib_clust_read_tuple_create(crsr); assert(old_tpl != NULL); new_tpl = ib_clust_read_tuple_create(crsr); assert(new_tpl != NULL); /* Iterate over the records while the first column matches "a". */ while (1) { ib_u32_t score; ib_u32_t val; ib_ulint_t data_len; ib_col_meta_t col_meta; err = ib_cursor_read_row(crsr, old_tpl); assert(err == DB_SUCCESS); ib_col_get_meta(old_tpl, 0, &col_meta); err = ib_tuple_read_u32(old_tpl, 0, &val); assert(err == DB_SUCCESS); if (val != five) { goto clean_exit; } /* Copy the old contents to the new tuple. */ err = ib_tuple_copy(new_tpl, old_tpl); /* Update the score column in the new tuple. */ data_len = ib_col_get_meta(old_tpl, 1, &col_meta); assert(data_len != IB_SQL_NULL); err = ib_tuple_read_u32(old_tpl, 1, &score); assert(err == DB_SUCCESS); score += 100; /* Set the updated value in the new tuple. */ err = ib_tuple_write_u32(new_tpl, 1, score); assert(err == DB_SUCCESS); /* Set the updated value in the new tuple. */ err = ib_tuple_write_u32(new_tpl, 3, cb_arg->run_number); assert(err == DB_SUCCESS); err = ib_cursor_update_row(crsr, old_tpl, new_tpl); if (err != DB_SUCCESS) { goto err_exit; } update_err_stats(cb_arg->err_st, err); /* Move to the next record to update. */ err = ib_cursor_next(crsr); if (err != DB_SUCCESS) { goto err_exit; } /* Reset the old and new tuple instances. */ old_tpl = ib_tuple_clear(old_tpl); assert(old_tpl != NULL); new_tpl = ib_tuple_clear(new_tpl); assert(new_tpl != NULL); } err_exit: update_err_stats(cb_arg->err_st, err); clean_exit: if (old_tpl != NULL) { ib_tuple_delete(old_tpl); } if (new_tpl != NULL) { ib_tuple_delete(new_tpl); } if (crsr != NULL) { ib_err_t err2; err2 = ib_cursor_close(crsr); assert(err2 == DB_SUCCESS); crsr = NULL; } return(err); } /********************************************************************** DELETE FROM t2 WHERE c1 == 9 @return DB_SUCCESS or error code */ static ib_err_t delete_t2( /*======*/ void* arg) /*!< in: arguments for callback */ { ib_err_t err; int res = ~0; int nine = 9; ib_tpl_t key_tpl = NULL; ib_crsr_t crsr = NULL; cb_args_t* cb_arg = (cb_args_t *)arg; tbl_class_t* tbl = cb_arg->tbl; //fprintf(stderr, "t2: DELETE\n"); err = open_table(tbl->db_name, tbl->name, cb_arg->trx, &crsr); if (err != DB_SUCCESS) { goto err_exit; } err = ib_cursor_lock(crsr, IB_LOCK_IX); if (err != DB_SUCCESS) { goto err_exit; } err = ib_cursor_set_lock_mode(crsr, IB_LOCK_X); if (err != DB_SUCCESS) { goto err_exit; } /* Create a tuple for searching an index. */ key_tpl = ib_sec_search_tuple_create(crsr); assert(key_tpl != NULL); /* Set the value to delete. */ err = ib_col_set_value(key_tpl, 0, &nine, 4); assert(err == DB_SUCCESS); /* Search for the key using the cluster index (PK) */ err = ib_cursor_moveto(crsr, key_tpl, IB_CUR_GE, &res); if (res != 0) { goto clean_exit; } if (err != DB_SUCCESS) { goto err_exit; } /* InnoDB handles the updating of all secondary indexes. */ err = ib_cursor_delete_row(crsr); if (err != DB_SUCCESS) { goto err_exit; } update_err_stats(cb_arg->err_st, err); goto clean_exit; err_exit: update_err_stats(cb_arg->err_st, err); clean_exit: if (key_tpl != NULL) { ib_tuple_delete(key_tpl); } if (crsr != NULL) { ib_err_t err2; err2 = ib_cursor_close(crsr); assert(err2 == DB_SUCCESS); crsr = NULL; } return(err); } /********************************************************************** Function to register this table class with mt_drv test suite */ void register_t2_table( /*==============*/ tbl_class_t* tbl) /*!< in/out: table class to register */ { assert(tbl != NULL); strcpy((char *)tbl->name, "t2"); tbl->dml_fn[DML_OP_TYPE_INSERT] = insert_t2; tbl->dml_fn[DML_OP_TYPE_UPDATE] = update_t2; tbl->dml_fn[DML_OP_TYPE_DELETE] = delete_t2; tbl->ddl_fn[DDL_OP_TYPE_CREATE] = create_t2; tbl->ddl_fn[DDL_OP_TYPE_ALTER] = alter_t2; } haildb-2.3.2/tests/README0000644000175000017500000000475111513177357015674 0ustar00pcrewspcrews00000000000000# Copyright (C) 2009 Oracle/Innobase Oy # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; version 2 of the License. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Requirements ============================================================================== ZLib 1.2.3 http://www.zlib.net PThreads library unix only CMake 2.6 or above Windows only Build ============================================================================== You may have to tweak the include and library paths in the Makefile.examples file for your installation. By default it looks in "../..". * Unix It should just be a matter of running: make -f Makefile.examples * Windows 1. cmake -G The generator could be one of the followings: Visual Studio 7 = Generates Visual Studio .NET 2002 project files. Visual Studio 7 .NET 2003 = Generates Visual Studio .NET 2003 project files Visual Studio 8 2005 = Generates Visual Studio .NET 2005 project files Visual Studio 8 2005 Win64 = Generates Visual Studio .NET 2005 Win64 project files. Visual Studio 9 2008 = Generates Visual Studio 9 2008 project files. Visual Studio 9 2008 Win64 = Generates Visual Studio 9 2008 Win64 project files 2. start Visual Studio, and open solution file TESTS.sln 3. build ALL_BUILD Running ============================================================================== Once the examples are built, in order to run them it should just be a matter of invoking the binary. However, potential issues that you should be aware off are: * Missing libraries On Unix systems: You may need to set the LD_LIBRARY_PATH e.g., LD_LIBRARY_PATH=../../lib ./ib_test1 On Windows: You may need to set the PATH e.g., set PATH=%PATH%;../../lib Important ============================================================================== Remember to delete all data and log files before running the examples. Some of the programs will fail on an assertion if the table already exists. haildb-2.3.2/tests/ib_test3.c0000644000175000017500000002462711513177357016700 0ustar00pcrewspcrews00000000000000/*********************************************************************** Copyright (c) 2008 Innobase Oy. All rights reserved. Copyright (c) 2008 Oracle. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ************************************************************************/ /* Test handling of integer types by the API Create a database CREATE TABLE T8(c1 INT8, c UINT8); CREATE TABLE T16(c1 INT16, c UINT16); CREATE TABLE T32(c1 INT32, c UINT32); CREATE TABLE T64(c1 INT64, c UINT64); INSERT INT Tn VALUES(1, -1); INSERT INT Tn VALUES(100, -100); SELECT c1, c2 FROM Tn; TODO: Test limits, SQL_NULL and data mismatch handling. The test will create all the relevant sub-directories in the current working directory. */ #include #include #include #include #include #include "test0aux.h" #ifdef UNIV_DEBUG_VALGRIND #include #endif #define DATABASE "test" #define TABLE "t" /********************************************************************* Create an InnoDB database (sub-directory). */ static ib_err_t create_database( /*============*/ const char* name) { ib_bool_t err; err = ib_database_create(name); assert(err == IB_TRUE); return(DB_SUCCESS); } /********************************************************************* CREATE TABLE T( vchar VARCHAR(128), blob VARCHAR(n), count INT, PRIMARY KEY(vchar); */ static ib_err_t create_table( /*=========*/ const char* dbname, /*!< in: database name */ const char* name, /*!< in: table name */ int size) /*!< in: int size in bits */ { ib_trx_t ib_trx; ib_id_t table_id = 0; ib_err_t err = DB_SUCCESS; ib_tbl_sch_t ib_tbl_sch = NULL; char table_name[IB_MAX_TABLE_NAME_LEN]; assert(size == 8 || size == 16 || size == 32 || size == 64); #ifdef __WIN__ sprintf(table_name, "%s/%s%d", dbname, name, size); #else snprintf(table_name, sizeof(table_name), "%s/%s%d", dbname, name, size); #endif /* Pass a table page size of 0, ie., use default page size. */ err = ib_table_schema_create( table_name, &ib_tbl_sch, IB_TBL_COMPACT, 0); assert(err == DB_SUCCESS); err = ib_table_schema_add_col( ib_tbl_sch, "c1", IB_INT, IB_COL_NONE, 0, size / 8); assert(err == DB_SUCCESS); err = ib_table_schema_add_col( ib_tbl_sch, "c2", IB_INT, IB_COL_UNSIGNED, 0, size / 8); assert(err == DB_SUCCESS); /* create table */ ib_trx = ib_trx_begin(IB_TRX_REPEATABLE_READ); err = ib_schema_lock_exclusive(ib_trx); assert(err == DB_SUCCESS); err = ib_table_create(ib_trx, ib_tbl_sch, &table_id); assert(err == DB_SUCCESS); err = ib_trx_commit(ib_trx); assert(err == DB_SUCCESS); if (ib_tbl_sch != NULL) { ib_table_schema_delete(ib_tbl_sch); } return(err); } /********************************************************************* Open a table and return a cursor for the table. */ static ib_err_t open_table( /*=======*/ const char* dbname, /*!< in: database name */ const char* name, /*!< in: table name */ int size, /*!< in: int size in bits */ ib_trx_t ib_trx, /*!< in: transaction */ ib_crsr_t* crsr) /*!< out: innodb cursor */ { ib_err_t err = DB_SUCCESS; char table_name[IB_MAX_TABLE_NAME_LEN]; assert(size == 8 || size == 16 || size == 32 || size == 64); #ifdef __WIN__ sprintf(table_name, "%s/%s%d", dbname, name, size); #else snprintf(table_name, sizeof(table_name), "%s/%s%d", dbname, name, size); #endif err = ib_cursor_open_table(table_name, ib_trx, crsr); assert(err == DB_SUCCESS); return(err); } /********************************************************************* INSERT INTO Tn VALUE(1, -1); INSERT INTO Tn VALUE(100, -100); */ static ib_err_t insert( /*===*/ ib_crsr_t crsr, /*!< in, out: cursor to use for write */ int size) /*!< in: int size in bits */ { int i; ib_err_t err; ib_tpl_t tpl; assert(size == 8 || size == 16 || size == 32 || size == 64); tpl = ib_clust_read_tuple_create(crsr); assert(tpl != NULL); for (i = 0; i < 100; ++i) { switch (size) { case 8: { ib_u8_t u8 = (i + 1); ib_i8_t i8 = (i + 1) * -1; err = ib_tuple_write_i8(tpl, 0, i8); assert(err == DB_SUCCESS); err = ib_tuple_write_u8(tpl, 1, u8); assert(err == DB_SUCCESS); break; } case 16: { ib_u16_t u16 = (i + 1); ib_i16_t i16 = (i + 1) * -1; err = ib_tuple_write_i16(tpl, 0, i16); assert(err == DB_SUCCESS); err = ib_tuple_write_u16(tpl, 1, u16); assert(err == DB_SUCCESS); break; } case 32: { ib_u32_t u32 = (i + 1); ib_i32_t i32 = (i + 1) * -1; err = ib_tuple_write_i32(tpl, 0, i32); assert(err == DB_SUCCESS); err = ib_tuple_write_u32(tpl, 1, u32); assert(err == DB_SUCCESS); break; } case 64: { ib_u64_t u64 = (i + 1); ib_i64_t i64 = (i + 1) * -1; err = ib_tuple_write_i64(tpl, 0, i64); assert(err == DB_SUCCESS); err = ib_tuple_write_u64(tpl, 1, u64); assert(err == DB_SUCCESS); break; } default: assert(0); } err = ib_cursor_insert_row(crsr, tpl); assert(err == DB_SUCCESS); tpl = ib_tuple_clear(tpl); assert(tpl != NULL); } if (tpl != NULL) { ib_tuple_delete(tpl); } return(err); } /********************************************************************* Check int columns in a tuple. */ static void read_col( /*==========*/ const ib_tpl_t tpl, int i, ib_col_meta_t* col_meta, int* c) { ib_err_t err = DB_SUCCESS; switch (col_meta->type_len) { case 1: { if (col_meta->attr & IB_COL_UNSIGNED) { ib_u8_t u8; err = ib_tuple_read_u8(tpl, i, &u8); *c += u8; } else { ib_i8_t i8; err = ib_tuple_read_i8(tpl, i, &i8); *c -= i8; } break; } case 2: { if (col_meta->attr & IB_COL_UNSIGNED) { ib_u16_t u16; err = ib_tuple_read_u16(tpl, i, &u16); *c += u16; } else { ib_i16_t i16; err = ib_tuple_read_i16(tpl, i, &i16); *c -= i16; } break; } case 4: { if (col_meta->attr & IB_COL_UNSIGNED) { ib_u32_t u32; err = ib_tuple_read_u32(tpl, i, &u32); *c += u32; } else { ib_i32_t i32; err = ib_tuple_read_i32(tpl, i, &i32); *c -= i32; } break; } case 8: { if (col_meta->attr & IB_COL_UNSIGNED) { ib_u64_t u64; err = ib_tuple_read_u64(tpl, i, &u64); *c += u64; } else { ib_i64_t i64; err = ib_tuple_read_i64(tpl, i, &i64); *c -= i64; } break; } default: assert(0); break; } assert(err == DB_SUCCESS); } /********************************************************************* Check that the column value read matches what was written. */ static void check_row( /*======*/ ib_tpl_t tpl, int* c1, int* c2) { ib_ulint_t data_len; ib_col_meta_t col_meta; data_len = ib_col_get_meta(tpl, 0, &col_meta); assert(col_meta.type == IB_INT); assert(!(col_meta.attr & IB_COL_UNSIGNED)); read_col(tpl, 0, &col_meta, c1); data_len = ib_col_get_meta(tpl, 1, &col_meta); assert(col_meta.type == IB_INT); assert(col_meta.attr & IB_COL_UNSIGNED); read_col(tpl, 1, &col_meta, c2); } /********************************************************************* SELECT * FROM T; */ static ib_err_t read_rows( /*======*/ ib_crsr_t crsr) { ib_tpl_t tpl; ib_err_t err; int c1 = 0; int c2 = 0; tpl = ib_clust_read_tuple_create(crsr); assert(tpl != NULL); err = ib_cursor_first(crsr); assert(err == DB_SUCCESS); while (err == DB_SUCCESS) { err = ib_cursor_read_row(crsr, tpl); assert(err == DB_SUCCESS || err == DB_END_OF_INDEX || err == DB_RECORD_NOT_FOUND); if (err == DB_RECORD_NOT_FOUND || err == DB_END_OF_INDEX) { break; } check_row(tpl, &c1, &c2); print_tuple(stdout, tpl); err = ib_cursor_next(crsr); assert(err == DB_SUCCESS || err == DB_END_OF_INDEX || err == DB_RECORD_NOT_FOUND); } if (tpl != NULL) { ib_tuple_delete(tpl); } if (err == DB_RECORD_NOT_FOUND || err == DB_END_OF_INDEX) { err = DB_SUCCESS; } assert(c1 == c2); return(err); } /********************************************************************* Drop the table. */ static ib_err_t drop_table_n( /*=========*/ const char* dbname, /*!< in: database name */ const char* name, /*!< in: table to drop */ int size) /*!< in: int size in bits */ { ib_err_t err; ib_trx_t ib_trx; char table_name[IB_MAX_TABLE_NAME_LEN]; #ifdef __WIN__ sprintf(table_name, "%s/%s%d", dbname, name, size); #else snprintf(table_name, sizeof(table_name), "%s/%s%d", dbname, name, size); #endif ib_trx = ib_trx_begin(IB_TRX_REPEATABLE_READ); assert(ib_trx != NULL); err = ib_schema_lock_exclusive(ib_trx); assert(err == DB_SUCCESS); err = ib_table_drop(ib_trx, table_name); assert(err == DB_SUCCESS); err = ib_trx_commit(ib_trx); assert(err == DB_SUCCESS); return(err); } int main(int argc, char* argv[]) { int i; ib_err_t err; ib_crsr_t crsr; ib_trx_t ib_trx; int size = 8; (void)argc; (void)argv; #ifdef __WIN__ srand((int) time(NULL)); #else srandom(time(NULL)); #endif err = ib_init(); assert(err == DB_SUCCESS); test_configure(); err = ib_startup("barracuda"); assert(err == DB_SUCCESS); err = create_database(DATABASE); assert(err == DB_SUCCESS); for (i = 0; i < 4; ++i, size <<= 1) { printf("Create table %d\n", size); err = create_table(DATABASE, TABLE, size); assert(err == DB_SUCCESS); printf("Begin transaction\n"); ib_trx = ib_trx_begin(IB_TRX_REPEATABLE_READ); assert(ib_trx != NULL); err = open_table(DATABASE, TABLE, size, ib_trx, &crsr); assert(err == DB_SUCCESS); err = ib_cursor_lock(crsr, IB_LOCK_IX); assert(err == DB_SUCCESS); insert(crsr, size); read_rows(crsr); printf("Close cursor\n"); err = ib_cursor_close(crsr); assert(err == DB_SUCCESS); crsr = NULL; printf("Commit transaction\n"); err = ib_trx_commit(ib_trx); assert(err == DB_SUCCESS); printf("Drop table\n"); err = drop_table_n(DATABASE, TABLE, size); assert(err == DB_SUCCESS); } err = ib_shutdown(IB_SHUTDOWN_NORMAL); assert(err == DB_SUCCESS); #ifdef UNIV_DEBUG_VALGRIND VALGRIND_DO_LEAK_CHECK; #endif return(EXIT_SUCCESS); } haildb-2.3.2/tests/ib_mt_base.c0000644000175000017500000001671611513177357017250 0ustar00pcrewspcrews00000000000000/*********************************************************************** Copyright (c) 2009 Innobase Oy. All rights reserved. Copyright (c) 2009 Oracle. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ************************************************************************/ /********************************************************************** This is the base table class. In C++ terminology you can consider this an abstract class that implements the generic functionality. The DDL and DML functions implemented in this class are: 1) SELECT 2) DROP 3) TRUNCATE This module also provides some helper functions for the derived class. If you want to add another table to the test suite mt_drv just copy one of the derived table class i.e.: mt_t1.c, mt_t2.c and make appropriate changes to it. You'll need to then register that table with the test suite by calling, say, register_t3_table() from register_test_table() in mt_drv.c ***********************************************************************/ #include #include #include #include #include #include #include "test0aux.h" #include "ib_mt_drv.h" #include "ib_mt_base.h" #ifdef UNIV_DEBUG_VALGRIND #include #endif /********************************************************************* Open a table and return a cursor for the table. */ ib_err_t open_table( /*=======*/ const char* dbname, /*!< in: database name */ const char* name, /*!< in: table name */ ib_trx_t ib_trx, /*!< in: transaction */ ib_crsr_t* crsr) /*!< out: innodb cursor */ { ib_err_t err = DB_SUCCESS; char table_name[IB_MAX_TABLE_NAME_LEN]; snprintf(table_name, sizeof(table_name), "%s/%s", dbname, name); err = ib_cursor_open_table(table_name, ib_trx, crsr); return(err); } #if 0 /********************************************************************** SELECT * from t */ static ib_err_t select_base( /*========*/ void* arg) /*!< in: arguments for callback */ { ib_err_t err; ib_err_t err2; ib_tpl_t tpl = NULL; ib_crsr_t crsr = NULL; cb_args_t* cb_arg = (cb_args_t *)arg; tbl_class_t* tbl = cb_arg->tbl; //fprintf(stderr, "base: SELECT\n"); err = open_table(tbl->db_name, tbl->name, cb_arg->trx, &crsr); if (err != DB_SUCCESS) { goto err_exit; } err = ib_cursor_lock(crsr, IB_LOCK_IS); if (err != DB_SUCCESS) { goto err_exit; } tpl = ib_clust_read_tuple_create(crsr); if (tpl == NULL) { err = DB_OUT_OF_MEMORY; goto err_exit; } err = ib_cursor_first(crsr); if (err != DB_SUCCESS) { goto err_exit; } while (1) { err = ib_cursor_read_row(crsr, tpl); if (err != DB_SUCCESS) { goto err_exit; } update_err_stats(cb_arg->err_st, err); if (cb_arg->print_res) { print_tuple(stderr, tpl); } err = ib_cursor_next(crsr); if (err != DB_SUCCESS) { goto err_exit; } tpl = ib_tuple_clear(tpl); if (tpl == NULL) { err = DB_OUT_OF_MEMORY; goto err_exit; } } err_exit: update_err_stats(cb_arg->err_st, err); if (tpl != NULL) { ib_tuple_delete(tpl); } if (crsr != NULL) { err2 = ib_cursor_close(crsr); assert(err2 == DB_SUCCESS); crsr = NULL; } return(err); } #endif /********************************************************************** stub for SELECT * from t */ ib_err_t select_stub( /*========*/ void* arg) /*!< in: arguments for callback */ { (void)arg; //usleep(100000); return(DB_SUCCESS); } /********************************************************************** DROP TABLE t */ static ib_err_t drop_base( /*======*/ void* arg) /*!< in: arguments for callback */ { ib_err_t err; ib_trx_t ib_trx; char table_name[IB_MAX_TABLE_NAME_LEN]; cb_args_t* cb_arg = (cb_args_t *)arg; tbl_class_t* tbl = cb_arg->tbl; //fprintf(stderr, "base: DROP\n"); snprintf(table_name, sizeof(table_name), "%s/%s", tbl->db_name, tbl->name); ib_trx = ib_trx_begin(IB_TRX_REPEATABLE_READ); assert(ib_trx != NULL); err = ib_schema_lock_exclusive(ib_trx); assert(err == DB_SUCCESS); err = ib_table_drop(ib_trx, table_name); /* We can get the DB_TABLESPACE_DELETED if the table is already on the background delete queue. */ assert(err == DB_SUCCESS || err == DB_TABLE_NOT_FOUND || err == DB_TABLESPACE_DELETED); err = ib_trx_commit(ib_trx); assert(err == DB_SUCCESS); update_err_stats(cb_arg->err_st, err); return(err); } /********************************************************************** TRUNCATE TABLE t */ static ib_err_t truncate_base( /*==========*/ void* arg) /*!< in: arguments for callback */ { ib_err_t err; ib_id_t table_id; char table_name[IB_MAX_TABLE_NAME_LEN]; cb_args_t* cb_arg = (cb_args_t *)arg; tbl_class_t* tbl = cb_arg->tbl; //fprintf(stderr, "base: TRUNCATE\n"); snprintf(table_name, sizeof(table_name), "%s/%s", tbl->db_name, tbl->name); err = ib_table_truncate(table_name, &table_id); update_err_stats(cb_arg->err_st, err); return(err); } /********************************************************************** Function stub */ static ib_err_t update_base( /*========*/ void* arg) /*!< in: arguments for callback */ { (void)arg; //fprintf(stderr, "base: UPDATE\n"); usleep(100000); return(DB_SUCCESS); } /********************************************************************** Function stub */ static ib_err_t insert_base( /*========*/ void* arg) /*!< in: arguments for callback */ { (void)arg; //fprintf(stderr, "base: INSERT\n"); usleep(100000); return(DB_SUCCESS); } /********************************************************************** Function stub */ static ib_err_t delete_base( /*========*/ void* arg) /*!< in: arguments for callback */ { (void)arg; //fprintf(stderr, "base: DELETE\n"); usleep(100000); return(DB_SUCCESS); } /********************************************************************** Function stub */ static ib_err_t create_base( /*========*/ void* arg) /*!< in: arguments for callback */ { (void)arg; //fprintf(stderr, "base: CREATE\n"); usleep(100000); return(DB_SUCCESS); } /********************************************************************** Function stub */ static ib_err_t alter_base( /*=======*/ void* arg) /*!< in: arguments for callback */ { (void)arg; //fprintf(stderr, "base: ALTER\n"); usleep(100000); return(DB_SUCCESS); } /********************************************************************** Function to register this table class with mt_drv test suite */ void register_base_table( /*================*/ tbl_class_t* tbl) /*!< in/out: table class to register */ { assert(tbl != NULL); strcpy((char *)tbl->name, "dummy"); //tbl->dml_fn[DML_OP_TYPE_SELECT] = select_base; tbl->dml_fn[DML_OP_TYPE_SELECT] = select_stub; tbl->dml_fn[DML_OP_TYPE_UPDATE] = update_base; tbl->dml_fn[DML_OP_TYPE_INSERT] = insert_base; tbl->dml_fn[DML_OP_TYPE_DELETE] = delete_base; tbl->ddl_fn[DDL_OP_TYPE_CREATE] = create_base; tbl->ddl_fn[DDL_OP_TYPE_DROP] = drop_base; tbl->ddl_fn[DDL_OP_TYPE_ALTER] = alter_base; tbl->ddl_fn[DDL_OP_TYPE_TRUNCATE] = truncate_base; } haildb-2.3.2/tests/ib_mt_base.h0000644000175000017500000000062011513177357017240 0ustar00pcrewspcrews00000000000000#ifndef _MT_BASE_H #define _MT_BASE_H /********************************************************************* Open a table and return a cursor for the table. */ ib_err_t open_table( /*=======*/ const char* dbname, /*!< in: database name */ const char* name, /*!< in: table name */ ib_trx_t ib_trx, /*!< in: transaction */ ib_crsr_t* crsr); /*!< out: innodb cursor */ #endif /* _MT_BASE_H */ haildb-2.3.2/tests/ib_mt_drv.h0000644000175000017500000000317111513177357017125 0ustar00pcrewspcrews00000000000000#ifndef _MT_DRV_H #define _MT_DRV_H #include /* Type of DML operations */ typedef enum dml_op_type { DML_OP_TYPE_SELECT = 0, DML_OP_TYPE_INSERT, DML_OP_TYPE_UPDATE, DML_OP_TYPE_DELETE, DML_OP_TYPE_MAX, } dml_op_type_t; /* Type of DDL operations */ typedef enum ddl_op_type { DDL_OP_TYPE_CREATE = 0, DDL_OP_TYPE_DROP, DDL_OP_TYPE_ALTER, DDL_OP_TYPE_TRUNCATE, DDL_OP_TYPE_MAX, } ddl_op_type_t; /* Call back function definition for various DML and DDL operations */ typedef ib_err_t fn(void*); /* to hold statistics of a particular type of operation */ typedef struct op_err_struct { int n_ops; /* Total ops performed */ int n_errs; /* Total errors */ int errs[DB_SCHEMA_NOT_LOCKED]; /* This is taken from db_err.h and it is going to be a very sparse array but we can live with it for testing. */ pthread_mutex_t mutex; /*mutex protecting this struct. */ } op_err_t; /* To hold function pointers and other parameters for a table */ typedef struct tbl_class { const char name[32]; const char db_name[32]; ib_tbl_fmt_t format; ib_ulint_t page_size; fn* dml_fn[DML_OP_TYPE_MAX]; fn* ddl_fn[DDL_OP_TYPE_MAX]; } tbl_class_t; /* Arguments to be passed to the callback functions */ typedef struct call_back_args { ib_trx_t trx; int isolation_level; int run_number; int batch_size; ib_bool_t print_res; op_err_t* err_st; tbl_class_t* tbl; } cb_args_t; /********************************************************************** Update the error stats */ void update_err_stats( /*=============*/ op_err_t* e, /*!< in: error stat struct */ ib_err_t err); /*!< in: error code */ #endif /* _MT_DRV_H */ haildb-2.3.2/tests/ib_cursor.c0000644000175000017500000002116211513177357017142 0ustar00pcrewspcrews00000000000000/*********************************************************************** Copyright (c) 2008 Innobase Oy. All rights reserved. Copyright (c) 2008 Oracle. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ************************************************************************/ /* Simple single threaded test that does the equivalent of: Create a database CREATE TABLE T(c1 INT, PK(c1)); INSERT INTO T VALUES(1); ... SELECT * FROM T; SELECT * FROM T WHERE c1 = 5; SELECT * FROM T WHERE c1 > 5; SELECT * FROM T WHERE c1 < 5; SELECT * FROM T WHERE c1 >= 1 AND c1 < 5; DROP TABLE T; The test will create all the relevant sub-directories in the current working directory. */ #include #include #include #include #include "test0aux.h" #ifdef UNIV_DEBUG_VALGRIND #include #endif #define DATABASE "test" #define TABLE "t" /********************************************************************* Create an InnoDB database (sub-directory). */ static ib_err_t create_database( /*============*/ const char* name) { ib_bool_t err; err = ib_database_create(name); assert(err == IB_TRUE); return(DB_SUCCESS); } /********************************************************************* CREATE TABLE T(c1 INT, PK(C1)); */ static ib_err_t create_table( /*=========*/ const char* dbname, /*!< in: database name */ const char* name) /*!< in: table name */ { ib_trx_t ib_trx; ib_id_t table_id = 0; ib_err_t err = DB_SUCCESS; ib_tbl_sch_t ib_tbl_sch = NULL; ib_idx_sch_t ib_idx_sch = NULL; char table_name[IB_MAX_TABLE_NAME_LEN]; #ifdef __WIN__ sprintf(table_name, "%s/%s", dbname, name); #else snprintf(table_name, sizeof(table_name), "%s/%s", dbname, name); #endif /* Pass a table page size of 0, ie., use default page size. */ err = ib_table_schema_create( table_name, &ib_tbl_sch, IB_TBL_COMPACT, 0); assert(err == DB_SUCCESS); err = ib_table_schema_add_col( ib_tbl_sch, "c1", IB_INT, IB_COL_NONE, 0, sizeof(int)); assert(err == DB_SUCCESS); err = ib_table_schema_add_index(ib_tbl_sch, "PRIMARY", &ib_idx_sch); assert(err == DB_SUCCESS); /* Set prefix length to 0. */ err = ib_index_schema_add_col(ib_idx_sch, "c1", 0); assert(err == DB_SUCCESS); err = ib_index_schema_set_clustered(ib_idx_sch); assert(err == DB_SUCCESS); /* create table */ ib_trx = ib_trx_begin(IB_TRX_REPEATABLE_READ); err = ib_schema_lock_exclusive(ib_trx); assert(err == DB_SUCCESS); err = ib_table_create(ib_trx, ib_tbl_sch, &table_id); assert(err == DB_SUCCESS); err = ib_trx_commit(ib_trx); assert(err == DB_SUCCESS); if (ib_tbl_sch != NULL) { ib_table_schema_delete(ib_tbl_sch); } return(err); } /********************************************************************* Open a table and return a cursor for the table. */ static ib_err_t open_table( /*=======*/ const char* dbname, /*!< in: database name */ const char* name, /*!< in: table name */ ib_trx_t ib_trx, /*!< in: transaction */ ib_crsr_t* crsr) /*!< out: innodb cursor */ { ib_err_t err = DB_SUCCESS; char table_name[IB_MAX_TABLE_NAME_LEN]; #ifdef __WIN__ sprintf(table_name, "%s/%s", dbname, name); #else snprintf(table_name, sizeof(table_name), "%s/%s", dbname, name); #endif err = ib_cursor_open_table(table_name, ib_trx, crsr); assert(err == DB_SUCCESS); return(err); } /********************************************************************* INSERT INTO T VALUES(0); ... 10 */ static ib_err_t insert_rows( /*========*/ ib_crsr_t crsr) /*!< in, out: cursor to use for write */ { int i; ib_err_t err; ib_tpl_t tpl = NULL; tpl = ib_clust_read_tuple_create(crsr); assert(tpl != NULL); for (i = 0; i < 10; ++i) { err = ib_tuple_write_i32(tpl, 0, i); assert(err == DB_SUCCESS); err = ib_cursor_insert_row(crsr, tpl); assert(err == DB_SUCCESS); tpl = ib_tuple_clear(tpl); assert(tpl != NULL); } if (tpl != NULL) { ib_tuple_delete(tpl); } return(err); } /********************************************************************* For all the select functions. */ static ib_err_t iterate( /*====*/ ib_crsr_t crsr, void* arg, ib_err_t (*selector)(const ib_tpl_t, void* arg)) { ib_tpl_t tpl; ib_err_t err = DB_SUCCESS; tpl = ib_clust_read_tuple_create(crsr); assert(tpl != NULL); while (err == DB_SUCCESS) { err = ib_cursor_read_row(crsr, tpl); assert(err == DB_SUCCESS || err == DB_END_OF_INDEX || err == DB_RECORD_NOT_FOUND); if (err == DB_RECORD_NOT_FOUND || err == DB_END_OF_INDEX) { break; } err = selector(tpl, arg); if (err == DB_SUCCESS) { err = ib_cursor_next(crsr); } assert(err == DB_SUCCESS || err == DB_END_OF_INDEX || err == DB_RECORD_NOT_FOUND); tpl = ib_tuple_clear(tpl); assert(tpl != NULL); } if (tpl != NULL) { ib_tuple_delete(tpl); } if (err == DB_RECORD_NOT_FOUND || err == DB_END_OF_INDEX) { err = DB_SUCCESS; } return(err); } static ib_err_t print_all( /*======*/ const ib_tpl_t tpl, void* arg) { (void)arg; print_tuple(stdout, tpl); return(DB_SUCCESS); } static ib_err_t print_eq_5( /*=======*/ const ib_tpl_t tpl, void* arg) { int c1; ib_err_t err; (void)arg; err = ib_tuple_read_i32(tpl, 0, &c1); assert(err == DB_SUCCESS); if (c1 == 5) { print_tuple(stdout, tpl); return(DB_SUCCESS); } return(DB_END_OF_INDEX); } static ib_err_t print_lt_5( /*=======*/ const ib_tpl_t tpl, void* arg) { int c1; ib_err_t err; (void)arg; err = ib_tuple_read_i32(tpl, 0, &c1); assert(err == DB_SUCCESS); if (c1 < 5) { print_tuple(stdout, tpl); return(DB_SUCCESS); } return(DB_END_OF_INDEX); } int main(int argc, char* argv[]) { int ret; ib_err_t err; ib_crsr_t crsr; ib_trx_t ib_trx; ib_tpl_t tpl = NULL; (void)argc; (void)argv; err = ib_init(); assert(err == DB_SUCCESS); test_configure(); err = ib_startup("barracuda"); assert(err == DB_SUCCESS); err = create_database(DATABASE); assert(err == DB_SUCCESS); err = create_table(DATABASE, TABLE); assert(err == DB_SUCCESS); ib_trx = ib_trx_begin(IB_TRX_REPEATABLE_READ); assert(ib_trx != NULL); err = open_table(DATABASE, TABLE, ib_trx, &crsr); assert(err == DB_SUCCESS); err = ib_cursor_lock(crsr, IB_LOCK_IX); assert(err == DB_SUCCESS); err = insert_rows(crsr); assert(err == DB_SUCCESS); /*==========================================*/ printf("SELECT * FROM T;\n"); err = ib_cursor_first(crsr); assert(err == DB_SUCCESS); err = iterate(crsr, NULL, print_all); assert(err == DB_SUCCESS); /*==========================================*/ printf("SELECT * FROM T WHERE c1 = 5;\n"); tpl = ib_clust_search_tuple_create(crsr); assert(tpl != NULL); err = ib_tuple_write_i32(tpl, 0, 5); assert(err == DB_SUCCESS); err = ib_cursor_moveto(crsr, tpl, IB_CUR_GE, &ret); assert(err == DB_SUCCESS); assert(ret == 0); err = iterate(crsr, NULL, print_eq_5); assert(err == DB_SUCCESS); /*==========================================*/ printf("SELECT * FROM T WHERE c1 > 5;\n"); err = ib_cursor_moveto(crsr, tpl, IB_CUR_G, &ret); assert(err == DB_SUCCESS); assert(ret < 0); err = iterate(crsr, NULL, print_all); assert(err == DB_SUCCESS); /*==========================================*/ printf("SELECT * FROM T WHERE c1 < 5;\n"); err = ib_cursor_first(crsr); assert(err == DB_SUCCESS); err = iterate(crsr, NULL, print_lt_5); assert(err == DB_SUCCESS); /*==========================================*/ printf("SELECT * FROM T WHERE c1 >= 1 AND c1 < 5;\n"); tpl = ib_clust_search_tuple_create(crsr); assert(tpl != NULL); err = ib_tuple_write_i32(tpl, 0, 1); assert(err == DB_SUCCESS); err = ib_cursor_moveto(crsr, tpl, IB_CUR_GE, &ret); assert(err == DB_SUCCESS); assert(ret == 0); err = iterate(crsr, NULL, print_lt_5); assert(err == DB_SUCCESS); /*==========================================*/ err = ib_cursor_close(crsr); assert(err == DB_SUCCESS); crsr = NULL; err = ib_trx_commit(ib_trx); assert(err == DB_SUCCESS); err = drop_table(DATABASE, TABLE); assert(err == DB_SUCCESS); err = ib_shutdown(IB_SHUTDOWN_NORMAL); assert(err == DB_SUCCESS); #ifdef UNIV_DEBUG_VALGRIND VALGRIND_DO_LEAK_CHECK; #endif return(EXIT_SUCCESS); } haildb-2.3.2/tests/ib_compressed.c0000644000175000017500000000575311513177357020001 0ustar00pcrewspcrews00000000000000/*********************************************************************** Copyright (c) 2009 Innobase Oy. All rights reserved. Copyright (c) 2009 Oracle. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ************************************************************************/ /* Simple test that creates a few compressed tables and checks for valid page_size */ #include #include #include "haildb.h" #include "test0aux.h" /* use a common database name "test" so it can be cleaned up by "make test-clean" if we exit abnormally */ #ifdef DBNAME #undef DBNAME #endif /* DBNAME */ #define DBNAME "test" /* use a name that is not going to collide with names from other tests */ #define TABLENAME DBNAME "/t_compressed" int main(int argc, char* argv[]) { ib_tbl_sch_t ib_tbl_sch = NULL; ib_id_t table_id; int valid_page_sizes[] = {0, 1, 2, 4, 8, 16}; int invalid_page_sizes[] = {3, 5, 6, 14, 17, 32, 128, 301}; size_t i; (void)argc; (void)argv; OK(ib_init()); test_configure(); OK(ib_startup("barracuda")); OK(ib_cfg_set("file_per_table", IB_TRUE)); OK(ib_database_create(DBNAME)); for (i = 0; i < sizeof(valid_page_sizes) / sizeof(valid_page_sizes[0]); i++) { ib_trx_t ib_trx; OK(ib_table_schema_create(TABLENAME, &ib_tbl_sch, IB_TBL_COMPRESSED, valid_page_sizes[i])); OK(ib_table_schema_add_col(ib_tbl_sch, "c1", IB_INT, IB_COL_UNSIGNED, 0 /* ignored */, sizeof(int))); ib_trx = ib_trx_begin(IB_TRX_REPEATABLE_READ); OK(ib_schema_lock_exclusive(ib_trx)); OK(ib_table_create(ib_trx, ib_tbl_sch, &table_id)); OK(ib_trx_commit(ib_trx)); ib_table_schema_delete(ib_tbl_sch); ib_trx = ib_trx_begin(IB_TRX_REPEATABLE_READ); OK(ib_schema_lock_exclusive(ib_trx)); OK(ib_table_drop(ib_trx, TABLENAME)); OK(ib_trx_commit(ib_trx)); } for (i = 0; i < sizeof(invalid_page_sizes) / sizeof(invalid_page_sizes[0]); i++) { ib_err_t ib_err; ib_err = ib_table_schema_create(TABLENAME, &ib_tbl_sch, IB_TBL_COMPRESSED, invalid_page_sizes[i]); if (ib_err == DB_SUCCESS) { fprintf(stderr, "Creating a compressed table with " "page size %d succeeded but should have " "failed", invalid_page_sizes[i]); exit(EXIT_FAILURE); } } /* ignore errors as there may be tables left over from other tests */ OK(ib_database_drop(DBNAME)); OK(ib_shutdown(IB_SHUTDOWN_NORMAL)); return(EXIT_SUCCESS); } haildb-2.3.2/tests/ib_test1.c0000644000175000017500000002736211513177357016675 0ustar00pcrewspcrews00000000000000/*********************************************************************** Copyright (c) 2008 Innobase Oy. All rights reserved. Copyright (c) 2008 Oracle. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ************************************************************************/ /* Simple single threaded test that does the equivalent of: Create a database CREATE TABLE T(c1 VARCHAR(n), c2 VARCHAR(n), c3 INT, PK(c1, c2)); INSERT INTO T VALUES('x', 'y', 1); ... SELECT * FROM T; UPDATE T SET c3 = c3 + 100 WHERE c1 = 'x'; SELECT * FROM T; DELETE FROM T WHERE c1 = 'x'; SELECT * FROM T; DROP TABLE T; The test will create all the relevant sub-directories in the current working directory. */ #include #include #include #include #include "test0aux.h" #ifdef UNIV_DEBUG_VALGRIND #include #endif #define DATABASE "test" #define TABLE "t" /* A row from our test table. */ typedef struct row_t { char c1[32]; char c2[32]; ib_u32_t c3; } row_t; static row_t in_rows[] = { {"a", "t", 1}, {"b", "u", 2}, {"c", "b", 3}, {"d", "n", 4}, {"e", "s", 5}, {"e", "j", 6}, {"d", "f", 7}, {"c", "n", 8}, {"b", "z", 9}, {"a", "i", 10}, {"", "", 0}}; #define COL_LEN(n) (sizeof(((row_t*)0)->n)) /********************************************************************* Create an InnoDB database (sub-directory). */ static ib_err_t create_database( /*============*/ const char* name) { ib_bool_t err; err = ib_database_create(name); assert(err == IB_TRUE); return(DB_SUCCESS); } /********************************************************************* CREATE TABLE T( c1 VARCHAR(n), c2 VARCHAR(n), c3 INT, PRIMARY KEY(c1, c2); */ static ib_err_t create_table( /*=========*/ const char* dbname, /*!< in: database name */ const char* name) /*!< in: table name */ { ib_trx_t ib_trx; ib_id_t table_id = 0; ib_err_t err = DB_SUCCESS; ib_tbl_sch_t ib_tbl_sch = NULL; ib_idx_sch_t ib_idx_sch = NULL; char table_name[IB_MAX_TABLE_NAME_LEN]; #ifdef __WIN__ sprintf(table_name, "%s/%s", dbname, name); #else snprintf(table_name, sizeof(table_name), "%s/%s", dbname, name); #endif /* Pass a table page size of 0, ie., use default page size. */ err = ib_table_schema_create( table_name, &ib_tbl_sch, IB_TBL_COMPACT, 0); assert(err == DB_SUCCESS); err = ib_table_schema_add_col( ib_tbl_sch, "c1", IB_VARCHAR, IB_COL_NONE, 0, COL_LEN(c1)-1); assert(err == DB_SUCCESS); err = ib_table_schema_add_col( ib_tbl_sch, "c2", IB_VARCHAR, IB_COL_NONE, 0, COL_LEN(c2)-1); assert(err == DB_SUCCESS); err = ib_table_schema_add_col( ib_tbl_sch, "c3", IB_INT, IB_COL_UNSIGNED, 0, COL_LEN(c3)); assert(err == DB_SUCCESS); err = ib_table_schema_add_index(ib_tbl_sch, "c1_c2", &ib_idx_sch); assert(err == DB_SUCCESS); /* Set prefix length to 0. */ err = ib_index_schema_add_col( ib_idx_sch, "c1", 0); assert(err == DB_SUCCESS); /* Set prefix length to 0. */ err = ib_index_schema_add_col( ib_idx_sch, "c2", 0); assert(err == DB_SUCCESS); err = ib_index_schema_set_clustered(ib_idx_sch); assert(err == DB_SUCCESS); /* create table */ ib_trx = ib_trx_begin(IB_TRX_REPEATABLE_READ); err = ib_schema_lock_exclusive(ib_trx); assert(err == DB_SUCCESS); err = ib_table_create(ib_trx, ib_tbl_sch, &table_id); assert(err == DB_SUCCESS); err = ib_trx_commit(ib_trx); assert(err == DB_SUCCESS); if (ib_tbl_sch != NULL) { ib_table_schema_delete(ib_tbl_sch); } return(err); } /********************************************************************* Open a table and return a cursor for the table. */ static ib_err_t open_table( /*=======*/ const char* dbname, /*!< in: database name */ const char* name, /*!< in: table name */ ib_trx_t ib_trx, /*!< in: transaction */ ib_crsr_t* crsr) /*!< out: innodb cursor */ { ib_err_t err = DB_SUCCESS; char table_name[IB_MAX_TABLE_NAME_LEN]; #ifdef __WIN__ sprintf(table_name, "%s/%s", dbname, name); #else snprintf(table_name, sizeof(table_name), "%s/%s", dbname, name); #endif err = ib_cursor_open_table(table_name, ib_trx, crsr); assert(err == DB_SUCCESS); return(err); } /********************************************************************* INSERT INTO T VALUE('c1', 'c2', c3); */ static ib_err_t insert_rows( /*========*/ ib_crsr_t crsr) /*!< in, out: cursor to use for write */ { row_t* row; ib_tpl_t tpl = NULL; ib_err_t err = DB_ERROR; tpl = ib_clust_read_tuple_create(crsr); assert(tpl != NULL); for (row = in_rows; *row->c1; ++row) { err = ib_col_set_value(tpl, 0, row->c1, strlen(row->c1)); assert(err == DB_SUCCESS); err = ib_col_set_value(tpl, 1, row->c2, strlen(row->c2)); assert(err == DB_SUCCESS); err = ib_col_set_value(tpl, 2, &row->c3, sizeof(row->c3)); assert(err == DB_SUCCESS); err = ib_cursor_insert_row(crsr, tpl); assert(err == DB_SUCCESS); tpl = ib_tuple_clear(tpl); assert(tpl != NULL); } if (tpl != NULL) { ib_tuple_delete(tpl); } return(err); } /********************************************************************* UPDATE T SET c3 = c3 + 100 WHERE c1 = 'a'; */ static ib_err_t update_a_row( /*=========*/ ib_crsr_t crsr) { ib_err_t err; int res = ~0; ib_tpl_t key_tpl; ib_tpl_t old_tpl = NULL; ib_tpl_t new_tpl = NULL; /* Create a tuple for searching an index. */ key_tpl = ib_sec_search_tuple_create(crsr); assert(key_tpl != NULL); /* Set the value to look for. */ err = ib_col_set_value(key_tpl, 0, "a", 1); assert(err == DB_SUCCESS); /* Search for the key using the cluster index (PK) */ err = ib_cursor_moveto(crsr, key_tpl, IB_CUR_GE, &res); assert(err == DB_SUCCESS); /* Must be positioned on a record that's greater than search key. */ assert(res == -1); if (key_tpl != NULL) { ib_tuple_delete(key_tpl); } /* Create the tuple instance that we will use to update the table. old_tpl is used for reading the existing row and new_tpl will contain the update row data. */ old_tpl = ib_clust_read_tuple_create(crsr); assert(old_tpl != NULL); new_tpl = ib_clust_read_tuple_create(crsr); assert(new_tpl != NULL); /* Iterate over the records while the c1 column matches "a". */ while (err == DB_SUCCESS) { const char* c1; ib_u32_t c3; ib_ulint_t c1_len; ib_ulint_t data_len; ib_col_meta_t col_meta; err = ib_cursor_read_row(crsr, old_tpl); assert(err == DB_SUCCESS); /* Get the c1 column value. */ c1 = ib_col_get_value(old_tpl, 0); c1_len = ib_col_get_meta(old_tpl, 0, &col_meta); /* There are no SQL_NULL values in our test data. */ assert(c1 != NULL); /* Only update c1 values that are == "a". */ if (strncmp(c1, "a", 1) != 0) { break; } /* Copy the old contents to the new tuple. */ err = ib_tuple_copy(new_tpl, old_tpl); /* Update the c3 column in the new tuple. */ data_len = ib_col_get_meta(old_tpl, 2, &col_meta); assert(data_len != IB_SQL_NULL); err = ib_tuple_read_u32(old_tpl, 2, &c3); assert(err == DB_SUCCESS); c3 += 100; /* Set the updated value in the new tuple. */ err = ib_tuple_write_u32(new_tpl, 2, c3); assert(err == DB_SUCCESS); err = ib_cursor_update_row(crsr, old_tpl, new_tpl); assert(err == DB_SUCCESS); /* Move to the next record to update. */ err = ib_cursor_next(crsr); /* Since we are searching for "a" it must always succeed. */ assert(err == DB_SUCCESS); /* Reset the old and new tuple instances. */ old_tpl = ib_tuple_clear(old_tpl); assert(old_tpl != NULL); new_tpl = ib_tuple_clear(new_tpl); assert(new_tpl != NULL); } if (old_tpl != NULL) { ib_tuple_delete(old_tpl); } if (new_tpl != NULL) { ib_tuple_delete(new_tpl); } return(err); } /********************************************************************* DELETE RFOM T WHERE c1 = 'b' and c2 = 'z'; */ static ib_err_t delete_a_row( /*=========*/ ib_crsr_t crsr) { ib_err_t err; int res = ~0; ib_tpl_t key_tpl; /* Create a tuple for searching an index. */ key_tpl = ib_sec_search_tuple_create(crsr); assert(key_tpl != NULL); /* Set the value to delete. */ err = ib_col_set_value(key_tpl, 0, "b", 1); assert(err == DB_SUCCESS); err = ib_col_set_value(key_tpl, 1, "z", 1); assert(err == DB_SUCCESS); /* Search for the key using the cluster index (PK) */ err = ib_cursor_moveto(crsr, key_tpl, IB_CUR_GE, &res); assert(err == DB_SUCCESS); /* Must be positioned on the record to delete, since we've specified an exact prefix match. */ assert(res == 0); if (key_tpl != NULL) { ib_tuple_delete(key_tpl); } /* InnoDB handles the updating of all secondary indexes. */ err = ib_cursor_delete_row(crsr); assert(err == DB_SUCCESS); return(err); } /********************************************************************* SELECT * FROM T; */ static ib_err_t do_query( /*=====*/ ib_crsr_t crsr) { ib_err_t err; ib_tpl_t tpl; tpl = ib_clust_read_tuple_create(crsr); assert(tpl != NULL); err = ib_cursor_first(crsr); assert(err == DB_SUCCESS); while (err == DB_SUCCESS) { err = ib_cursor_read_row(crsr, tpl); assert(err == DB_SUCCESS || err == DB_END_OF_INDEX || err == DB_RECORD_NOT_FOUND); if (err == DB_RECORD_NOT_FOUND || err == DB_END_OF_INDEX) { break; } print_tuple(stdout, tpl); err = ib_cursor_next(crsr); assert(err == DB_SUCCESS || err == DB_END_OF_INDEX || err == DB_RECORD_NOT_FOUND); tpl = ib_tuple_clear(tpl); assert(tpl != NULL); } if (tpl != NULL) { ib_tuple_delete(tpl); } if (err == DB_RECORD_NOT_FOUND || err == DB_END_OF_INDEX) { err = DB_SUCCESS; } return(err); } int main(int argc, char* argv[]) { ib_err_t err; ib_crsr_t crsr; ib_trx_t ib_trx; ib_u64_t version; (void)argc; (void)argv; version = ib_api_version(); printf("API: %d.%d.%d\n", (int) (version >> 32), /* Current version */ (int) ((version >> 16)) & 0xffff, /* Revisiion */ (int) (version & 0xffff)); /* Age */ err = ib_init(); assert(err == DB_SUCCESS); test_configure(); err = ib_startup("barracuda"); assert(err == DB_SUCCESS); err = create_database(DATABASE); assert(err == DB_SUCCESS); printf("Create table\n"); err = create_table(DATABASE, TABLE); assert(err == DB_SUCCESS); printf("Begin transaction\n"); ib_trx = ib_trx_begin(IB_TRX_REPEATABLE_READ); assert(ib_trx != NULL); printf("Open cursor\n"); err = open_table(DATABASE, TABLE, ib_trx, &crsr); assert(err == DB_SUCCESS); printf("Lock table in IX\n"); err = ib_cursor_lock(crsr, IB_LOCK_IX); assert(err == DB_SUCCESS); printf("Insert rows\n"); err = insert_rows(crsr); assert(err == DB_SUCCESS); printf("Query table\n"); err = do_query(crsr); assert(err == DB_SUCCESS); printf("Update a row\n"); err = update_a_row(crsr); assert(err == DB_SUCCESS); printf("Query table\n"); err = do_query(crsr); assert(err == DB_SUCCESS); printf("Delete a row\n"); err = delete_a_row(crsr); assert(err == DB_SUCCESS); printf("Query table\n"); err = do_query(crsr); assert(err == DB_SUCCESS); printf("Close cursor\n"); err = ib_cursor_close(crsr); assert(err == DB_SUCCESS); crsr = NULL; printf("Commit transaction\n"); err = ib_trx_commit(ib_trx); assert(err == DB_SUCCESS); printf("Drop table\n"); err = drop_table(DATABASE, TABLE); assert(err == DB_SUCCESS); err = ib_shutdown(IB_SHUTDOWN_NORMAL); assert(err == DB_SUCCESS); #ifdef UNIV_DEBUG_VALGRIND VALGRIND_DO_LEAK_CHECK; #endif return(EXIT_SUCCESS); } haildb-2.3.2/tests/Makefile.examples0000644000175000017500000000402711513177357020265 0ustar00pcrewspcrews00000000000000# Copyright (C) 2009 Oracle/Innobase Oy # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; version 2 of the License. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA CC=gcc TOP=../../.. AUX=test0aux.o CFLAGS=-g -Wall INC=-I$(TOP)/include/embedded_innodb-1.0 -I. LIB=-L$(TOP)/lib -linnodb -lpthread -lz -lm all: ib_cfg ib_compressed ib_cursor ib_mt_stress ib_perf1 \ ib_status ib_test1 ib_test2 ib_test3 ib_test5 ib_types \ ib_update ib_cfg: ib_cfg.o $(AUX) $(CC) -o ib_cfg ib_cfg.o $(AUX) $(LIB) ib_compressed: ib_compressed.o $(AUX) $(CC) -o ib_compressed ib_compressed.o $(AUX) $(LIB) ib_cursor: ib_cursor.o $(AUX) $(CC) -o ib_cursor ib_cursor.o $(AUX) $(LIB) ib_index: ib_index.o $(AUX) $(CC) -o ib_index ib_index.o $(AUX) $(LIB) ib_logger: ib_logger.o $(AUX) $(CC) -o ib_logger ib_logger.o $(AUX) $(LIB) ib_mt_stress: ib_mt_stress.o $(AUX) $(CC) -o ib_mt_stress ib_mt_stress.o $(AUX) $(LIB) ib_perf1: ib_perf1.o $(AUX) $(CC) -o ib_perf1 ib_perf1.o $(AUX) $(LIB) ib_status: ib_status.o $(AUX) $(CC) -o ib_status ib_status.o $(AUX) $(LIB) ib_test1: ib_test1.o $(AUX) $(CC) -o ib_test1 ib_test1.o $(AUX) $(LIB) ib_test2: ib_test2.o $(AUX) $(CC) -o ib_test2 ib_test2.o $(AUX) $(LIB) ib_test3: ib_test3.o $(AUX) $(CC) -o ib_test3 ib_test3.o $(AUX) $(LIB) ib_test5: ib_test5.o $(AUX) $(CC) -o ib_test5 ib_test5.o $(AUX) $(LIB) ib_types: ib_types.o $(AUX) $(CC) -o ib_types ib_types.o $(AUX) $(LIB) ib_update: ib_update.o $(AUX) $(CC) -o ib_update ib_update.o $(AUX) $(LIB) .c.o: $(CC) -c $(CFLAGS) $(INC) $< haildb-2.3.2/tests/bug579934_open_index_by_name_segv.c0000644000175000017500000001407611513177357023371 0ustar00pcrewspcrews00000000000000/* Copyright (C) 2010 Stewart Smith This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /*********************************************************************** Copyright (c) 2008 Innobase Oy. All rights reserved. Copyright (c) 2008 Oracle. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ************************************************************************/ #include #include #include #include #include "test0aux.h" #define DATABASE "test" #define TABLE "t" /* A row from our test table. */ typedef struct row_t { char c1[32]; char c2[32]; ib_u32_t c3; } row_t; #define COL_LEN(n) (sizeof(((row_t*)0)->n)) /********************************************************************* Create an InnoDB database (sub-directory). */ static ib_err_t create_database( /*============*/ const char* name) { ib_bool_t err; err = ib_database_create(name); assert(err == IB_TRUE); return(DB_SUCCESS); } /********************************************************************* CREATE TABLE T( c1 VARCHAR(n), c2 VARCHAR(n), c3 INT, PRIMARY KEY(c1, c2); */ static ib_err_t create_table( /*=========*/ const char* dbname, /*!< in: database name */ const char* name) /*!< in: table name */ { ib_trx_t ib_trx; ib_id_t table_id = 0; ib_err_t err = DB_SUCCESS; ib_tbl_sch_t ib_tbl_sch = NULL; ib_idx_sch_t ib_idx_sch = NULL; char table_name[IB_MAX_TABLE_NAME_LEN]; #ifdef __WIN__ sprintf(table_name, "%s/%s", dbname, name); #else snprintf(table_name, sizeof(table_name), "%s/%s", dbname, name); #endif /* Pass a table page size of 0, ie., use default page size. */ err = ib_table_schema_create( table_name, &ib_tbl_sch, IB_TBL_COMPACT, 0); assert(err == DB_SUCCESS); err = ib_table_schema_add_col( ib_tbl_sch, "c1", IB_VARCHAR, IB_COL_NONE, 0, COL_LEN(c1)-1); assert(err == DB_SUCCESS); err = ib_table_schema_add_col( ib_tbl_sch, "c2", IB_VARCHAR, IB_COL_NONE, 0, COL_LEN(c2)-1); assert(err == DB_SUCCESS); err = ib_table_schema_add_col( ib_tbl_sch, "c3", IB_INT, IB_COL_UNSIGNED, 0, COL_LEN(c3)); assert(err == DB_SUCCESS); err = ib_table_schema_add_index(ib_tbl_sch, "c1_c2", &ib_idx_sch); assert(err == DB_SUCCESS); /* Set prefix length to 0. */ err = ib_index_schema_add_col( ib_idx_sch, "c1", 0); assert(err == DB_SUCCESS); /* Set prefix length to 0. */ err = ib_index_schema_add_col( ib_idx_sch, "c2", 0); assert(err == DB_SUCCESS); err = ib_index_schema_set_clustered(ib_idx_sch); assert(err == DB_SUCCESS); /* create table */ ib_trx = ib_trx_begin(IB_TRX_REPEATABLE_READ); err = ib_schema_lock_exclusive(ib_trx); assert(err == DB_SUCCESS); err = ib_table_create(ib_trx, ib_tbl_sch, &table_id); assert(err == DB_SUCCESS); err = ib_trx_commit(ib_trx); assert(err == DB_SUCCESS); if (ib_tbl_sch != NULL) { ib_table_schema_delete(ib_tbl_sch); } return(err); } /********************************************************************* Open a table and return a cursor for the table. */ static ib_err_t open_table( /*=======*/ const char* dbname, /*!< in: database name */ const char* name, /*!< in: table name */ ib_trx_t ib_trx, /*!< in: transaction */ ib_crsr_t* crsr) /*!< out: innodb cursor */ { ib_err_t err = DB_SUCCESS; char table_name[IB_MAX_TABLE_NAME_LEN]; #ifdef __WIN__ sprintf(table_name, "%s/%s", dbname, name); #else snprintf(table_name, sizeof(table_name), "%s/%s", dbname, name); #endif err = ib_cursor_open_table(table_name, ib_trx, crsr); assert(err == DB_SUCCESS); return(err); } int main(int argc, char* argv[]) { ib_err_t err; ib_crsr_t crsr; ib_trx_t ib_trx; ib_u64_t version; (void)argc; (void)argv; version = ib_api_version(); printf("API: %d.%d.%d\n", (int) (version >> 32), /* Current version */ (int) ((version >> 16)) & 0xffff, /* Revisiion */ (int) (version & 0xffff)); /* Age */ err = ib_init(); assert(err == DB_SUCCESS); test_configure(); err = ib_startup("barracuda"); assert(err == DB_SUCCESS); err = create_database(DATABASE); assert(err == DB_SUCCESS); printf("Create table\n"); err = create_table(DATABASE, TABLE); assert(err == DB_SUCCESS); printf("Begin transaction\n"); ib_trx = ib_trx_begin(IB_TRX_REPEATABLE_READ); assert(ib_trx != NULL); printf("Open cursor\n"); err = open_table(DATABASE, TABLE, ib_trx, &crsr); assert(err == DB_SUCCESS); printf("Test ib_cursor_open_index_using_name doesn't segv"); ib_crsr_t index_crsr; err = ib_cursor_open_index_using_name(crsr, "foobar", &index_crsr); assert(err == DB_TABLE_NOT_FOUND); printf("Close cursors\n"); err = ib_cursor_close(crsr); assert(err == DB_SUCCESS); crsr = NULL; printf("Commit transaction\n"); err = ib_trx_commit(ib_trx); assert(err == DB_SUCCESS); printf("Drop table\n"); err = drop_table(DATABASE, TABLE); assert(err == DB_SUCCESS); err = ib_shutdown(IB_SHUTDOWN_NORMAL); assert(err == DB_SUCCESS); return(EXIT_SUCCESS); } haildb-2.3.2/tests/ib_status.c0000644000175000017500000000314011513177357017144 0ustar00pcrewspcrews00000000000000/*********************************************************************** Copyright (c) 2009 Innobase Oy. All rights reserved. Copyright (c) 2009 Oracle. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ************************************************************************/ #include #include #include "haildb.h" #include "test0aux.h" static void get_all(void) { const char** var_names; ib_u32_t num_status_var; OK(ib_status_get_all(&var_names, &num_status_var)); assert(num_status_var > 1); const char** ptr; ib_i64_t val; for (ptr = var_names; *ptr ; ++ptr) { ib_err_t err; err = ib_status_get_i64(*ptr, &val); assert(err == DB_SUCCESS); printf("%s: %d\n", *ptr, (int) val); } } int main(int argc, char** argv) { ib_err_t err; (void)argc; (void)argv; err = ib_init(); assert(err == DB_SUCCESS); test_configure(); err = ib_startup("barracuda"); assert(err == DB_SUCCESS); get_all(); err = ib_shutdown(IB_SHUTDOWN_NORMAL); assert(err == DB_SUCCESS); return(0); } haildb-2.3.2/tests/ib_test5.c0000644000175000017500000002755711513177357016707 0ustar00pcrewspcrews00000000000000/*********************************************************************** Copyright (c) 2008 Innobase Oy. All rights reserved. Copyright (c) 2008 Oracle. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ************************************************************************/ /* Multi-threaded test that does the equivalent of: Create a database FOR 1 TO 10 CREATE TABLE T( c1 VARCHAR(128), c2 BLOB, c3 INT, PRIMARY KEY(c1), INDEX(c3)); INSERT INTO T VALUES(RANDOM(STRING), RANDOM(DATA), MOD(RANDOM(INT), 10)); ... FOR 1 TO 1000 SELECT * FROM T; UPDATE T SET c1 = RANDOM(string), c3 = MOD(c3 + 1, 10) WHERE c3 = MOD(RANDOM(INT), 10); SELECT * FROM T WHERE c1 LIKE RANDOM(STRING); END DROP TABLE T; END The test will create all the relevant sub-directories in the current working directory. */ #include #include #include #include #include #include "test0aux.h" #ifdef UNIV_DEBUG_VALGRIND #include #endif #define DATABASE "test" #define TABLE "t" /* The page size for compressed tables, if this value is > 0 then we create compressed tables. It's set via the command line parameter --page-size INT */ static int page_size = 0; /********************************************************************* Create an InnoDB database (sub-directory). */ static ib_err_t create_database( /*============*/ const char* name) { ib_bool_t err; err = ib_database_create(name); assert(err == IB_TRUE); return(DB_SUCCESS); } /********************************************************************* CREATE TABLE T( c1 VARCHAR(128), c2 BLOB, c3 INT, PRIMARY KEY(c1), INDEX(c3)); */ static ib_err_t create_table( /*=========*/ const char* dbname, /*!< in: database name */ const char* name) /*!< in: table name */ { ib_trx_t ib_trx; ib_id_t table_id = 0; ib_err_t err = DB_SUCCESS; ib_tbl_sch_t ib_tbl_sch = NULL; ib_idx_sch_t ib_idx_sch = NULL; ib_tbl_fmt_t tbl_fmt = IB_TBL_COMPACT; char table_name[IB_MAX_TABLE_NAME_LEN]; #ifdef __WIN__ sprintf(table_name, "%s/%s", dbname, name); #else snprintf(table_name, sizeof(table_name), "%s/%s", dbname, name); #endif if (page_size > 0) { tbl_fmt = IB_TBL_COMPRESSED; printf("Creating compressed table with page size %d\n", page_size); } err = ib_table_schema_create( table_name, &ib_tbl_sch, tbl_fmt, page_size); assert(err == DB_SUCCESS); err = ib_table_schema_add_col( ib_tbl_sch, "c1", IB_VARCHAR, IB_COL_NONE, 0, 128); assert(err == DB_SUCCESS); err = ib_table_schema_add_col( ib_tbl_sch, "c2", IB_BLOB, IB_COL_NONE, 0, 0); assert(err == DB_SUCCESS); err = ib_table_schema_add_col( ib_tbl_sch, "c3", IB_INT, IB_COL_NONE, 0, 4); assert(err == DB_SUCCESS); err = ib_table_schema_add_index(ib_tbl_sch, "PRIMARY", &ib_idx_sch); assert(err == DB_SUCCESS); /* Set prefix length to 0. */ err = ib_index_schema_add_col( ib_idx_sch, "c1", 0); assert(err == DB_SUCCESS); err = ib_index_schema_set_clustered(ib_idx_sch); assert(err == DB_SUCCESS); err = ib_index_schema_set_unique(ib_idx_sch); assert(err == DB_SUCCESS); /* Create secondary index on c3. */ err = ib_table_schema_add_index(ib_tbl_sch, "c3", &ib_idx_sch); assert(err == DB_SUCCESS); /* Set prefix length to 0. */ err = ib_index_schema_add_col( ib_idx_sch, "c3", 0); assert(err == DB_SUCCESS); /* create table */ ib_trx = ib_trx_begin(IB_TRX_REPEATABLE_READ); err = ib_schema_lock_exclusive(ib_trx); assert(err == DB_SUCCESS); err = ib_table_create(ib_trx, ib_tbl_sch, &table_id); assert(err == DB_SUCCESS); err = ib_trx_commit(ib_trx); assert(err == DB_SUCCESS); if (ib_tbl_sch != NULL) { ib_table_schema_delete(ib_tbl_sch); } return(err); } /********************************************************************* Open a table and return a cursor for the table. */ static ib_err_t open_table( /*=======*/ const char* dbname, /*!< in: database name */ const char* name, /*!< in: table name */ ib_trx_t ib_trx, /*!< in: transaction */ ib_crsr_t* crsr) /*!< out: innodb cursor */ { ib_err_t err = DB_SUCCESS; char table_name[IB_MAX_TABLE_NAME_LEN]; #ifdef __WIN__ sprintf(table_name, "%s/%s", dbname, name); #else snprintf(table_name, sizeof(table_name), "%s/%s", dbname, name); #endif err = ib_cursor_open_table(table_name, ib_trx, crsr); assert(err == DB_SUCCESS); return(err); } /********************************************************************* INSERT INTO T VALUE(RANDOM(TEXT), RANDOM(TEXT), 0); */ static ib_err_t insert_random_rows( /*===============*/ ib_crsr_t crsr) /*!< in, out: cursor to use for write */ { ib_i32_t i; ib_err_t err; ib_tpl_t tpl; char* ptr = malloc(8192); tpl = ib_clust_read_tuple_create(crsr); assert(tpl != NULL); for (i = 0; i < 100; ++i) { int l; l = gen_rand_text(ptr, 128); err = ib_col_set_value(tpl, 0, ptr, l); assert(err == DB_SUCCESS); l = gen_rand_text(ptr, 8192); err = ib_col_set_value(tpl, 1, ptr, l); assert(err == DB_SUCCESS); err = ib_tuple_write_i32(tpl, 2, i % 10); assert(err == DB_SUCCESS); err = ib_cursor_insert_row(crsr, tpl); assert(err == DB_SUCCESS || err == DB_DUPLICATE_KEY); tpl = ib_tuple_clear(tpl); assert(tpl != NULL); } if (tpl != NULL) { ib_tuple_delete(tpl); } free(ptr); return(err); } /********************************************************************* UPDATE T SET c1 = RANDOM(string), c3 = MOD(c3 + 1, 10) WHERE c3 = MOD(RANDOM(INT), 10); */ static ib_err_t update_random_rows( /*===============*/ ib_crsr_t crsr) { ib_i32_t c3; ib_err_t err; ib_i32_t key; int res = ~0; ib_crsr_t index_crsr; ib_tpl_t sec_key_tpl; /* Open the secondary index. */ err = ib_cursor_open_index_using_name(crsr, "c3", &index_crsr); assert(err == DB_SUCCESS); /* Set the record lock mode */ err = ib_cursor_set_lock_mode(index_crsr, IB_LOCK_X); assert(err == DB_SUCCESS); /* Since we will be updating the clustered index record, set the need to access clustered index flag in the cursor. */ ib_cursor_set_cluster_access(index_crsr); /* Create a tuple for searching the secondary index. */ sec_key_tpl = ib_sec_search_tuple_create(index_crsr); assert(sec_key_tpl != NULL); /* Set the value to look for. */ #ifdef __WIN__ key = rand() % 10; #else key = random() % 10; #endif err = ib_tuple_write_i32(sec_key_tpl, 0, key); assert(err == DB_SUCCESS); /* Search for the key using the cluster index (PK) */ err = ib_cursor_moveto(index_crsr, sec_key_tpl, IB_CUR_GE, &res); assert(err == DB_SUCCESS || err == DB_END_OF_INDEX || err == DB_RECORD_NOT_FOUND); ib_tuple_delete(sec_key_tpl); /* Match found */ if (res == 0) { int l; char* ptr; const char* first; ib_ulint_t data_len; ib_col_meta_t col_meta; ib_ulint_t first_len; ib_tpl_t old_tpl = NULL; ib_tpl_t new_tpl = NULL; /* Create the tuple instance that we will use to update the table. old_tpl is used for reading the existing row and new_tpl will contain the update row data. */ old_tpl = ib_clust_read_tuple_create(crsr); assert(old_tpl != NULL); new_tpl = ib_clust_read_tuple_create(crsr); assert(new_tpl != NULL); err = ib_cursor_read_row(index_crsr, old_tpl); assert(err == DB_SUCCESS); /* Get the first column value. */ first = ib_col_get_value(old_tpl, 0); first_len = ib_col_get_meta(old_tpl, 0, &col_meta); /* There are no SQL_NULL values in our test data. */ assert(first != NULL); /* Copy the old contents to the new tuple. */ err = ib_tuple_copy(new_tpl, old_tpl); /* Update the c3 column in the new tuple. */ data_len = ib_col_get_meta(old_tpl, 2, &col_meta); assert(data_len != IB_SQL_NULL); err = ib_tuple_read_i32(old_tpl, 2, &c3); assert(err == DB_SUCCESS); assert(c3 == key); c3 = (c3 + 1) % 10; ptr = (char*) malloc(8192); l = gen_rand_text(ptr, 128); /* Get the new text to insert. */ l = gen_rand_text(ptr, 8192); /* Set the new key value in the new tuple. */ err = ib_col_set_value(new_tpl, 0, ptr, l); assert(err == DB_SUCCESS); /* Get the new text to insert. */ l = gen_rand_text(ptr, 8192); /* Set the c2 value in the new tuple. */ err = ib_col_set_value(new_tpl, 1, ptr, l); assert(err == DB_SUCCESS); /* Set the updated c3 value in the new tuple. */ err = ib_tuple_write_i32(new_tpl, 2, c3); assert(err == DB_SUCCESS); /* NOTE: We are using the secondary index cursor to update the record and not the cluster index cursor. */ err = ib_cursor_update_row(index_crsr, old_tpl, new_tpl); assert(err == DB_SUCCESS || err == DB_DUPLICATE_KEY); /* Reset the old and new tuple instances. */ old_tpl = ib_tuple_clear(old_tpl); assert(old_tpl != NULL); new_tpl = ib_tuple_clear(new_tpl); assert(new_tpl != NULL); free(ptr); ib_tuple_delete(old_tpl); ib_tuple_delete(new_tpl); } err = ib_cursor_close(index_crsr); assert(err == DB_SUCCESS); return(err); } #ifndef __WIN__ /********************************************************************* Set the runtime global options. */ static void set_options( /*========*/ int argc, char* argv[]) { int opt; int size = 0; struct option* longopts; int count = 0; /* Count the number of InnoDB system options. */ while (ib_longopts[count].name) { ++count; } /* Add one of our options and a spot for the sentinel. */ size = sizeof(struct option) * (count + 2); longopts = (struct option*) malloc(size); memset(longopts, 0x0, size); memcpy(longopts, ib_longopts, sizeof(struct option) * count); /* Add the local parameter (page-size). */ longopts[count].name = "page-size"; longopts[count].has_arg = required_argument; longopts[count].flag = NULL; longopts[count].val = USER_OPT + 1; ++count; while ((opt = getopt_long(argc, argv, "", longopts, NULL)) != -1) { switch(opt) { case USER_OPT + 1: page_size = strtoul(optarg, NULL, 10); break; default: /* If it's an InnoDB parameter, then we let the auxillary function handle it. */ if (set_global_option(opt, optarg) != DB_SUCCESS) { print_usage(argv[0]); exit(EXIT_FAILURE); } } /* switch */ } free(longopts); } #endif /* __WIN__ */ int main(int argc, char* argv[]) { int i; ib_err_t err; ib_crsr_t crsr; ib_trx_t ib_trx; #ifdef __WIN__ srand((int) time(NULL)); #else srandom(time(NULL)); #endif /* __WIN__ */ err = ib_init(); assert(err == DB_SUCCESS); test_configure(); #ifndef __WIN__ set_options(argc, argv); #endif /* __WIN__ */ err = ib_startup("barracuda"); assert(err == DB_SUCCESS); err = create_database(DATABASE); assert(err == DB_SUCCESS); for (i = 0; i < 10; ++i) { int j; printf("Create table\n"); err = create_table(DATABASE, TABLE); assert(err == DB_SUCCESS); for (j = 0; j < 10; ++j) { ib_trx = ib_trx_begin(IB_TRX_REPEATABLE_READ); assert(ib_trx != NULL); err = open_table(DATABASE, TABLE, ib_trx, &crsr); assert(err == DB_SUCCESS); err = ib_cursor_lock(crsr, IB_LOCK_IX); assert(err == DB_SUCCESS); insert_random_rows(crsr); update_random_rows(crsr); err = ib_cursor_close(crsr); assert(err == DB_SUCCESS); crsr = NULL; err = ib_trx_commit(ib_trx); assert(err == DB_SUCCESS); } printf("Drop table\n"); err = drop_table(DATABASE, TABLE); assert(err == DB_SUCCESS); } err = ib_shutdown(IB_SHUTDOWN_NORMAL); assert(err == DB_SUCCESS); #ifdef UNIV_DEBUG_VALGRIND VALGRIND_DO_LEAK_CHECK; #endif return(EXIT_SUCCESS); } haildb-2.3.2/tests/ib_tablename.c0000644000175000017500000000533511513177357017561 0ustar00pcrewspcrews00000000000000/*********************************************************************** Copyright (c) 2009 Innobase Oy. All rights reserved. Copyright (c) 2009 Oracle. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ************************************************************************/ /* Test to check whether invalid table names are rejected. */ #include #include #include #include #include "test0aux.h" /********************************************************************* All attempts to create the table should fail */ static void create_table(void) /*=============*/ { ib_err_t err = DB_SUCCESS; ib_tbl_sch_t ib_tbl_sch = NULL; err = ib_table_schema_create("", &ib_tbl_sch, IB_TBL_COMPACT, 0); assert(err == DB_DATA_MISMATCH); err = ib_table_schema_create("a", &ib_tbl_sch, IB_TBL_COMPACT, 0); assert(err == DB_DATA_MISMATCH); err = ib_table_schema_create("ab", &ib_tbl_sch, IB_TBL_COMPACT, 0); assert(err == DB_DATA_MISMATCH); err = ib_table_schema_create(".", &ib_tbl_sch, IB_TBL_COMPACT, 0); assert(err == DB_DATA_MISMATCH); err = ib_table_schema_create("./", &ib_tbl_sch, IB_TBL_COMPACT, 0); assert(err == DB_DATA_MISMATCH); err = ib_table_schema_create("../", &ib_tbl_sch, IB_TBL_COMPACT, 0); assert(err == DB_DATA_MISMATCH); err = ib_table_schema_create("/", &ib_tbl_sch, IB_TBL_COMPACT, 0); assert(err == DB_DATA_MISMATCH); err = ib_table_schema_create("/aaaaa", &ib_tbl_sch, IB_TBL_COMPACT, 0); assert(err == DB_DATA_MISMATCH); err = ib_table_schema_create("/a/a", &ib_tbl_sch, IB_TBL_COMPACT, 0); assert(err == DB_DATA_MISMATCH); err = ib_table_schema_create("abcdef/", &ib_tbl_sch, IB_TBL_COMPACT, 0); assert(err == DB_DATA_MISMATCH); err = ib_table_schema_create("a/b", &ib_tbl_sch, IB_TBL_COMPACT, 0); assert(err == DB_SUCCESS); ib_table_schema_delete(ib_tbl_sch); } int main(int argc, char* argv[]) { ib_err_t err; (void)argc; (void)argv; err = ib_init(); assert(err == DB_SUCCESS); test_configure(); err = ib_startup("barracuda"); assert(err == DB_SUCCESS); create_table(); err = ib_shutdown(IB_SHUTDOWN_NORMAL); assert(err == DB_SUCCESS); return(EXIT_SUCCESS); } haildb-2.3.2/tests/ib_update.c0000644000175000017500000002074311513177357017113 0ustar00pcrewspcrews00000000000000/*********************************************************************** Copyright (c) 2009 Innobase Oy. All rights reserved. Copyright (c) 2009 Oracle. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ************************************************************************/ /* Simple single threaded test that does the equivalent of: Create a database CREATE TABLE t(c1 INT, c2 VARCHAR(10), PK(c1)); FOR I IN 1 ... 10 STEP 2 INSERT INTO t VALUES(I, CHAR('a' + I)); ... END FOR SELECT * FROM t; UPDATE t SET c1 = c1 / 2; SELECT * FROM t; DROP TABLE t; The test will create all the relevant sub-directories in the current working directory. */ #include #include #include #include #ifdef UNIV_DEBUG_VALGRIND #include #endif #include "test0aux.h" #define DATABASE "test" #define TABLE "t" /********************************************************************* Create an InnoDB database (sub-directory). */ static ib_err_t create_database( /*============*/ const char* name) { ib_bool_t err; err = ib_database_create(name); assert(err == IB_TRUE); return(DB_SUCCESS); } /********************************************************************* CREATE TABLE t(c1 INT, c2 VARCHAR(10), PRIMARY KEY(c1); */ static ib_err_t create_table( /*=========*/ const char* dbname, /*!< in: database name */ const char* name) /*!< in: table name */ { ib_trx_t ib_trx; ib_id_t table_id = 0; ib_err_t err = DB_SUCCESS; ib_tbl_sch_t ib_tbl_sch = NULL; ib_idx_sch_t ib_idx_sch = NULL; char table_name[IB_MAX_TABLE_NAME_LEN]; #ifdef __WIN__ sprintf(table_name, "%s/%s", dbname, name); #else snprintf(table_name, sizeof(table_name), "%s/%s", dbname, name); #endif /* Pass a table page size of 0, ie., use default page size. */ err = ib_table_schema_create( table_name, &ib_tbl_sch, IB_TBL_COMPACT, 0); assert(err == DB_SUCCESS); err = ib_table_schema_add_col( ib_tbl_sch, "c1", IB_INT, IB_COL_NONE, 0, 4); assert(err == DB_SUCCESS); err = ib_table_schema_add_col( ib_tbl_sch, "c2", IB_VARCHAR, IB_COL_NONE, 0, 10); assert(err == DB_SUCCESS); err = ib_table_schema_add_index(ib_tbl_sch, "c1", &ib_idx_sch); assert(err == DB_SUCCESS); /* Set prefix length to 0. */ err = ib_index_schema_add_col( ib_idx_sch, "c1", 0); assert(err == DB_SUCCESS); err = ib_index_schema_set_clustered(ib_idx_sch); assert(err == DB_SUCCESS); /* Create the table */ ib_trx = ib_trx_begin(IB_TRX_REPEATABLE_READ); err = ib_schema_lock_exclusive(ib_trx); assert(err == DB_SUCCESS); err = ib_table_create(ib_trx, ib_tbl_sch, &table_id); assert(err == DB_SUCCESS); err = ib_trx_commit(ib_trx); assert(err == DB_SUCCESS); if (ib_tbl_sch != NULL) { ib_table_schema_delete(ib_tbl_sch); } return(err); } /********************************************************************* Open a table and return a cursor for the table. */ static ib_err_t open_table( /*=======*/ const char* dbname, /*!< in: database name */ const char* name, /*!< in: table name */ ib_trx_t ib_trx, /*!< in: transaction */ ib_crsr_t* crsr) /*!< out: innodb cursor */ { ib_err_t err = DB_SUCCESS; char table_name[IB_MAX_TABLE_NAME_LEN]; #ifdef __WIN__ sprintf(table_name, "%s/%s", dbname, name); #else snprintf(table_name, sizeof(table_name), "%s/%s", dbname, name); #endif err = ib_cursor_open_table(table_name, ib_trx, crsr); assert(err == DB_SUCCESS); return(err); } /********************************************************************* INSERT INTO T VALUE(I, CHAR('a' + I)); */ static ib_err_t insert_rows( /*========*/ ib_crsr_t crsr) /*!< in, out: cursor to use for write */ { int i; char ptr[] = "a"; ib_tpl_t tpl = NULL; ib_err_t err = DB_ERROR; tpl = ib_clust_read_tuple_create(crsr); assert(tpl != NULL); for (i = 0; i < 10; i += 2, ++*ptr) { err = ib_tuple_write_i32(tpl, 0, i); assert(err == DB_SUCCESS); err = ib_col_set_value(tpl, 1, ptr, 1); assert(err == DB_SUCCESS); err = ib_cursor_insert_row(crsr, tpl); assert(err == DB_SUCCESS); } if (tpl != NULL) { ib_tuple_delete(tpl); } return(err); } /********************************************************************* UPDATE t SET c1 = c1 / 2; */ static ib_err_t update_rows( /*========*/ ib_crsr_t crsr) { ib_err_t err; ib_tpl_t old_tpl = NULL; ib_tpl_t new_tpl = NULL; /* Set the record lock mode, since we are doing a SELECT FOR UPDATE */ err = ib_cursor_set_lock_mode(crsr, IB_LOCK_X); assert(err == DB_SUCCESS); /* Prepare for a table scan. */ err = ib_cursor_first(crsr); assert(err == DB_SUCCESS); /* Create the tuple instance that we will use to update the table. old_tpl is used for reading the existing row and new_tpl will contain the update row data. */ old_tpl = ib_clust_read_tuple_create(crsr); assert(old_tpl != NULL); new_tpl = ib_clust_read_tuple_create(crsr); assert(new_tpl != NULL); /* Iterate over the records and update all records read. */ while (err == DB_SUCCESS) { int c1; err = ib_cursor_read_row(crsr, old_tpl); assert(err == DB_SUCCESS); /* Copy the old contents to the new tuple. */ err = ib_tuple_copy(new_tpl, old_tpl); /* Get the first column value. */ err = ib_tuple_read_i32(new_tpl, 0, &c1); assert(err == DB_SUCCESS); c1 /= 2; /* Set the updated value in the new tuple. */ err = ib_tuple_write_i32(new_tpl, 0, c1); assert(err == DB_SUCCESS); err = ib_cursor_update_row(crsr, old_tpl, new_tpl); assert(err == DB_SUCCESS); /* Move to the next record to update. */ err = ib_cursor_next(crsr); } assert(err == DB_END_OF_INDEX || err == DB_RECORD_NOT_FOUND); if (old_tpl != NULL) { ib_tuple_delete(old_tpl); } if (new_tpl != NULL) { ib_tuple_delete(new_tpl); } return(DB_SUCCESS); } /********************************************************************* SELECT * FROM T; */ static ib_err_t do_query( /*=====*/ ib_crsr_t crsr) { ib_err_t err; ib_tpl_t tpl; tpl = ib_clust_read_tuple_create(crsr); assert(tpl != NULL); err = ib_cursor_first(crsr); assert(err == DB_SUCCESS); while (err == DB_SUCCESS) { err = ib_cursor_read_row(crsr, tpl); assert(err == DB_SUCCESS || err == DB_END_OF_INDEX || err == DB_RECORD_NOT_FOUND); if (err == DB_RECORD_NOT_FOUND || err == DB_END_OF_INDEX) { break; } print_tuple(stdout, tpl); err = ib_cursor_next(crsr); assert(err == DB_SUCCESS || err == DB_END_OF_INDEX || err == DB_RECORD_NOT_FOUND); } if (tpl != NULL) { ib_tuple_delete(tpl); } if (err == DB_RECORD_NOT_FOUND || err == DB_END_OF_INDEX) { err = DB_SUCCESS; } return(err); } int main(int argc, char* argv[]) { ib_err_t err; ib_crsr_t crsr; ib_trx_t ib_trx; ib_u64_t version; (void)argc; (void)argv; version = ib_api_version(); printf("API: %d.%d.%d\n", (int) (version >> 32), /* Current version */ (int) ((version >> 16)) & 0xffff, /* Revisiion */ (int) (version & 0xffff)); /* Age */ err = ib_init(); assert(err == DB_SUCCESS); test_configure(); err = ib_startup("barracuda"); assert(err == DB_SUCCESS); err = create_database(DATABASE); assert(err == DB_SUCCESS); err = create_table(DATABASE, TABLE); assert(err == DB_SUCCESS); ib_trx = ib_trx_begin(IB_TRX_REPEATABLE_READ); assert(ib_trx != NULL); err = open_table(DATABASE, TABLE, ib_trx, &crsr); assert(err == DB_SUCCESS); err = ib_cursor_lock(crsr, IB_LOCK_IX); assert(err == DB_SUCCESS); err = insert_rows(crsr); assert(err == DB_SUCCESS); err = do_query(crsr); assert(err == DB_SUCCESS); printf("Update all the rows\n"); err = update_rows(crsr); assert(err == DB_SUCCESS); err = do_query(crsr); assert(err == DB_SUCCESS); err = ib_cursor_close(crsr); assert(err == DB_SUCCESS); crsr = NULL; err = ib_trx_commit(ib_trx); assert(err == DB_SUCCESS); err = drop_table(DATABASE, TABLE); assert(err == DB_SUCCESS); err = ib_shutdown(IB_SHUTDOWN_NORMAL); assert(err == DB_SUCCESS); #ifdef UNIV_DEBUG_VALGRIND VALGRIND_DO_LEAK_CHECK; #endif return(EXIT_SUCCESS); } haildb-2.3.2/tests/ib_panic.c0000644000175000017500000000342111513177357016715 0ustar00pcrewspcrews00000000000000/*********************************************************************** Copyright (c) 2009 Innobase Oy. All rights reserved. Copyright (c) 2009 Oracle. All rights reserved. Copyright (c) 2010 Stewart Smith This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ************************************************************************/ #include #include #include "haildb.h" #include "test0aux.h" #include void srv_panic(int, char*, ...); int panic_called= 0; void db_panic(void* data, int er, char* msg, ...); void db_panic(void* data, int er, char* msg, ...) { va_list ap; (void)data; va_start(ap, msg); fprintf(stderr, "TEST PANIC HANDLER: %d ", er); fprintf(stderr, msg, ap); fprintf(stderr, "\n"); panic_called= 1; va_end(ap); } int main(int argc, char** argv) { ib_err_t err; (void)argc; (void)argv; err = ib_init(); assert(err == DB_SUCCESS); test_configure(); err = ib_startup("barracuda"); assert(err == DB_SUCCESS); fprintf(stderr, "Set panic handler\n"); ib_set_panic_handler(db_panic); fprintf(stderr, "Test panic\n"); ib_error_inject(1); err = ib_shutdown(IB_SHUTDOWN_NORMAL); assert(err == DB_CORRUPTION); assert(panic_called == 1); return(0); } haildb-2.3.2/tests/CMakeLists.examples0000644000175000017500000000746011513177357020553 0ustar00pcrewspcrews00000000000000# Copyright (C) 2009 Oracle/Innobase Oy # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; version 2 of the License. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # This is the CMakeLists for Embedded InnoDB CMAKE_MINIMUM_REQUIRED(VERSION 2.6 FATAL_ERROR) PROJECT (TESTS) IF(DEFINED WIN32) ADD_DEFINITIONS("-D__WIN__ -D_WINDOWS -D_CRT_SECURE_NO_DEPRECATE") ENDIF(DEFINED WIN32) IF(DEFINED UNIX) SET(LIBS innodb pthread z m) ELSE(DEFINED UNIX) SET(LIBS innodb) ENDIF(DEFINED UNIX) INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/../include) ADD_EXECUTABLE(ib_cfg ib_cfg.c test0aux.c) ADD_EXECUTABLE(ib_compressed ib_compressed.c test0aux.c) ADD_EXECUTABLE(ib_cursor ib_cursor.c test0aux.c) ADD_EXECUTABLE(ib_index ib_index.c test0aux.c) ADD_EXECUTABLE(ib_logger ib_logger.c test0aux.c) ADD_EXECUTABLE(ib_search ib_search.c test0aux.c) ADD_EXECUTABLE(ib_status ib_status.c test0aux.c) ADD_EXECUTABLE(ib_test1 ib_test1.c test0aux.c) ADD_EXECUTABLE(ib_test2 ib_test2.c test0aux.c) ADD_EXECUTABLE(ib_test3 ib_test3.c test0aux.c) ADD_EXECUTABLE(ib_test5 ib_test5.c test0aux.c) ADD_EXECUTABLE(ib_types ib_types.c test0aux.c) ADD_EXECUTABLE(ib_update ib_update.c test0aux.c) IF(DEFINED UNIX) ADD_EXECUTABLE(ib_mt_drv ib_mt_drv.c ib_mt_base.c ib_mt_t1.c ib_mt_t2.c test0aux.c) ADD_EXECUTABLE(ib_mt_stress ib_mt_stress.c test0aux.c) ADD_EXECUTABLE(ib_perf1 ib_perf1.c test0aux.c) ENDIF(DEFINED UNIX) LINK_DIRECTORIES(${EMBEDDED_INNODB}) TARGET_LINK_LIBRARIES(ib_cfg ${LIBS}) TARGET_LINK_LIBRARIES(ib_compressed ${LIBS}) TARGET_LINK_LIBRARIES(ib_cursor ${LIBS}) TARGET_LINK_LIBRARIES(ib_index ${LIBS}) TARGET_LINK_LIBRARIES(ib_logger ${LIBS}) TARGET_LINK_LIBRARIES(ib_search ${LIBS}) TARGET_LINK_LIBRARIES(ib_status ${LIBS}) TARGET_LINK_LIBRARIES(ib_test1 ${LIBS}) TARGET_LINK_LIBRARIES(ib_test2 ${LIBS}) TARGET_LINK_LIBRARIES(ib_test3 ${LIBS}) TARGET_LINK_LIBRARIES(ib_test5 ${LIBS}) TARGET_LINK_LIBRARIES(ib_types ${LIBS}) TARGET_LINK_LIBRARIES(ib_update ${LIBS}) IF(DEFINED UNIX) TARGET_LINK_LIBRARIES(ib_perf1 ${LIBS}) ENDIF(DEFINED UNIX) IF(DEFINED WIN32) # for some reason, LINK_DIRECTORIES() does not work correctly SET_TARGET_PROPERTIES(ib_cfg PROPERTIES LINK_FLAGS "/LIBPATH:..\\lib") SET_TARGET_PROPERTIES(ib_compressed PROPERTIES LINK_FLAGS "/LIBPATH:..\\lib") SET_TARGET_PROPERTIES(ib_cursor PROPERTIES LINK_FLAGS "/LIBPATH:..\\lib") SET_TARGET_PROPERTIES(ib_index PROPERTIES LINK_FLAGS "/LIBPATH:..\\lib") SET_TARGET_PROPERTIES(ib_logger PROPERTIES LINK_FLAGS "/LIBPATH:..\\lib") SET_TARGET_PROPERTIES(ib_search PROPERTIES LINK_FLAGS "/LIBPATH:..\\lib") SET_TARGET_PROPERTIES(ib_status PROPERTIES LINK_FLAGS "/LIBPATH:..\\lib") SET_TARGET_PROPERTIES(ib_test1 PROPERTIES LINK_FLAGS "/LIBPATH:..\\lib") SET_TARGET_PROPERTIES(ib_test2 PROPERTIES LINK_FLAGS "/LIBPATH:..\\lib") SET_TARGET_PROPERTIES(ib_test3 PROPERTIES LINK_FLAGS "/LIBPATH:..\\lib") SET_TARGET_PROPERTIES(ib_test5 PROPERTIES LINK_FLAGS "/LIBPATH:..\\lib") SET_TARGET_PROPERTIES(ib_types PROPERTIES LINK_FLAGS "/LIBPATH:..\\lib") SET_TARGET_PROPERTIES(ib_update PROPERTIES LINK_FLAGS "/LIBPATH:..\\lib") ELSEIF(DEFINED UNIX) TARGET_LINK_LIBRARIES(ib_mt_drv ${LIBS}) TARGET_LINK_LIBRARIES(ib_mt_stress ${LIBS}) TARGET_LINK_LIBRARIES(ib_perf1 ${LIBS}) ENDIF(DEFINED WIN32) haildb-2.3.2/tests/CMakeLists.txt0000644000175000017500000001237311513177357017553 0ustar00pcrewspcrews00000000000000# Copyright (C) 2009 Oracle/Innobase Oy # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; version 2 of the License. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # This is the CMakeLists for Embedded InnoDB CMAKE_MINIMUM_REQUIRED(VERSION 2.6 FATAL_ERROR) PROJECT (TESTS) IF(DEFINED WIN32) ADD_DEFINITIONS("-D__WIN__ -D_WINDOWS -D_CRT_SECURE_NO_DEPRECATE") ENDIF(DEFINED WIN32) IF(DEFINED UNIX) SET(LIBS innodb pthread z m) ELSE(DEFINED UNIX) SET(LIBS innodb) ENDIF(DEFINED UNIX) INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/../include) ADD_EXECUTABLE(ib_cfg ib_cfg.c test0aux.c) ADD_EXECUTABLE(ib_compressed ib_compressed.c test0aux.c) ADD_EXECUTABLE(ib_cursor ib_cursor.c test0aux.c) ADD_EXECUTABLE(ib_ddl ib_ddl.c test0aux.c) ADD_EXECUTABLE(ib_dict ib_dict.c test0aux.c) ADD_EXECUTABLE(ib_drop ib_drop.c test0aux.c) ADD_EXECUTABLE(ib_index ib_index.c test0aux.c) ADD_EXECUTABLE(ib_logger ib_logger.c test0aux.c) ADD_EXECUTABLE(ib_recover ib_recover.c test0aux.c) ADD_EXECUTABLE(ib_shutdown ib_shutdown.c test0aux.c) ADD_EXECUTABLE(ib_status ib_status.c test0aux.c) ADD_EXECUTABLE(ib_tablename ib_tablename.c test0aux.c) ADD_EXECUTABLE(ib_test1 ib_test1.c test0aux.c) ADD_EXECUTABLE(ib_test2 ib_test2.c test0aux.c) ADD_EXECUTABLE(ib_test3 ib_test3.c test0aux.c) ADD_EXECUTABLE(ib_test5 ib_test5.c test0aux.c) ADD_EXECUTABLE(ib_types ib_types.c test0aux.c) ADD_EXECUTABLE(ib_update ib_update.c test0aux.c) ADD_EXECUTABLE(ib_zip ib_zip.c test0aux.c) ADD_EXECUTABLE(ib_search ib_search.c test0aux.c) IF(DEFINED UNIX) ADD_EXECUTABLE(ib_deadlock ib_deadlock.c test0aux.c) ADD_EXECUTABLE(ib_mt_drv ib_mt_drv.c ib_mt_base.c ib_mt_t1.c ib_mt_t2.c test0aux.c) ADD_EXECUTABLE(ib_mt_stress ib_mt_stress.c test0aux.c) ADD_EXECUTABLE(ib_perf1 ib_perf1.c test0aux.c) ENDIF(DEFINED UNIX) LINK_DIRECTORIES(${EMBEDDED_INNODB}) TARGET_LINK_LIBRARIES(ib_cfg ${LIBS}) TARGET_LINK_LIBRARIES(ib_compressed ${LIBS}) TARGET_LINK_LIBRARIES(ib_cursor ${LIBS}) TARGET_LINK_LIBRARIES(ib_ddl ${LIBS}) TARGET_LINK_LIBRARIES(ib_dict ${LIBS}) TARGET_LINK_LIBRARIES(ib_drop ${LIBS}) TARGET_LINK_LIBRARIES(ib_index ${LIBS}) TARGET_LINK_LIBRARIES(ib_logger ${LIBS}) TARGET_LINK_LIBRARIES(ib_recover ${LIBS}) TARGET_LINK_LIBRARIES(ib_shutdown ${LIBS}) TARGET_LINK_LIBRARIES(ib_status ${LIBS}) TARGET_LINK_LIBRARIES(ib_tablename ${LIBS}) TARGET_LINK_LIBRARIES(ib_test1 ${LIBS}) TARGET_LINK_LIBRARIES(ib_test2 ${LIBS}) TARGET_LINK_LIBRARIES(ib_test3 ${LIBS}) TARGET_LINK_LIBRARIES(ib_test5 ${LIBS}) TARGET_LINK_LIBRARIES(ib_types ${LIBS}) TARGET_LINK_LIBRARIES(ib_update ${LIBS}) TARGET_LINK_LIBRARIES(ib_zip ${LIBS}) TARGET_LINK_LIBRARIES(ib_search ${LIBS}) IF(DEFINED UNIX) TARGET_LINK_LIBRARIES(ib_deadlock ${LIBS}) TARGET_LINK_LIBRARIES(ib_mt_drv ${LIBS}) TARGET_LINK_LIBRARIES(ib_mt_stress ${LIBS}) TARGET_LINK_LIBRARIES(ib_perf1 ${LIBS}) ENDIF(DEFINED UNIX) # for some reason, LINK_DIRECTORIES() does not work correctly SET_TARGET_PROPERTIES(ib_cfg PROPERTIES LINK_FLAGS "/LIBPATH:..\\lib") SET_TARGET_PROPERTIES(ib_compressed PROPERTIES LINK_FLAGS "/LIBPATH:..\\lib") SET_TARGET_PROPERTIES(ib_cursor PROPERTIES LINK_FLAGS "/LIBPATH:..\\lib") SET_TARGET_PROPERTIES(ib_ddl PROPERTIES LINK_FLAGS "/LIBPATH:..\\lib") SET_TARGET_PROPERTIES(ib_dict PROPERTIES LINK_FLAGS "/LIBPATH:..\\lib") SET_TARGET_PROPERTIES(ib_drop PROPERTIES LINK_FLAGS "/LIBPATH:..\\lib") SET_TARGET_PROPERTIES(ib_index PROPERTIES LINK_FLAGS "/LIBPATH:..\\lib") SET_TARGET_PROPERTIES(ib_logger PROPERTIES LINK_FLAGS "/LIBPATH:..\\lib") SET_TARGET_PROPERTIES(ib_recover PROPERTIES LINK_FLAGS "/LIBPATH:..\\lib") SET_TARGET_PROPERTIES(ib_shutdown PROPERTIES LINK_FLAGS "/LIBPATH:..\\lib") SET_TARGET_PROPERTIES(ib_status PROPERTIES LINK_FLAGS "/LIBPATH:..\\lib") SET_TARGET_PROPERTIES(ib_tablename PROPERTIES LINK_FLAGS "/LIBPATH:..\\lib") SET_TARGET_PROPERTIES(ib_test1 PROPERTIES LINK_FLAGS "/LIBPATH:..\\lib") SET_TARGET_PROPERTIES(ib_test2 PROPERTIES LINK_FLAGS "/LIBPATH:..\\lib") SET_TARGET_PROPERTIES(ib_test3 PROPERTIES LINK_FLAGS "/LIBPATH:..\\lib") SET_TARGET_PROPERTIES(ib_test5 PROPERTIES LINK_FLAGS "/LIBPATH:..\\lib") SET_TARGET_PROPERTIES(ib_types PROPERTIES LINK_FLAGS "/LIBPATH:..\\lib") SET_TARGET_PROPERTIES(ib_update PROPERTIES LINK_FLAGS "/LIBPATH:..\\lib") SET_TARGET_PROPERTIES(ib_zip PROPERTIES LINK_FLAGS "/LIBPATH:..\\lib") SET_TARGET_PROPERTIES(ib_search PROPERTIES LINK_FLAGS "/LIBPATH:..\\lib") IF(DEFINED UNIX) SET_TARGET_PROPERTIES(ib_deadlock PROPERTIES LINK_FLAGS "/LIBPATH:..\\lib") SET_TARGET_PROPERTIES(ib_mt_drv PROPERTIES LINK_FLAGS "/LIBPATH:..\\lib") SET_TARGET_PROPERTIES(ib_mt_stress PROPERTIES LINK_FLAGS "/LIBPATH:..\\lib") SET_TARGET_PROPERTIES(ib_perf1 PROPERTIES LINK_FLAGS "/LIBPATH:..\\lib") ENDIF(DEFINED UNIX) haildb-2.3.2/tests/ib_index.c0000644000175000017500000001627711513177357016747 0ustar00pcrewspcrews00000000000000/*********************************************************************** Copyright (c) 2009 Innobase Oy. All rights reserved. Copyright (c) 2009 Oracle. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ************************************************************************/ /* Simple single threaded test that does the equivalent of: Create a database CREATE TABLE T( c1 VARCHAR(n), c2 INT, C3 FLOAT, C4 DOUBLE, C5 DECIMAL, PRIMARY KEY(c1, 4)); INSERT INTO T VALUES('xxxxaaaa', 1); INSERT INTO T VALUES('xxxxbbbb', 2); -- should result in duplicate key Test whether we catch attempts to create prefix length indexes on INT, FLOAT, DOUBLE and DECIMAL column types. The test will create all the relevant sub-directories in the current working directory. */ #include #include #include #include #include "test0aux.h" #ifdef UNIV_DEBUG_VALGRIND #include #endif #define DATABASE "test" #define TABLE "t" /********************************************************************* Create an InnoDB database (sub-directory). */ static ib_err_t create_database( /*============*/ const char* name) { ib_bool_t err; err = ib_database_create(name); assert(err == IB_TRUE); return(DB_SUCCESS); } /********************************************************************* CREATE TABLE T( C1 VARCHAR(10), c2 INT, C3 FLOAT, C4 DOUBLE, C5 DECIMAL, PRIMARY KEY(C1, 4); */ static ib_err_t create_table( /*=========*/ const char* dbname, /*!< in: database name */ const char* name, /*!< in: table name */ int page_size) /*!< in: page size */ { ib_trx_t ib_trx; ib_id_t table_id = 0; ib_err_t err = DB_SUCCESS; ib_tbl_sch_t ib_tbl_sch = NULL; ib_idx_sch_t ib_idx_sch = NULL; ib_tbl_fmt_t tbl_fmt = IB_TBL_COMPACT; char table_name[IB_MAX_TABLE_NAME_LEN]; #ifdef __WIN__ sprintf(table_name, "%s/%s", dbname, name); #else snprintf(table_name, sizeof(table_name), "%s/%s", dbname, name); #endif if (page_size > 0) { tbl_fmt = IB_TBL_COMPRESSED; printf("Creating compressed table with page size %d\n", page_size); } /* Pass a table page size of 0, ie., use default page size. */ err = ib_table_schema_create( table_name, &ib_tbl_sch, tbl_fmt, page_size); assert(err == DB_SUCCESS); err = ib_table_schema_add_col( ib_tbl_sch, "c1", IB_VARCHAR, IB_COL_NONE, 0, 10); assert(err == DB_SUCCESS); err = ib_table_schema_add_col( ib_tbl_sch, "c2", IB_INT, 0, 0, sizeof(ib_i32_t)); assert(err == DB_SUCCESS); err = ib_table_schema_add_col( ib_tbl_sch, "c3", IB_FLOAT, 0, 0, sizeof(float)); assert(err == DB_SUCCESS); err = ib_table_schema_add_col( ib_tbl_sch, "c4", IB_DOUBLE, 0, 0, sizeof(double)); assert(err == DB_SUCCESS); err = ib_table_schema_add_col(ib_tbl_sch, "c5", IB_DECIMAL, 0, 0, 0); assert(err == DB_SUCCESS); err = ib_table_schema_add_index(ib_tbl_sch, "c1", &ib_idx_sch); assert(err == DB_SUCCESS); /* Set prefix length to 4 for C1. */ err = ib_index_schema_add_col( ib_idx_sch, "c1", 4); assert(err == DB_SUCCESS); /* This should fail. */ err = ib_index_schema_add_col( ib_idx_sch, "c2", 2); assert(err == DB_SCHEMA_ERROR); /* This should fail. */ err = ib_index_schema_add_col( ib_idx_sch, "c3", 2); assert(err == DB_SCHEMA_ERROR); /* This should fail. */ err = ib_index_schema_add_col( ib_idx_sch, "c4", 2); assert(err == DB_SCHEMA_ERROR); /* This should fail. */ err = ib_index_schema_add_col( ib_idx_sch, "c5", 2); assert(err == DB_SCHEMA_ERROR); err = ib_index_schema_set_clustered(ib_idx_sch); assert(err == DB_SUCCESS); /* create table */ ib_trx = ib_trx_begin(IB_TRX_REPEATABLE_READ); err = ib_schema_lock_exclusive(ib_trx); assert(err == DB_SUCCESS); err = ib_table_create(ib_trx, ib_tbl_sch, &table_id); assert(err == DB_SUCCESS); err = ib_trx_commit(ib_trx); assert(err == DB_SUCCESS); if (ib_tbl_sch != NULL) { ib_table_schema_delete(ib_tbl_sch); } return(err); } /********************************************************************* Open a table and return a cursor for the table. */ static ib_err_t open_table( /*=======*/ const char* dbname, /*!< in: database name */ const char* name, /*!< in: table name */ ib_trx_t ib_trx, /*!< in: transaction */ ib_crsr_t* crsr) /*!< out: innodb cursor */ { ib_err_t err = DB_SUCCESS; char table_name[IB_MAX_TABLE_NAME_LEN]; #ifdef __WIN__ sprintf(table_name, "%s/%s", dbname, name); #else snprintf(table_name, sizeof(table_name), "%s/%s", dbname, name); #endif err = ib_cursor_open_table(table_name, ib_trx, crsr); assert(err == DB_SUCCESS); return(err); } /********************************************************************* INSERT INTO T VALUE('xxxxaaaa', 1); INSERT INTO T VALUE('xxxxbbbb', 2); */ static void insert_rows( /*========*/ ib_crsr_t crsr) /*!< in, out: cursor to use for write */ { ib_tpl_t tpl = NULL; ib_err_t err = DB_ERROR; tpl = ib_clust_read_tuple_create(crsr); assert(tpl != NULL); err = ib_col_set_value(tpl, 0, "xxxxaaaa", 8); assert(err == DB_SUCCESS); err = ib_tuple_write_i32(tpl, 1, 2); assert(err == DB_SUCCESS); err = ib_cursor_insert_row(crsr, tpl); assert(err == DB_SUCCESS); /* Insert a record with the same prefix as the one inserted above. */ err = ib_col_set_value(tpl, 0, "xxxxbbbb", 8); assert(err == DB_SUCCESS); err = ib_tuple_write_i32(tpl, 1, 2); assert(err == DB_SUCCESS); err = ib_cursor_insert_row(crsr, tpl); assert(err == DB_DUPLICATE_KEY); ib_tuple_delete(tpl); } int main(int argc, char* argv[]) { ib_err_t err; ib_crsr_t crsr; ib_trx_t ib_trx; ib_u64_t version; (void) argc; (void) argv; version = ib_api_version(); printf("API: %d.%d.%d\n", (int) (version >> 32), /* Current version */ (int) ((version >> 16)) & 0xffff, /* Revisiion */ (int) (version & 0xffff)); /* Age */ err = ib_init(); assert(err == DB_SUCCESS); test_configure(); err = ib_startup("barracuda"); assert(err == DB_SUCCESS); err = create_database(DATABASE); assert(err == DB_SUCCESS); err = create_table(DATABASE, TABLE, 0); assert(err == DB_SUCCESS); ib_trx = ib_trx_begin(IB_TRX_REPEATABLE_READ); assert(ib_trx != NULL); err = open_table(DATABASE, TABLE, ib_trx, &crsr); assert(err == DB_SUCCESS); err = ib_cursor_lock(crsr, IB_LOCK_IX); assert(err == DB_SUCCESS); insert_rows(crsr); err = ib_cursor_close(crsr); assert(err == DB_SUCCESS); crsr = NULL; err = ib_trx_commit(ib_trx); assert(err == DB_SUCCESS); err = drop_table(DATABASE, TABLE); assert(err == DB_SUCCESS); err = ib_shutdown(IB_SHUTDOWN_NORMAL); assert(err == DB_SUCCESS); #ifdef UNIV_DEBUG_VALGRIND VALGRIND_DO_LEAK_CHECK; #endif return(EXIT_SUCCESS); } haildb-2.3.2/win/0000755000175000017500000000000011513177437014437 5ustar00pcrewspcrews00000000000000haildb-2.3.2/win/innodb.def0000644000175000017500000000452611513177357016400 0ustar00pcrewspcrews00000000000000LIBRARY ; Embedded InnoDB EXPORTS ; init functions ib_init ib_startup ib_shutdown ib_api_version ; transactions ib_trx_start ib_trx_begin ib_trx_state ib_trx_release ib_trx_commit ib_trx_rollback ib_database_create ib_database_drop ib_schema_lock_shared ib_schema_lock_exclusive ib_schema_lock_is_shared ib_schema_lock_is_exclusive ib_schema_unlock ib_schema_tables_iterate ib_cursor_lock ib_table_lock ib_cursor_set_lock_mode ib_table_schema_add_col ib_table_schema_add_index ib_table_schema_create ib_table_schema_delete ib_table_schema_visit ib_index_schema_create ib_index_schema_delete ib_index_schema_add_col ib_index_schema_set_clustered ib_index_schema_set_unique ib_cursor_open_table ib_cursor_set_simple_select ib_cursor_open_table_using_id ib_cursor_open_index_using_id ib_cursor_open_index_using_name ib_cursor_reset ib_cursor_close ib_cursor_insert_row ib_cursor_update_row ib_cursor_delete_row ib_cursor_read_row ib_cursor_first ib_cursor_prev ib_cursor_next ib_cursor_last ib_cursor_moveto ib_cursor_attach_trx ib_cursor_set_match_mode ib_cursor_set_cluster_access ib_cursor_truncate ib_cursor_is_positioned ib_cursor_stmt_begin ib_col_set_value ib_col_get_len ib_col_copy_value ib_col_get_value ib_col_get_meta ib_tuple_read_i8 ib_tuple_read_u8 ib_tuple_read_i16 ib_tuple_read_u16 ib_tuple_read_i32 ib_tuple_read_u32 ib_tuple_read_i64 ib_tuple_read_u64 ib_tuple_read_double ib_tuple_read_float ib_tuple_clear ib_tuple_get_cluster_key ib_tuple_get_n_user_cols ib_tuple_get_n_cols ib_tuple_copy ib_tuple_delete ib_tuple_write_i8 ib_tuple_write_u8 ib_tuple_write_i16 ib_tuple_write_u16 ib_tuple_write_i32 ib_tuple_write_u32 ib_tuple_write_i64 ib_tuple_write_u64 ib_tuple_write_double ib_tuple_write_float ib_table_create ib_table_rename ib_table_drop ib_table_truncate ib_table_get_id ib_index_create ib_index_drop ib_index_get_id ib_set_client_compare ib_sec_search_tuple_create ib_sec_read_tuple_create ib_clust_search_tuple_create ib_clust_read_tuple_create ib_cfg_var_get_type ib_cfg_set ib_cfg_get ib_cfg_get_all ib_savepoint_take ib_savepoint_release ib_savepoint_rollback ib_logger_set ib_strerror ib_status_get_i64 haildb-2.3.2/libhaildb.ver0000644000175000017500000000002711513177357016272 0ustar00pcrewspcrews00000000000000HAILDB { global: *; }; haildb-2.3.2/haildb.spec.in0000644000175000017500000000334411513177357016353 0ustar00pcrewspcrews00000000000000# substituted by autoconf %global version @HAILDB_FULL_VERSION@ %global abi_ver @IB_API_VERSION@ Name: haildb Version: %{version} Release: 1%{?dist} Summary: A relational database in shared library form Group: Development/Libraries # Original source and contributions from Percona are GNU GPLv2. # Additional contributions from Google and Sun Microsystems are BSD License: GPLv2 and BSD URL: http://haildb.com Source0: http://launchpad.net/%{name}/2.0/release-2.0/+download/%{name}-%{version}.tar.gz BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) BuildRequires: zlib-devel Provides: haildb-abi(%{abi_ver}) %description A relational database in shared library form. Not a SQL database, although you can use this library as the storage backend for a SQL database. %package devel Summary: Development headers and libraries Group: Development/Libraries Requires: %{name} = %{version}-%{release} %description devel Development headers and libraries for HailDB. %prep %setup -q %build %configure make %{?_smp_mflags} %install rm -rf %{buildroot} make install DESTDIR=%{buildroot} %clean rm -rf %{buildroot} %post -p /sbin/ldconfig %postun -p /sbin/ldconfig %files %defattr(-,root,root,-) %doc ChangeLog README %doc COPYING COPYING.Google COPYING.Percona COPYING.Sun_Microsystems %{_libdir}/libhaildb.so.* %exclude %{_libdir}/libhaildb.la %files devel %defattr(-,root,root,-) %{_includedir}/haildb.h %{_libdir}/libhaildb.so %changelog * Tue Aug 03 2010 BJ Dierkes - %{version}-%{release} - Latest source version * Tue Aug 03 2010 BJ Dierkes - 2.0.0-1 - Initial spec build haildb-2.3.2/sync/0000755000175000017500000000000011513177437014616 5ustar00pcrewspcrews00000000000000haildb-2.3.2/sync/sync0arr.c0000644000175000017500000006425511513177357016540 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2009, Innobase Oy. All Rights Reserved. Copyright (c) 2008, Google Inc. Portions of this file contain modifications contributed and copyrighted by Google, Inc. Those modifications are gratefully acknowledged and are described briefly in the InnoDB documentation. The contributions by Google are incorporated with their permission, and subject to the conditions contained in the file COPYING.Google. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file sync/sync0arr.c The wait array used in synchronization primitives Created 9/5/1995 Heikki Tuuri *******************************************************/ #include "sync0arr.h" #ifdef UNIV_NONINL #include "sync0arr.ic" #endif #include "sync0sync.h" #include "sync0rw.h" #include "os0sync.h" #include "os0file.h" #include "srv0srv.h" /* WAIT ARRAY ========== The wait array consists of cells each of which has an an operating system event object created for it. The threads waiting for a mutex, for example, can reserve a cell in the array and suspend themselves to wait for the event to become signaled. When using the wait array, remember to make sure that some thread holding the synchronization object will eventually know that there is a waiter in the array and signal the object, to prevent infinite wait. Why we chose to implement a wait array? First, to make mutexes fast, we had to code our own implementation of them, which only in usually uncommon cases resorts to using slow operating system primitives. Then we had the choice of assigning a unique OS event for each mutex, which would be simpler, or using a global wait array. In some operating systems, the global wait array solution is more efficient and flexible, because we can do with a very small number of OS events, say 200. In NT 3.51, allocating events seems to be a quadratic algorithm, because 10 000 events are created fast, but 100 000 events takes a couple of minutes to create. As of 5.0.30 the above mentioned design is changed. Since now OS can handle millions of wait events efficiently, we no longer have this concept of each cell of wait array having one event. Instead, now the event that a thread wants to wait on is embedded in the wait object (mutex or rw_lock). We still keep the global wait array for the sake of diagnostics and also to avoid infinite wait The error_monitor thread scans the global wait array to signal any waiting threads who have missed the signal. */ /** A cell where an individual thread may wait suspended until a resource is released. The suspending is implemented using an operating system event semaphore. */ struct sync_cell_struct { void* wait_object; /*!< pointer to the object the thread is waiting for; if NULL the cell is free for use */ mutex_t* old_wait_mutex; /*!< the latest wait mutex in cell */ rw_lock_t* old_wait_rw_lock; /*!< the latest wait rw-lock in cell */ ulint request_type; /*!< lock type requested on the object */ const char* file; /*!< in debug version file where requested */ ulint line; /*!< in debug version line where requested */ os_thread_id_t thread; /*!< thread id of this waiting thread */ ibool waiting; /*!< TRUE if the thread has already called sync_array_event_wait on this cell */ ib_int64_t signal_count; /*!< We capture the signal_count of the wait_object when we reset the event. This value is then passed on to os_event_wait and we wait only if the event has not been signalled in the period between the reset and wait call. */ time_t reservation_time;/*!< time when the thread reserved the wait cell */ }; /* NOTE: It is allowed for a thread to wait for an event allocated for the array without owning the protecting mutex (depending on the case: OS or database mutex), but all changes (set or reset) to the state of the event must be made while owning the mutex. */ /** Synchronization array */ struct sync_array_struct { ulint n_reserved; /*!< number of currently reserved cells in the wait array */ ulint n_cells; /*!< number of cells in the wait array */ sync_cell_t* array; /*!< pointer to wait array */ ulint protection; /*!< this flag tells which mutex protects the data */ mutex_t mutex; /*!< possible database mutex protecting this data structure */ os_mutex_t os_mutex; /*!< Possible operating system mutex protecting the data structure. As this data structure is used in constructing the database mutex, to prevent infinite recursion in implementation, we fall back to an OS mutex. */ ulint sg_count; /*!< count of how many times an object has been signalled */ ulint res_count; /*!< count of cell reservations since creation of the array */ }; #ifdef UNIV_SYNC_DEBUG /******************************************************************//** This function is called only in the debug version. Detects a deadlock of one or more threads because of waits of semaphores. @return TRUE if deadlock detected */ static ibool sync_array_detect_deadlock( /*=======================*/ sync_array_t* arr, /*!< in: wait array; NOTE! the caller must own the mutex to array */ sync_cell_t* start, /*!< in: cell where recursive search started */ sync_cell_t* cell, /*!< in: cell to search */ ulint depth); /*!< in: recursion depth */ #endif /* UNIV_SYNC_DEBUG */ /*****************************************************************//** Gets the nth cell in array. @return cell */ static sync_cell_t* sync_array_get_nth_cell( /*====================*/ sync_array_t* arr, /*!< in: sync array */ ulint n) /*!< in: index */ { ut_a(arr); ut_a(n < arr->n_cells); return(arr->array + n); } /******************************************************************//** Reserves the mutex semaphore protecting a sync array. */ static void sync_array_enter( /*=============*/ sync_array_t* arr) /*!< in: sync wait array */ { ulint protection; protection = arr->protection; if (protection == SYNC_ARRAY_OS_MUTEX) { os_mutex_enter(arr->os_mutex); } else if (protection == SYNC_ARRAY_MUTEX) { mutex_enter(&(arr->mutex)); } else { ut_error; } } /******************************************************************//** Releases the mutex semaphore protecting a sync array. */ static void sync_array_exit( /*============*/ sync_array_t* arr) /*!< in: sync wait array */ { ulint protection; protection = arr->protection; if (protection == SYNC_ARRAY_OS_MUTEX) { os_mutex_exit(arr->os_mutex); } else if (protection == SYNC_ARRAY_MUTEX) { mutex_exit(&(arr->mutex)); } else { ut_error; } } /*******************************************************************//** Creates a synchronization wait array. It is protected by a mutex which is automatically reserved when the functions operating on it are called. @return own: created wait array */ UNIV_INTERN sync_array_t* sync_array_create( /*==============*/ ulint n_cells, /*!< in: number of cells in the array to create */ ulint protection) /*!< in: either SYNC_ARRAY_OS_MUTEX or SYNC_ARRAY_MUTEX: determines the type of mutex protecting the data structure */ { ulint sz; sync_array_t* arr; ut_a(n_cells > 0); /* Allocate memory for the data structures */ arr = ut_malloc(sizeof(sync_array_t)); memset(arr, 0x0, sizeof(*arr)); sz = sizeof(sync_cell_t) * n_cells; arr->array = ut_malloc(sz); memset(arr->array, 0x0, sz); arr->n_cells = n_cells; arr->protection = protection; /* Then create the mutex to protect the wait array complex */ if (protection == SYNC_ARRAY_OS_MUTEX) { arr->os_mutex = os_mutex_create(NULL); } else if (protection == SYNC_ARRAY_MUTEX) { mutex_create(&arr->mutex, SYNC_NO_ORDER_CHECK); } else { ut_error; } return(arr); } /******************************************************************//** Frees the resources in a wait array. */ UNIV_INTERN void sync_array_free( /*============*/ sync_array_t* arr) /*!< in, own: sync wait array */ { ulint protection; ut_a(arr->n_reserved == 0); sync_array_validate(arr); protection = arr->protection; /* Release the mutex protecting the wait array complex */ if (protection == SYNC_ARRAY_OS_MUTEX) { os_mutex_free(arr->os_mutex); } else if (protection == SYNC_ARRAY_MUTEX) { mutex_free(&(arr->mutex)); } else { ut_error; } ut_free(arr->array); ut_free(arr); } /********************************************************************//** Validates the integrity of the wait array. Checks that the number of reserved cells equals the count variable. */ UNIV_INTERN void sync_array_validate( /*================*/ sync_array_t* arr) /*!< in: sync wait array */ { ulint i; sync_cell_t* cell; ulint count = 0; sync_array_enter(arr); for (i = 0; i < arr->n_cells; i++) { cell = sync_array_get_nth_cell(arr, i); if (cell->wait_object != NULL) { count++; } } ut_a(count == arr->n_reserved); sync_array_exit(arr); } /*******************************************************************//** Returns the event that the thread owning the cell waits for. */ static os_event_t sync_cell_get_event( /*================*/ sync_cell_t* cell) /*!< in: non-empty sync array cell */ { ulint type = cell->request_type; if (type == SYNC_MUTEX) { return(((mutex_t *) cell->wait_object)->event); } else if (type == RW_LOCK_WAIT_EX) { return(((rw_lock_t *) cell->wait_object)->wait_ex_event); } else { /* RW_LOCK_SHARED and RW_LOCK_EX wait on the same event */ return(((rw_lock_t *) cell->wait_object)->event); } } /******************************************************************//** Reserves a wait array cell for waiting for an object. The event of the cell is reset to nonsignalled state. */ UNIV_INTERN void sync_array_reserve_cell( /*====================*/ sync_array_t* arr, /*!< in: wait array */ void* object, /*!< in: pointer to the object to wait for */ ulint type, /*!< in: lock request type */ const char* file, /*!< in: file where requested */ ulint line, /*!< in: line where requested */ ulint* index) /*!< out: index of the reserved cell */ { sync_cell_t* cell; os_event_t event; ulint i; ut_a(object); ut_a(index); sync_array_enter(arr); arr->res_count++; /* Reserve a new cell. */ for (i = 0; i < arr->n_cells; i++) { cell = sync_array_get_nth_cell(arr, i); if (cell->wait_object == NULL) { cell->waiting = FALSE; cell->wait_object = object; if (type == SYNC_MUTEX) { cell->old_wait_mutex = object; } else { cell->old_wait_rw_lock = object; } cell->request_type = type; cell->file = file; cell->line = line; arr->n_reserved++; *index = i; sync_array_exit(arr); /* Make sure the event is reset and also store the value of signal_count at which the event was reset. */ event = sync_cell_get_event(cell); cell->signal_count = os_event_reset(event); cell->reservation_time = time(NULL); cell->thread = os_thread_get_curr_id(); return; } } ut_error; /* No free cell found */ return; } /******************************************************************//** This function should be called when a thread starts to wait on a wait array cell. In the debug version this function checks if the wait for a semaphore will result in a deadlock, in which case prints info and asserts. */ UNIV_INTERN void sync_array_wait_event( /*==================*/ sync_array_t* arr, /*!< in: wait array */ ulint index) /*!< in: index of the reserved cell */ { sync_cell_t* cell; os_event_t event; ut_a(arr); sync_array_enter(arr); cell = sync_array_get_nth_cell(arr, index); ut_a(cell->wait_object); ut_a(!cell->waiting); ut_ad(os_thread_get_curr_id() == cell->thread); event = sync_cell_get_event(cell); cell->waiting = TRUE; #ifdef UNIV_SYNC_DEBUG /* We use simple enter to the mutex below, because if we cannot acquire it at once, mutex_enter would call recursively sync_array routines, leading to trouble. rw_lock_debug_mutex freezes the debug lists. */ rw_lock_debug_mutex_enter(); if (TRUE == sync_array_detect_deadlock(arr, cell, cell, 0)) { ib_logger(ib_stream, "########################################\n"); ut_error; } rw_lock_debug_mutex_exit(); #endif sync_array_exit(arr); os_event_wait_low(event, cell->signal_count); sync_array_free_cell(arr, index); } /******************************************************************//** Reports info of a wait array cell. */ static void sync_array_cell_print( /*==================*/ ib_stream_t ib_stream, /*!< in: stream where to print */ sync_cell_t* cell) /*!< in: sync cell */ { mutex_t* mutex; rw_lock_t* rwlock; ulint type; ulint writer; type = cell->request_type; ib_logger(ib_stream, "--Thread %lu has waited at %s line %lu" " for %.2f seconds the semaphore:\n", (ulong) os_thread_pf(cell->thread), cell->file, (ulong) cell->line, difftime(time(NULL), cell->reservation_time)); if (type == SYNC_MUTEX) { /* We use old_wait_mutex in case the cell has already been freed meanwhile */ mutex = cell->old_wait_mutex; ib_logger(ib_stream, "Mutex at %p created file %s line %lu, lock var %lu\n" #ifdef UNIV_SYNC_DEBUG "Last time reserved in file %s line %lu, " #endif /* UNIV_SYNC_DEBUG */ "waiters flag %lu\n", (void*) mutex, mutex->cfile_name, (ulong) mutex->cline, (ulong) mutex->lock_word, #ifdef UNIV_SYNC_DEBUG mutex->file_name, (ulong) mutex->line, #endif /* UNIV_SYNC_DEBUG */ (ulong) mutex->waiters); } else if (type == RW_LOCK_EX || type == RW_LOCK_WAIT_EX || type == RW_LOCK_SHARED) { ib_logger(ib_stream, "%s", type == RW_LOCK_EX ? "X-lock on" : "S-lock on"); rwlock = cell->old_wait_rw_lock; ib_logger(ib_stream, " RW-latch at %p created in file %s line %lu\n", (void*) rwlock, rwlock->cfile_name, (ulong) rwlock->cline); writer = rw_lock_get_writer(rwlock); if (writer != RW_LOCK_NOT_LOCKED) { ib_logger(ib_stream, "a writer (thread id %lu) has" " reserved it in mode %s", (ulong) os_thread_pf(rwlock->writer_thread), writer == RW_LOCK_EX ? " exclusive\n" : " wait exclusive\n"); } ib_logger(ib_stream, "number of readers %lu, waiters flag %lu, " "lock_word: %lx\n" "Last time read locked in file %s line %lu\n" "Last time write locked in file %s line %lu\n", (ulong) rw_lock_get_reader_count(rwlock), (ulong) rwlock->waiters, rwlock->lock_word, rwlock->last_s_file_name, (ulong) rwlock->last_s_line, rwlock->last_x_file_name, (ulong) rwlock->last_x_line); } else { ut_error; } if (!cell->waiting) { ib_logger(ib_stream, "wait has ended\n"); } } #ifdef UNIV_SYNC_DEBUG /******************************************************************//** Looks for a cell with the given thread id. @return pointer to cell or NULL if not found */ static sync_cell_t* sync_array_find_thread( /*===================*/ sync_array_t* arr, /*!< in: wait array */ os_thread_id_t thread) /*!< in: thread id */ { ulint i; sync_cell_t* cell; for (i = 0; i < arr->n_cells; i++) { cell = sync_array_get_nth_cell(arr, i); if (cell->wait_object != NULL && os_thread_eq(cell->thread, thread)) { return(cell); /* Found */ } } return(NULL); /* Not found */ } /******************************************************************//** Recursion step for deadlock detection. @return TRUE if deadlock detected */ static ibool sync_array_deadlock_step( /*=====================*/ sync_array_t* arr, /*!< in: wait array; NOTE! the caller must own the mutex to array */ sync_cell_t* start, /*!< in: cell where recursive search started */ os_thread_id_t thread, /*!< in: thread to look at */ ulint pass, /*!< in: pass value */ ulint depth) /*!< in: recursion depth */ { sync_cell_t* new; ibool ret; depth++; if (pass != 0) { /* If pass != 0, then we do not know which threads are responsible of releasing the lock, and no deadlock can be detected. */ return(FALSE); } new = sync_array_find_thread(arr, thread); if (new == start) { /* Stop running of other threads */ ut_dbg_stop_threads = TRUE; /* Deadlock */ ib_logger(ib_stream, "########################################\n" "DEADLOCK of threads detected!\n"); return(TRUE); } else if (new) { ret = sync_array_detect_deadlock(arr, start, new, depth); if (ret) { return(TRUE); } } return(FALSE); } /******************************************************************//** This function is called only in the debug version. Detects a deadlock of one or more threads because of waits of semaphores. @return TRUE if deadlock detected */ static ibool sync_array_detect_deadlock( /*=======================*/ sync_array_t* arr, /*!< in: wait array; NOTE! the caller must own the mutex to array */ sync_cell_t* start, /*!< in: cell where recursive search started */ sync_cell_t* cell, /*!< in: cell to search */ ulint depth) /*!< in: recursion depth */ { mutex_t* mutex; rw_lock_t* lock; os_thread_id_t thread; ibool ret; rw_lock_debug_t*debug; ut_a(arr); ut_a(start); ut_a(cell); ut_ad(cell->wait_object); ut_ad(os_thread_get_curr_id() == start->thread); ut_ad(depth < 100); depth++; if (!cell->waiting) { return(FALSE); /* No deadlock here */ } if (cell->request_type == SYNC_MUTEX) { mutex = cell->wait_object; if (mutex_get_lock_word(mutex) != 0) { thread = mutex->thread_id; /* Note that mutex->thread_id above may be also OS_THREAD_ID_UNDEFINED, because the thread which held the mutex maybe has not yet updated the value, or it has already released the mutex: in this case no deadlock can occur, as the wait array cannot contain a thread with ID_UNDEFINED value. */ ret = sync_array_deadlock_step(arr, start, thread, 0, depth); if (ret) { ib_logger(ib_stream, "Mutex %p owned by thread %lu file %s " "line %lu\n", mutex, (ulong) os_thread_pf(mutex->thread_id), mutex->file_name, (ulong) mutex->line); sync_array_cell_print(ib_stream, cell); return(TRUE); } } return(FALSE); /* No deadlock */ } else if (cell->request_type == RW_LOCK_EX || cell->request_type == RW_LOCK_WAIT_EX) { lock = cell->wait_object; debug = UT_LIST_GET_FIRST(lock->debug_list); while (debug != NULL) { thread = debug->thread_id; if (((debug->lock_type == RW_LOCK_EX) && !os_thread_eq(thread, cell->thread)) || ((debug->lock_type == RW_LOCK_WAIT_EX) && !os_thread_eq(thread, cell->thread)) || (debug->lock_type == RW_LOCK_SHARED)) { /* The (wait) x-lock request can block infinitely only if someone (can be also cell thread) is holding s-lock, or someone (cannot be cell thread) (wait) x-lock, and he is blocked by start thread */ ret = sync_array_deadlock_step( arr, start, thread, debug->pass, depth); if (ret) { print: ib_logger(ib_stream, "rw-lock %p ", (void*) lock); sync_array_cell_print(ib_stream, cell); rw_lock_debug_print(debug); return(TRUE); } } debug = UT_LIST_GET_NEXT(list, debug); } return(FALSE); } else if (cell->request_type == RW_LOCK_SHARED) { lock = cell->wait_object; debug = UT_LIST_GET_FIRST(lock->debug_list); while (debug != NULL) { thread = debug->thread_id; if ((debug->lock_type == RW_LOCK_EX) || (debug->lock_type == RW_LOCK_WAIT_EX)) { /* The s-lock request can block infinitely only if someone (can also be cell thread) is holding (wait) x-lock, and he is blocked by start thread */ ret = sync_array_deadlock_step( arr, start, thread, debug->pass, depth); if (ret) { goto print; } } debug = UT_LIST_GET_NEXT(list, debug); } return(FALSE); } else { ut_error; } return(TRUE); /* Execution never reaches this line: for compiler fooling only */ } #endif /* UNIV_SYNC_DEBUG */ /******************************************************************//** Determines if we can wake up the thread waiting for a sempahore. */ static ibool sync_arr_cell_can_wake_up( /*======================*/ sync_cell_t* cell) /*!< in: cell to search */ { mutex_t* mutex; rw_lock_t* lock; if (cell->request_type == SYNC_MUTEX) { mutex = cell->wait_object; if (mutex_get_lock_word(mutex) == 0) { return(TRUE); } } else if (cell->request_type == RW_LOCK_EX) { lock = cell->wait_object; if (lock->lock_word > 0) { /* Either unlocked or only read locked. */ return(TRUE); } } else if (cell->request_type == RW_LOCK_WAIT_EX) { lock = cell->wait_object; /* lock_word == 0 means all readers have left */ if (lock->lock_word == 0) { return(TRUE); } } else if (cell->request_type == RW_LOCK_SHARED) { lock = cell->wait_object; /* lock_word > 0 means no writer or reserved writer */ if (lock->lock_word > 0) { return(TRUE); } } return(FALSE); } /******************************************************************//** Frees the cell. NOTE! sync_array_wait_event frees the cell automatically! */ UNIV_INTERN void sync_array_free_cell( /*=================*/ sync_array_t* arr, /*!< in: wait array */ ulint index) /*!< in: index of the cell in array */ { sync_cell_t* cell; sync_array_enter(arr); cell = sync_array_get_nth_cell(arr, index); ut_a(cell->wait_object != NULL); cell->waiting = FALSE; cell->wait_object = NULL; cell->signal_count = 0; ut_a(arr->n_reserved > 0); arr->n_reserved--; sync_array_exit(arr); } /**********************************************************************//** Increments the signalled count. */ UNIV_INTERN void sync_array_object_signalled( /*========================*/ sync_array_t* arr) /*!< in: wait array */ { #ifdef HAVE_ATOMIC_BUILTINS (void) os_atomic_increment_ulint(&arr->sg_count, 1); #else sync_array_enter(arr); arr->sg_count++; sync_array_exit(arr); #endif } /**********************************************************************//** If the wakeup algorithm does not work perfectly at semaphore relases, this function will do the waking (see the comment in mutex_exit). This function should be called about every 1 second in the server. Note that there's a race condition between this thread and mutex_exit changing the lock_word and calling signal_object, so sometimes this finds threads to wake up even when nothing has gone wrong. */ UNIV_INTERN void sync_arr_wake_threads_if_sema_free(void) /*====================================*/ { sync_array_t* arr = sync_primary_wait_array; sync_cell_t* cell; ulint count; ulint i; os_event_t event; sync_array_enter(arr); i = 0; count = 0; while (count < arr->n_reserved) { cell = sync_array_get_nth_cell(arr, i); i++; if (cell->wait_object == NULL) { continue; } count++; if (sync_arr_cell_can_wake_up(cell)) { event = sync_cell_get_event(cell); os_event_set(event); } } sync_array_exit(arr); } /**********************************************************************//** Prints warnings of long semaphore waits to ib_stream. @return TRUE if fatal semaphore wait threshold was exceeded */ UNIV_INTERN ibool sync_array_print_long_waits(void) /*=============================*/ { sync_cell_t* cell; ibool old_val; ibool noticed = FALSE; ulint i; ulint fatal_timeout = srv_fatal_semaphore_wait_threshold; ibool fatal = FALSE; for (i = 0; i < sync_primary_wait_array->n_cells; i++) { cell = sync_array_get_nth_cell(sync_primary_wait_array, i); if (cell->wait_object != NULL && cell->waiting && difftime(time(NULL), cell->reservation_time) > 240) { ib_logger(ib_stream, "InnoDB: Warning: a long semaphore wait:\n"); sync_array_cell_print(ib_stream, cell); noticed = TRUE; } if (cell->wait_object != NULL && cell->waiting && difftime(time(NULL), cell->reservation_time) > fatal_timeout) { fatal = TRUE; } } if (noticed) { ib_logger(ib_stream, "InnoDB: ###### Starts InnoDB Monitor" " for 30 secs to print diagnostic info:\n"); old_val = srv_print_innodb_monitor; /* If some crucial semaphore is reserved, then also the InnoDB Monitor can hang, and we do not get diagnostics. Since in many cases an InnoDB hang is caused by a pwrite() or a pread() call hanging inside the operating system, let us print right now the values of pending calls of these. */ ib_logger(ib_stream, "InnoDB: Pending preads %lu, pwrites %lu\n", (ulong)os_file_n_pending_preads, (ulong)os_file_n_pending_pwrites); srv_print_innodb_monitor = TRUE; os_event_set(srv_lock_timeout_thread_event); os_thread_sleep(30000000); srv_print_innodb_monitor = old_val; ib_logger(ib_stream, "InnoDB: ###### Diagnostic info printed" " to the standard error stream\n"); } return(fatal); } /**********************************************************************//** Prints info of the wait array. */ static void sync_array_output_info( /*===================*/ ib_stream_t ib_stream, /*!< in: stream where to print */ sync_array_t* arr) /*!< in: wait array; NOTE! caller must own the mutex */ { sync_cell_t* cell; ulint count; ulint i; ib_logger(ib_stream, "OS WAIT ARRAY INFO: reservation count %ld, signal count %ld\n", (long) arr->res_count, (long) arr->sg_count); i = 0; count = 0; while (count < arr->n_reserved) { cell = sync_array_get_nth_cell(arr, i); if (cell->wait_object != NULL) { count++; sync_array_cell_print(ib_stream, cell); } i++; } } /**********************************************************************//** Prints info of the wait array. */ UNIV_INTERN void sync_array_print_info( /*==================*/ ib_stream_t ib_stream, /*!< in: file where to print */ sync_array_t* arr) /*!< in: wait array */ { sync_array_enter(arr); sync_array_output_info(ib_stream, arr); sync_array_exit(arr); } haildb-2.3.2/sync/sync0rw.c0000644000175000017500000007427311513177357016405 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2009, Innobase Oy. All Rights Reserved. Copyright (c) 2008, Google Inc. Portions of this file contain modifications contributed and copyrighted by Google, Inc. Those modifications are gratefully acknowledged and are described briefly in the InnoDB documentation. The contributions by Google are incorporated with their permission, and subject to the conditions contained in the file COPYING.Google. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file sync/sync0rw.c The read-write lock (for thread synchronization) Created 9/11/1995 Heikki Tuuri *******************************************************/ #include "sync0rw.h" #ifdef UNIV_NONINL #include "sync0rw.ic" #endif #include "os0thread.h" #include "mem0mem.h" #include "srv0srv.h" #include "os0sync.h" /* for INNODB_RW_LOCKS_USE_ATOMICS */ /* IMPLEMENTATION OF THE RW_LOCK ============================= The status of a rw_lock is held in lock_word. The initial value of lock_word is X_LOCK_DECR. lock_word is decremented by 1 for each s-lock and by X_LOCK_DECR for each x-lock. This describes the lock state for each value of lock_word: lock_word == X_LOCK_DECR: Unlocked. 0 < lock_word < X_LOCK_DECR: Read locked, no waiting writers. (X_LOCK_DECR - lock_word) is the number of readers that hold the lock. lock_word == 0: Write locked -X_LOCK_DECR < lock_word < 0: Read locked, with a waiting writer. (-lock_word) is the number of readers that hold the lock. lock_word <= -X_LOCK_DECR: Recursively write locked. lock_word has been decremented by X_LOCK_DECR once for each lock, so the number of locks is: ((-lock_word) / X_LOCK_DECR) + 1 When lock_word <= -X_LOCK_DECR, we also know that lock_word % X_LOCK_DECR == 0: other values of lock_word are invalid. The lock_word is always read and updated atomically and consistently, so that it always represents the state of the lock, and the state of the lock changes with a single atomic operation. This lock_word holds all of the information that a thread needs in order to determine if it is eligible to gain the lock or if it must spin or sleep. The one exception to this is that writer_thread must be verified before recursive write locks: to solve this scenario, we make writer_thread readable by all threads, but only writeable by the x-lock holder. The other members of the lock obey the following rules to remain consistent: recursive: This and the writer_thread field together control the behaviour of recursive x-locking. lock->recursive must be FALSE in following states: 1) The writer_thread contains garbage i.e.: the lock has just been initialized. 2) The lock is not x-held and there is no x-waiter waiting on WAIT_EX event. 3) The lock is x-held or there is an x-waiter waiting on WAIT_EX event but the 'pass' value is non-zero. lock->recursive is TRUE iff: 1) The lock is x-held or there is an x-waiter waiting on WAIT_EX event and the 'pass' value is zero. This flag must be set after the writer_thread field has been updated with a memory ordering barrier. It is unset before the lock_word has been incremented. writer_thread: Is used only in recursive x-locking. Can only be safely read iff lock->recursive flag is TRUE. This field is uninitialized at lock creation time and is updated atomically when x-lock is acquired or when move_ownership is called. A thread is only allowed to set the value of this field to it's thread_id i.e.: a thread cannot set writer_thread to some other thread's id. waiters: May be set to 1 anytime, but to avoid unnecessary wake-up signals, it should only be set to 1 when there are threads waiting on event. Must be 1 when a writer starts waiting to ensure the current x-locking thread sends a wake-up signal during unlock. May only be reset to 0 immediately before a a wake-up signal is sent to event. On most platforms, a memory barrier is required after waiters is set, and before verifying lock_word is still held, to ensure some unlocker really does see the flags new value. event: Threads wait on event for read or writer lock when another thread has an x-lock or an x-lock reservation (wait_ex). A thread may only wait on event after performing the following actions in order: (1) Record the counter value of event (with os_event_reset). (2) Set waiters to 1. (3) Verify lock_word <= 0. (1) must come before (2) to ensure signal is not missed. (2) must come before (3) to ensure a signal is sent. These restrictions force the above ordering. Immediately before sending the wake-up signal, we should: (1) Verify lock_word == X_LOCK_DECR (unlocked) (2) Reset waiters to 0. wait_ex_event: A thread may only wait on the wait_ex_event after it has performed the following actions in order: (1) Decrement lock_word by X_LOCK_DECR. (2) Record counter value of wait_ex_event (os_event_reset, called from sync_array_reserve_cell). (3) Verify that lock_word < 0. (1) must come first to ensures no other threads become reader or next writer, and notifies unlocker that signal must be sent. (2) must come before (3) to ensure the signal is not missed. These restrictions force the above ordering. Immediately before sending the wake-up signal, we should: Verify lock_word == 0 (waiting thread holds x_lock) */ /** number of spin waits on rw-latches, resulted during shared (read) locks */ UNIV_INTERN ib_int64_t rw_s_spin_wait_count = 0; /** number of spin loop rounds on rw-latches, resulted during shared (read) locks */ UNIV_INTERN ib_int64_t rw_s_spin_round_count = 0; /** number of OS waits on rw-latches, resulted during shared (read) locks */ UNIV_INTERN ib_int64_t rw_s_os_wait_count = 0; /** number of unlocks (that unlock shared locks), set only when UNIV_SYNC_PERF_STAT is defined */ UNIV_INTERN ib_int64_t rw_s_exit_count = 0; /** number of spin waits on rw-latches, resulted during exclusive (write) locks */ UNIV_INTERN ib_int64_t rw_x_spin_wait_count = 0; /** number of spin loop rounds on rw-latches, resulted during exclusive (write) locks */ UNIV_INTERN ib_int64_t rw_x_spin_round_count = 0; /** number of OS waits on rw-latches, resulted during exclusive (write) locks */ UNIV_INTERN ib_int64_t rw_x_os_wait_count = 0; /** number of unlocks (that unlock exclusive locks), set only when UNIV_SYNC_PERF_STAT is defined */ UNIV_INTERN ib_int64_t rw_x_exit_count = 0; /* The global list of rw-locks */ UNIV_INTERN rw_lock_list_t rw_lock_list; UNIV_INTERN mutex_t rw_lock_list_mutex; #ifdef UNIV_SYNC_DEBUG /* The global mutex which protects debug info lists of all rw-locks. To modify the debug info list of an rw-lock, this mutex has to be acquired in addition to the mutex protecting the lock. */ UNIV_INTERN mutex_t rw_lock_debug_mutex; /* If deadlock detection does not get immediately the mutex, it may wait for this event */ UNIV_INTERN os_event_t rw_lock_debug_event; /* This is set to TRUE, if there may be waiters for the event */ UNIV_INTERN ibool rw_lock_debug_waiters; /******************************************************************//** Creates a debug info struct. */ static rw_lock_debug_t* rw_lock_debug_create(void); /*======================*/ /******************************************************************//** Frees a debug info struct. */ static void rw_lock_debug_free( /*===============*/ rw_lock_debug_t* info); /******************************************************************//** Creates a debug info struct. @return own: debug info struct */ static rw_lock_debug_t* rw_lock_debug_create(void) /*======================*/ { return((rw_lock_debug_t*) mem_alloc(sizeof(rw_lock_debug_t))); } /******************************************************************//** Frees a debug info struct. */ static void rw_lock_debug_free( /*===============*/ rw_lock_debug_t* info) { mem_free(info); } #endif /* UNIV_SYNC_DEBUG */ /******************************************************************//** Reset the variables. */ UNIV_INTERN void rw_lock_var_init(void) /*==================*/ { rw_s_spin_wait_count = 0; rw_s_os_wait_count = 0; rw_s_exit_count = 0; rw_x_spin_wait_count = 0; rw_x_os_wait_count = 0; rw_x_exit_count = 0; memset(&rw_lock_list, 0x0, sizeof(rw_lock_list)); memset(&rw_lock_list_mutex, 0x0, sizeof(rw_lock_list_mutex)); #ifdef UNIV_SYNC_DEBUG memset(&rw_lock_debug_mutex, 0x0, sizeof(rw_lock_debug_mutex)); memset(&rw_lock_debug_event, 0x0, sizeof(rw_lock_debug_event)); memset(&rw_lock_debug_waiters, 0x0, sizeof(rw_lock_debug_waiters)); #endif } /********************************************************************** Creates, or rather, initializes an rw-lock object in a specified memory location (which must be appropriately aligned). The rw-lock is initialized to the non-locked state. Explicit freeing of the rw-lock with rw_lock_free is necessary only if the memory block containing it is freed. */ UNIV_INTERN void rw_lock_create_func( /*================*/ rw_lock_t* lock, /*!< in: pointer to memory */ #ifdef UNIV_DEBUG # ifdef UNIV_SYNC_DEBUG ulint level, /*!< in: level */ # endif /* UNIV_SYNC_DEBUG */ const char* cmutex_name, /*!< in: mutex name */ #endif /* UNIV_DEBUG */ const char* cfile_name, /*!< in: file name where created */ ulint cline) /*!< in: file line where created */ { /* If this is the very first time a synchronization object is created, then the following call initializes the sync system. */ #ifndef INNODB_RW_LOCKS_USE_ATOMICS mutex_create(rw_lock_get_mutex(lock), SYNC_NO_ORDER_CHECK); lock->mutex.cfile_name = cfile_name; lock->mutex.cline = cline; ut_d(lock->mutex.cmutex_name = cmutex_name); ut_d(lock->mutex.mutex_type = 1); #else /* INNODB_RW_LOCKS_USE_ATOMICS */ # ifdef UNIV_DEBUG UT_NOT_USED(cmutex_name); # endif #endif /* INNODB_RW_LOCKS_USE_ATOMICS */ lock->lock_word = X_LOCK_DECR; lock->waiters = 0; /* We set this value to signify that lock->writer_thread contains garbage at initialization and cannot be used for recursive x-locking. */ lock->recursive = FALSE; #ifdef UNIV_SYNC_DEBUG UT_LIST_INIT(lock->debug_list); lock->level = level; #endif /* UNIV_SYNC_DEBUG */ lock->magic_n = RW_LOCK_MAGIC_N; lock->cfile_name = cfile_name; lock->cline = (unsigned int) cline; lock->count_os_wait = 0; lock->last_s_file_name = "not yet reserved"; lock->last_x_file_name = "not yet reserved"; lock->last_s_line = 0; lock->last_x_line = 0; lock->event = os_event_create(NULL); lock->wait_ex_event = os_event_create(NULL); mutex_enter(&rw_lock_list_mutex); if (UT_LIST_GET_LEN(rw_lock_list) > 0) { ut_a(UT_LIST_GET_FIRST(rw_lock_list)->magic_n == RW_LOCK_MAGIC_N); } UT_LIST_ADD_FIRST(list, rw_lock_list, lock); mutex_exit(&rw_lock_list_mutex); } /******************************************************************//** Calling this function is obligatory only if the memory buffer containing the rw-lock is freed. Removes an rw-lock object from the global list. The rw-lock is checked to be in the non-locked state. */ UNIV_INTERN void rw_lock_free( /*=========*/ rw_lock_t* lock) /*!< in: rw-lock */ { ut_ad(rw_lock_validate(lock)); ut_a(lock->lock_word == X_LOCK_DECR); lock->magic_n = 0; #ifndef INNODB_RW_LOCKS_USE_ATOMICS mutex_free(rw_lock_get_mutex(lock)); #endif /* INNODB_RW_LOCKS_USE_ATOMICS */ mutex_enter(&rw_lock_list_mutex); os_event_free(lock->event); os_event_free(lock->wait_ex_event); if (UT_LIST_GET_PREV(list, lock)) { ut_a(UT_LIST_GET_PREV(list, lock)->magic_n == RW_LOCK_MAGIC_N); } if (UT_LIST_GET_NEXT(list, lock)) { ut_a(UT_LIST_GET_NEXT(list, lock)->magic_n == RW_LOCK_MAGIC_N); } UT_LIST_REMOVE(list, rw_lock_list, lock); mutex_exit(&rw_lock_list_mutex); } #ifdef UNIV_DEBUG /******************************************************************//** Checks that the rw-lock has been initialized and that there are no simultaneous shared and exclusive locks. @return TRUE */ UNIV_INTERN ibool rw_lock_validate( /*=============*/ rw_lock_t* lock) /*!< in: rw-lock */ { ut_a(lock); ulint waiters = rw_lock_get_waiters(lock); lint lock_word = lock->lock_word; ut_a(lock->magic_n == RW_LOCK_MAGIC_N); ut_a(waiters == 0 || waiters == 1); ut_a(lock_word > -X_LOCK_DECR ||(-lock_word) % X_LOCK_DECR == 0); return(TRUE); } #endif /* UNIV_DEBUG */ /******************************************************************//** Lock an rw-lock in shared mode for the current thread. If the rw-lock is locked in exclusive mode, or there is an exclusive lock request waiting, the function spins a preset time (controlled by SYNC_SPIN_ROUNDS), waiting for the lock, before suspending the thread. */ UNIV_INTERN void rw_lock_s_lock_spin( /*================*/ rw_lock_t* lock, /*!< in: pointer to rw-lock */ ulint pass, /*!< in: pass value; != 0, if the lock will be passed to another thread to unlock */ const char* file_name, /*!< in: file name where lock requested */ ulint line) /*!< in: line where requested */ { ulint index; /* index of the reserved wait cell */ ulint i = 0; /* spin round count */ ut_ad(rw_lock_validate(lock)); rw_s_spin_wait_count++; /*!< Count calls to this function */ lock_loop: /* Spin waiting for the writer field to become free */ while (i < SYNC_SPIN_ROUNDS && lock->lock_word <= 0) { if (srv_spin_wait_delay) { ut_delay(ut_rnd_interval(0, srv_spin_wait_delay)); } i++; } if (i == SYNC_SPIN_ROUNDS) { os_thread_yield(); } if (srv_print_latch_waits) { ib_logger(ib_stream, "Thread %lu spin wait rw-s-lock at %p" " cfile %s cline %lu rnds %lu\n", (ulong) os_thread_pf(os_thread_get_curr_id()), (void*) lock, lock->cfile_name, (ulong) lock->cline, (ulong) i); } /* We try once again to obtain the lock */ if (TRUE == rw_lock_s_lock_low(lock, pass, file_name, line)) { rw_s_spin_round_count += i; return; /* Success */ } else { if (i < SYNC_SPIN_ROUNDS) { goto lock_loop; } rw_s_spin_round_count += i; sync_array_reserve_cell(sync_primary_wait_array, lock, RW_LOCK_SHARED, file_name, line, &index); /* Set waiters before checking lock_word to ensure wake-up signal is sent. This may lead to some unnecessary signals. */ rw_lock_set_waiter_flag(lock); if (TRUE == rw_lock_s_lock_low(lock, pass, file_name, line)) { sync_array_free_cell(sync_primary_wait_array, index); return; /* Success */ } if (srv_print_latch_waits) { ib_logger(ib_stream, "Thread %lu OS wait rw-s-lock at %p" " cfile %s cline %lu\n", os_thread_pf(os_thread_get_curr_id()), (void*) lock, lock->cfile_name, (ulong) lock->cline); } /* these stats may not be accurate */ lock->count_os_wait++; rw_s_os_wait_count++; sync_array_wait_event(sync_primary_wait_array, index); i = 0; goto lock_loop; } } /******************************************************************//** This function is used in the insert buffer to move the ownership of an x-latch on a buffer frame to the current thread. The x-latch was set by the buffer read operation and it protected the buffer frame while the read was done. The ownership is moved because we want that the current thread is able to acquire a second x-latch which is stored in an mtr. This, in turn, is needed to pass the debug checks of index page operations. */ UNIV_INTERN void rw_lock_x_lock_move_ownership( /*==========================*/ rw_lock_t* lock) /*!< in: lock which was x-locked in the buffer read */ { ut_ad(rw_lock_is_locked(lock, RW_LOCK_EX)); rw_lock_set_writer_id_and_recursion_flag(lock, TRUE); } /******************************************************************//** Function for the next writer to call. Waits for readers to exit. The caller must have already decremented lock_word by X_LOCK_DECR. */ UNIV_INLINE void rw_lock_x_lock_wait( /*================*/ rw_lock_t* lock, /*!< in: pointer to rw-lock */ #ifdef UNIV_SYNC_DEBUG ulint pass, /*!< in: pass value; != 0, if the lock will be passed to another thread to unlock */ #endif const char* file_name,/*!< in: file name where lock requested */ ulint line) /*!< in: line where requested */ { ulint index; ulint i = 0; ut_ad(lock->lock_word <= 0); while (lock->lock_word < 0) { if (srv_spin_wait_delay) { ut_delay(ut_rnd_interval(0, srv_spin_wait_delay)); } if(i < SYNC_SPIN_ROUNDS) { i++; continue; } /* If there is still a reader, then go to sleep.*/ rw_x_spin_round_count += i; i = 0; sync_array_reserve_cell(sync_primary_wait_array, lock, RW_LOCK_WAIT_EX, file_name, line, &index); /* Check lock_word to ensure wake-up isn't missed.*/ if(lock->lock_word < 0) { /* these stats may not be accurate */ lock->count_os_wait++; rw_x_os_wait_count++; /* Add debug info as it is needed to detect possible deadlock. We must add info for WAIT_EX thread for deadlock detection to work properly. */ #ifdef UNIV_SYNC_DEBUG rw_lock_add_debug_info(lock, pass, RW_LOCK_WAIT_EX, file_name, line); #endif sync_array_wait_event(sync_primary_wait_array, index); #ifdef UNIV_SYNC_DEBUG rw_lock_remove_debug_info(lock, pass, RW_LOCK_WAIT_EX); #endif /* It is possible to wake when lock_word < 0. We must pass the while-loop check to proceed.*/ } else { sync_array_free_cell(sync_primary_wait_array, index); } } rw_x_spin_round_count += i; } /******************************************************************//** Low-level function for acquiring an exclusive lock. @return RW_LOCK_NOT_LOCKED if did not succeed, RW_LOCK_EX if success. */ UNIV_INLINE ibool rw_lock_x_lock_low( /*===============*/ rw_lock_t* lock, /*!< in: pointer to rw-lock */ ulint pass, /*!< in: pass value; != 0, if the lock will be passed to another thread to unlock */ const char* file_name,/*!< in: file name where lock requested */ ulint line) /*!< in: line where requested */ { os_thread_id_t curr_thread = os_thread_get_curr_id(); if (rw_lock_lock_word_decr(lock, X_LOCK_DECR)) { /* lock->recursive also tells us if the writer_thread field is stale or active. As we are going to write our own thread id in that field it must be that the current writer_thread value is not active. */ ut_a(!lock->recursive); /* Decrement occurred: we are writer or next-writer. */ rw_lock_set_writer_id_and_recursion_flag(lock, pass ? FALSE : TRUE); rw_lock_x_lock_wait(lock, #ifdef UNIV_SYNC_DEBUG pass, #endif file_name, line); } else { /* Decrement failed: relock or failed lock */ if (!pass && lock->recursive && os_thread_eq(lock->writer_thread, curr_thread)) { /* Relock */ lock->lock_word -= X_LOCK_DECR; } else { /* Another thread locked before us */ return(FALSE); } } #ifdef UNIV_SYNC_DEBUG rw_lock_add_debug_info(lock, pass, RW_LOCK_EX, file_name, line); #endif lock->last_x_file_name = file_name; lock->last_x_line = (unsigned int) line; return(TRUE); } /******************************************************************//** NOTE! Use the corresponding macro, not directly this function! Lock an rw-lock in exclusive mode for the current thread. If the rw-lock is locked in shared or exclusive mode, or there is an exclusive lock request waiting, the function spins a preset time (controlled by SYNC_SPIN_ROUNDS), waiting for the lock before suspending the thread. If the same thread has an x-lock on the rw-lock, locking succeed, with the following exception: if pass != 0, only a single x-lock may be taken on the lock. NOTE: If the same thread has an s-lock, locking does not succeed! */ UNIV_INTERN void rw_lock_x_lock_func( /*================*/ rw_lock_t* lock, /*!< in: pointer to rw-lock */ ulint pass, /*!< in: pass value; != 0, if the lock will be passed to another thread to unlock */ const char* file_name,/*!< in: file name where lock requested */ ulint line) /*!< in: line where requested */ { ulint index; /*!< index of the reserved wait cell */ ulint i; /*!< spin round count */ ibool spinning = FALSE; ut_ad(rw_lock_validate(lock)); i = 0; lock_loop: if (rw_lock_x_lock_low(lock, pass, file_name, line)) { rw_x_spin_round_count += i; return; /* Locking succeeded */ } else { if (!spinning) { spinning = TRUE; rw_x_spin_wait_count++; } /* Spin waiting for the lock_word to become free */ while (i < SYNC_SPIN_ROUNDS && lock->lock_word <= 0) { if (srv_spin_wait_delay) { ut_delay(ut_rnd_interval(0, srv_spin_wait_delay)); } i++; } if (i == SYNC_SPIN_ROUNDS) { os_thread_yield(); } else { goto lock_loop; } } rw_x_spin_round_count += i; if (srv_print_latch_waits) { ib_logger(ib_stream, "Thread %lu spin wait rw-x-lock at %p" " cfile %s cline %lu rnds %lu\n", os_thread_pf(os_thread_get_curr_id()), (void*) lock, lock->cfile_name, (ulong) lock->cline, (ulong) i); } sync_array_reserve_cell(sync_primary_wait_array, lock, RW_LOCK_EX, file_name, line, &index); /* Waiters must be set before checking lock_word, to ensure signal is sent. This could lead to a few unnecessary wake-up signals. */ rw_lock_set_waiter_flag(lock); if (rw_lock_x_lock_low(lock, pass, file_name, line)) { sync_array_free_cell(sync_primary_wait_array, index); return; /* Locking succeeded */ } if (srv_print_latch_waits) { ib_logger(ib_stream, "Thread %lu OS wait for rw-x-lock at %p" " cfile %s cline %lu\n", os_thread_pf(os_thread_get_curr_id()), (void*) lock, lock->cfile_name, (ulong) lock->cline); } /* these stats may not be accurate */ lock->count_os_wait++; rw_x_os_wait_count++; sync_array_wait_event(sync_primary_wait_array, index); i = 0; goto lock_loop; } #ifdef UNIV_SYNC_DEBUG /******************************************************************//** Acquires the debug mutex. We cannot use the mutex defined in sync0sync, because the debug mutex is also acquired in sync0arr while holding the OS mutex protecting the sync array, and the ordinary mutex_enter might recursively call routines in sync0arr, leading to a deadlock on the OS mutex. */ UNIV_INTERN void rw_lock_debug_mutex_enter(void) /*==========================*/ { loop: if (0 == mutex_enter_nowait(&rw_lock_debug_mutex)) { return; } os_event_reset(rw_lock_debug_event); rw_lock_debug_waiters = TRUE; if (0 == mutex_enter_nowait(&rw_lock_debug_mutex)) { return; } os_event_wait(rw_lock_debug_event); goto loop; } /******************************************************************//** Releases the debug mutex. */ UNIV_INTERN void rw_lock_debug_mutex_exit(void) /*==========================*/ { mutex_exit(&rw_lock_debug_mutex); if (rw_lock_debug_waiters) { rw_lock_debug_waiters = FALSE; os_event_set(rw_lock_debug_event); } } /******************************************************************//** Inserts the debug information for an rw-lock. */ UNIV_INTERN void rw_lock_add_debug_info( /*===================*/ rw_lock_t* lock, /*!< in: rw-lock */ ulint pass, /*!< in: pass value */ ulint lock_type, /*!< in: lock type */ const char* file_name, /*!< in: file where requested */ ulint line) /*!< in: line where requested */ { rw_lock_debug_t* info; ut_ad(lock); ut_ad(file_name); info = rw_lock_debug_create(); rw_lock_debug_mutex_enter(); info->file_name = file_name; info->line = line; info->lock_type = lock_type; info->thread_id = os_thread_get_curr_id(); info->pass = pass; UT_LIST_ADD_FIRST(list, lock->debug_list, info); rw_lock_debug_mutex_exit(); if ((pass == 0) && (lock_type != RW_LOCK_WAIT_EX)) { sync_thread_add_level(lock, lock->level); } } /******************************************************************//** Removes a debug information struct for an rw-lock. */ UNIV_INTERN void rw_lock_remove_debug_info( /*======================*/ rw_lock_t* lock, /*!< in: rw-lock */ ulint pass, /*!< in: pass value */ ulint lock_type) /*!< in: lock type */ { rw_lock_debug_t* info; ut_ad(lock); if ((pass == 0) && (lock_type != RW_LOCK_WAIT_EX)) { sync_thread_reset_level(lock); } rw_lock_debug_mutex_enter(); info = UT_LIST_GET_FIRST(lock->debug_list); while (info != NULL) { if ((pass == info->pass) && ((pass != 0) || os_thread_eq(info->thread_id, os_thread_get_curr_id())) && (info->lock_type == lock_type)) { /* Found! */ UT_LIST_REMOVE(list, lock->debug_list, info); rw_lock_debug_mutex_exit(); rw_lock_debug_free(info); return; } info = UT_LIST_GET_NEXT(list, info); } ut_error; } #endif /* UNIV_SYNC_DEBUG */ #ifdef UNIV_SYNC_DEBUG /******************************************************************//** Checks if the thread has locked the rw-lock in the specified mode, with the pass value == 0. @return TRUE if locked */ UNIV_INTERN ibool rw_lock_own( /*========*/ rw_lock_t* lock, /*!< in: rw-lock */ ulint lock_type) /*!< in: lock type: RW_LOCK_SHARED, RW_LOCK_EX */ { rw_lock_debug_t* info; ut_ad(lock); ut_ad(rw_lock_validate(lock)); rw_lock_debug_mutex_enter(); info = UT_LIST_GET_FIRST(lock->debug_list); while (info != NULL) { if (os_thread_eq(info->thread_id, os_thread_get_curr_id()) && (info->pass == 0) && (info->lock_type == lock_type)) { rw_lock_debug_mutex_exit(); /* Found! */ return(TRUE); } info = UT_LIST_GET_NEXT(list, info); } rw_lock_debug_mutex_exit(); return(FALSE); } #endif /* UNIV_SYNC_DEBUG */ /******************************************************************//** Checks if somebody has locked the rw-lock in the specified mode. @return TRUE if locked */ UNIV_INTERN ibool rw_lock_is_locked( /*==============*/ rw_lock_t* lock, /*!< in: rw-lock */ ulint lock_type) /*!< in: lock type: RW_LOCK_SHARED, RW_LOCK_EX */ { ibool ret = FALSE; ut_ad(lock); ut_ad(rw_lock_validate(lock)); if (lock_type == RW_LOCK_SHARED) { if (rw_lock_get_reader_count(lock) > 0) { ret = TRUE; } } else if (lock_type == RW_LOCK_EX) { if (rw_lock_get_writer(lock) == RW_LOCK_EX) { ret = TRUE; } } else { ut_error; } return(ret); } #ifdef UNIV_SYNC_DEBUG /***************************************************************//** Prints debug info of currently locked rw-locks. */ UNIV_INTERN void rw_lock_list_print_info( /*====================*/ ib_stream_t ib_stream) /*!< in: stream where to print */ { rw_lock_t* lock; ulint count = 0; rw_lock_debug_t* info; mutex_enter(&rw_lock_list_mutex); ib_logger(ib_stream, "-------------\n" "RW-LATCH INFO\n" "-------------\n"); lock = UT_LIST_GET_FIRST(rw_lock_list); while (lock != NULL) { count++; #ifndef INNODB_RW_LOCKS_USE_ATOMICS mutex_enter(&(lock->mutex)); #endif if (lock->lock_word != X_LOCK_DECR) { ib_logger(ib_stream, "RW-LOCK: %p ", (void*) lock); if (rw_lock_get_waiters(lock)) { ib_logger(ib_stream, " Waiters for the lock exist\n"); } else { ib_logger(ib_stream, "\n"); } info = UT_LIST_GET_FIRST(lock->debug_list); while (info != NULL) { rw_lock_debug_print(info); info = UT_LIST_GET_NEXT(list, info); } } #ifndef INNODB_RW_LOCKS_USE_ATOMICS mutex_exit(&(lock->mutex)); #endif lock = UT_LIST_GET_NEXT(list, lock); } ib_logger(ib_stream, "Total number of rw-locks %ld\n", count); mutex_exit(&rw_lock_list_mutex); } /***************************************************************//** Prints debug info of an rw-lock. */ UNIV_INTERN void rw_lock_print( /*==========*/ rw_lock_t* lock) /*!< in: rw-lock */ { rw_lock_debug_t* info; ib_logger(ib_stream, "-------------\n" "RW-LATCH INFO\n" "RW-LATCH: %p ", (void*) lock); #ifndef INNODB_RW_LOCKS_USE_ATOMICS /* We used to acquire lock->mutex here, but it would cause a recursive call to sync_thread_add_level() if UNIV_SYNC_DEBUG is defined. Since this function is only invoked from sync_thread_levels_g(), let us choose the smaller evil: performing dirty reads instead of causing bogus deadlocks or assertion failures. */ #endif if (lock->lock_word != X_LOCK_DECR) { if (rw_lock_get_waiters(lock)) { ib_logger(ib_stream, " Waiters for the lock exist\n"); } else { ib_logger(ib_stream, "\n"); } info = UT_LIST_GET_FIRST(lock->debug_list); while (info != NULL) { rw_lock_debug_print(info); info = UT_LIST_GET_NEXT(list, info); } } } /*********************************************************************//** Prints info of a debug struct. */ UNIV_INTERN void rw_lock_debug_print( /*================*/ rw_lock_debug_t* info) /*!< in: debug struct */ { ulint rwt; rwt = info->lock_type; ib_logger(ib_stream, "Locked: thread %ld file %s line %ld ", (ulong) os_thread_pf(info->thread_id), info->file_name, (ulong) info->line); if (rwt == RW_LOCK_SHARED) { ib_logger(ib_stream, "S-LOCK"); } else if (rwt == RW_LOCK_EX) { ib_logger(ib_stream, "X-LOCK"); } else if (rwt == RW_LOCK_WAIT_EX) { ib_logger(ib_stream, "WAIT X-LOCK"); } else { ut_error; } if (info->pass != 0) { ib_logger(ib_stream, " pass value %lu", (ulong) info->pass); } ib_logger(ib_stream, "\n", ib_stream); } /***************************************************************//** Returns the number of currently locked rw-locks. Works only in the debug version. @return number of locked rw-locks */ UNIV_INTERN ulint rw_lock_n_locked(void) /*==================*/ { rw_lock_t* lock; ulint count = 0; mutex_enter(&rw_lock_list_mutex); lock = UT_LIST_GET_FIRST(rw_lock_list); while (lock != NULL) { if (lock->lock_word != X_LOCK_DECR) { count++; } lock = UT_LIST_GET_NEXT(list, lock); } mutex_exit(&rw_lock_list_mutex); return(count); } #endif /* UNIV_SYNC_DEBUG */ haildb-2.3.2/sync/sync0sync.c0000644000175000017500000012506411513177357016724 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2010, Innobase Oy. All Rights Reserved. Copyright (c) 2008, Google Inc. Portions of this file contain modifications contributed and copyrighted by Google, Inc. Those modifications are gratefully acknowledged and are described briefly in the InnoDB documentation. The contributions by Google are incorporated with their permission, and subject to the conditions contained in the file COPYING.Google. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file sync/sync0sync.c Mutex, the basic synchronization primitive Created 9/5/1995 Heikki Tuuri *******************************************************/ #include "sync0sync.h" #ifdef UNIV_NONINL #include "sync0sync.ic" #endif #include "sync0rw.h" #include "buf0buf.h" #include "srv0srv.h" #include "buf0types.h" #include "os0sync.h" /* for HAVE_ATOMIC_BUILTINS */ /* REASONS FOR IMPLEMENTING THE SPIN LOCK MUTEX ============================================ Semaphore operations in operating systems are slow: Solaris on a 1993 Sparc takes 3 microseconds (us) for a lock-unlock pair and Windows NT on a 1995 Pentium takes 20 microseconds for a lock-unlock pair. Therefore, we have to implement our own efficient spin lock mutex. Future operating systems may provide efficient spin locks, but we cannot count on that. Another reason for implementing a spin lock is that on multiprocessor systems it can be more efficient for a processor to run a loop waiting for the semaphore to be released than to switch to a different thread. A thread switch takes 25 us on both platforms mentioned above. See Gray and Reuter's book Transaction processing for background. How long should the spin loop last before suspending the thread? On a uniprocessor, spinning does not help at all, because if the thread owning the mutex is not executing, it cannot be released. Spinning actually wastes resources. On a multiprocessor, we do not know if the thread owning the mutex is executing or not. Thus it would make sense to spin as long as the operation guarded by the mutex would typically last assuming that the thread is executing. If the mutex is not released by that time, we may assume that the thread owning the mutex is not executing and suspend the waiting thread. A typical operation (where no i/o involved) guarded by a mutex or a read-write lock may last 1 - 20 us on the current Pentium platform. The longest operations are the binary searches on an index node. We conclude that the best choice is to set the spin time at 20 us. Then the system should work well on a multiprocessor. On a uniprocessor we have to make sure that thread swithches due to mutex collisions are not frequent, i.e., they do not happen every 100 us or so, because that wastes too much resources. If the thread switches are not frequent, the 20 us wasted in spin loop is not too much. Empirical studies on the effect of spin time should be done for different platforms. IMPLEMENTATION OF THE MUTEX =========================== For background, see Curt Schimmel's book on Unix implementation on modern architectures. The key points in the implementation are atomicity and serialization of memory accesses. The test-and-set instruction (XCHG in Pentium) must be atomic. As new processors may have weak memory models, also serialization of memory references may be necessary. The successor of Pentium, P6, has at least one mode where the memory model is weak. As far as we know, in Pentium all memory accesses are serialized in the program order and we do not have to worry about the memory model. On other processors there are special machine instructions called a fence, memory barrier, or storage barrier (STBAR in Sparc), which can be used to serialize the memory accesses to happen in program order relative to the fence instruction. Leslie Lamport has devised a "bakery algorithm" to implement a mutex without the atomic test-and-set, but his algorithm should be modified for weak memory models. We do not use Lamport's algorithm, because we guess it is slower than the atomic test-and-set. Our mutex implementation works as follows: After that we perform the atomic test-and-set instruction on the memory word. If the test returns zero, we know we got the lock first. If the test returns not zero, some other thread was quicker and got the lock: then we spin in a loop reading the memory word, waiting it to become zero. It is wise to just read the word in the loop, not perform numerous test-and-set instructions, because they generate memory traffic between the cache and the main memory. The read loop can just access the cache, saving bus bandwidth. If we cannot acquire the mutex lock in the specified time, we reserve a cell in the wait array, set the waiters byte in the mutex to 1. To avoid a race condition, after setting the waiters byte and before suspending the waiting thread, we still have to check that the mutex is reserved, because it may have happened that the thread which was holding the mutex has just released it and did not see the waiters byte set to 1, a case which would lead the other thread to an infinite wait. LEMMA 1: After a thread resets the event of a mutex (or rw_lock), some ======= thread will eventually call os_event_set() on that particular event. Thus no infinite wait is possible in this case. Proof: After making the reservation the thread sets the waiters field in the mutex to 1. Then it checks that the mutex is still reserved by some thread, or it reserves the mutex for itself. In any case, some thread (which may be also some earlier thread, not necessarily the one currently holding the mutex) will set the waiters field to 0 in mutex_exit, and then call os_event_set() with the mutex as an argument. Q.E.D. LEMMA 2: If an os_event_set() call is made after some thread has called ======= the os_event_reset() and before it starts wait on that event, the call will not be lost to the second thread. This is true even if there is an intervening call to os_event_reset() by another thread. Thus no infinite wait is possible in this case. Proof (non-windows platforms): os_event_reset() returns a monotonically increasing value of signal_count. This value is increased at every call of os_event_set() If thread A has called os_event_reset() followed by thread B calling os_event_set() and then some other thread C calling os_event_reset(), the is_set flag of the event will be set to FALSE; but now if thread A calls os_event_wait_low() with the signal_count value returned from the earlier call of os_event_reset(), it will return immediately without waiting. Q.E.D. Proof (windows): If there is a writer thread which is forced to wait for the lock, it may be able to set the state of rw_lock to RW_LOCK_WAIT_EX The design of rw_lock ensures that there is one and only one thread that is able to change the state to RW_LOCK_WAIT_EX and this thread is guaranteed to acquire the lock after it is released by the current holders and before any other waiter gets the lock. On windows this thread waits on a separate event i.e.: wait_ex_event. Since only one thread can wait on this event there is no chance of this event getting reset before the writer starts wait on it. Therefore, this thread is guaranteed to catch the os_set_event() signalled unconditionally at the release of the lock. Q.E.D. */ /* Number of spin waits on mutexes: for performance monitoring */ /** The number of iterations in the mutex_spin_wait() spin loop. Intended for performance monitoring. */ static ib_int64_t mutex_spin_round_count = 0; /** The number of mutex_spin_wait() calls. Intended for performance monitoring. */ static ib_int64_t mutex_spin_wait_count = 0; /** The number of OS waits in mutex_spin_wait(). Intended for performance monitoring. */ static ib_int64_t mutex_os_wait_count = 0; /** The number of mutex_exit() calls. Intended for performance monitoring. */ UNIV_INTERN ib_int64_t mutex_exit_count = 0; /** The global array of wait cells for implementation of the database's own mutexes and read-write locks */ UNIV_INTERN sync_array_t* sync_primary_wait_array; /** This variable is set to TRUE when sync_init is called */ UNIV_INTERN ibool sync_initialized = FALSE; /** An acquired mutex or rw-lock and its level in the latching order */ typedef struct sync_level_struct sync_level_t; /** Mutexes or rw-locks held by a thread */ typedef struct sync_thread_struct sync_thread_t; #ifdef UNIV_SYNC_DEBUG /** The latch levels currently owned by threads are stored in this data structure; the size of this array is OS_THREAD_MAX_N */ UNIV_INTERN sync_thread_t* sync_thread_level_arrays; /** Mutex protecting sync_thread_level_arrays */ static mutex_t sync_thread_mutex; #endif /* UNIV_SYNC_DEBUG */ /** Global list of database mutexes (not OS mutexes) created. */ UNIV_INTERN ut_list_base_node_t mutex_list; /** Mutex protecting the mutex_list variable */ UNIV_INTERN mutex_t mutex_list_mutex; #ifdef UNIV_SYNC_DEBUG /** Latching order checks start when this is set TRUE */ UNIV_INTERN ibool sync_order_checks_on = FALSE; #endif /* UNIV_SYNC_DEBUG */ #ifdef UNIV_DEBUG /* FIXME: How is this used in MySQL code ? */ static ibool timed_mutexes; #endif /* UNIV_DEBUG */ struct sync_thread_struct{ os_thread_id_t id; /*!< OS thread id */ sync_level_t* levels; /*!< level array for this thread; if this is NULL this slot is unused */ }; /** Number of slots reserved for each OS thread in the sync level array */ #define SYNC_THREAD_N_LEVELS 10000 /** An acquired mutex or rw-lock and its level in the latching order */ struct sync_level_struct{ void* latch; /*!< pointer to a mutex or an rw-lock; NULL means that the slot is empty */ ulint level; /*!< level of the latch in the latching order */ }; /******************************************************************//** * Reset variables. */ UNIV_INTERN void sync_var_init(void) /*===============*/ { mutex_spin_round_count = 0; mutex_spin_wait_count = 0; mutex_os_wait_count = 0; mutex_exit_count = 0; sync_primary_wait_array = NULL; sync_initialized = FALSE; #ifdef UNIV_SYNC_DEBUG sync_thread_level_arrays = NULL; memset(&sync_thread_mutex, 0x0, sizeof(sync_thread_mutex)); #endif /* UNIV_SYNC_DEBUG */ memset(&mutex_list, 0x0, sizeof(mutex_list)); memset(&mutex_list_mutex, 0x0, sizeof(mutex_list_mutex)); #ifdef UNIV_SYNC_DEBUG sync_order_checks_on = FALSE; #endif /* UNIV_SYNC_DEBUG */ } /********************************************************************** Creates, or rather, initializes a mutex object in a specified memory location (which must be appropriately aligned). The mutex is initialized in the reset state. Explicit freeing of the mutex with mutex_free is necessary only if the memory block containing it is freed. */ UNIV_INTERN void mutex_create_func( /*==============*/ mutex_t* mutex, /*!< in: pointer to memory */ #ifdef UNIV_DEBUG const char* cmutex_name, /*!< in: mutex name */ # ifdef UNIV_SYNC_DEBUG ulint level, /*!< in: level */ # endif /* UNIV_SYNC_DEBUG */ #endif /* UNIV_DEBUG */ const char* cfile_name, /*!< in: file name where created */ ulint cline) /*!< in: file line where created */ { #if defined(HAVE_ATOMIC_BUILTINS) mutex_reset_lock_word(mutex); #else os_fast_mutex_init(&(mutex->os_fast_mutex)); mutex->lock_word = 0; #endif mutex->event = os_event_create(NULL); mutex_set_waiters(mutex, 0); #ifdef UNIV_DEBUG mutex->magic_n = MUTEX_MAGIC_N; #endif /* UNIV_DEBUG */ #ifdef UNIV_SYNC_DEBUG mutex->line = 0; mutex->file_name = "not yet reserved"; mutex->level = level; #endif /* UNIV_SYNC_DEBUG */ mutex->cfile_name = cfile_name; mutex->cline = cline; mutex->count_os_wait = 0; #ifdef UNIV_DEBUG mutex->cmutex_name= cmutex_name; mutex->count_using= 0; mutex->mutex_type= 0; mutex->lspent_time= 0; mutex->lmax_spent_time= 0; mutex->count_spin_loop= 0; mutex->count_spin_rounds= 0; mutex->count_os_yield= 0; #endif /* UNIV_DEBUG */ /* Check that lock_word is aligned; this is important on Intel */ ut_ad(((ulint)(&(mutex->lock_word))) % 4 == 0); /* NOTE! The very first mutexes are not put to the mutex list */ if ((mutex == &mutex_list_mutex) #ifdef UNIV_SYNC_DEBUG || (mutex == &sync_thread_mutex) #endif /* UNIV_SYNC_DEBUG */ ) { return; } mutex_enter(&mutex_list_mutex); ut_ad(UT_LIST_GET_LEN(mutex_list) == 0 || UT_LIST_GET_FIRST(mutex_list)->magic_n == MUTEX_MAGIC_N); UT_LIST_ADD_FIRST(list, mutex_list, mutex); mutex_exit(&mutex_list_mutex); } /******************************************************************//** Calling this function is obligatory only if the memory buffer containing the mutex is freed. Removes a mutex object from the mutex list. The mutex is checked to be in the reset state. */ UNIV_INTERN void mutex_free( /*=======*/ mutex_t* mutex) /*!< in: mutex */ { ut_ad(mutex_validate(mutex)); ut_a(mutex_get_lock_word(mutex) == 0); ut_a(mutex_get_waiters(mutex) == 0); #ifdef UNIV_MEM_DEBUG if (mutex == &mem_hash_mutex) { ut_ad(UT_LIST_GET_LEN(mutex_list) == 1); ut_ad(UT_LIST_GET_FIRST(mutex_list) == &mem_hash_mutex); UT_LIST_REMOVE(list, mutex_list, mutex); goto func_exit; } #endif /* UNIV_MEM_DEBUG */ #ifdef UNIV_MEM_DEBUG if (mutex == &mem_hash_mutex) { ut_ad(UT_LIST_GET_LEN(mutex_list) == 1); ut_ad(UT_LIST_GET_FIRST(mutex_list) == &mem_hash_mutex); UT_LIST_REMOVE(list, mutex_list, mutex); goto func_exit; } #endif /* UNIV_MEM_DEBUG */ if (mutex != &mutex_list_mutex #ifdef UNIV_SYNC_DEBUG && mutex != &sync_thread_mutex #endif /* UNIV_SYNC_DEBUG */ ) { mutex_enter(&mutex_list_mutex); ut_ad(!UT_LIST_GET_PREV(list, mutex) || UT_LIST_GET_PREV(list, mutex)->magic_n == MUTEX_MAGIC_N); ut_ad(!UT_LIST_GET_NEXT(list, mutex) || UT_LIST_GET_NEXT(list, mutex)->magic_n == MUTEX_MAGIC_N); UT_LIST_REMOVE(list, mutex_list, mutex); mutex_exit(&mutex_list_mutex); } os_event_free(mutex->event); #ifdef UNIV_MEM_DEBUG func_exit: #endif /* UNIV_MEM_DEBUG */ #if !defined(HAVE_ATOMIC_BUILTINS) os_fast_mutex_free(&(mutex->os_fast_mutex)); #endif /* If we free the mutex protecting the mutex list (freeing is not necessary), we have to reset the magic number AFTER removing it from the list. */ #ifdef UNIV_DEBUG mutex->magic_n = 0; #endif /* UNIV_DEBUG */ } /********************************************************************//** NOTE! Use the corresponding macro in the header file, not this function directly. Tries to lock the mutex for the current thread. If the lock is not acquired immediately, returns with return value 1. @return 0 if succeed, 1 if not */ UNIV_INTERN ulint mutex_enter_nowait_func( /*====================*/ mutex_t* mutex, /*!< in: pointer to mutex */ const char* file_name __attribute__((unused)), /*!< in: file name where mutex requested */ ulint line __attribute__((unused))) /*!< in: line where requested */ { ut_ad(mutex_validate(mutex)); if (!mutex_test_and_set(mutex)) { ut_d(mutex->thread_id = os_thread_get_curr_id()); #ifdef UNIV_SYNC_DEBUG mutex_set_debug_info(mutex, file_name, line); #endif return(0); /* Succeeded! */ } return(1); } #ifdef UNIV_DEBUG /******************************************************************//** Checks that the mutex has been initialized. @return TRUE */ UNIV_INTERN ibool mutex_validate( /*===========*/ const mutex_t* mutex) /*!< in: mutex */ { ut_a(mutex); ut_a(mutex->magic_n == MUTEX_MAGIC_N); return(TRUE); } /******************************************************************//** Checks that the current thread owns the mutex. Works only in the debug version. @return TRUE if owns */ UNIV_INTERN ibool mutex_own( /*======*/ const mutex_t* mutex) /*!< in: mutex */ { ut_ad(mutex_validate(mutex)); return(mutex_get_lock_word(mutex) == 1 && os_thread_eq(mutex->thread_id, os_thread_get_curr_id())); } #endif /* UNIV_DEBUG */ /******************************************************************//** Sets the waiters field in a mutex. */ UNIV_INTERN void mutex_set_waiters( /*==============*/ mutex_t* mutex, /*!< in: mutex */ ulint n) /*!< in: value to set */ { volatile ulint* ptr; /* declared volatile to ensure that the value is stored to memory */ ut_ad(mutex); ptr = &(mutex->waiters); *ptr = n; /* Here we assume that the write of a single word in memory is atomic */ } /******************************************************************//** Reserves a mutex for the current thread. If the mutex is reserved, the function spins a preset time (controlled by SYNC_SPIN_ROUNDS), waiting for the mutex before suspending the thread. */ UNIV_INTERN void mutex_spin_wait( /*============*/ mutex_t* mutex, /*!< in: pointer to mutex */ const char* file_name, /*!< in: file name where mutex requested */ ulint line) /*!< in: line where requested */ { ulint index; /* index of the reserved wait cell */ ulint i; /* spin round count */ #ifdef UNIV_DEBUG ib_int64_t lstart_time = 0, lfinish_time; /* for timing os_wait */ ulint ltime_diff; ulint sec; ulint ms; ulint timer_started = 0; #endif /* UNIV_DEBUG */ ut_ad(mutex); /* This update is not thread safe, but we don't mind if the count isn't exact. Moved out of ifdef that follows because we are willing to sacrifice the cost of counting this as the data is valuable. Count the number of calls to mutex_spin_wait. */ mutex_spin_wait_count++; mutex_loop: i = 0; /* Spin waiting for the lock word to become zero. Note that we do not have to assume that the read access to the lock word is atomic, as the actual locking is always committed with atomic test-and-set. In reality, however, all processors probably have an atomic read of a memory word. */ spin_loop: ut_d(mutex->count_spin_loop++); while (mutex_get_lock_word(mutex) != 0 && i < SYNC_SPIN_ROUNDS) { if (srv_spin_wait_delay) { ut_delay(ut_rnd_interval(0, srv_spin_wait_delay)); } i++; } if (i == SYNC_SPIN_ROUNDS) { #ifdef UNIV_DEBUG mutex->count_os_yield++; #ifndef UNIV_HOTBACKUP if (timed_mutexes && timer_started == 0) { ut_usectime(&sec, &ms); lstart_time= (ib_int64_t)sec * 1000000 + ms; timer_started = 1; } #endif /* UNIV_HOTBACKUP */ #endif /* UNIV_DEBUG */ os_thread_yield(); } #ifdef UNIV_SRV_PRINT_LATCH_WAITS ib_logger(ib_stream, "Thread %lu spin wait mutex at %p" " cfile %s cline %lu rnds %lu\n", (ulong) os_thread_pf(os_thread_get_curr_id()), (void*) mutex, mutex->cfile_name, (ulong) mutex->cline, (ulong) i); #endif mutex_spin_round_count += i; ut_d(mutex->count_spin_rounds += i); if (mutex_test_and_set(mutex) == 0) { /* Succeeded! */ ut_d(mutex->thread_id = os_thread_get_curr_id()); #ifdef UNIV_SYNC_DEBUG mutex_set_debug_info(mutex, file_name, line); #endif goto finish_timing; } /* We may end up with a situation where lock_word is 0 but the OS fast mutex is still reserved. On FreeBSD the OS does not seem to schedule a thread which is constantly calling pthread_mutex_trylock (in mutex_test_and_set implementation). Then we could end up spinning here indefinitely. The following 'i++' stops this infinite spin. */ i++; if (i < SYNC_SPIN_ROUNDS) { goto spin_loop; } sync_array_reserve_cell(sync_primary_wait_array, mutex, SYNC_MUTEX, file_name, line, &index); /* The memory order of the array reservation and the change in the waiters field is important: when we suspend a thread, we first reserve the cell and then set waiters field to 1. When threads are released in mutex_exit, the waiters field is first set to zero and then the event is set to the signaled state. */ mutex_set_waiters(mutex, 1); /* Try to reserve still a few times */ for (i = 0; i < 4; i++) { if (mutex_test_and_set(mutex) == 0) { /* Succeeded! Free the reserved wait cell */ sync_array_free_cell(sync_primary_wait_array, index); ut_d(mutex->thread_id = os_thread_get_curr_id()); #ifdef UNIV_SYNC_DEBUG mutex_set_debug_info(mutex, file_name, line); #endif #ifdef UNIV_SRV_PRINT_LATCH_WAITS ib_logger(ib_stream, "Thread %lu spin wait succeeds at 2:" " mutex at %p\n", (ulong) os_thread_pf(os_thread_get_curr_id()), (void*) mutex); #endif goto finish_timing; /* Note that in this case we leave the waiters field set to 1. We cannot reset it to zero, as we do not know if there are other waiters. */ } } /* Now we know that there has been some thread holding the mutex after the change in the wait array and the waiters field was made. Now there is no risk of infinite wait on the event. */ #ifdef UNIV_SRV_PRINT_LATCH_WAITS ib_logger(ib_stream, "Thread %lu OS wait mutex at %p cfile %s cline %lu rnds %lu\n", (ulong) os_thread_pf(os_thread_get_curr_id()), (void*) mutex, mutex->cfile_name, (ulong) mutex->cline, (ulong) i); #endif mutex_os_wait_count++; mutex->count_os_wait++; #ifdef UNIV_DEBUG /* !!!!! Sometimes os_wait can be called without os_thread_yield */ #ifndef UNIV_HOTBACKUP if (timed_mutexes == 1 && timer_started == 0) { ut_usectime(&sec, &ms); lstart_time= (ib_int64_t)sec * 1000000 + ms; timer_started = 1; } #endif /* UNIV_HOTBACKUP */ #endif /* UNIV_DEBUG */ sync_array_wait_event(sync_primary_wait_array, index); goto mutex_loop; finish_timing: #ifdef UNIV_DEBUG if (timed_mutexes == 1 && timer_started==1) { ut_usectime(&sec, &ms); lfinish_time= (ib_int64_t)sec * 1000000 + ms; ltime_diff= (ulint) (lfinish_time - lstart_time); mutex->lspent_time += ltime_diff; if (mutex->lmax_spent_time < ltime_diff) { mutex->lmax_spent_time= ltime_diff; } } #endif /* UNIV_DEBUG */ return; } /******************************************************************//** Releases the threads waiting in the primary wait array for this mutex. */ UNIV_INTERN void mutex_signal_object( /*================*/ mutex_t* mutex) /*!< in: mutex */ { mutex_set_waiters(mutex, 0); /* The memory order of resetting the waiters field and signaling the object is important. See LEMMA 1 above. */ os_event_set(mutex->event); sync_array_object_signalled(sync_primary_wait_array); } #ifdef UNIV_SYNC_DEBUG /******************************************************************//** Sets the debug information for a reserved mutex. */ UNIV_INTERN void mutex_set_debug_info( /*=================*/ mutex_t* mutex, /*!< in: mutex */ const char* file_name, /*!< in: file where requested */ ulint line) /*!< in: line where requested */ { ut_ad(mutex); ut_ad(file_name); sync_thread_add_level(mutex, mutex->level); mutex->file_name = file_name; mutex->line = line; } /******************************************************************//** Gets the debug information for a reserved mutex. */ UNIV_INTERN void mutex_get_debug_info( /*=================*/ mutex_t* mutex, /*!< in: mutex */ const char** file_name, /*!< out: file where requested */ ulint* line, /*!< out: line where requested */ os_thread_id_t* thread_id) /*!< out: id of the thread which owns the mutex */ { ut_ad(mutex); *file_name = mutex->file_name; *line = mutex->line; *thread_id = mutex->thread_id; } /******************************************************************//** Prints debug info of currently reserved mutexes. */ static void mutex_list_print_info( /*==================*/ ib_stream_t ib_stream) /*!< in: stream where to print */ { mutex_t* mutex; const char* file_name; ulint line; os_thread_id_t thread_id; ulint count = 0; ib_logger(ib_stream, "----------\n" "MUTEX INFO\n" "----------\n"); mutex_enter(&mutex_list_mutex); mutex = UT_LIST_GET_FIRST(mutex_list); while (mutex != NULL) { count++; if (mutex_get_lock_word(mutex) != 0) { mutex_get_debug_info(mutex, &file_name, &line, &thread_id); ib_logger(ib_stream, "Locked mutex: addr %p thread %ld" " file %s line %ld\n", (void*) mutex, os_thread_pf(thread_id), file_name, line); } mutex = UT_LIST_GET_NEXT(list, mutex); } ib_logger(ib_stream, "Total number of mutexes %ld\n", count); mutex_exit(&mutex_list_mutex); } /******************************************************************//** Counts currently reserved mutexes. Works only in the debug version. @return number of reserved mutexes */ UNIV_INTERN ulint mutex_n_reserved(void) /*==================*/ { mutex_t* mutex; ulint count = 0; mutex_enter(&mutex_list_mutex); mutex = UT_LIST_GET_FIRST(mutex_list); while (mutex != NULL) { if (mutex_get_lock_word(mutex) != 0) { count++; } mutex = UT_LIST_GET_NEXT(list, mutex); } mutex_exit(&mutex_list_mutex); ut_a(count >= 1); return(count - 1); /* Subtract one, because this function itself was holding one mutex (mutex_list_mutex) */ } /******************************************************************//** Returns TRUE if no mutex or rw-lock is currently locked. Works only in the debug version. @return TRUE if no mutexes and rw-locks reserved */ UNIV_INTERN ibool sync_all_freed(void) /*================*/ { return(mutex_n_reserved() + rw_lock_n_locked() == 0); } /******************************************************************//** Gets the value in the nth slot in the thread level arrays. @return pointer to thread slot */ static sync_thread_t* sync_thread_level_arrays_get_nth( /*=============================*/ ulint n) /*!< in: slot number */ { ut_ad(n < OS_THREAD_MAX_N); return(sync_thread_level_arrays + n); } /******************************************************************//** Looks for the thread slot for the calling thread. @return pointer to thread slot, NULL if not found */ static sync_thread_t* sync_thread_level_arrays_find_slot(void) /*====================================*/ { sync_thread_t* slot; os_thread_id_t id; ulint i; id = os_thread_get_curr_id(); for (i = 0; i < OS_THREAD_MAX_N; i++) { slot = sync_thread_level_arrays_get_nth(i); if (slot->levels && os_thread_eq(slot->id, id)) { return(slot); } } return(NULL); } /******************************************************************//** Looks for an unused thread slot. @return pointer to thread slot */ static sync_thread_t* sync_thread_level_arrays_find_free(void) /*====================================*/ { sync_thread_t* slot; ulint i; for (i = 0; i < OS_THREAD_MAX_N; i++) { slot = sync_thread_level_arrays_get_nth(i); if (slot->levels == NULL) { return(slot); } } return(NULL); } /******************************************************************//** Gets the value in the nth slot in the thread level array. @return pointer to level slot */ static sync_level_t* sync_thread_levels_get_nth( /*=======================*/ sync_level_t* arr, /*!< in: pointer to level array for an OS thread */ ulint n) /*!< in: slot number */ { ut_ad(n < SYNC_THREAD_N_LEVELS); return(arr + n); } /******************************************************************//** Checks if all the level values stored in the level array are greater than the given limit. @return TRUE if all greater */ static ibool sync_thread_levels_g( /*=================*/ sync_level_t* arr, /*!< in: pointer to level array for an OS thread */ ulint limit, /*!< in: level limit */ ulint warn) /*!< in: TRUE=display a diagnostic message */ { sync_level_t* slot; rw_lock_t* lock; mutex_t* mutex; ulint i; for (i = 0; i < SYNC_THREAD_N_LEVELS; i++) { slot = sync_thread_levels_get_nth(arr, i); if (slot->latch != NULL) { if (slot->level <= limit) { if (!warn) { return(FALSE); } lock = slot->latch; mutex = slot->latch; ib_logger(ib_stream, "InnoDB: sync levels should be" " > %lu but a level is %lu\n", (ulong) limit, (ulong) slot->level); if (mutex->magic_n == MUTEX_MAGIC_N) { ib_logger(ib_stream, "Mutex created at %s %lu\n", mutex->cfile_name, (ulong) mutex->cline); if (mutex_get_lock_word(mutex) != 0) { const char* file_name; ulint line; os_thread_id_t thread_id; mutex_get_debug_info( mutex, &file_name, &line, &thread_id); ib_logger(ib_stream, "InnoDB: Locked mutex:" " addr %p thread %ld" " file %s line %ld\n", (void*) mutex, os_thread_pf( thread_id), file_name, (ulong) line); } else { ib_logger(ib_stream, "Not locked\n"); } } else { rw_lock_print(lock); } return(FALSE); } } } return(TRUE); } /******************************************************************//** Checks if the level value is stored in the level array. @return TRUE if stored */ static ibool sync_thread_levels_contain( /*=======================*/ sync_level_t* arr, /*!< in: pointer to level array for an OS thread */ ulint level) /*!< in: level */ { sync_level_t* slot; ulint i; for (i = 0; i < SYNC_THREAD_N_LEVELS; i++) { slot = sync_thread_levels_get_nth(arr, i); if (slot->latch != NULL) { if (slot->level == level) { return(TRUE); } } } return(FALSE); } /******************************************************************//** Checks if the level array for the current thread contains a mutex or rw-latch at the specified level. @return a matching latch, or NULL if not found */ UNIV_INTERN void* sync_thread_levels_contains( /*========================*/ ulint level) /*!< in: latching order level (SYNC_DICT, ...)*/ { sync_level_t* arr; sync_thread_t* thread_slot; sync_level_t* slot; ulint i; if (!sync_order_checks_on) { return(NULL); } mutex_enter(&sync_thread_mutex); thread_slot = sync_thread_level_arrays_find_slot(); if (thread_slot == NULL) { mutex_exit(&sync_thread_mutex); return(NULL); } arr = thread_slot->levels; for (i = 0; i < SYNC_THREAD_N_LEVELS; i++) { slot = sync_thread_levels_get_nth(arr, i); if (slot->latch != NULL && slot->level == level) { mutex_exit(&sync_thread_mutex); return(slot->latch); } } mutex_exit(&sync_thread_mutex); return(NULL); } /******************************************************************//** Checks that the level array for the current thread is empty. @return a latch, or NULL if empty except the exceptions specified below */ UNIV_INTERN void* sync_thread_levels_nonempty_gen( /*============================*/ ibool dict_mutex_allowed) /*!< in: TRUE if dictionary mutex is allowed to be owned by the thread, also purge_is_running mutex is allowed */ { sync_level_t* arr; sync_thread_t* thread_slot; sync_level_t* slot; ulint i; if (!sync_order_checks_on) { return(NULL); } mutex_enter(&sync_thread_mutex); thread_slot = sync_thread_level_arrays_find_slot(); if (thread_slot == NULL) { mutex_exit(&sync_thread_mutex); return(NULL); } arr = thread_slot->levels; for (i = 0; i < SYNC_THREAD_N_LEVELS; i++) { slot = sync_thread_levels_get_nth(arr, i); if (slot->latch != NULL && (!dict_mutex_allowed || (slot->level != SYNC_DICT && slot->level != SYNC_DICT_OPERATION))) { mutex_exit(&sync_thread_mutex); ut_error; return(slot->latch); } } mutex_exit(&sync_thread_mutex); return(NULL); } /******************************************************************//** Checks that the level array for the current thread is empty. @return TRUE if empty */ UNIV_INTERN ibool sync_thread_levels_empty(void) /*==========================*/ { return(sync_thread_levels_empty_gen(FALSE)); } /******************************************************************//** Adds a latch and its level in the thread level array. Allocates the memory for the array if called first time for this OS thread. Makes the checks against other latch levels stored in the array for this thread. */ UNIV_INTERN void sync_thread_add_level( /*==================*/ void* latch, /*!< in: pointer to a mutex or an rw-lock */ ulint level) /*!< in: level in the latching order; if SYNC_LEVEL_VARYING, nothing is done */ { sync_level_t* array; sync_level_t* slot; sync_thread_t* thread_slot; ulint i; if (!sync_order_checks_on) { return; } if ((latch == (void*)&sync_thread_mutex) || (latch == (void*)&mutex_list_mutex) || (latch == (void*)&rw_lock_debug_mutex) || (latch == (void*)&rw_lock_list_mutex)) { return; } if (level == SYNC_LEVEL_VARYING) { return; } mutex_enter(&sync_thread_mutex); thread_slot = sync_thread_level_arrays_find_slot(); if (thread_slot == NULL) { /* We have to allocate the level array for a new thread */ array = ut_malloc(sizeof(sync_level_t) * SYNC_THREAD_N_LEVELS); thread_slot = sync_thread_level_arrays_find_free(); thread_slot->id = os_thread_get_curr_id(); thread_slot->levels = array; for (i = 0; i < SYNC_THREAD_N_LEVELS; i++) { slot = sync_thread_levels_get_nth(array, i); slot->latch = NULL; } } array = thread_slot->levels; /* NOTE that there is a problem with _NODE and _LEAF levels: if the B-tree height changes, then a leaf can change to an internal node or the other way around. We do not know at present if this can cause unnecessary assertion failures below. */ switch (level) { case SYNC_NO_ORDER_CHECK: case SYNC_EXTERN_STORAGE: case SYNC_TREE_NODE_FROM_HASH: /* Do no order checking */ break; case SYNC_MEM_POOL: case SYNC_MEM_HASH: case SYNC_RECV: case SYNC_WORK_QUEUE: case SYNC_LOG: case SYNC_THR_LOCAL: case SYNC_ANY_LATCH: case SYNC_TRX_SYS_HEADER: case SYNC_FILE_FORMAT_TAG: case SYNC_DOUBLEWRITE: case SYNC_BUF_POOL: case SYNC_SEARCH_SYS: case SYNC_SEARCH_SYS_CONF: case SYNC_TRX_LOCK_HEAP: case SYNC_KERNEL: case SYNC_IBUF_BITMAP_MUTEX: case SYNC_RSEG: case SYNC_TRX_UNDO: case SYNC_PURGE_LATCH: case SYNC_PURGE_SYS: case SYNC_DICT_AUTOINC_MUTEX: case SYNC_DICT_OPERATION: case SYNC_DICT_HEADER: case SYNC_TRX_I_S_RWLOCK: case SYNC_TRX_I_S_LAST_READ: if (!sync_thread_levels_g(array, level, TRUE)) { ib_logger(ib_stream, "InnoDB: sync_thread_levels_g(array, %lu)" " does not hold!\n", level); ut_error; } break; case SYNC_BUF_BLOCK: /* Either the thread must own the buffer pool mutex (buf_pool_mutex), or it is allowed to latch only ONE buffer block (block->mutex or buf_pool_zip_mutex). */ if (!sync_thread_levels_g(array, level, FALSE)) { ut_a(sync_thread_levels_g(array, level - 1, TRUE)); ut_a(sync_thread_levels_contain(array, SYNC_BUF_POOL)); } break; case SYNC_REC_LOCK: if (sync_thread_levels_contain(array, SYNC_KERNEL)) { ut_a(sync_thread_levels_g(array, SYNC_REC_LOCK - 1, TRUE)); } else { ut_a(sync_thread_levels_g(array, SYNC_REC_LOCK, TRUE)); } break; case SYNC_IBUF_BITMAP: /* Either the thread must own the master mutex to all the bitmap pages, or it is allowed to latch only ONE bitmap page. */ if (sync_thread_levels_contain(array, SYNC_IBUF_BITMAP_MUTEX)) { ut_a(sync_thread_levels_g(array, SYNC_IBUF_BITMAP - 1, TRUE)); } else { ut_a(sync_thread_levels_g(array, SYNC_IBUF_BITMAP, TRUE)); } break; case SYNC_FSP_PAGE: ut_a(sync_thread_levels_contain(array, SYNC_FSP)); break; case SYNC_FSP: ut_a(sync_thread_levels_contain(array, SYNC_FSP) || sync_thread_levels_g(array, SYNC_FSP, TRUE)); break; case SYNC_TRX_UNDO_PAGE: ut_a(sync_thread_levels_contain(array, SYNC_TRX_UNDO) || sync_thread_levels_contain(array, SYNC_RSEG) || sync_thread_levels_contain(array, SYNC_PURGE_SYS) || sync_thread_levels_g(array, SYNC_TRX_UNDO_PAGE, TRUE)); break; case SYNC_RSEG_HEADER: ut_a(sync_thread_levels_contain(array, SYNC_RSEG)); break; case SYNC_RSEG_HEADER_NEW: ut_a(sync_thread_levels_contain(array, SYNC_KERNEL) && sync_thread_levels_contain(array, SYNC_FSP_PAGE)); break; case SYNC_TREE_NODE: ut_a(sync_thread_levels_contain(array, SYNC_INDEX_TREE) || sync_thread_levels_contain(array, SYNC_DICT_OPERATION) || sync_thread_levels_g(array, SYNC_TREE_NODE - 1, TRUE)); break; case SYNC_TREE_NODE_NEW: ut_a(sync_thread_levels_contain(array, SYNC_FSP_PAGE) || sync_thread_levels_contain(array, SYNC_IBUF_MUTEX)); break; case SYNC_INDEX_TREE: if (sync_thread_levels_contain(array, SYNC_IBUF_MUTEX) && sync_thread_levels_contain(array, SYNC_FSP)) { ut_a(sync_thread_levels_g(array, SYNC_FSP_PAGE - 1, TRUE)); } else { ut_a(sync_thread_levels_g(array, SYNC_TREE_NODE - 1, TRUE)); } break; case SYNC_IBUF_MUTEX: ut_a(sync_thread_levels_g(array, SYNC_FSP_PAGE - 1, TRUE)); break; case SYNC_IBUF_PESS_INSERT_MUTEX: ut_a(sync_thread_levels_g(array, SYNC_FSP - 1, TRUE)); ut_a(!sync_thread_levels_contain(array, SYNC_IBUF_MUTEX)); break; case SYNC_IBUF_HEADER: ut_a(sync_thread_levels_g(array, SYNC_FSP - 1, TRUE)); ut_a(!sync_thread_levels_contain(array, SYNC_IBUF_MUTEX)); ut_a(!sync_thread_levels_contain(array, SYNC_IBUF_PESS_INSERT_MUTEX)); break; case SYNC_DICT: #ifdef UNIV_DEBUG ut_a(buf_debug_prints || sync_thread_levels_g(array, SYNC_DICT, TRUE)); #else /* UNIV_DEBUG */ ut_a(sync_thread_levels_g(array, SYNC_DICT, TRUE)); #endif /* UNIV_DEBUG */ break; default: ut_error; } for (i = 0; i < SYNC_THREAD_N_LEVELS; i++) { slot = sync_thread_levels_get_nth(array, i); if (slot->latch == NULL) { slot->latch = latch; slot->level = level; break; } } ut_a(i < SYNC_THREAD_N_LEVELS); mutex_exit(&sync_thread_mutex); } /******************************************************************//** Removes a latch from the thread level array if it is found there. @return TRUE if found in the array; it is no error if the latch is not found, as we presently are not able to determine the level for every latch reservation the program does */ UNIV_INTERN ibool sync_thread_reset_level( /*====================*/ void* latch) /*!< in: pointer to a mutex or an rw-lock */ { sync_level_t* array; sync_level_t* slot; sync_thread_t* thread_slot; ulint i; if (!sync_order_checks_on) { return(FALSE); } if ((latch == (void*)&sync_thread_mutex) || (latch == (void*)&mutex_list_mutex) || (latch == (void*)&rw_lock_debug_mutex) || (latch == (void*)&rw_lock_list_mutex)) { return(FALSE); } mutex_enter(&sync_thread_mutex); thread_slot = sync_thread_level_arrays_find_slot(); if (thread_slot == NULL) { ut_error; mutex_exit(&sync_thread_mutex); return(FALSE); } array = thread_slot->levels; for (i = 0; i < SYNC_THREAD_N_LEVELS; i++) { slot = sync_thread_levels_get_nth(array, i); if (slot->latch == latch) { slot->latch = NULL; mutex_exit(&sync_thread_mutex); return(TRUE); } } if (((mutex_t*) latch)->magic_n != MUTEX_MAGIC_N) { rw_lock_t* rw_lock; rw_lock = (rw_lock_t*) latch; if (rw_lock->level == SYNC_LEVEL_VARYING) { mutex_exit(&sync_thread_mutex); return(TRUE); } } ut_error; mutex_exit(&sync_thread_mutex); return(FALSE); } #endif /* UNIV_SYNC_DEBUG */ /******************************************************************//** Initializes the synchronization data structures. */ UNIV_INTERN void sync_init(void) /*===========*/ { #ifdef UNIV_SYNC_DEBUG sync_thread_t* thread_slot; ulint i; #endif /* UNIV_SYNC_DEBUG */ ut_a(sync_initialized == FALSE); sync_initialized = TRUE; /* Create the primary system wait array which is protected by an OS mutex */ sync_primary_wait_array = sync_array_create(OS_THREAD_MAX_N, SYNC_ARRAY_OS_MUTEX); #ifdef UNIV_SYNC_DEBUG /* Create the thread latch level array where the latch levels are stored for each OS thread */ sync_thread_level_arrays = ut_malloc(OS_THREAD_MAX_N * sizeof(sync_thread_t)); for (i = 0; i < OS_THREAD_MAX_N; i++) { thread_slot = sync_thread_level_arrays_get_nth(i); thread_slot->levels = NULL; } #endif /* UNIV_SYNC_DEBUG */ /* Init the mutex list and create the mutex to protect it. */ UT_LIST_INIT(mutex_list); mutex_create(&mutex_list_mutex, SYNC_NO_ORDER_CHECK); #ifdef UNIV_SYNC_DEBUG mutex_create(&sync_thread_mutex, SYNC_NO_ORDER_CHECK); #endif /* UNIV_SYNC_DEBUG */ /* Init the rw-lock list and create the mutex to protect it. */ UT_LIST_INIT(rw_lock_list); mutex_create(&rw_lock_list_mutex, SYNC_NO_ORDER_CHECK); #ifdef UNIV_SYNC_DEBUG mutex_create(&rw_lock_debug_mutex, SYNC_NO_ORDER_CHECK); rw_lock_debug_event = os_event_create(NULL); rw_lock_debug_waiters = FALSE; #endif /* UNIV_SYNC_DEBUG */ } /******************************************************************//** Frees the resources in InnoDB's own synchronization data structures. Use os_sync_free() after calling this. */ UNIV_INTERN void sync_close(void) /*===========*/ { mutex_t* mutex; sync_array_free(sync_primary_wait_array); mutex = UT_LIST_GET_FIRST(mutex_list); while (mutex) { #ifdef UNIV_MEM_DEBUG if (mutex == &mem_hash_mutex) { mutex = UT_LIST_GET_NEXT(list, mutex); continue; } #endif /* UNIV_MEM_DEBUG */ mutex_free(mutex); mutex = UT_LIST_GET_FIRST(mutex_list); } mutex_free(&mutex_list_mutex); #ifdef UNIV_SYNC_DEBUG mutex_free(&sync_thread_mutex); sync_order_checks_on = FALSE; /* Switch latching order checks on in sync0sync.c */ sync_order_checks_on = FALSE; #endif /* UNIV_SYNC_DEBUG */ sync_initialized = FALSE; } /*******************************************************************//** Prints wait info of the sync system. */ UNIV_INTERN void sync_print_wait_info( /*=================*/ ib_stream_t ib_stream) /*!< in: stream where to print */ { #ifdef UNIV_SYNC_DEBUG ib_logger(ib_stream, "Mutex exits %llu, rws exits %llu, rwx exits %llu\n", mutex_exit_count, rw_s_exit_count, rw_x_exit_count); #endif ib_logger(ib_stream, "Mutex spin waits %llu, rounds %llu, OS waits %llu\n" "RW-shared spins %llu, OS waits %llu;" " RW-excl spins %llu, OS waits %llu\n", mutex_spin_wait_count, mutex_spin_round_count, mutex_os_wait_count, rw_s_spin_wait_count, rw_s_os_wait_count, rw_x_spin_wait_count, rw_x_os_wait_count); ib_logger(ib_stream, "Spin rounds per wait: %.2f mutex, %.2f RW-shared, " "%.2f RW-excl\n", (double) mutex_spin_round_count / (mutex_spin_wait_count ? mutex_spin_wait_count : 1), (double) rw_s_spin_round_count / (rw_s_spin_wait_count ? rw_s_spin_wait_count : 1), (double) rw_x_spin_round_count / (rw_x_spin_wait_count ? rw_x_spin_wait_count : 1)); } /*******************************************************************//** Prints info of the sync system. */ UNIV_INTERN void sync_print( /*=======*/ ib_stream_t ib_stream) /*!< in: stream where to print */ { #ifdef UNIV_SYNC_DEBUG mutex_list_print_info(ib_stream); rw_lock_list_print_info(ib_stream); #endif /* UNIV_SYNC_DEBUG */ sync_array_print_info(ib_stream, sync_primary_wait_array); sync_print_wait_info(ib_stream); } haildb-2.3.2/dyn/0000755000175000017500000000000011513177437014434 5ustar00pcrewspcrews00000000000000haildb-2.3.2/dyn/dyn0dyn.c0000644000175000017500000000346611513177357016177 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file dyn/dyn0dyn.c The dynamically allocated array Created 2/5/1996 Heikki Tuuri *******************************************************/ #include "dyn0dyn.h" #ifdef UNIV_NONINL #include "dyn0dyn.ic" #endif /************************************************************//** Adds a new block to a dyn array. @return created block */ UNIV_INTERN dyn_block_t* dyn_array_add_block( /*================*/ dyn_array_t* arr) /*!< in: dyn array */ { mem_heap_t* heap; dyn_block_t* block; ut_ad(arr); ut_ad(arr->magic_n == DYN_BLOCK_MAGIC_N); if (arr->heap == NULL) { UT_LIST_INIT(arr->base); UT_LIST_ADD_FIRST(list, arr->base, arr); arr->heap = mem_heap_create(sizeof(dyn_block_t)); } block = dyn_array_get_last_block(arr); block->used = block->used | DYN_BLOCK_FULL_FLAG; heap = arr->heap; block = mem_heap_alloc(heap, sizeof(dyn_block_t)); block->used = 0; UT_LIST_ADD_LAST(list, arr->base, block); return(block); } haildb-2.3.2/config.h.in0000644000175000017500000004037111513177416015667 0ustar00pcrewspcrews00000000000000/* config.h.in. Generated from configure.ac by autoheader. */ #ifndef __CONFIG_H__ #define __CONFIG_H__ /* _SYS_FEATURE_TESTS_H is Solaris, _FEATURES_H is GCC */ #if defined( _SYS_FEATURE_TESTS_H) || defined(_FEATURES_H) #error "You should include config.h as your first include file" #endif #include "config/top.h" /* Define if building universal (internal helper macro) */ #undef AC_APPLE_UNIVERSAL_BUILD /* the location of */ #undef CINTTYPES_H /* the location of */ #undef CSTDINT_H /* Another magical number */ #undef EAI_SYSTEM /* Define to 1 if you have the header file. */ #undef HAVE_ASSERT_H /* Define to 1 if you have the `atomic_add_long' function. */ #undef HAVE_ATOMIC_ADD_LONG /* Define to 1 if you have the `atomic_cas_32' function. */ #undef HAVE_ATOMIC_CAS_32 /* Define to 1 if you have the `atomic_cas_64' function. */ #undef HAVE_ATOMIC_CAS_64 /* Define to 1 if you have the `atomic_cas_ulong' function. */ #undef HAVE_ATOMIC_CAS_ULONG /* Define to 1 if you have the `bcmp' function. */ #undef HAVE_BCMP /* Have a working clock_gettime function */ #undef HAVE_CLOCK_GETTIME /* Define to 1 if you have the declaration of `HAVE_IB_GCC_ATOMIC_BUILTINS', and to 0 if you don't. */ #undef HAVE_DECL_HAVE_IB_GCC_ATOMIC_BUILTINS /* Define to 1 if you have the declaration of `HAVE_IB_SOLARIS_ATOMICS', and to 0 if you don't. */ #undef HAVE_DECL_HAVE_IB_SOLARIS_ATOMICS /* Define to 1 if you have the declaration of `madvise', and to 0 if you don't. */ #undef HAVE_DECL_MADVISE /* Define to 1 if you have the declaration of `SHM_HUGETLB', and to 0 if you don't. */ #undef HAVE_DECL_SHM_HUGETLB /* Define to 1 if you have the header file. */ #undef HAVE_DLFCN_H /* Enables DTRACE Support */ #undef HAVE_DTRACE /* Define to 1 if you have the `fcntl' function. */ #undef HAVE_FCNTL /* Define to 1 if you have the `finite' function. */ #undef HAVE_FINITE /* Define to 1 if you have the `fsync' function. */ #undef HAVE_FSYNC /* Define to 1 if you have the `ftruncate' function. */ #undef HAVE_FTRUNCATE /* Define to 1 if compiler provides atomic builtins. */ #undef HAVE_GCC_ATOMIC_BUILTINS /* Define to 1 if you have the `getcwd' function. */ #undef HAVE_GETCWD /* Define to 1 if you have the `getpagesize' function. */ #undef HAVE_GETPAGESIZE /* Define to 1 if you have the `getrusage' function. */ #undef HAVE_GETRUSAGE /* pthread_t can be used by GCC atomic builtins */ #undef HAVE_IB_ATOMIC_PTHREAD_T_GCC /* pthread_t can be used by solaris atomics */ #undef HAVE_IB_ATOMIC_PTHREAD_T_SOLARIS /* GCC atomic builtins are available */ #undef HAVE_IB_GCC_ATOMIC_BUILTINS /* Does x86 PAUSE instruction exist */ #undef HAVE_IB_PAUSE_INSTRUCTION /* Define to 1 if Solaris libc atomic functions are available */ #undef HAVE_IB_SOLARIS_ATOMICS /* Define to 1 if you have the `index' function. */ #undef HAVE_INDEX /* Define to 1 if the system has the type `int16_t'. */ #undef HAVE_INT16_T /* Define to 1 if the system has the type `int32_t'. */ #undef HAVE_INT32_T /* Define to 1 if the system has the type `int64_t'. */ #undef HAVE_INT64_T /* Define to 1 if the system has the type `int8_t'. */ #undef HAVE_INT8_T /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H /* Define if you have large pages support */ #undef HAVE_LARGE_PAGES /* Define to 1 if you have the `bind' library (-lbind). */ #undef HAVE_LIBBIND /* Define to 1 if you have the `c_p' library (-lc_p). */ #undef HAVE_LIBC_P /* Define to 1 if you have the `mtmalloc' library (-lmtmalloc). */ #undef HAVE_LIBMTMALLOC /* Define to 1 if you have the `posix4' library (-lposix4). */ #undef HAVE_LIBPOSIX4 /* Define to 1 if you have the `pthread' library (-lpthread). */ #undef HAVE_LIBPTHREAD /* Define to 1 if you have the `socket' library (-lsocket). */ #undef HAVE_LIBSOCKET /* Define to 1 if you have the `tcmalloc' library (-ltcmalloc). */ #undef HAVE_LIBTCMALLOC /* Define to 1 if you have the `tcmalloc-minimal' library (-ltcmalloc-minimal). */ #undef HAVE_LIBTCMALLOC_MINIMAL /* Define to 1 if you have the `umem' library (-lumem). */ #undef HAVE_LIBUMEM /* Define to 1 if you have the `z' library (-lz). */ #undef HAVE_LIBZ /* Define to 1 if you have the `localtime_r' function. */ #undef HAVE_LOCALTIME_R /* Define to 1 if you have the `locking' function. */ #undef HAVE_LOCKING /* Define to 1 if the system has the type `long long int'. */ #undef HAVE_LONG_LONG_INT /* Define to 1 if you have the `memcpy' function. */ #undef HAVE_MEMCPY /* Define to 1 if you have the `memmove' function. */ #undef HAVE_MEMMOVE /* Define to 1 if you have the header file. */ #undef HAVE_MEMORY_H /* Define to 1 if you have the `mmap' function. */ #undef HAVE_MMAP /* Define to 1 if the system has the type `off_t'. */ #undef HAVE_OFF_T /* Define to 1 if you have the `perror' function. */ #undef HAVE_PERROR /* Define to 1 if you have the `pread' function. */ #undef HAVE_PREAD /* Define to 1 if you have the `pthread_attr_setstacksize' function. */ #undef HAVE_PTHREAD_ATTR_SETSTACKSIZE /* Define to 1 if you have the `pthread_barrier_destroy' function. */ #undef HAVE_PTHREAD_BARRIER_DESTROY /* Define to 1 if you have the `pthread_barrier_init' function. */ #undef HAVE_PTHREAD_BARRIER_INIT /* Define to 1 if you have the `pthread_barrier_wait' function. */ #undef HAVE_PTHREAD_BARRIER_WAIT /* Define to 1 if you have the header file. */ #undef HAVE_PTHREAD_H /* Define to 1 if you have the `pthread_setprio' function. */ #undef HAVE_PTHREAD_SETPRIO /* Define to 1 if you have the `rename' function. */ #undef HAVE_RENAME /* Define to 1 if you have the `rint' function. */ #undef HAVE_RINT /* Define to 1 if you have the `shmat' function. */ #undef HAVE_SHMAT /* Define to 1 if you have the `shmctl' function. */ #undef HAVE_SHMCTL /* Define to 1 if you have the `shmdt' function. */ #undef HAVE_SHMDT /* Define to 1 if you have the `shmget' function. */ #undef HAVE_SHMGET /* Define to 1 if the system has the type `size_t'. */ #undef HAVE_SIZE_T /* Define to 1 if you have the `sleep' function. */ #undef HAVE_SLEEP /* Define to 1 if you have the `snprintf' function. */ #undef HAVE_SNPRINTF /* Define to 1 if you have the header file. */ #undef HAVE_STDINT_H /* Define to 1 if you have the header file. */ #undef HAVE_STDLIB_H /* Define to 1 if you have the `stpcpy' function. */ #undef HAVE_STPCPY /* Define to 1 if you have the `strcasecmp' function. */ #undef HAVE_STRCASECMP /* Define to 1 if you have the `strdup' function. */ #undef HAVE_STRDUP /* Define to 1 if you have the `strerror' function. */ #undef HAVE_STRERROR /* Define to 1 if you have the header file. */ #undef HAVE_STRINGS_H /* Define to 1 if you have the header file. */ #undef HAVE_STRING_H /* Define to 1 if you have the `strlcpy' function. */ #undef HAVE_STRLCPY /* Define to 1 if you have the `strstr' function. */ #undef HAVE_STRSTR /* Define to 1 if you have the `strtoul' function. */ #undef HAVE_STRTOUL /* Define to 1 if `st_rdev' is a member of `struct stat'. */ #undef HAVE_STRUCT_STAT_ST_RDEV /* Define to 1 if your `struct stat' has `st_rdev'. Deprecated, use `HAVE_STRUCT_STAT_ST_RDEV' instead. */ #undef HAVE_ST_RDEV /* Define to 1 if you have the header file. */ #undef HAVE_SYS_SDT_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_SOCKET_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_STAT_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_TYPES_H /* Define to 1 if you have the `tell' function. */ #undef HAVE_TELL /* Define to 1 if the system has the type `uint16_t'. */ #undef HAVE_UINT16_T /* Define to 1 if the system has the type `uint32_t'. */ #undef HAVE_UINT32_T /* Define to 1 if the system has the type `uint64_t'. */ #undef HAVE_UINT64_T /* Define to 1 if the system has the type `uint8_t'. */ #undef HAVE_UINT8_T /* Define to 1 if the system has the type `ulong'. */ #undef HAVE_ULONG /* Define to 1 if you have the header file. */ #undef HAVE_UNISTD_H /* Define to 1 if the system has the type `unsigned long long int'. */ #undef HAVE_UNSIGNED_LONG_LONG_INT /* Define to 1 if the system has the type `u_int32_t'. */ #undef HAVE_U_INT32_T /* Define to 1 if you have the header file. */ #undef HAVE_VALGRIND_MEMCHECK_H /* Define to 1 or 0, depending whether the compiler supports simple visibility declarations. */ #undef HAVE_VISIBILITY /* Define to 1 if you have the header file. */ #undef HAVE_ZLIB_H /* CPU of Build System */ #undef HOST_CPU /* OS of Build System */ #undef HOST_OS /* Vendor of Build System */ #undef HOST_VENDOR /* Define if /proc/meminfo shows the huge page size (Linux only) */ #undef HUGETLB_USE_PROC_MEMINFO /* Age Version */ #undef IB_API_VERSION_AGE /* Current Version */ #undef IB_API_VERSION_CURRENT /* Revision Version */ #undef IB_API_VERSION_REVISION /* Use GCC builtin atomic functions */ #undef IB_ATOMIC_MODE_GCC_ATOMIC_BUILTINS /* Use InnoDB's own implementation */ #undef IB_ATOMIC_MODE_INNODB /* Use Solaris 10 atomic_ops(3C) from libc */ #undef IB_ATOMIC_MODE_SOLARIS_ATOMICS /* Define to the sub-directory in which libtool stores uninstalled libraries. */ #undef LT_OBJDIR /* Define to 1 if assertions should be disabled. */ #undef NDEBUG /* Define to 1 if your C compiler doesn't accept -c and -o together. */ #undef NO_MINUS_C_MINUS_O /* Name of package */ #undef PACKAGE /* Define to the address where bug reports for this package should be sent. */ #undef PACKAGE_BUGREPORT /* Define to the full name of this package. */ #undef PACKAGE_NAME /* Define to the full name and version of this package. */ #undef PACKAGE_STRING /* Define to the one symbol short name of this package. */ #undef PACKAGE_TARNAME /* Define to the home page for this package. */ #undef PACKAGE_URL /* Define to the version of this package. */ #undef PACKAGE_VERSION /* Version of the software */ #undef PANDORA_RELEASE_VERSION /* Define if system doesn't define */ #undef RUSAGE_THREAD /* The size of `char*', as computed by sizeof. */ #undef SIZEOF_CHARP /* The size of `int', as computed by sizeof. */ #undef SIZEOF_INT /* The size of `long', as computed by sizeof. */ #undef SIZEOF_LONG /* The size of `long long', as computed by sizeof. */ #undef SIZEOF_LONG_LONG /* The size of `off_t', as computed by sizeof. */ #undef SIZEOF_OFF_T /* The size of `pthread_t', as computed by sizeof. */ #undef SIZEOF_PTHREAD_T /* The size of `short', as computed by sizeof. */ #undef SIZEOF_SHORT /* Size of size_t as computed by sizeof() */ #undef SIZEOF_SIZE_T /* The size of `void*', as computed by sizeof. */ #undef SIZEOF_VOIDP /* Define if ISO C++ 1998 header files are present. */ #undef STDCXX_98_HEADERS /* Define to 1 if you have the ANSI C header files. */ #undef STDC_HEADERS /* CPU of Target System */ #undef TARGET_CPU /* OS of Target System */ #undef TARGET_OS /* Whether we are building for FreeBSD */ #undef TARGET_OS_FREEBSD /* Whether we build for Linux */ #undef TARGET_OS_LINUX /* Whether we build for OSX */ #undef TARGET_OS_OSX /* Whether we are building for Solaris */ #undef TARGET_OS_SOLARIS /* Whether we are building for Windows */ #undef TARGET_OS_WINDOWS /* Vendor of Target System */ #undef TARGET_VENDOR /* Define to 1 if time_t is unsigned */ #undef TIME_T_UNSIGNED /* Define to 1 if you can safely include both and . */ #undef TIME_WITH_SYS_TIME /* Define to 1 if your declares `struct tm'. */ #undef TM_IN_SYS_TIME /* Enable extensions on AIX 3, Interix. */ #ifndef _ALL_SOURCE # undef _ALL_SOURCE #endif /* Enable GNU extensions on systems that have them. */ #ifndef _GNU_SOURCE # undef _GNU_SOURCE #endif /* Enable threading extensions on Solaris. */ #ifndef _POSIX_PTHREAD_SEMANTICS # undef _POSIX_PTHREAD_SEMANTICS #endif /* Enable extensions on HP NonStop. */ #ifndef _TANDEM_SOURCE # undef _TANDEM_SOURCE #endif /* Enable general extensions on Solaris. */ #ifndef __EXTENSIONS__ # undef __EXTENSIONS__ #endif /* Version number of package */ #undef VERSION /* Version of Windows */ #undef WINVER /* XA support */ #undef WITH_XOPEN /* Compressed tables support */ #undef WITH_ZIP /* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most significant byte first (like Motorola and SPARC, unlike Intel). */ #if defined AC_APPLE_UNIVERSAL_BUILD # if defined __BIG_ENDIAN__ # define WORDS_BIGENDIAN 1 # endif #else # ifndef WORDS_BIGENDIAN # undef WORDS_BIGENDIAN # endif #endif /* Number of bits in a file offset, on hosts where this is settable. */ #undef _FILE_OFFSET_BITS /* Define for large files, on AIX-style hosts. */ #undef _LARGE_FILES /* Define to 1 if on MINIX. */ #undef _MINIX /* Define to 2 if the system does not provide POSIX.1 features except with this defined. */ #undef _POSIX_1_SOURCE /* Define to 1 if you need to in order for `stat' and other things to work. */ #undef _POSIX_SOURCE /* Cause Sun Studio to not be quite so strict with standards conflicts */ #undef _STLP_NO_NEW_C_HEADERS /* Magical number to make things work */ #undef _WIN32_WINNT /* Workaround for bug in FreeBSD headers */ #undef __APPLE_CC__ /* Use STDC Constant Macros in C++ */ #undef __STDC_CONSTANT_MACROS /* Use STDC Format Macros in C++ */ #undef __STDC_FORMAT_MACROS /* Use STDC Limit Macros in C++ */ #undef __STDC_LIMIT_MACROS /* Define to empty if `const' does not conform to ANSI C. */ #undef const /* Define to `__inline__' or `__inline' if that's what the C compiler calls it, or to nothing if 'inline' is not supported under any name. */ #ifndef __cplusplus #undef inline #endif /* Define to `long int' if does not define. */ #undef off_t /* Define to the equivalent of the C99 'restrict' keyword, or to nothing if this is not supported. Do not define if restrict is supported directly. */ #undef restrict /* Work around a bug in Sun C++: it does not support _Restrict or __restrict__, even though the corresponding Sun C compiler ends up with "#define restrict _Restrict" or "#define restrict __restrict__" in the previous line. Perhaps some future version of Sun C++ will work with restrict; if so, hopefully it defines __RESTRICT like Sun C does. */ #if defined __SUNPRO_CC && !defined __RESTRICT # define _Restrict # define __restrict__ #endif /* Define to `unsigned int' if does not define. */ #undef size_t /* Define to empty if the keyword `volatile' does not work. Warning: valid code using `volatile' can become incorrect without. Disable with care. */ #undef volatile #ifndef HAVE_SYS_SOCKET_H # define SHUT_RD SD_RECEIVE # define SHUT_WR SD_SEND # define SHUT_RDWR SD_BOTH #endif #if defined(__cplusplus) # include CSTDINT_H # include CINTTYPES_H #else # include # include #endif #if !defined(HAVE_ULONG) && !defined(__USE_MISC) # define HAVE_ULONG 1 typedef unsigned long int ulong; #endif /* To hide the platform differences between MS Windows and Unix, I am * going to use the Microsoft way and #define the Microsoft-specific * functions to the unix way. Microsoft use a separate subsystem for sockets, * but Unix normally just use a filedescriptor on the same functions. It is * a lot easier to map back to the unix way with macros than going the other * way without side effect ;-) */ #ifdef TARGET_OS_WINDOWS #define random() rand() #define srandom(a) srand(a) #define get_socket_errno() WSAGetLastError() #else #define INVALID_SOCKET -1 #define SOCKET_ERROR -1 #define closesocket(a) close(a) #define get_socket_errno() errno #endif #if defined(__cplusplus) # if defined(DEBUG) # include # include # endif template inline To implicit_cast(From const &f) { return f; } template // use like this: down_cast(foo); inline To down_cast(From* f) { // so we only accept pointers // Ensures that To is a sub-type of From *. This test is here only // for compile-time type checking, and has no overhead in an // optimized build at run-time, as it will be optimized away // completely. if (false) { implicit_cast(0); } #if defined(DEBUG) assert(f == NULL || dynamic_cast(f) != NULL); // RTTI: debug mode only! #endif return static_cast(f); } #endif /* defined(__cplusplus) */ #endif /* __CONFIG_H__ */ haildb-2.3.2/trx/0000755000175000017500000000000011513177437014457 5ustar00pcrewspcrews00000000000000haildb-2.3.2/trx/trx0rseg.c0000644000175000017500000001726611513177357016416 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file trx/trx0rseg.c Rollback segment Created 3/26/1996 Heikki Tuuri *******************************************************/ #include "trx0rseg.h" #ifdef UNIV_NONINL #include "trx0rseg.ic" #endif #include "trx0undo.h" #include "fut0lst.h" #include "srv0srv.h" #include "trx0purge.h" /******************************************************************//** Looks for a rollback segment, based on the rollback segment id. @return rollback segment */ UNIV_INTERN trx_rseg_t* trx_rseg_get_on_id( /*===============*/ ulint id) /*!< in: rollback segment id */ { trx_rseg_t* rseg; rseg = UT_LIST_GET_FIRST(trx_sys->rseg_list); ut_ad(rseg); while (rseg->id != id) { rseg = UT_LIST_GET_NEXT(rseg_list, rseg); ut_ad(rseg); } return(rseg); } /****************************************************************//** Creates a rollback segment header. This function is called only when a new rollback segment is created in the database. @return page number of the created segment, FIL_NULL if fail */ UNIV_INTERN ulint trx_rseg_header_create( /*===================*/ ulint space, /*!< in: space id */ ulint zip_size, /*!< in: compressed page size in bytes or 0 for uncompressed pages */ ulint max_size, /*!< in: max size in pages */ ulint* slot_no, /*!< out: rseg id == slot number in trx sys */ mtr_t* mtr) /*!< in: mtr */ { ulint page_no; trx_rsegf_t* rsegf; trx_sysf_t* sys_header; ulint i; buf_block_t* block; ut_ad(mtr); ut_ad(mutex_own(&kernel_mutex)); ut_ad(mtr_memo_contains(mtr, fil_space_get_latch(space, NULL), MTR_MEMO_X_LOCK)); sys_header = trx_sysf_get(mtr); *slot_no = trx_sysf_rseg_find_free(mtr); if (*slot_no == ULINT_UNDEFINED) { return(FIL_NULL); } /* Allocate a new file segment for the rollback segment */ block = fseg_create(space, 0, TRX_RSEG + TRX_RSEG_FSEG_HEADER, mtr); if (block == NULL) { /* No space left */ return(FIL_NULL); } buf_block_dbg_add_level(block, SYNC_RSEG_HEADER_NEW); page_no = buf_block_get_page_no(block); /* Get the rollback segment file page */ rsegf = trx_rsegf_get_new(space, zip_size, page_no, mtr); /* Initialize max size field */ mlog_write_ulint(rsegf + TRX_RSEG_MAX_SIZE, max_size, MLOG_4BYTES, mtr); /* Initialize the history list */ mlog_write_ulint(rsegf + TRX_RSEG_HISTORY_SIZE, 0, MLOG_4BYTES, mtr); flst_init(rsegf + TRX_RSEG_HISTORY, mtr); /* Reset the undo log slots */ for (i = 0; i < TRX_RSEG_N_SLOTS; i++) { trx_rsegf_set_nth_undo(rsegf, i, FIL_NULL, mtr); } /* Add the rollback segment info to the free slot in the trx system header */ trx_sysf_rseg_set_space(sys_header, *slot_no, space, mtr); trx_sysf_rseg_set_page_no(sys_header, *slot_no, page_no, mtr); return(page_no); } /***********************************************************************//** Free's an instance of the rollback segment in memory. */ UNIV_INTERN void trx_rseg_mem_free( /*==============*/ trx_rseg_t* rseg) /*!< in, own: instance to free */ { trx_undo_t* undo; mutex_free(&rseg->mutex); /* There can't be any active transactions. */ ut_a(UT_LIST_GET_LEN(rseg->update_undo_list) == 0); ut_a(UT_LIST_GET_LEN(rseg->insert_undo_list) == 0); undo = UT_LIST_GET_FIRST(rseg->update_undo_cached); while (undo != NULL) { trx_undo_t* prev_undo = undo; undo = UT_LIST_GET_NEXT(undo_list, undo); UT_LIST_REMOVE(undo_list, rseg->update_undo_cached, prev_undo); trx_undo_mem_free(prev_undo); } undo = UT_LIST_GET_FIRST(rseg->insert_undo_cached); while (undo != NULL) { trx_undo_t* prev_undo = undo; undo = UT_LIST_GET_NEXT(undo_list, undo); UT_LIST_REMOVE(undo_list, rseg->insert_undo_cached, prev_undo); trx_undo_mem_free(prev_undo); } trx_sys_set_nth_rseg(trx_sys, rseg->id, NULL); mem_free(rseg); } /*************************************************************************** Creates and initializes a rollback segment object. The values for the fields are read from the header. The object is inserted to the rseg list of the trx system object and a pointer is inserted in the rseg array in the trx system object. @return own: rollback segment object */ static trx_rseg_t* trx_rseg_mem_create( /*================*/ ib_recovery_t recovery,/*!< in: recovery flag */ ulint id, /*!< in: rollback segment id */ ulint space, /*!< in: space where the segment placed */ ulint zip_size, /*!< in: compressed page size in bytes or 0 for uncompressed pages */ ulint page_no, /*!< in: page number of the segment header */ mtr_t* mtr) /*!< in: mtr */ { trx_rsegf_t* rseg_header; trx_rseg_t* rseg; trx_ulogf_t* undo_log_hdr; fil_addr_t node_addr; ulint sum_of_undo_sizes; ulint len; ut_ad(mutex_own(&kernel_mutex)); rseg = mem_alloc(sizeof(trx_rseg_t)); rseg->id = id; rseg->space = space; rseg->zip_size = zip_size; rseg->page_no = page_no; mutex_create(&rseg->mutex, SYNC_RSEG); UT_LIST_ADD_LAST(rseg_list, trx_sys->rseg_list, rseg); trx_sys_set_nth_rseg(trx_sys, id, rseg); rseg_header = trx_rsegf_get_new(space, zip_size, page_no, mtr); rseg->max_size = mtr_read_ulint(rseg_header + TRX_RSEG_MAX_SIZE, MLOG_4BYTES, mtr); /* Initialize the undo log lists according to the rseg header */ sum_of_undo_sizes = trx_undo_lists_init(recovery, rseg); rseg->curr_size = mtr_read_ulint(rseg_header + TRX_RSEG_HISTORY_SIZE, MLOG_4BYTES, mtr) + 1 + sum_of_undo_sizes; len = flst_get_len(rseg_header + TRX_RSEG_HISTORY, mtr); if (len > 0) { trx_sys->rseg_history_len += len; node_addr = trx_purge_get_log_from_hist( flst_get_last(rseg_header + TRX_RSEG_HISTORY, mtr)); rseg->last_page_no = node_addr.page; rseg->last_offset = node_addr.boffset; undo_log_hdr = trx_undo_page_get(rseg->space, rseg->zip_size, node_addr.page, mtr) + node_addr.boffset; rseg->last_trx_no = mtr_read_dulint( undo_log_hdr + TRX_UNDO_TRX_NO, mtr); rseg->last_del_marks = mtr_read_ulint( undo_log_hdr + TRX_UNDO_DEL_MARKS, MLOG_2BYTES, mtr); } else { rseg->last_page_no = FIL_NULL; } return(rseg); } /*********************************************************************//** Creates the memory copies for rollback segments and initializes the rseg list and array in trx_sys at a database startup. */ UNIV_INTERN void trx_rseg_list_and_array_init( /*=========================*/ ib_recovery_t recovery, /*!< in: recovery flag */ trx_sysf_t* sys_header, /*!< in: trx system header */ mtr_t* mtr) /*!< in: mtr */ { ulint i; ulint page_no; ulint space; UT_LIST_INIT(trx_sys->rseg_list); trx_sys->rseg_history_len = 0; for (i = 0; i < TRX_SYS_N_RSEGS; i++) { page_no = trx_sysf_rseg_get_page_no(sys_header, i, mtr); if (page_no == FIL_NULL) { trx_sys_set_nth_rseg(trx_sys, i, NULL); } else { ulint zip_size; space = trx_sysf_rseg_get_space(sys_header, i, mtr); zip_size = space ? fil_space_get_zip_size(space) : 0; trx_rseg_mem_create( recovery, i, space, zip_size, page_no, mtr); } } } haildb-2.3.2/trx/trx0rec.c0000644000175000017500000013154711513177357016226 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2010, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file trx/trx0rec.c Transaction undo log record Created 3/26/1996 Heikki Tuuri *******************************************************/ #include "trx0rec.h" #ifdef UNIV_NONINL #include "trx0rec.ic" #endif #include "fsp0fsp.h" #include "mach0data.h" #include "trx0undo.h" #include "mtr0log.h" #ifndef UNIV_HOTBACKUP #include "dict0dict.h" #include "ut0mem.h" #include "row0ext.h" #include "row0upd.h" #include "que0que.h" #include "trx0purge.h" #include "trx0rseg.h" #include "row0row.h" /*=========== UNDO LOG RECORD CREATION AND DECODING ====================*/ /**********************************************************************//** Writes the mtr log entry of the inserted undo log record on the undo log page. */ UNIV_INLINE void trx_undof_page_add_undo_rec_log( /*============================*/ page_t* undo_page, /*!< in: undo log page */ ulint old_free, /*!< in: start offset of the inserted entry */ ulint new_free, /*!< in: end offset of the entry */ mtr_t* mtr) /*!< in: mtr */ { byte* log_ptr; const byte* log_end; ulint len; log_ptr = mlog_open(mtr, 11 + 13 + MLOG_BUF_MARGIN); if (log_ptr == NULL) { return; } log_end = &log_ptr[11 + 13 + MLOG_BUF_MARGIN]; log_ptr = mlog_write_initial_log_record_fast( undo_page, MLOG_UNDO_INSERT, log_ptr, mtr); len = new_free - old_free - 4; mach_write_to_2(log_ptr, len); log_ptr += 2; if (log_ptr + len <= log_end) { memcpy(log_ptr, undo_page + old_free + 2, len); mlog_close(mtr, log_ptr + len); } else { mlog_close(mtr, log_ptr); mlog_catenate_string(mtr, undo_page + old_free + 2, len); } } #endif /* !UNIV_HOTBACKUP */ /***********************************************************//** Parses a redo log record of adding an undo log record. @return end of log record or NULL */ UNIV_INTERN byte* trx_undo_parse_add_undo_rec( /*========================*/ byte* ptr, /*!< in: buffer */ byte* end_ptr,/*!< in: buffer end */ page_t* page) /*!< in: page or NULL */ { ulint len; byte* rec; ulint first_free; if (end_ptr < ptr + 2) { return(NULL); } len = mach_read_from_2(ptr); ptr += 2; if (end_ptr < ptr + len) { return(NULL); } if (page == NULL) { return(ptr + len); } first_free = mach_read_from_2(page + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_FREE); rec = page + first_free; mach_write_to_2(rec, first_free + 4 + len); mach_write_to_2(rec + 2 + len, first_free); mach_write_to_2(page + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_FREE, first_free + 4 + len); ut_memcpy(rec + 2, ptr, len); return(ptr + len); } #ifndef UNIV_HOTBACKUP /**********************************************************************//** Calculates the free space left for extending an undo log record. @return bytes left */ UNIV_INLINE ulint trx_undo_left( /*==========*/ const page_t* page, /*!< in: undo log page */ const byte* ptr) /*!< in: pointer to page */ { /* The '- 10' is a safety margin, in case we have some small calculation error below */ return(UNIV_PAGE_SIZE - (ptr - page) - 10 - FIL_PAGE_DATA_END); } /**********************************************************************//** Set the next and previous pointers in the undo page for the undo record that was written to ptr. Update the first free value by the number of bytes written for this undo record. @return offset of the inserted entry on the page if succeeded, 0 if fail */ static ulint trx_undo_page_set_next_prev_and_add( /*================================*/ page_t* undo_page, /*!< in/out: undo log page */ byte* ptr, /*!< in: ptr up to where data has been written on this undo page. */ mtr_t* mtr) /*!< in: mtr */ { ulint first_free; /*!< offset within undo_page */ ulint end_of_rec; /*!< offset within undo_page */ byte* ptr_to_first_free; /* pointer within undo_page that points to the next free offset value within undo_page.*/ ut_ad(ptr > undo_page); ut_ad(ptr < undo_page + UNIV_PAGE_SIZE); if (UNIV_UNLIKELY(trx_undo_left(undo_page, ptr) < 2)) { return(0); } ptr_to_first_free = undo_page + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_FREE; first_free = mach_read_from_2(ptr_to_first_free); /* Write offset of the previous undo log record */ mach_write_to_2(ptr, first_free); ptr += 2; end_of_rec = ptr - undo_page; /* Write offset of the next undo log record */ mach_write_to_2(undo_page + first_free, end_of_rec); /* Update the offset to first free undo record */ mach_write_to_2(ptr_to_first_free, end_of_rec); /* Write this log entry to the UNDO log */ trx_undof_page_add_undo_rec_log(undo_page, first_free, end_of_rec, mtr); return(first_free); } /**********************************************************************//** Reports in the undo log of an insert of a clustered index record. @return offset of the inserted entry on the page if succeed, 0 if fail */ static ulint trx_undo_page_report_insert( /*========================*/ page_t* undo_page, /*!< in: undo log page */ trx_t* trx, /*!< in: transaction */ dict_index_t* index, /*!< in: clustered index */ const dtuple_t* clust_entry, /*!< in: index entry which will be inserted to the clustered index */ mtr_t* mtr) /*!< in: mtr */ { ulint first_free; byte* ptr; ulint i; ut_ad(dict_index_is_clust(index)); ut_ad(mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_TYPE) == TRX_UNDO_INSERT); first_free = mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_FREE); ptr = undo_page + first_free; ut_ad(first_free <= UNIV_PAGE_SIZE); if (trx_undo_left(undo_page, ptr) < 2 + 1 + 11 + 11) { /* Not enough space for writing the general parameters */ return(0); } /* Reserve 2 bytes for the pointer to the next undo log record */ ptr += 2; /* Store first some general parameters to the undo log */ *ptr++ = TRX_UNDO_INSERT_REC; ptr += mach_dulint_write_much_compressed(ptr, trx->undo_no); ptr += mach_dulint_write_much_compressed(ptr, index->table->id); /*----------------------------------------*/ /* Store then the fields required to uniquely determine the record to be inserted in the clustered index */ for (i = 0; i < dict_index_get_n_unique(index); i++) { const dfield_t* field = dtuple_get_nth_field(clust_entry, i); ulint flen = dfield_get_len(field); if (trx_undo_left(undo_page, ptr) < 5) { return(0); } ptr += mach_write_compressed(ptr, flen); if (flen != UNIV_SQL_NULL) { if (trx_undo_left(undo_page, ptr) < flen) { return(0); } ut_memcpy(ptr, dfield_get_data(field), flen); ptr += flen; } } return(trx_undo_page_set_next_prev_and_add(undo_page, ptr, mtr)); } /**********************************************************************//** Reads from an undo log record the general parameters. @return remaining part of undo log record after reading these values */ UNIV_INTERN byte* trx_undo_rec_get_pars( /*==================*/ trx_undo_rec_t* undo_rec, /*!< in: undo log record */ ulint* type, /*!< out: undo record type: TRX_UNDO_INSERT_REC, ... */ ulint* cmpl_info, /*!< out: compiler info, relevant only for update type records */ ibool* updated_extern, /*!< out: TRUE if we updated an externally stored fild */ undo_no_t* undo_no, /*!< out: undo log record number */ dulint* table_id) /*!< out: table id */ { byte* ptr; ulint type_cmpl; ptr = undo_rec + 2; type_cmpl = mach_read_from_1(ptr); ptr++; if (type_cmpl & TRX_UNDO_UPD_EXTERN) { *updated_extern = TRUE; type_cmpl -= TRX_UNDO_UPD_EXTERN; } else { *updated_extern = FALSE; } *type = type_cmpl & (TRX_UNDO_CMPL_INFO_MULT - 1); *cmpl_info = type_cmpl / TRX_UNDO_CMPL_INFO_MULT; *undo_no = mach_dulint_read_much_compressed(ptr); ptr += mach_dulint_get_much_compressed_size(*undo_no); *table_id = mach_dulint_read_much_compressed(ptr); ptr += mach_dulint_get_much_compressed_size(*table_id); return(ptr); } /**********************************************************************//** Reads from an undo log record a stored column value. @return remaining part of undo log record after reading these values */ static byte* trx_undo_rec_get_col_val( /*=====================*/ byte* ptr, /*!< in: pointer to remaining part of undo log record */ byte** field, /*!< out: pointer to stored field */ ulint* len, /*!< out: length of the field, or UNIV_SQL_NULL */ ulint* orig_len)/*!< out: original length of the locally stored part of an externally stored column, or 0 */ { *len = mach_read_compressed(ptr); ptr += mach_get_compressed_size(*len); *orig_len = 0; switch (*len) { case UNIV_SQL_NULL: *field = NULL; break; case UNIV_EXTERN_STORAGE_FIELD: *orig_len = mach_read_compressed(ptr); ptr += mach_get_compressed_size(*orig_len); *len = mach_read_compressed(ptr); ptr += mach_get_compressed_size(*len); *field = ptr; ptr += *len; ut_ad(*orig_len >= BTR_EXTERN_FIELD_REF_SIZE); ut_ad(*len > *orig_len); /* @see dtuple_convert_big_rec() */ ut_ad(*len >= BTR_EXTERN_FIELD_REF_SIZE * 2); /* we do not have access to index->table here ut_ad(dict_table_get_format(index->table) >= DICT_TF_FORMAT_ZIP || *len >= REC_MAX_INDEX_COL_LEN + BTR_EXTERN_FIELD_REF_SIZE); */ *len += UNIV_EXTERN_STORAGE_FIELD; break; default: *field = ptr; if (*len >= UNIV_EXTERN_STORAGE_FIELD) { ptr += *len - UNIV_EXTERN_STORAGE_FIELD; } else { ptr += *len; } } return(ptr); } /*******************************************************************//** Builds a row reference from an undo log record. @return pointer to remaining part of undo record */ UNIV_INTERN byte* trx_undo_rec_get_row_ref( /*=====================*/ byte* ptr, /*!< in: remaining part of a copy of an undo log record, at the start of the row reference; NOTE that this copy of the undo log record must be preserved as long as the row reference is used, as we do NOT copy the data in the record! */ dict_index_t* index, /*!< in: clustered index */ dtuple_t** ref, /*!< out, own: row reference */ mem_heap_t* heap) /*!< in: memory heap from which the memory needed is allocated */ { ulint ref_len; ulint i; ut_ad(index && ptr && ref && heap); ut_a(dict_index_is_clust(index)); ref_len = dict_index_get_n_unique(index); *ref = dtuple_create(heap, ref_len); dict_index_copy_types(*ref, index, ref_len); for (i = 0; i < ref_len; i++) { dfield_t* dfield; byte* field; ulint len; ulint orig_len; dfield = dtuple_get_nth_field(*ref, i); ptr = trx_undo_rec_get_col_val(ptr, &field, &len, &orig_len); dfield_set_data(dfield, field, len); } return(ptr); } /*******************************************************************//** Skips a row reference from an undo log record. @return pointer to remaining part of undo record */ UNIV_INTERN byte* trx_undo_rec_skip_row_ref( /*======================*/ byte* ptr, /*!< in: remaining part in update undo log record, at the start of the row reference */ dict_index_t* index) /*!< in: clustered index */ { ulint ref_len; ulint i; ut_ad(index && ptr); ut_a(dict_index_is_clust(index)); ref_len = dict_index_get_n_unique(index); for (i = 0; i < ref_len; i++) { byte* field; ulint len; ulint orig_len; ptr = trx_undo_rec_get_col_val(ptr, &field, &len, &orig_len); } return(ptr); } /**********************************************************************//** Fetch a prefix of an externally stored column, for writing to the undo log of an update or delete marking of a clustered index record. @return ext_buf */ static byte* trx_undo_page_fetch_ext( /*====================*/ byte* ext_buf, /*!< in: a buffer of REC_MAX_INDEX_COL_LEN + BTR_EXTERN_FIELD_REF_SIZE */ ulint zip_size, /*!< compressed page size in bytes, or 0 for uncompressed BLOB */ const byte* field, /*!< in: an externally stored column */ ulint* len) /*!< in: length of field; out: used length of ext_buf */ { /* Fetch the BLOB. */ ulint ext_len = btr_copy_externally_stored_field_prefix( ext_buf, REC_MAX_INDEX_COL_LEN, zip_size, field, *len); /* BLOBs should always be nonempty. */ ut_a(ext_len); /* Append the BLOB pointer to the prefix. */ memcpy(ext_buf + ext_len, field + *len - BTR_EXTERN_FIELD_REF_SIZE, BTR_EXTERN_FIELD_REF_SIZE); *len = ext_len + BTR_EXTERN_FIELD_REF_SIZE; return(ext_buf); } /**********************************************************************//** Writes to the undo log a prefix of an externally stored column. @return undo log position */ static byte* trx_undo_page_report_modify_ext( /*============================*/ byte* ptr, /*!< in: undo log position, at least 15 bytes must be available */ byte* ext_buf, /*!< in: a buffer of REC_MAX_INDEX_COL_LEN + BTR_EXTERN_FIELD_REF_SIZE, or NULL when should not fetch a longer prefix */ ulint zip_size, /*!< compressed page size in bytes, or 0 for uncompressed BLOB */ const byte** field, /*!< in/out: the locally stored part of the externally stored column */ ulint* len) /*!< in/out: length of field, in bytes */ { if (ext_buf) { /* If an ordering column is externally stored, we will have to store a longer prefix of the field. In this case, write to the log a marker followed by the original length and the real length of the field. */ ptr += mach_write_compressed(ptr, UNIV_EXTERN_STORAGE_FIELD); ptr += mach_write_compressed(ptr, *len); *field = trx_undo_page_fetch_ext(ext_buf, zip_size, *field, len); ptr += mach_write_compressed(ptr, *len); } else { ptr += mach_write_compressed(ptr, UNIV_EXTERN_STORAGE_FIELD + *len); } return(ptr); } /**********************************************************************//** Reports in the undo log of an update or delete marking of a clustered index record. @return byte offset of the inserted undo log entry on the page if succeed, 0 if fail */ static ulint trx_undo_page_report_modify( /*========================*/ page_t* undo_page, /*!< in: undo log page */ trx_t* trx, /*!< in: transaction */ dict_index_t* index, /*!< in: clustered index where update or delete marking is done */ const rec_t* rec, /*!< in: clustered index record which has NOT yet been modified */ const ulint* offsets, /*!< in: rec_get_offsets(rec, index) */ const upd_t* update, /*!< in: update vector which tells the columns to be updated; in the case of a delete, this should be set to NULL */ ulint cmpl_info, /*!< in: compiler info on secondary index updates */ mtr_t* mtr) /*!< in: mtr */ { dict_table_t* table; ulint first_free; byte* ptr; const byte* field; ulint flen; ulint col_no; ulint type_cmpl; byte* type_cmpl_ptr; ulint i; trx_id_t trx_id; ibool ignore_prefix = FALSE; byte ext_buf[REC_MAX_INDEX_COL_LEN + BTR_EXTERN_FIELD_REF_SIZE]; ut_a(dict_index_is_clust(index)); ut_ad(rec_offs_validate(rec, index, offsets)); ut_ad(mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_TYPE) == TRX_UNDO_UPDATE); table = index->table; first_free = mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_FREE); ptr = undo_page + first_free; ut_ad(first_free <= UNIV_PAGE_SIZE); if (trx_undo_left(undo_page, ptr) < 50) { /* NOTE: the value 50 must be big enough so that the general fields written below fit on the undo log page */ return(0); } /* Reserve 2 bytes for the pointer to the next undo log record */ ptr += 2; /* Store first some general parameters to the undo log */ if (!update) { type_cmpl = TRX_UNDO_DEL_MARK_REC; } else if (rec_get_deleted_flag(rec, dict_table_is_comp(table))) { type_cmpl = TRX_UNDO_UPD_DEL_REC; /* We are about to update a delete marked record. We don't typically need the prefix in this case unless the delete marking is done by the same transaction (which we check below). */ ignore_prefix = TRUE; } else { type_cmpl = TRX_UNDO_UPD_EXIST_REC; } type_cmpl |= cmpl_info * TRX_UNDO_CMPL_INFO_MULT; type_cmpl_ptr = ptr; *ptr++ = (byte) type_cmpl; ptr += mach_dulint_write_much_compressed(ptr, trx->undo_no); ptr += mach_dulint_write_much_compressed(ptr, table->id); /*----------------------------------------*/ /* Store the state of the info bits */ *ptr++ = (byte) rec_get_info_bits(rec, dict_table_is_comp(table)); /* Store the values of the system columns */ field = rec_get_nth_field(rec, offsets, dict_index_get_sys_col_pos( index, DATA_TRX_ID), &flen); ut_ad(flen == DATA_TRX_ID_LEN); trx_id = trx_read_trx_id(field); /* If it is an update of a delete marked record, then we are allowed to ignore blob prefixes if the delete marking was done by some other trx as it must have committed by now for us to allow an over-write. */ if (ignore_prefix) { ignore_prefix = ut_dulint_cmp(trx_id, trx->id) != 0; } ptr += mach_dulint_write_compressed(ptr, trx_id); field = rec_get_nth_field(rec, offsets, dict_index_get_sys_col_pos( index, DATA_ROLL_PTR), &flen); ut_ad(flen == DATA_ROLL_PTR_LEN); ptr += mach_dulint_write_compressed(ptr, trx_read_roll_ptr(field)); /*----------------------------------------*/ /* Store then the fields required to uniquely determine the record which will be modified in the clustered index */ for (i = 0; i < dict_index_get_n_unique(index); i++) { field = rec_get_nth_field(rec, offsets, i, &flen); /* The ordering columns must not be stored externally. */ ut_ad(!rec_offs_nth_extern(offsets, i)); ut_ad(dict_index_get_nth_col(index, i)->ord_part); if (trx_undo_left(undo_page, ptr) < 5) { return(0); } ptr += mach_write_compressed(ptr, flen); if (flen != UNIV_SQL_NULL) { if (trx_undo_left(undo_page, ptr) < flen) { return(0); } ut_memcpy(ptr, field, flen); ptr += flen; } } /*----------------------------------------*/ /* Save to the undo log the old values of the columns to be updated. */ if (update) { if (trx_undo_left(undo_page, ptr) < 5) { return(0); } ptr += mach_write_compressed(ptr, upd_get_n_fields(update)); for (i = 0; i < upd_get_n_fields(update); i++) { ulint pos = upd_get_nth_field(update, i)->field_no; /* Write field number to undo log */ if (trx_undo_left(undo_page, ptr) < 5) { return(0); } ptr += mach_write_compressed(ptr, pos); /* Save the old value of field */ field = rec_get_nth_field(rec, offsets, pos, &flen); if (trx_undo_left(undo_page, ptr) < 15) { return(0); } if (rec_offs_nth_extern(offsets, pos)) { ptr = trx_undo_page_report_modify_ext( ptr, dict_index_get_nth_col(index, pos) ->ord_part && !ignore_prefix && flen < REC_MAX_INDEX_COL_LEN ? ext_buf : NULL, dict_table_zip_size(table), &field, &flen); /* Notify purge that it eventually has to free the old externally stored field */ trx->update_undo->del_marks = TRUE; *type_cmpl_ptr |= TRX_UNDO_UPD_EXTERN; } else { ptr += mach_write_compressed(ptr, flen); } if (flen != UNIV_SQL_NULL) { if (trx_undo_left(undo_page, ptr) < flen) { return(0); } ut_memcpy(ptr, field, flen); ptr += flen; } } } /*----------------------------------------*/ /* In the case of a delete marking, and also in the case of an update where any ordering field of any index changes, store the values of all columns which occur as ordering fields in any index. This info is used in the purge of old versions where we use it to build and search the delete marked index records, to look if we can remove them from the index tree. Note that starting from 4.0.14 also externally stored fields can be ordering in some index. Starting from 5.2, we no longer store REC_MAX_INDEX_COL_LEN first bytes to the undo log record, but we can construct the column prefix fields in the index by fetching the first page of the BLOB that is pointed to by the clustered index. This works also in crash recovery, because all pages (including BLOBs) are recovered before anything is rolled back. */ if (!update || !(cmpl_info & UPD_NODE_NO_ORD_CHANGE)) { byte* old_ptr = ptr; trx->update_undo->del_marks = TRUE; if (trx_undo_left(undo_page, ptr) < 5) { return(0); } /* Reserve 2 bytes to write the number of bytes the stored fields take in this undo record */ ptr += 2; for (col_no = 0; col_no < dict_table_get_n_cols(table); col_no++) { const dict_col_t* col = dict_table_get_nth_col(table, col_no); if (col->ord_part) { ulint pos; /* Write field number to undo log */ if (trx_undo_left(undo_page, ptr) < 5 + 15) { return(0); } pos = dict_index_get_nth_col_pos(index, col_no); ptr += mach_write_compressed(ptr, pos); /* Save the old value of field */ field = rec_get_nth_field(rec, offsets, pos, &flen); if (rec_offs_nth_extern(offsets, pos)) { ptr = trx_undo_page_report_modify_ext( ptr, flen < REC_MAX_INDEX_COL_LEN && !ignore_prefix ? ext_buf : NULL, dict_table_zip_size(table), &field, &flen); } else { ptr += mach_write_compressed( ptr, flen); } if (flen != UNIV_SQL_NULL) { if (trx_undo_left(undo_page, ptr) < flen) { return(0); } ut_memcpy(ptr, field, flen); ptr += flen; } } } mach_write_to_2(old_ptr, ptr - old_ptr); } /*----------------------------------------*/ /* Write pointers to the previous and the next undo log records */ if (trx_undo_left(undo_page, ptr) < 2) { return(0); } mach_write_to_2(ptr, first_free); ptr += 2; mach_write_to_2(undo_page + first_free, ptr - undo_page); mach_write_to_2(undo_page + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_FREE, ptr - undo_page); /* Write to the REDO log about this change in the UNDO log */ trx_undof_page_add_undo_rec_log(undo_page, first_free, ptr - undo_page, mtr); return(first_free); } /**********************************************************************//** Reads from an undo log update record the system field values of the old version. @return remaining part of undo log record after reading these values */ UNIV_INTERN byte* trx_undo_update_rec_get_sys_cols( /*=============================*/ byte* ptr, /*!< in: remaining part of undo log record after reading general parameters */ trx_id_t* trx_id, /*!< out: trx id */ roll_ptr_t* roll_ptr, /*!< out: roll ptr */ ulint* info_bits) /*!< out: info bits state */ { /* Read the state of the info bits */ *info_bits = mach_read_from_1(ptr); ptr += 1; /* Read the values of the system columns */ *trx_id = mach_dulint_read_compressed(ptr); ptr += mach_dulint_get_compressed_size(*trx_id); *roll_ptr = mach_dulint_read_compressed(ptr); ptr += mach_dulint_get_compressed_size(*roll_ptr); return(ptr); } /**********************************************************************//** Reads from an update undo log record the number of updated fields. @return remaining part of undo log record after reading this value */ UNIV_INLINE byte* trx_undo_update_rec_get_n_upd_fields( /*=================================*/ byte* ptr, /*!< in: pointer to remaining part of undo log record */ ulint* n) /*!< out: number of fields */ { *n = mach_read_compressed(ptr); ptr += mach_get_compressed_size(*n); return(ptr); } /**********************************************************************//** Reads from an update undo log record a stored field number. @return remaining part of undo log record after reading this value */ UNIV_INLINE byte* trx_undo_update_rec_get_field_no( /*=============================*/ byte* ptr, /*!< in: pointer to remaining part of undo log record */ ulint* field_no)/*!< out: field number */ { *field_no = mach_read_compressed(ptr); ptr += mach_get_compressed_size(*field_no); return(ptr); } /*******************************************************************//** Builds an update vector based on a remaining part of an undo log record. @return remaining part of the record, NULL if an error detected, which means that the record is corrupted */ UNIV_INTERN byte* trx_undo_update_rec_get_update( /*===========================*/ byte* ptr, /*!< in: remaining part in update undo log record, after reading the row reference NOTE that this copy of the undo log record must be preserved as long as the update vector is used, as we do NOT copy the data in the record! */ dict_index_t* index, /*!< in: clustered index */ ulint type, /*!< in: TRX_UNDO_UPD_EXIST_REC, TRX_UNDO_UPD_DEL_REC, or TRX_UNDO_DEL_MARK_REC; in the last case, only trx id and roll ptr fields are added to the update vector */ trx_id_t trx_id, /*!< in: transaction id from this undo record */ roll_ptr_t roll_ptr,/*!< in: roll pointer from this undo record */ ulint info_bits,/*!< in: info bits from this undo record */ trx_t* trx, /*!< in: transaction */ mem_heap_t* heap, /*!< in: memory heap from which the memory needed is allocated */ upd_t** upd) /*!< out, own: update vector */ { upd_field_t* upd_field; upd_t* update; ulint n_fields; byte* buf; ulint i; ut_a(dict_index_is_clust(index)); if (type != TRX_UNDO_DEL_MARK_REC) { ptr = trx_undo_update_rec_get_n_upd_fields(ptr, &n_fields); } else { n_fields = 0; } update = upd_create(n_fields + 2, heap); update->info_bits = info_bits; /* Store first trx id and roll ptr to update vector */ upd_field = upd_get_nth_field(update, n_fields); buf = mem_heap_alloc(heap, DATA_TRX_ID_LEN); trx_write_trx_id(buf, trx_id); upd_field_set_field_no(upd_field, dict_index_get_sys_col_pos(index, DATA_TRX_ID), index, trx); dfield_set_data(&(upd_field->new_val), buf, DATA_TRX_ID_LEN); upd_field = upd_get_nth_field(update, n_fields + 1); buf = mem_heap_alloc(heap, DATA_ROLL_PTR_LEN); trx_write_roll_ptr(buf, roll_ptr); upd_field_set_field_no( upd_field, dict_index_get_sys_col_pos(index, DATA_ROLL_PTR), index, trx); dfield_set_data(&(upd_field->new_val), buf, DATA_ROLL_PTR_LEN); /* Store then the updated ordinary columns to the update vector */ for (i = 0; i < n_fields; i++) { byte* field; ulint len; ulint field_no; ulint orig_len; ptr = trx_undo_update_rec_get_field_no(ptr, &field_no); if (field_no >= dict_index_get_n_fields(index)) { ib_logger(ib_stream, "InnoDB: Error: trying to access" " update undo rec field %lu in ", (ulong) field_no); dict_index_name_print(ib_stream, trx, index); ib_logger(ib_stream, "\n" "InnoDB: but index has only %lu fields\n" "InnoDB: Submit a detailed bug report, " "check the InnoDB website for details\n" "InnoDB: Run also CHECK TABLE ", (ulong) dict_index_get_n_fields(index)); ut_print_name(ib_stream, trx, TRUE, index->table_name); ib_logger(ib_stream, "\n" "InnoDB: n_fields = %lu, i = %lu, ptr %p\n", (ulong) n_fields, (ulong) i, ptr); return(NULL); } upd_field = upd_get_nth_field(update, i); upd_field_set_field_no(upd_field, field_no, index, trx); ptr = trx_undo_rec_get_col_val(ptr, &field, &len, &orig_len); upd_field->orig_len = orig_len; if (len == UNIV_SQL_NULL) { dfield_set_null(&upd_field->new_val); } else if (len < UNIV_EXTERN_STORAGE_FIELD) { dfield_set_data(&upd_field->new_val, field, len); } else { len -= UNIV_EXTERN_STORAGE_FIELD; dfield_set_data(&upd_field->new_val, field, len); dfield_set_ext(&upd_field->new_val); } } *upd = update; return(ptr); } /*******************************************************************//** Builds a partial row from an update undo log record. It contains the columns which occur as ordering in any index of the table. @return pointer to remaining part of undo record */ UNIV_INTERN byte* trx_undo_rec_get_partial_row( /*=========================*/ byte* ptr, /*!< in: remaining part in update undo log record of a suitable type, at the start of the stored index columns; NOTE that this copy of the undo log record must be preserved as long as the partial row is used, as we do NOT copy the data in the record! */ dict_index_t* index, /*!< in: clustered index */ dtuple_t** row, /*!< out, own: partial row */ ibool ignore_prefix, /*!< in: flag to indicate if we expect blob prefixes in undo. Used only in the assertion. */ mem_heap_t* heap) /*!< in: memory heap from which the memory needed is allocated */ { const byte* end_ptr; ulint row_len; ut_ad(index); ut_ad(ptr); ut_ad(row); ut_ad(heap); ut_ad(dict_index_is_clust(index)); row_len = dict_table_get_n_cols(index->table); *row = dtuple_create(heap, row_len); dict_table_copy_types(*row, index->table); end_ptr = ptr + mach_read_from_2(ptr); ptr += 2; while (ptr != end_ptr) { dfield_t* dfield; byte* field; ulint field_no; const dict_col_t* col; ulint col_no; ulint len; ulint orig_len; ptr = trx_undo_update_rec_get_field_no(ptr, &field_no); col = dict_index_get_nth_col(index, field_no); col_no = dict_col_get_no(col); ptr = trx_undo_rec_get_col_val(ptr, &field, &len, &orig_len); dfield = dtuple_get_nth_field(*row, col_no); dfield_set_data(dfield, field, len); if (len != UNIV_SQL_NULL && len >= UNIV_EXTERN_STORAGE_FIELD) { dfield_set_len(dfield, len - UNIV_EXTERN_STORAGE_FIELD); dfield_set_ext(dfield); /* If the prefix of this column is indexed, ensure that enough prefix is stored in the undo log record. */ if (!ignore_prefix && col->ord_part) { ut_a(dfield_get_len(dfield) >= 2 * BTR_EXTERN_FIELD_REF_SIZE); ut_a(dict_table_get_format(index->table) >= DICT_TF_FORMAT_ZIP || dfield_get_len(dfield) >= REC_MAX_INDEX_COL_LEN + BTR_EXTERN_FIELD_REF_SIZE); } } } return(ptr); } #endif /* !UNIV_HOTBACKUP */ /***********************************************************************//** Erases the unused undo log page end. */ static void trx_undo_erase_page_end( /*====================*/ page_t* undo_page, /*!< in: undo page whose end to erase */ mtr_t* mtr) /*!< in: mtr */ { ulint first_free; first_free = mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_FREE); memset(undo_page + first_free, 0xff, (UNIV_PAGE_SIZE - FIL_PAGE_DATA_END) - first_free); mlog_write_initial_log_record(undo_page, MLOG_UNDO_ERASE_END, mtr); } /***********************************************************//** Parses a redo log record of erasing of an undo page end. @return end of log record or NULL */ UNIV_INTERN byte* trx_undo_parse_erase_page_end( /*==========================*/ byte* ptr, /*!< in: buffer */ byte* end_ptr __attribute__((unused)), /*!< in: buffer end */ page_t* page, /*!< in: page or NULL */ mtr_t* mtr) /*!< in: mtr or NULL */ { ut_ad(ptr && end_ptr); if (page == NULL) { return(ptr); } trx_undo_erase_page_end(page, mtr); return(ptr); } #ifndef UNIV_HOTBACKUP /***********************************************************************//** Writes information to an undo log about an insert, update, or a delete marking of a clustered index record. This information is used in a rollback of the transaction and in consistent reads that must look to the history of this transaction. @return DB_SUCCESS or error code */ UNIV_INTERN ulint trx_undo_report_row_operation( /*==========================*/ ulint flags, /*!< in: if BTR_NO_UNDO_LOG_FLAG bit is set, does nothing */ ulint op_type, /*!< in: TRX_UNDO_INSERT_OP or TRX_UNDO_MODIFY_OP */ que_thr_t* thr, /*!< in: query thread */ dict_index_t* index, /*!< in: clustered index */ const dtuple_t* clust_entry, /*!< in: in the case of an insert, index entry to insert into the clustered index, otherwise NULL */ const upd_t* update, /*!< in: in the case of an update, the update vector, otherwise NULL */ ulint cmpl_info, /*!< in: compiler info on secondary index updates */ const rec_t* rec, /*!< in: in case of an update or delete marking, the record in the clustered index, otherwise NULL */ roll_ptr_t* roll_ptr) /*!< out: rollback pointer to the inserted undo log record, ut_dulint_zero if BTR_NO_UNDO_LOG flag was specified */ { trx_t* trx; trx_undo_t* undo; ulint page_no; trx_rseg_t* rseg; mtr_t mtr; ulint err = DB_SUCCESS; mem_heap_t* heap = NULL; ulint offsets_[REC_OFFS_NORMAL_SIZE]; ulint* offsets = offsets_; rec_offs_init(offsets_); ut_a(dict_index_is_clust(index)); if (flags & BTR_NO_UNDO_LOG_FLAG) { *roll_ptr = ut_dulint_zero; return(DB_SUCCESS); } ut_ad(thr); ut_ad((op_type != TRX_UNDO_INSERT_OP) || (clust_entry && !update && !rec)); trx = thr_get_trx(thr); rseg = trx->rseg; mutex_enter(&(trx->undo_mutex)); /* If the undo log is not assigned yet, assign one */ if (op_type == TRX_UNDO_INSERT_OP) { if (trx->insert_undo == NULL) { err = trx_undo_assign_undo(trx, TRX_UNDO_INSERT); } undo = trx->insert_undo; if (UNIV_UNLIKELY(!undo)) { /* Did not succeed */ mutex_exit(&(trx->undo_mutex)); return(err); } } else { ut_ad(op_type == TRX_UNDO_MODIFY_OP); if (trx->update_undo == NULL) { err = trx_undo_assign_undo(trx, TRX_UNDO_UPDATE); } undo = trx->update_undo; if (UNIV_UNLIKELY(!undo)) { /* Did not succeed */ mutex_exit(&(trx->undo_mutex)); return(err); } offsets = rec_get_offsets(rec, index, offsets, ULINT_UNDEFINED, &heap); } page_no = undo->last_page_no; mtr_start(&mtr); for (;;) { buf_block_t* undo_block; page_t* undo_page; ulint offset; undo_block = buf_page_get_gen(undo->space, undo->zip_size, page_no, RW_X_LATCH, undo->guess_block, BUF_GET, __FILE__, __LINE__, &mtr); buf_block_dbg_add_level(undo_block, SYNC_TRX_UNDO_PAGE); undo_page = buf_block_get_frame(undo_block); if (op_type == TRX_UNDO_INSERT_OP) { offset = trx_undo_page_report_insert( undo_page, trx, index, clust_entry, &mtr); } else { offset = trx_undo_page_report_modify( undo_page, trx, index, rec, offsets, update, cmpl_info, &mtr); } if (UNIV_UNLIKELY(offset == 0)) { /* The record did not fit on the page. We erase the end segment of the undo log page and write a log record of it: this is to ensure that in the debug version the replicate page constructed using the log records stays identical to the original page */ trx_undo_erase_page_end(undo_page, &mtr); mtr_commit(&mtr); } else { /* Success */ mtr_commit(&mtr); undo->empty = FALSE; undo->top_page_no = page_no; undo->top_offset = offset; undo->top_undo_no = trx->undo_no; undo->guess_block = undo_block; UT_DULINT_INC(trx->undo_no); mutex_exit(&trx->undo_mutex); *roll_ptr = trx_undo_build_roll_ptr( op_type == TRX_UNDO_INSERT_OP, rseg->id, page_no, offset); if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } return(DB_SUCCESS); } ut_ad(page_no == undo->last_page_no); /* We have to extend the undo log by one page */ mtr_start(&mtr); /* When we add a page to an undo log, this is analogous to a pessimistic insert in a B-tree, and we must reserve the counterpart of the tree latch, which is the rseg mutex. */ mutex_enter(&(rseg->mutex)); page_no = trx_undo_add_page(trx, undo, &mtr); mutex_exit(&(rseg->mutex)); if (UNIV_UNLIKELY(page_no == FIL_NULL)) { /* Did not succeed: out of space */ mutex_exit(&(trx->undo_mutex)); mtr_commit(&mtr); if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } return(DB_OUT_OF_FILE_SPACE); } } } /*============== BUILDING PREVIOUS VERSION OF A RECORD ===============*/ /******************************************************************//** Copies an undo record to heap. This function can be called if we know that the undo log record exists. @return own: copy of the record */ UNIV_INTERN trx_undo_rec_t* trx_undo_get_undo_rec_low( /*======================*/ roll_ptr_t roll_ptr, /*!< in: roll pointer to record */ mem_heap_t* heap) /*!< in: memory heap where copied */ { trx_undo_rec_t* undo_rec; ulint rseg_id; ulint page_no; ulint offset; const page_t* undo_page; trx_rseg_t* rseg; ibool is_insert; mtr_t mtr; trx_undo_decode_roll_ptr(roll_ptr, &is_insert, &rseg_id, &page_no, &offset); rseg = trx_rseg_get_on_id(rseg_id); mtr_start(&mtr); undo_page = trx_undo_page_get_s_latched(rseg->space, rseg->zip_size, page_no, &mtr); undo_rec = trx_undo_rec_copy(undo_page + offset, heap); mtr_commit(&mtr); return(undo_rec); } /******************************************************************//** Copies an undo record to heap. NOTE: the caller must have latches on the clustered index page and purge_view. @return DB_SUCCESS, or DB_MISSING_HISTORY if the undo log has been truncated and we cannot fetch the old version */ UNIV_INTERN ulint trx_undo_get_undo_rec( /*==================*/ roll_ptr_t roll_ptr, /*!< in: roll pointer to record */ trx_id_t trx_id, /*!< in: id of the trx that generated the roll pointer: it points to an undo log of this transaction */ trx_undo_rec_t** undo_rec, /*!< out, own: copy of the record */ mem_heap_t* heap) /*!< in: memory heap where copied */ { #ifdef UNIV_SYNC_DEBUG ut_ad(rw_lock_own(&(purge_sys->latch), RW_LOCK_SHARED)); #endif /* UNIV_SYNC_DEBUG */ if (!trx_purge_update_undo_must_exist(trx_id)) { /* It may be that the necessary undo log has already been deleted */ return(DB_MISSING_HISTORY); } *undo_rec = trx_undo_get_undo_rec_low(roll_ptr, heap); return(DB_SUCCESS); } /*******************************************************************//** Build a previous version of a clustered index record. This function checks that the caller has a latch on the index page of the clustered index record and an s-latch on the purge_view. This guarantees that the stack of versions is locked all the way down to the purge_view. @return DB_SUCCESS, or DB_MISSING_HISTORY if the previous version is earlier than purge_view, which means that it may have been removed, DB_ERROR if corrupted record */ UNIV_INTERN ulint trx_undo_prev_version_build( /*========================*/ const rec_t* index_rec,/*!< in: clustered index record in the index tree */ mtr_t* index_mtr __attribute__((unused)), /*!< in: mtr which contains the latch to index_rec page and purge_view */ const rec_t* rec, /*!< in: version of a clustered index record */ dict_index_t* index, /*!< in: clustered index */ ulint* offsets,/*!< in: rec_get_offsets(rec, index) */ mem_heap_t* heap, /*!< in: memory heap from which the memory needed is allocated */ rec_t** old_vers)/*!< out, own: previous version, or NULL if rec is the first inserted version, or if history data has been deleted (an error), or if the purge COULD have removed the version though it has not yet done so */ { trx_undo_rec_t* undo_rec = NULL; dtuple_t* entry; trx_id_t rec_trx_id; ulint type; undo_no_t undo_no; dulint table_id; trx_id_t trx_id; roll_ptr_t roll_ptr; roll_ptr_t old_roll_ptr; upd_t* update; byte* ptr; ulint info_bits; ulint cmpl_info; ibool dummy_extern; byte* buf; ulint err; #ifdef UNIV_SYNC_DEBUG ut_ad(rw_lock_own(&(purge_sys->latch), RW_LOCK_SHARED)); #endif /* UNIV_SYNC_DEBUG */ ut_ad(mtr_memo_contains_page(index_mtr, index_rec, MTR_MEMO_PAGE_S_FIX) || mtr_memo_contains_page(index_mtr, index_rec, MTR_MEMO_PAGE_X_FIX)); ut_ad(rec_offs_validate(rec, index, offsets)); if (!dict_index_is_clust(index)) { ib_logger(ib_stream, "InnoDB: Error: trying to access" " update undo rec for non-clustered index %s\n" "InnoDB: Submit a detailed bug report, " "check the InnoDB website for details\n" "InnoDB: index record ", index->name); rec_print(ib_stream, index_rec, index); ib_logger(ib_stream, "\nInnoDB: record version "); rec_print_new(ib_stream, rec, offsets); ib_logger(ib_stream, "\n"); return(DB_ERROR); } roll_ptr = row_get_rec_roll_ptr(rec, index, offsets); old_roll_ptr = roll_ptr; *old_vers = NULL; if (trx_undo_roll_ptr_is_insert(roll_ptr)) { /* The record rec is the first inserted version */ return(DB_SUCCESS); } rec_trx_id = row_get_rec_trx_id(rec, index, offsets); err = trx_undo_get_undo_rec(roll_ptr, rec_trx_id, &undo_rec, heap); if (UNIV_UNLIKELY(err != DB_SUCCESS)) { /* The undo record may already have been purged. This should never happen in InnoDB. */ return(err); } ptr = trx_undo_rec_get_pars(undo_rec, &type, &cmpl_info, &dummy_extern, &undo_no, &table_id); ptr = trx_undo_update_rec_get_sys_cols(ptr, &trx_id, &roll_ptr, &info_bits); /* (a) If a clustered index record version is such that the trx id stamp in it is bigger than purge_sys->view, then the BLOBs in that version are known to exist (the purge has not progressed that far); (b) if the version is the first version such that trx id in it is less than purge_sys->view, and it is not delete-marked, then the BLOBs in that version are known to exist (the purge cannot have purged the BLOBs referenced by that version yet). This function does not fetch any BLOBs. The callers might, by possibly invoking row_ext_create() via row_build(). However, they should have all needed information in the *old_vers returned by this function. This is because *old_vers is based on the transaction undo log records. The function trx_undo_page_fetch_ext() will write BLOB prefixes to the transaction undo log that are at least as long as the longest possible column prefix in a secondary index. Thus, secondary index entries for *old_vers can be constructed without dereferencing any BLOB pointers. */ ptr = trx_undo_rec_skip_row_ref(ptr, index); ptr = trx_undo_update_rec_get_update(ptr, index, type, trx_id, roll_ptr, info_bits, NULL, heap, &update); if (ut_dulint_cmp(table_id, index->table->id) != 0) { ptr = NULL; ib_logger(ib_stream, "InnoDB: Error: trying to access update undo rec" " for table %s\n" "InnoDB: but the table id in the" " undo record is wrong\n" "InnoDB: Submit a detailed bug report, " "check InnoDB website for details\n" "InnoDB: Run also CHECK TABLE %s\n", index->table_name, index->table_name); } if (ptr == NULL) { /* The record was corrupted, return an error; these printfs should catch an elusive bug in row_vers_old_has_index_entry */ ib_logger(ib_stream, "InnoDB: table %s, index %s, n_uniq %lu\n" "InnoDB: undo rec address %p, type %lu cmpl_info %lu\n" "InnoDB: undo rec table id %lu %lu," " index table id %lu %lu\n" "InnoDB: dump of 150 bytes in undo rec: ", index->table_name, index->name, (ulong) dict_index_get_n_unique(index), undo_rec, (ulong) type, (ulong) cmpl_info, (ulong) ut_dulint_get_high(table_id), (ulong) ut_dulint_get_low(table_id), (ulong) ut_dulint_get_high(index->table->id), (ulong) ut_dulint_get_low(index->table->id)); ut_print_buf(ib_stream, undo_rec, 150); ib_logger(ib_stream, "\nInnoDB: index record "); rec_print(ib_stream, index_rec, index); ib_logger(ib_stream, "\nInnoDB: record version "); rec_print_new(ib_stream, rec, offsets); ib_logger(ib_stream, "\n" "InnoDB: Record trx id " TRX_ID_FMT ", update rec trx id " TRX_ID_FMT "\n" "InnoDB: Roll ptr in rec %lu %lu, in update rec" " %lu %lu\n", TRX_ID_PREP_PRINTF(rec_trx_id), TRX_ID_PREP_PRINTF(trx_id), (ulong) ut_dulint_get_high(old_roll_ptr), (ulong) ut_dulint_get_low(old_roll_ptr), (ulong) ut_dulint_get_high(roll_ptr), (ulong) ut_dulint_get_low(roll_ptr)); trx_purge_sys_print(); return(DB_ERROR); } if (row_upd_changes_field_size_or_external(index, offsets, update)) { ulint n_ext; /* We have to set the appropriate extern storage bits in the old version of the record: the extern bits in rec for those fields that update does NOT update, as well as the bits for those fields that update updates to become externally stored fields. Store the info: */ entry = row_rec_to_index_entry(ROW_COPY_DATA, rec, index, offsets, &n_ext, heap); n_ext += btr_push_update_extern_fields(entry, update, heap); /* The page containing the clustered index record corresponding to entry is latched in mtr. Thus the following call is safe. */ row_upd_index_replace_new_col_vals(entry, index, update, heap); buf = mem_heap_alloc(heap, rec_get_converted_size(index, entry, n_ext)); *old_vers = rec_convert_dtuple_to_rec(buf, index, entry, n_ext); } else { buf = mem_heap_alloc(heap, rec_offs_size(offsets)); *old_vers = rec_copy(buf, rec, offsets); rec_offs_make_valid(*old_vers, index, offsets); row_upd_rec_in_place(*old_vers, index, offsets, update, NULL); } return(DB_SUCCESS); } #endif /* !UNIV_HOTBACKUP */ haildb-2.3.2/trx/trx0undo.c0000644000175000017500000015624511513177357016424 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2010, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file trx/trx0undo.c Transaction undo log Created 3/26/1996 Heikki Tuuri *******************************************************/ #include "trx0undo.h" #ifdef UNIV_NONINL #include "trx0undo.ic" #endif #include "fsp0fsp.h" #ifndef UNIV_HOTBACKUP #include "mach0data.h" #include "mtr0log.h" #include "trx0rseg.h" #include "trx0trx.h" #include "srv0srv.h" #include "trx0rec.h" #include "trx0purge.h" /* How should the old versions in the history list be managed? ---------------------------------------------------------- If each transaction is given a whole page for its update undo log, file space consumption can be 10 times higher than necessary. Therefore, partly filled update undo log pages should be reusable. But then there is no way individual pages can be ordered so that the ordering agrees with the serialization numbers of the transactions on the pages. Thus, the history list must be formed of undo logs, not their header pages as it was in the old implementation. However, on a single header page the transactions are placed in the order of their serialization numbers. As old versions are purged, we may free the page when the last transaction on the page has been purged. A problem is that the purge has to go through the transactions in the serialization order. This means that we have to look through all rollback segments for the one that has the smallest transaction number in its history list. When should we do a purge? A purge is necessary when space is running out in any of the rollback segments. Then we may have to purge also old version which might be needed by some consistent read. How do we trigger the start of a purge? When a transaction writes to an undo log, it may notice that the space is running out. When a read view is closed, it may make some history superfluous. The server can have an utility which periodically checks if it can purge some history. In a parallellized purge we have the problem that a query thread can remove a delete marked clustered index record before another query thread has processed an earlier version of the record, which cannot then be done because the row cannot be constructed from the clustered index record. To avoid this problem, we will store in the update and delete mark undo record also the columns necessary to construct the secondary index entries which are modified. We can latch the stack of versions of a single clustered index record by taking a latch on the clustered index page. As long as the latch is held, no new versions can be added and no versions removed by undo. But, a purge can still remove old versions from the bottom of the stack. */ /* How to protect rollback segments, undo logs, and history lists with ------------------------------------------------------------------- latches? ------- The contention of the kernel mutex should be minimized. When a transaction does its first insert or modify in an index, an undo log is assigned for it. Then we must have an x-latch to the rollback segment header. When the transaction does more modifys or rolls back, the undo log is protected with undo_mutex in the transaction. When the transaction commits, its insert undo log is either reset and cached for a fast reuse, or freed. In these cases we must have an x-latch on the rollback segment page. The update undo log is put to the history list. If it is not suitable for reuse, its slot in the rollback segment is reset. In both cases, an x-latch must be acquired on the rollback segment. The purge operation steps through the history list without modifying it until a truncate operation occurs, which can remove undo logs from the end of the list and release undo log segments. In stepping through the list, s-latches on the undo log pages are enough, but in a truncate, x-latches must be obtained on the rollback segment and individual pages. */ #endif /* !UNIV_HOTBACKUP */ /********************************************************************//** Initializes the fields in an undo log segment page. */ static void trx_undo_page_init( /*===============*/ page_t* undo_page, /*!< in: undo log segment page */ ulint type, /*!< in: undo log segment type */ mtr_t* mtr); /*!< in: mtr */ #ifndef UNIV_HOTBACKUP /********************************************************************//** Creates and initializes an undo log memory object. @return own: the undo log memory object */ static trx_undo_t* trx_undo_mem_create( /*================*/ trx_rseg_t* rseg, /*!< in: rollback segment memory object */ ulint id, /*!< in: slot index within rseg */ ulint type, /*!< in: type of the log: TRX_UNDO_INSERT or TRX_UNDO_UPDATE */ trx_id_t trx_id, /*!< in: id of the trx for which the undo log is created */ const XID* xid, /*!< in: X/Open XA transaction identification*/ ulint page_no,/*!< in: undo log header page number */ ulint offset);/*!< in: undo log header byte offset on page */ #endif /* !UNIV_HOTBACKUP */ /***************************************************************//** Initializes a cached insert undo log header page for new use. NOTE that this function has its own log record type MLOG_UNDO_HDR_REUSE. You must NOT change the operation of this function! @return undo log header byte offset on page */ static ulint trx_undo_insert_header_reuse( /*=========================*/ page_t* undo_page, /*!< in/out: insert undo log segment header page, x-latched */ trx_id_t trx_id, /*!< in: transaction id */ mtr_t* mtr); /*!< in: mtr */ /**********************************************************************//** If an update undo log can be discarded immediately, this function frees the space, resetting the page to the proper state for caching. */ static void trx_undo_discard_latest_update_undo( /*================================*/ page_t* undo_page, /*!< in: header page of an undo log of size 1 */ mtr_t* mtr); /*!< in: mtr */ #ifndef UNIV_HOTBACKUP /***********************************************************************//** Gets the previous record in an undo log from the previous page. @return undo log record, the page s-latched, NULL if none */ static trx_undo_rec_t* trx_undo_get_prev_rec_from_prev_page( /*=================================*/ trx_undo_rec_t* rec, /*!< in: undo record */ ulint page_no,/*!< in: undo log header page number */ ulint offset, /*!< in: undo log header offset on page */ mtr_t* mtr) /*!< in: mtr */ { ulint space; ulint zip_size; ulint prev_page_no; page_t* prev_page; page_t* undo_page; undo_page = page_align(rec); prev_page_no = flst_get_prev_addr(undo_page + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_NODE, mtr) .page; if (prev_page_no == FIL_NULL) { return(NULL); } space = page_get_space_id(undo_page); zip_size = fil_space_get_zip_size(space); prev_page = trx_undo_page_get_s_latched(space, zip_size, prev_page_no, mtr); return(trx_undo_page_get_last_rec(prev_page, page_no, offset)); } /***********************************************************************//** Gets the previous record in an undo log. @return undo log record, the page s-latched, NULL if none */ UNIV_INTERN trx_undo_rec_t* trx_undo_get_prev_rec( /*==================*/ trx_undo_rec_t* rec, /*!< in: undo record */ ulint page_no,/*!< in: undo log header page number */ ulint offset, /*!< in: undo log header offset on page */ mtr_t* mtr) /*!< in: mtr */ { trx_undo_rec_t* prev_rec; prev_rec = trx_undo_page_get_prev_rec(rec, page_no, offset); if (prev_rec) { return(prev_rec); } /* We have to go to the previous undo log page to look for the previous record */ return(trx_undo_get_prev_rec_from_prev_page(rec, page_no, offset, mtr)); } /***********************************************************************//** Gets the next record in an undo log from the next page. @return undo log record, the page latched, NULL if none */ static trx_undo_rec_t* trx_undo_get_next_rec_from_next_page( /*=================================*/ ulint space, /*!< in: undo log header space */ ulint zip_size,/*!< in: compressed page size in bytes or 0 for uncompressed pages */ page_t* undo_page, /*!< in: undo log page */ ulint page_no,/*!< in: undo log header page number */ ulint offset, /*!< in: undo log header offset on page */ ulint mode, /*!< in: latch mode: RW_S_LATCH or RW_X_LATCH */ mtr_t* mtr) /*!< in: mtr */ { trx_ulogf_t* log_hdr; ulint next_page_no; page_t* next_page; ulint next; if (page_no == page_get_page_no(undo_page)) { log_hdr = undo_page + offset; next = mach_read_from_2(log_hdr + TRX_UNDO_NEXT_LOG); if (next != 0) { return(NULL); } } next_page_no = flst_get_next_addr(undo_page + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_NODE, mtr) .page; if (next_page_no == FIL_NULL) { return(NULL); } if (mode == RW_S_LATCH) { next_page = trx_undo_page_get_s_latched(space, zip_size, next_page_no, mtr); } else { ut_ad(mode == RW_X_LATCH); next_page = trx_undo_page_get(space, zip_size, next_page_no, mtr); } return(trx_undo_page_get_first_rec(next_page, page_no, offset)); } /***********************************************************************//** Gets the next record in an undo log. @return undo log record, the page s-latched, NULL if none */ UNIV_INTERN trx_undo_rec_t* trx_undo_get_next_rec( /*==================*/ trx_undo_rec_t* rec, /*!< in: undo record */ ulint page_no,/*!< in: undo log header page number */ ulint offset, /*!< in: undo log header offset on page */ mtr_t* mtr) /*!< in: mtr */ { ulint space; ulint zip_size; trx_undo_rec_t* next_rec; next_rec = trx_undo_page_get_next_rec(rec, page_no, offset); if (next_rec) { return(next_rec); } space = page_get_space_id(page_align(rec)); zip_size = fil_space_get_zip_size(space); return(trx_undo_get_next_rec_from_next_page(space, zip_size, page_align(rec), page_no, offset, RW_S_LATCH, mtr)); } /***********************************************************************//** Gets the first record in an undo log. @return undo log record, the page latched, NULL if none */ UNIV_INTERN trx_undo_rec_t* trx_undo_get_first_rec( /*===================*/ ulint space, /*!< in: undo log header space */ ulint zip_size,/*!< in: compressed page size in bytes or 0 for uncompressed pages */ ulint page_no,/*!< in: undo log header page number */ ulint offset, /*!< in: undo log header offset on page */ ulint mode, /*!< in: latching mode: RW_S_LATCH or RW_X_LATCH */ mtr_t* mtr) /*!< in: mtr */ { page_t* undo_page; trx_undo_rec_t* rec; if (mode == RW_S_LATCH) { undo_page = trx_undo_page_get_s_latched(space, zip_size, page_no, mtr); } else { undo_page = trx_undo_page_get(space, zip_size, page_no, mtr); } rec = trx_undo_page_get_first_rec(undo_page, page_no, offset); if (rec) { return(rec); } return(trx_undo_get_next_rec_from_next_page(space, zip_size, undo_page, page_no, offset, mode, mtr)); } /*============== UNDO LOG FILE COPY CREATION AND FREEING ==================*/ /**********************************************************************//** Writes the mtr log entry of an undo log page initialization. */ UNIV_INLINE void trx_undo_page_init_log( /*===================*/ page_t* undo_page, /*!< in: undo log page */ ulint type, /*!< in: undo log type */ mtr_t* mtr) /*!< in: mtr */ { mlog_write_initial_log_record(undo_page, MLOG_UNDO_INIT, mtr); mlog_catenate_ulint_compressed(mtr, type); } #else /* !UNIV_HOTBACKUP */ # define trx_undo_page_init_log(undo_page,type,mtr) ((void) 0) #endif /* !UNIV_HOTBACKUP */ /***********************************************************//** Parses the redo log entry of an undo log page initialization. @return end of log record or NULL */ UNIV_INTERN byte* trx_undo_parse_page_init( /*=====================*/ byte* ptr, /*!< in: buffer */ byte* end_ptr,/*!< in: buffer end */ page_t* page, /*!< in: page or NULL */ mtr_t* mtr) /*!< in: mtr or NULL */ { ulint type; ptr = mach_parse_compressed(ptr, end_ptr, &type); if (ptr == NULL) { return(NULL); } if (page) { trx_undo_page_init(page, type, mtr); } return(ptr); } /********************************************************************//** Initializes the fields in an undo log segment page. */ static void trx_undo_page_init( /*===============*/ page_t* undo_page, /*!< in: undo log segment page */ ulint type, /*!< in: undo log segment type */ mtr_t* mtr) /*!< in: mtr */ { trx_upagef_t* page_hdr; page_hdr = undo_page + TRX_UNDO_PAGE_HDR; mach_write_to_2(page_hdr + TRX_UNDO_PAGE_TYPE, type); mach_write_to_2(page_hdr + TRX_UNDO_PAGE_START, TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_HDR_SIZE); mach_write_to_2(page_hdr + TRX_UNDO_PAGE_FREE, TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_HDR_SIZE); fil_page_set_type(undo_page, FIL_PAGE_UNDO_LOG); trx_undo_page_init_log(undo_page, type, mtr); } #ifndef UNIV_HOTBACKUP /***************************************************************//** Creates a new undo log segment in file. @return DB_SUCCESS if page creation OK possible error codes are: DB_TOO_MANY_CONCURRENT_TRXS DB_OUT_OF_FILE_SPACE */ static ulint trx_undo_seg_create( /*================*/ trx_rseg_t* rseg __attribute__((unused)),/*!< in: rollback segment */ trx_rsegf_t* rseg_hdr,/*!< in: rollback segment header, page x-latched */ ulint type, /*!< in: type of the segment: TRX_UNDO_INSERT or TRX_UNDO_UPDATE */ ulint* id, /*!< out: slot index within rseg header */ page_t** undo_page, /*!< out: segment header page x-latched, NULL if there was an error */ mtr_t* mtr) /*!< in: mtr */ { ulint slot_no; ulint space; buf_block_t* block; trx_upagef_t* page_hdr; trx_usegf_t* seg_hdr; ulint n_reserved; ibool success; ulint err = DB_SUCCESS; ut_ad(mtr && id && rseg_hdr); ut_ad(mutex_own(&(rseg->mutex))); /* ib_logger(ib_stream, "%s\n", type == TRX_UNDO_INSERT ? "Creating insert undo log segment" : "Creating update undo log segment"); */ slot_no = trx_rsegf_undo_find_free(rseg_hdr, mtr); if (slot_no == ULINT_UNDEFINED) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Warning: cannot find a free slot for" " an undo log. Do you have too\n" "InnoDB: many active transactions" " running concurrently?\n"); return(DB_TOO_MANY_CONCURRENT_TRXS); } space = page_get_space_id(page_align(rseg_hdr)); success = fsp_reserve_free_extents(&n_reserved, space, 2, FSP_UNDO, mtr); if (!success) { return(DB_OUT_OF_FILE_SPACE); } /* Allocate a new file segment for the undo log */ block = fseg_create_general(space, 0, TRX_UNDO_SEG_HDR + TRX_UNDO_FSEG_HEADER, TRUE, mtr); fil_space_release_free_extents(space, n_reserved); if (block == NULL) { /* No space left */ return(DB_OUT_OF_FILE_SPACE); } buf_block_dbg_add_level(block, SYNC_TRX_UNDO_PAGE); *undo_page = buf_block_get_frame(block); page_hdr = *undo_page + TRX_UNDO_PAGE_HDR; seg_hdr = *undo_page + TRX_UNDO_SEG_HDR; trx_undo_page_init(*undo_page, type, mtr); mlog_write_ulint(page_hdr + TRX_UNDO_PAGE_FREE, TRX_UNDO_SEG_HDR + TRX_UNDO_SEG_HDR_SIZE, MLOG_2BYTES, mtr); mlog_write_ulint(seg_hdr + TRX_UNDO_LAST_LOG, 0, MLOG_2BYTES, mtr); flst_init(seg_hdr + TRX_UNDO_PAGE_LIST, mtr); flst_add_last(seg_hdr + TRX_UNDO_PAGE_LIST, page_hdr + TRX_UNDO_PAGE_NODE, mtr); trx_rsegf_set_nth_undo(rseg_hdr, slot_no, page_get_page_no(*undo_page), mtr); *id = slot_no; return(err); } /**********************************************************************//** Writes the mtr log entry of an undo log header initialization. */ UNIV_INLINE void trx_undo_header_create_log( /*=======================*/ const page_t* undo_page, /*!< in: undo log header page */ trx_id_t trx_id, /*!< in: transaction id */ mtr_t* mtr) /*!< in: mtr */ { mlog_write_initial_log_record(undo_page, MLOG_UNDO_HDR_CREATE, mtr); mlog_catenate_dulint_compressed(mtr, trx_id); } #else /* !UNIV_HOTBACKUP */ # define trx_undo_header_create_log(undo_page,trx_id,mtr) ((void) 0) #endif /* !UNIV_HOTBACKUP */ /***************************************************************//** Creates a new undo log header in file. NOTE that this function has its own log record type MLOG_UNDO_HDR_CREATE. You must NOT change the operation of this function! @return header byte offset on page */ static ulint trx_undo_header_create( /*===================*/ page_t* undo_page, /*!< in/out: undo log segment header page, x-latched; it is assumed that there is TRX_UNDO_LOG_XA_HDR_SIZE bytes free space on it */ trx_id_t trx_id, /*!< in: transaction id */ mtr_t* mtr) /*!< in: mtr */ { trx_upagef_t* page_hdr; trx_usegf_t* seg_hdr; trx_ulogf_t* log_hdr; trx_ulogf_t* prev_log_hdr; ulint prev_log; ulint free; ulint new_free; ut_ad(mtr && undo_page); page_hdr = undo_page + TRX_UNDO_PAGE_HDR; seg_hdr = undo_page + TRX_UNDO_SEG_HDR; free = mach_read_from_2(page_hdr + TRX_UNDO_PAGE_FREE); log_hdr = undo_page + free; new_free = free + TRX_UNDO_LOG_OLD_HDR_SIZE; #ifdef WITH_XOPEN ut_a(free + TRX_UNDO_LOG_XA_HDR_SIZE < UNIV_PAGE_SIZE - 100); #endif /* WITH_XOPEN */ mach_write_to_2(page_hdr + TRX_UNDO_PAGE_START, new_free); mach_write_to_2(page_hdr + TRX_UNDO_PAGE_FREE, new_free); mach_write_to_2(seg_hdr + TRX_UNDO_STATE, TRX_UNDO_ACTIVE); prev_log = mach_read_from_2(seg_hdr + TRX_UNDO_LAST_LOG); if (prev_log != 0) { prev_log_hdr = undo_page + prev_log; mach_write_to_2(prev_log_hdr + TRX_UNDO_NEXT_LOG, free); } mach_write_to_2(seg_hdr + TRX_UNDO_LAST_LOG, free); log_hdr = undo_page + free; mach_write_to_2(log_hdr + TRX_UNDO_DEL_MARKS, TRUE); mach_write_to_8(log_hdr + TRX_UNDO_TRX_ID, trx_id); mach_write_to_2(log_hdr + TRX_UNDO_LOG_START, new_free); mach_write_to_1(log_hdr + TRX_UNDO_XID_EXISTS, FALSE); mach_write_to_1(log_hdr + TRX_UNDO_DICT_TRANS, FALSE); mach_write_to_2(log_hdr + TRX_UNDO_NEXT_LOG, 0); mach_write_to_2(log_hdr + TRX_UNDO_PREV_LOG, prev_log); /* Write the log record about the header creation */ trx_undo_header_create_log(undo_page, trx_id, mtr); return(free); } #ifndef UNIV_HOTBACKUP /********************************************************************//** Write X/Open XA Transaction Identification (XID) to undo log header */ static void trx_undo_write_xid( /*===============*/ trx_ulogf_t* log_hdr,/*!< in: undo log header */ #ifdef WITH_XOPEN const XID* xid, /*!< in: X/Open XA Transaction Identification */ #endif /* WITH_XOPEN */ mtr_t* mtr) /*!< in: mtr */ { #ifdef WITH_XOPEN mlog_write_ulint(log_hdr + TRX_UNDO_XA_FORMAT, (ulint)xid->formatID, MLOG_4BYTES, mtr); mlog_write_ulint(log_hdr + TRX_UNDO_XA_TRID_LEN, (ulint)xid->gtrid_length, MLOG_4BYTES, mtr); mlog_write_ulint(log_hdr + TRX_UNDO_XA_BQUAL_LEN, (ulint)xid->bqual_length, MLOG_4BYTES, mtr); mlog_write_string(log_hdr + TRX_UNDO_XA_XID, (const byte*) xid->data, XIDDATASIZE, mtr); #else static byte buf[XIDDATASIZE]; mlog_write_ulint(log_hdr + TRX_UNDO_XA_FORMAT, (ulint) -1, MLOG_4BYTES, mtr); mlog_write_ulint(log_hdr + TRX_UNDO_XA_TRID_LEN, (ulint) 0, MLOG_4BYTES, mtr); mlog_write_ulint(log_hdr + TRX_UNDO_XA_BQUAL_LEN, (ulint) 0, MLOG_4BYTES, mtr); mlog_write_string(log_hdr + TRX_UNDO_XA_XID, (const byte*) buf, XIDDATASIZE, mtr); #endif /* WITH_XOPEN */ } #ifdef WITH_XOPEN /********************************************************************//** Read X/Open XA Transaction Identification (XID) from undo log header */ static void trx_undo_read_xid( /*==============*/ trx_ulogf_t* log_hdr,/*!< in: undo log header */ XID* xid) /*!< out: X/Open XA Transaction Identification */ { xid->formatID = (long)mach_read_from_4(log_hdr + TRX_UNDO_XA_FORMAT); xid->gtrid_length = (long) mach_read_from_4(log_hdr + TRX_UNDO_XA_TRID_LEN); xid->bqual_length = (long) mach_read_from_4(log_hdr + TRX_UNDO_XA_BQUAL_LEN); memcpy(xid->data, log_hdr + TRX_UNDO_XA_XID, XIDDATASIZE); } /***************************************************************//** Adds space for the XA XID after an undo log old-style header. */ static void trx_undo_header_add_space_for_xid( /*==============================*/ page_t* undo_page,/*!< in: undo log segment header page */ trx_ulogf_t* log_hdr,/*!< in: undo log header */ mtr_t* mtr) /*!< in: mtr */ { trx_upagef_t* page_hdr; ulint free; ulint new_free; page_hdr = undo_page + TRX_UNDO_PAGE_HDR; free = mach_read_from_2(page_hdr + TRX_UNDO_PAGE_FREE); /* free is now the end offset of the old style undo log header */ ut_a(free == (ulint)(log_hdr - undo_page) + TRX_UNDO_LOG_OLD_HDR_SIZE); new_free = free + (TRX_UNDO_LOG_XA_HDR_SIZE - TRX_UNDO_LOG_OLD_HDR_SIZE); /* Add space for a XID after the header, update the free offset fields on the undo log page and in the undo log header */ mlog_write_ulint(page_hdr + TRX_UNDO_PAGE_START, new_free, MLOG_2BYTES, mtr); mlog_write_ulint(page_hdr + TRX_UNDO_PAGE_FREE, new_free, MLOG_2BYTES, mtr); mlog_write_ulint(log_hdr + TRX_UNDO_LOG_START, new_free, MLOG_2BYTES, mtr); } #endif /* WITH_XOPEN */ /**********************************************************************//** Writes the mtr log entry of an undo log header reuse. */ UNIV_INLINE void trx_undo_insert_header_reuse_log( /*=============================*/ const page_t* undo_page, /*!< in: undo log header page */ trx_id_t trx_id, /*!< in: transaction id */ mtr_t* mtr) /*!< in: mtr */ { mlog_write_initial_log_record(undo_page, MLOG_UNDO_HDR_REUSE, mtr); mlog_catenate_dulint_compressed(mtr, trx_id); } #else /* !UNIV_HOTBACKUP */ # define trx_undo_insert_header_reuse_log(undo_page,trx_id,mtr) ((void) 0) #endif /* !UNIV_HOTBACKUP */ /***********************************************************//** Parses the redo log entry of an undo log page header create or reuse. @return end of log record or NULL */ UNIV_INTERN byte* trx_undo_parse_page_header( /*=======================*/ ulint type, /*!< in: MLOG_UNDO_HDR_CREATE or MLOG_UNDO_HDR_REUSE */ byte* ptr, /*!< in: buffer */ byte* end_ptr,/*!< in: buffer end */ page_t* page, /*!< in: page or NULL */ mtr_t* mtr) /*!< in: mtr or NULL */ { trx_id_t trx_id; ptr = mach_dulint_parse_compressed(ptr, end_ptr, &trx_id); if (ptr == NULL) { return(NULL); } if (page) { if (type == MLOG_UNDO_HDR_CREATE) { trx_undo_header_create(page, trx_id, mtr); } else { ut_ad(type == MLOG_UNDO_HDR_REUSE); trx_undo_insert_header_reuse(page, trx_id, mtr); } } return(ptr); } /***************************************************************//** Initializes a cached insert undo log header page for new use. NOTE that this function has its own log record type MLOG_UNDO_HDR_REUSE. You must NOT change the operation of this function! @return undo log header byte offset on page */ static ulint trx_undo_insert_header_reuse( /*=========================*/ page_t* undo_page, /*!< in/out: insert undo log segment header page, x-latched */ trx_id_t trx_id, /*!< in: transaction id */ mtr_t* mtr) /*!< in: mtr */ { trx_upagef_t* page_hdr; trx_usegf_t* seg_hdr; trx_ulogf_t* log_hdr; ulint free; ulint new_free; ut_ad(mtr && undo_page); page_hdr = undo_page + TRX_UNDO_PAGE_HDR; seg_hdr = undo_page + TRX_UNDO_SEG_HDR; free = TRX_UNDO_SEG_HDR + TRX_UNDO_SEG_HDR_SIZE; ut_a(free + TRX_UNDO_LOG_XA_HDR_SIZE < UNIV_PAGE_SIZE - 100); log_hdr = undo_page + free; new_free = free + TRX_UNDO_LOG_OLD_HDR_SIZE; /* Insert undo data is not needed after commit: we may free all the space on the page */ ut_a(mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_TYPE) == TRX_UNDO_INSERT); mach_write_to_2(page_hdr + TRX_UNDO_PAGE_START, new_free); mach_write_to_2(page_hdr + TRX_UNDO_PAGE_FREE, new_free); mach_write_to_2(seg_hdr + TRX_UNDO_STATE, TRX_UNDO_ACTIVE); log_hdr = undo_page + free; mach_write_to_8(log_hdr + TRX_UNDO_TRX_ID, trx_id); mach_write_to_2(log_hdr + TRX_UNDO_LOG_START, new_free); mach_write_to_1(log_hdr + TRX_UNDO_XID_EXISTS, FALSE); mach_write_to_1(log_hdr + TRX_UNDO_DICT_TRANS, FALSE); /* Write the log record MLOG_UNDO_HDR_REUSE */ trx_undo_insert_header_reuse_log(undo_page, trx_id, mtr); return(free); } #ifndef UNIV_HOTBACKUP /**********************************************************************//** Writes the redo log entry of an update undo log header discard. */ UNIV_INLINE void trx_undo_discard_latest_log( /*========================*/ page_t* undo_page, /*!< in: undo log header page */ mtr_t* mtr) /*!< in: mtr */ { mlog_write_initial_log_record(undo_page, MLOG_UNDO_HDR_DISCARD, mtr); } #else /* !UNIV_HOTBACKUP */ # define trx_undo_discard_latest_log(undo_page, mtr) ((void) 0) #endif /* !UNIV_HOTBACKUP */ /***********************************************************//** Parses the redo log entry of an undo log page header discard. @return end of log record or NULL */ UNIV_INTERN byte* trx_undo_parse_discard_latest( /*==========================*/ byte* ptr, /*!< in: buffer */ byte* end_ptr __attribute__((unused)), /*!< in: buffer end */ page_t* page, /*!< in: page or NULL */ mtr_t* mtr) /*!< in: mtr or NULL */ { ut_ad(end_ptr); if (page) { trx_undo_discard_latest_update_undo(page, mtr); } return(ptr); } /**********************************************************************//** If an update undo log can be discarded immediately, this function frees the space, resetting the page to the proper state for caching. */ static void trx_undo_discard_latest_update_undo( /*================================*/ page_t* undo_page, /*!< in: header page of an undo log of size 1 */ mtr_t* mtr) /*!< in: mtr */ { trx_usegf_t* seg_hdr; trx_upagef_t* page_hdr; trx_ulogf_t* log_hdr; trx_ulogf_t* prev_log_hdr; ulint free; ulint prev_hdr_offset; seg_hdr = undo_page + TRX_UNDO_SEG_HDR; page_hdr = undo_page + TRX_UNDO_PAGE_HDR; free = mach_read_from_2(seg_hdr + TRX_UNDO_LAST_LOG); log_hdr = undo_page + free; prev_hdr_offset = mach_read_from_2(log_hdr + TRX_UNDO_PREV_LOG); if (prev_hdr_offset != 0) { prev_log_hdr = undo_page + prev_hdr_offset; mach_write_to_2(page_hdr + TRX_UNDO_PAGE_START, mach_read_from_2(prev_log_hdr + TRX_UNDO_LOG_START)); mach_write_to_2(prev_log_hdr + TRX_UNDO_NEXT_LOG, 0); } mach_write_to_2(page_hdr + TRX_UNDO_PAGE_FREE, free); mach_write_to_2(seg_hdr + TRX_UNDO_STATE, TRX_UNDO_CACHED); mach_write_to_2(seg_hdr + TRX_UNDO_LAST_LOG, prev_hdr_offset); trx_undo_discard_latest_log(undo_page, mtr); } #ifndef UNIV_HOTBACKUP /********************************************************************//** Tries to add a page to the undo log segment where the undo log is placed. @return page number if success, else FIL_NULL */ UNIV_INTERN ulint trx_undo_add_page( /*==============*/ trx_t* trx, /*!< in: transaction */ trx_undo_t* undo, /*!< in: undo log memory object */ mtr_t* mtr) /*!< in: mtr which does not have a latch to any undo log page; the caller must have reserved the rollback segment mutex */ { page_t* header_page; page_t* new_page; trx_rseg_t* rseg; ulint page_no; ulint n_reserved; ibool success; ut_ad(mutex_own(&(trx->undo_mutex))); ut_ad(!mutex_own(&kernel_mutex)); ut_ad(mutex_own(&(trx->rseg->mutex))); rseg = trx->rseg; if (rseg->curr_size == rseg->max_size) { return(FIL_NULL); } header_page = trx_undo_page_get(undo->space, undo->zip_size, undo->hdr_page_no, mtr); success = fsp_reserve_free_extents(&n_reserved, undo->space, 1, FSP_UNDO, mtr); if (!success) { return(FIL_NULL); } page_no = fseg_alloc_free_page_general(header_page + TRX_UNDO_SEG_HDR + TRX_UNDO_FSEG_HEADER, undo->top_page_no + 1, FSP_UP, TRUE, mtr); fil_space_release_free_extents(undo->space, n_reserved); if (page_no == FIL_NULL) { /* No space left */ return(FIL_NULL); } undo->last_page_no = page_no; new_page = trx_undo_page_get(undo->space, undo->zip_size, page_no, mtr); trx_undo_page_init(new_page, undo->type, mtr); flst_add_last(header_page + TRX_UNDO_SEG_HDR + TRX_UNDO_PAGE_LIST, new_page + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_NODE, mtr); undo->size++; rseg->curr_size++; return(page_no); } /********************************************************************//** Frees an undo log page that is not the header page. @return last page number in remaining log */ static ulint trx_undo_free_page( /*===============*/ trx_rseg_t* rseg, /*!< in: rollback segment */ ibool in_history, /*!< in: TRUE if the undo log is in the history list */ ulint space, /*!< in: space */ ulint hdr_page_no, /*!< in: header page number */ ulint page_no, /*!< in: page number to free: must not be the header page */ mtr_t* mtr) /*!< in: mtr which does not have a latch to any undo log page; the caller must have reserved the rollback segment mutex */ { page_t* header_page; page_t* undo_page; fil_addr_t last_addr; trx_rsegf_t* rseg_header; ulint hist_size; ulint zip_size; ut_a(hdr_page_no != page_no); ut_ad(!mutex_own(&kernel_mutex)); ut_ad(mutex_own(&(rseg->mutex))); zip_size = rseg->zip_size; undo_page = trx_undo_page_get(space, zip_size, page_no, mtr); header_page = trx_undo_page_get(space, zip_size, hdr_page_no, mtr); flst_remove(header_page + TRX_UNDO_SEG_HDR + TRX_UNDO_PAGE_LIST, undo_page + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_NODE, mtr); fseg_free_page(header_page + TRX_UNDO_SEG_HDR + TRX_UNDO_FSEG_HEADER, space, page_no, mtr); last_addr = flst_get_last(header_page + TRX_UNDO_SEG_HDR + TRX_UNDO_PAGE_LIST, mtr); rseg->curr_size--; if (in_history) { rseg_header = trx_rsegf_get(space, zip_size, rseg->page_no, mtr); hist_size = mtr_read_ulint(rseg_header + TRX_RSEG_HISTORY_SIZE, MLOG_4BYTES, mtr); ut_ad(hist_size > 0); mlog_write_ulint(rseg_header + TRX_RSEG_HISTORY_SIZE, hist_size - 1, MLOG_4BYTES, mtr); } return(last_addr.page); } /********************************************************************//** Frees an undo log page when there is also the memory object for the undo log. */ static void trx_undo_free_page_in_rollback( /*===========================*/ trx_t* trx __attribute__((unused)), /*!< in: transaction */ trx_undo_t* undo, /*!< in: undo log memory copy */ ulint page_no,/*!< in: page number to free: must not be the header page */ mtr_t* mtr) /*!< in: mtr which does not have a latch to any undo log page; the caller must have reserved the rollback segment mutex */ { ulint last_page_no; ut_ad(undo->hdr_page_no != page_no); ut_ad(mutex_own(&(trx->undo_mutex))); last_page_no = trx_undo_free_page(undo->rseg, FALSE, undo->space, undo->hdr_page_no, page_no, mtr); undo->last_page_no = last_page_no; undo->size--; } /********************************************************************//** Empties an undo log header page of undo records for that undo log. Other undo logs may still have records on that page, if it is an update undo log. */ static void trx_undo_empty_header_page( /*=======================*/ ulint space, /*!< in: space */ ulint zip_size, /*!< in: compressed page size in bytes or 0 for uncompressed pages */ ulint hdr_page_no, /*!< in: header page number */ ulint hdr_offset, /*!< in: header offset */ mtr_t* mtr) /*!< in: mtr */ { page_t* header_page; trx_ulogf_t* log_hdr; ulint end; header_page = trx_undo_page_get(space, zip_size, hdr_page_no, mtr); log_hdr = header_page + hdr_offset; end = trx_undo_page_get_end(header_page, hdr_page_no, hdr_offset); mlog_write_ulint(log_hdr + TRX_UNDO_LOG_START, end, MLOG_2BYTES, mtr); } /***********************************************************************//** Truncates an undo log from the end. This function is used during a rollback to free space from an undo log. */ UNIV_INTERN void trx_undo_truncate_end( /*==================*/ trx_t* trx, /*!< in: transaction whose undo log it is */ trx_undo_t* undo, /*!< in: undo log */ undo_no_t limit) /*!< in: all undo records with undo number >= this value should be truncated */ { page_t* undo_page; ulint last_page_no; trx_undo_rec_t* rec; trx_undo_rec_t* trunc_here; trx_rseg_t* rseg; mtr_t mtr; ut_ad(mutex_own(&(trx->undo_mutex))); ut_ad(mutex_own(&(trx->rseg->mutex))); rseg = trx->rseg; for (;;) { mtr_start(&mtr); trunc_here = NULL; last_page_no = undo->last_page_no; undo_page = trx_undo_page_get(undo->space, undo->zip_size, last_page_no, &mtr); rec = trx_undo_page_get_last_rec(undo_page, undo->hdr_page_no, undo->hdr_offset); for (;;) { if (rec == NULL) { if (last_page_no == undo->hdr_page_no) { goto function_exit; } trx_undo_free_page_in_rollback( trx, undo, last_page_no, &mtr); break; } if (ut_dulint_cmp(trx_undo_rec_get_undo_no(rec), limit) >= 0) { /* Truncate at least this record off, maybe more */ trunc_here = rec; } else { goto function_exit; } rec = trx_undo_page_get_prev_rec(rec, undo->hdr_page_no, undo->hdr_offset); } mtr_commit(&mtr); } function_exit: if (trunc_here) { mlog_write_ulint(undo_page + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_FREE, trunc_here - undo_page, MLOG_2BYTES, &mtr); } mtr_commit(&mtr); } /***********************************************************************//** Truncates an undo log from the start. This function is used during a purge operation. */ UNIV_INTERN void trx_undo_truncate_start( /*====================*/ trx_rseg_t* rseg, /*!< in: rollback segment */ ulint space, /*!< in: space id of the log */ ulint hdr_page_no, /*!< in: header page number */ ulint hdr_offset, /*!< in: header offset on the page */ undo_no_t limit) /*!< in: all undo pages with undo numbers < this value should be truncated; NOTE that the function only frees whole pages; the header page is not freed, but emptied, if all the records there are < limit */ { page_t* undo_page; trx_undo_rec_t* rec; trx_undo_rec_t* last_rec; ulint page_no; mtr_t mtr; ut_ad(mutex_own(&(rseg->mutex))); if (ut_dulint_is_zero(limit)) { return; } loop: mtr_start(&mtr); rec = trx_undo_get_first_rec(space, rseg->zip_size, hdr_page_no, hdr_offset, RW_X_LATCH, &mtr); if (rec == NULL) { /* Already empty */ mtr_commit(&mtr); return; } undo_page = page_align(rec); last_rec = trx_undo_page_get_last_rec(undo_page, hdr_page_no, hdr_offset); if (ut_dulint_cmp(trx_undo_rec_get_undo_no(last_rec), limit) >= 0) { mtr_commit(&mtr); return; } page_no = page_get_page_no(undo_page); if (page_no == hdr_page_no) { trx_undo_empty_header_page(space, rseg->zip_size, hdr_page_no, hdr_offset, &mtr); } else { trx_undo_free_page(rseg, TRUE, space, hdr_page_no, page_no, &mtr); } mtr_commit(&mtr); goto loop; } /**********************************************************************//** Frees an undo log segment which is not in the history list. */ static void trx_undo_seg_free( /*==============*/ trx_undo_t* undo) /*!< in: undo log */ { trx_rseg_t* rseg; fseg_header_t* file_seg; trx_rsegf_t* rseg_header; trx_usegf_t* seg_header; ibool finished; mtr_t mtr; rseg = undo->rseg; do { mtr_start(&mtr); ut_ad(!mutex_own(&kernel_mutex)); mutex_enter(&(rseg->mutex)); seg_header = trx_undo_page_get(undo->space, undo->zip_size, undo->hdr_page_no, &mtr) + TRX_UNDO_SEG_HDR; file_seg = seg_header + TRX_UNDO_FSEG_HEADER; finished = fseg_free_step(file_seg, &mtr); if (finished) { /* Update the rseg header */ rseg_header = trx_rsegf_get( rseg->space, rseg->zip_size, rseg->page_no, &mtr); trx_rsegf_set_nth_undo(rseg_header, undo->id, FIL_NULL, &mtr); } mutex_exit(&(rseg->mutex)); mtr_commit(&mtr); } while (!finished); } /*========== UNDO LOG MEMORY COPY INITIALIZATION =====================*/ /********************************************************************//** Creates and initializes an undo log memory object according to the values in the header in file, when the database is started. The memory object is inserted in the appropriate list of rseg. @return own: the undo log memory object */ static trx_undo_t* trx_undo_mem_create_at_db_start( /*============================*/ trx_rseg_t* rseg, /*!< in: rollback segment memory object */ ulint id, /*!< in: slot index within rseg */ ulint page_no,/*!< in: undo log segment page number */ mtr_t* mtr) /*!< in: mtr */ { page_t* undo_page; trx_upagef_t* page_header; trx_usegf_t* seg_header; trx_ulogf_t* undo_header; trx_undo_t* undo; ulint type; ulint state; trx_id_t trx_id; ulint offset; fil_addr_t last_addr; page_t* last_page; trx_undo_rec_t* rec; #ifdef WITH_XOPEN XID xid; ibool xid_exists = FALSE; #endif /* WITH_XOPEN */ if (id >= TRX_RSEG_N_SLOTS) { ib_logger(ib_stream, "InnoDB: Error: undo->id is %lu\n", (ulong) id); ut_error; } undo_page = trx_undo_page_get(rseg->space, rseg->zip_size, page_no, mtr); page_header = undo_page + TRX_UNDO_PAGE_HDR; type = mtr_read_ulint(page_header + TRX_UNDO_PAGE_TYPE, MLOG_2BYTES, mtr); seg_header = undo_page + TRX_UNDO_SEG_HDR; state = mach_read_from_2(seg_header + TRX_UNDO_STATE); offset = mach_read_from_2(seg_header + TRX_UNDO_LAST_LOG); undo_header = undo_page + offset; trx_id = mtr_read_dulint(undo_header + TRX_UNDO_TRX_ID, mtr); #ifdef WITH_XOPEN xid_exists = mtr_read_ulint(undo_header + TRX_UNDO_XID_EXISTS, MLOG_1BYTE, mtr); /* Read X/Open XA transaction identification if it exists, or set it to NULL. */ memset(&xid, 0, sizeof(xid)); xid.formatID = -1; if (xid_exists == TRUE) { trx_undo_read_xid(undo_header, &xid); } #endif /* WITH_XOPEN */ mutex_enter(&(rseg->mutex)); #ifdef WITH_XOPEN undo = trx_undo_mem_create( rseg, id, type, trx_id, &xid, page_no, offset); #else undo = trx_undo_mem_create( rseg, id, type, trx_id, NULL, page_no, offset); #endif /* WITH_XOPEN */ mutex_exit(&(rseg->mutex)); undo->dict_operation = mtr_read_ulint( undo_header + TRX_UNDO_DICT_TRANS, MLOG_1BYTE, mtr); undo->table_id = mtr_read_dulint(undo_header + TRX_UNDO_TABLE_ID, mtr); undo->state = state; undo->size = flst_get_len(seg_header + TRX_UNDO_PAGE_LIST, mtr); /* If the log segment is being freed, the page list is inconsistent! */ if (state == TRX_UNDO_TO_FREE) { goto add_to_list; } last_addr = flst_get_last(seg_header + TRX_UNDO_PAGE_LIST, mtr); undo->last_page_no = last_addr.page; undo->top_page_no = last_addr.page; last_page = trx_undo_page_get(rseg->space, rseg->zip_size, undo->last_page_no, mtr); rec = trx_undo_page_get_last_rec(last_page, page_no, offset); if (rec == NULL) { undo->empty = TRUE; } else { undo->empty = FALSE; undo->top_offset = rec - last_page; undo->top_undo_no = trx_undo_rec_get_undo_no(rec); } add_to_list: if (type == TRX_UNDO_INSERT) { if (state != TRX_UNDO_CACHED) { UT_LIST_ADD_LAST(undo_list, rseg->insert_undo_list, undo); } else { UT_LIST_ADD_LAST(undo_list, rseg->insert_undo_cached, undo); } } else { ut_ad(type == TRX_UNDO_UPDATE); if (state != TRX_UNDO_CACHED) { UT_LIST_ADD_LAST(undo_list, rseg->update_undo_list, undo); } else { UT_LIST_ADD_LAST(undo_list, rseg->update_undo_cached, undo); } } return(undo); } /********************************************************************//** Initializes the undo log lists for a rollback segment memory copy. This function is only called when the database is started or a new rollback segment is created. @return the combined size of undo log segments in pages */ UNIV_INTERN ulint trx_undo_lists_init( /*================*/ ib_recovery_t recovery,/*!< in: recovery flag */ trx_rseg_t* rseg) /*!< in: rollback segment memory object */ { ulint page_no; trx_undo_t* undo; ulint size = 0; trx_rsegf_t* rseg_header; ulint i; mtr_t mtr; UT_LIST_INIT(rseg->update_undo_list); UT_LIST_INIT(rseg->update_undo_cached); UT_LIST_INIT(rseg->insert_undo_list); UT_LIST_INIT(rseg->insert_undo_cached); mtr_start(&mtr); rseg_header = trx_rsegf_get_new(rseg->space, rseg->zip_size, rseg->page_no, &mtr); for (i = 0; i < TRX_RSEG_N_SLOTS; i++) { page_no = trx_rsegf_get_nth_undo(rseg_header, i, &mtr); /* In forced recovery: try to avoid operations which look at database pages; undo logs are rapidly changing data, and the probability that they are in an inconsistent state is high */ if (page_no != FIL_NULL && recovery < IB_RECOVERY_NO_UNDO_LOG_SCAN) { undo = trx_undo_mem_create_at_db_start(rseg, i, page_no, &mtr); size += undo->size; mtr_commit(&mtr); mtr_start(&mtr); rseg_header = trx_rsegf_get( rseg->space, rseg->zip_size, rseg->page_no, &mtr); } } mtr_commit(&mtr); return(size); } /********************************************************************//** Creates and initializes an undo log memory object. @return own: the undo log memory object */ static trx_undo_t* trx_undo_mem_create( /*================*/ trx_rseg_t* rseg, /*!< in: rollback segment memory object */ ulint id, /*!< in: slot index within rseg */ ulint type, /*!< in: type of the log: TRX_UNDO_INSERT or TRX_UNDO_UPDATE */ trx_id_t trx_id, /*!< in: id of the trx for which the undo log is created */ const XID* xid, /*!< in: X/Open transaction identification */ ulint page_no,/*!< in: undo log header page number */ ulint offset) /*!< in: undo log header byte offset on page */ { trx_undo_t* undo; ut_ad(mutex_own(&(rseg->mutex))); if (id >= TRX_RSEG_N_SLOTS) { ib_logger(ib_stream, "InnoDB: Error: undo->id is %lu\n", (ulong) id); ut_error; } undo = mem_alloc(sizeof(trx_undo_t)); if (undo == NULL) { return NULL; } undo->id = id; undo->type = type; undo->state = TRX_UNDO_ACTIVE; undo->del_marks = FALSE; undo->trx_id = trx_id; #ifdef WITH_XOPEN undo->xid = *xid; #endif /* WITH_XOPEN */ undo->dict_operation = FALSE; undo->rseg = rseg; undo->space = rseg->space; undo->zip_size = rseg->zip_size; undo->hdr_page_no = page_no; undo->hdr_offset = offset; undo->last_page_no = page_no; undo->size = 1; undo->empty = TRUE; undo->top_page_no = page_no; undo->guess_block = NULL; return(undo); } /********************************************************************//** Initializes a cached undo log object for new use. */ static void trx_undo_mem_init_for_reuse( /*========================*/ trx_undo_t* undo, /*!< in: undo log to init */ trx_id_t trx_id, /*!< in: id of the trx for which the undo log is created */ #ifdef WITH_XOPEN const XID* xid, /*!< in: X/Open XA transaction identification*/ #endif /* WITH_XOPEN */ ulint offset) /*!< in: undo log header byte offset on page */ { ut_ad(mutex_own(&((undo->rseg)->mutex))); if (UNIV_UNLIKELY(undo->id >= TRX_RSEG_N_SLOTS)) { ib_logger(ib_stream, "InnoDB: Error: undo->id is %lu\n", (ulong) undo->id); ut_error; } undo->state = TRX_UNDO_ACTIVE; undo->del_marks = FALSE; undo->trx_id = trx_id; #ifdef WITH_XOPEN undo->xid = *xid; #endif /* WITH_XOPEN */ undo->dict_operation = FALSE; undo->hdr_offset = offset; undo->empty = TRUE; } /********************************************************************//** Frees an undo log memory copy. */ UNIV_INTERN void trx_undo_mem_free( /*==============*/ trx_undo_t* undo) /*!< in: the undo object to be freed */ { if (undo->id >= TRX_RSEG_N_SLOTS) { ib_logger(ib_stream, "InnoDB: Error: undo->id is %lu\n", (ulong) undo->id); ut_error; } mem_free(undo); } /**********************************************************************//** Creates a new undo log. @return DB_SUCCESS if successful in creating the new undo lob object, possible error codes are: DB_TOO_MANY_CONCURRENT_TRXS DB_OUT_OF_FILE_SPACE DB_OUT_OF_MEMORY */ static ulint trx_undo_create( /*============*/ trx_t* trx, /*!< in: transaction */ trx_rseg_t* rseg, /*!< in: rollback segment memory copy */ ulint type, /*!< in: type of the log: TRX_UNDO_INSERT or TRX_UNDO_UPDATE */ trx_id_t trx_id, /*!< in: id of the trx for which the undo log is created */ const XID* xid, /*!< in: X/Open transaction identification*/ trx_undo_t** undo, /*!< out: the new undo log object, undefined * if did not succeed */ mtr_t* mtr) /*!< in: mtr */ { trx_rsegf_t* rseg_header; ulint page_no; ulint offset; ulint id; page_t* undo_page; ulint err; ut_ad(mutex_own(&(rseg->mutex))); if (rseg->curr_size == rseg->max_size) { return(DB_OUT_OF_FILE_SPACE); } rseg->curr_size++; rseg_header = trx_rsegf_get(rseg->space, rseg->zip_size, rseg->page_no, mtr); err = trx_undo_seg_create(rseg, rseg_header, type, &id, &undo_page, mtr); if (err != DB_SUCCESS) { /* Did not succeed */ rseg->curr_size--; return(err); } page_no = page_get_page_no(undo_page); offset = trx_undo_header_create(undo_page, trx_id, mtr); #ifdef WITH_XOPEN if (trx->support_xa) { trx_undo_header_add_space_for_xid(undo_page, undo_page + offset, mtr); } #endif /* WITH_XOPEN */ *undo = trx_undo_mem_create(rseg, id, type, trx_id, xid, page_no, offset); if (*undo == NULL) { err = DB_OUT_OF_MEMORY; } return(err); } /*================ UNDO LOG ASSIGNMENT AND CLEANUP =====================*/ /********************************************************************//** Reuses a cached undo log. @return the undo log memory object, NULL if none cached */ static trx_undo_t* trx_undo_reuse_cached( /*==================*/ trx_t* trx, /*!< in: transaction */ trx_rseg_t* rseg, /*!< in: rollback segment memory object */ ulint type, /*!< in: type of the log: TRX_UNDO_INSERT or TRX_UNDO_UPDATE */ trx_id_t trx_id, /*!< in: id of the trx for which the undo log is used */ #ifdef WITH_XOPEN const XID* xid, /*!< in: X/Open XA transaction identification */ #endif /* WITH_XOPEN */ mtr_t* mtr) /*!< in: mtr */ { trx_undo_t* undo; page_t* undo_page; ulint offset; ut_ad(mutex_own(&(rseg->mutex))); if (type == TRX_UNDO_INSERT) { undo = UT_LIST_GET_FIRST(rseg->insert_undo_cached); if (undo == NULL) { return(NULL); } UT_LIST_REMOVE(undo_list, rseg->insert_undo_cached, undo); } else { ut_ad(type == TRX_UNDO_UPDATE); undo = UT_LIST_GET_FIRST(rseg->update_undo_cached); if (undo == NULL) { return(NULL); } UT_LIST_REMOVE(undo_list, rseg->update_undo_cached, undo); } ut_ad(undo->size == 1); if (undo->id >= TRX_RSEG_N_SLOTS) { ib_logger(ib_stream, "InnoDB: Error: undo->id is %lu\n", (ulong) undo->id); ut_error; } undo_page = trx_undo_page_get(undo->space, undo->zip_size, undo->hdr_page_no, mtr); if (type == TRX_UNDO_INSERT) { offset = trx_undo_insert_header_reuse(undo_page, trx_id, mtr); #ifdef WITH_XOPEN if (trx->support_xa) { trx_undo_header_add_space_for_xid( undo_page, undo_page + offset, mtr); } #endif /* WITH_XOPEN */ } else { ut_a(mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_TYPE) == TRX_UNDO_UPDATE); offset = trx_undo_header_create(undo_page, trx_id, mtr); #ifdef WITH_XOPEN if (trx->support_xa) { trx_undo_header_add_space_for_xid( undo_page, undo_page + offset, mtr); } #endif /* WITH_XOPEN */ } #ifdef WITH_XOPEN trx_undo_mem_init_for_reuse(undo, trx_id, xid, offset); #else trx_undo_mem_init_for_reuse(undo, trx_id, offset); #endif /* WITH_XOPEN */ return(undo); } /**********************************************************************//** Marks an undo log header as a header of a data dictionary operation transaction. */ static void trx_undo_mark_as_dict_operation( /*============================*/ trx_t* trx, /*!< in: dict op transaction */ trx_undo_t* undo, /*!< in: assigned undo log */ mtr_t* mtr) /*!< in: mtr */ { page_t* hdr_page; hdr_page = trx_undo_page_get(undo->space, undo->zip_size, undo->hdr_page_no, mtr); switch (trx_get_dict_operation(trx)) { case TRX_DICT_OP_NONE: ut_error; case TRX_DICT_OP_INDEX: /* Do not discard the table on recovery. */ undo->table_id = ut_dulint_zero; break; case TRX_DICT_OP_TABLE: undo->table_id = trx->table_id; break; } mlog_write_ulint(hdr_page + undo->hdr_offset + TRX_UNDO_DICT_TRANS, TRUE, MLOG_1BYTE, mtr); mlog_write_dulint(hdr_page + undo->hdr_offset + TRX_UNDO_TABLE_ID, undo->table_id, mtr); undo->dict_operation = TRUE; } /**********************************************************************//** Assigns an undo log for a transaction. A new undo log is created or a cached undo log reused. @return DB_SUCCESS if undo log assign successful, possible error codes are: DB_TOO_MANY_CONCURRENT_TRXS DB_OUT_OF_FILE_SPACE DB_OUT_OF_MEMORY */ UNIV_INTERN ulint trx_undo_assign_undo( /*=================*/ trx_t* trx, /*!< in: transaction */ ulint type) /*!< in: TRX_UNDO_INSERT or TRX_UNDO_UPDATE */ { trx_rseg_t* rseg; trx_undo_t* undo; mtr_t mtr; ulint err = DB_SUCCESS; ut_ad(trx); ut_ad(trx->rseg); rseg = trx->rseg; ut_ad(mutex_own(&(trx->undo_mutex))); mtr_start(&mtr); ut_ad(!mutex_own(&kernel_mutex)); mutex_enter(&(rseg->mutex)); #ifdef WITH_XOPEN undo = trx_undo_reuse_cached(trx, rseg, type, trx->id, &trx->xid, &mtr); #else undo = trx_undo_reuse_cached(trx, rseg, type, trx->id, &mtr); #endif /* WITH_XOPEN */ if (undo == NULL) { #ifdef WITH_XOPEN err = trx_undo_create( trx, rseg, type, trx->id, &trx->xid, &undo, &mtr); #else err = trx_undo_create( trx, rseg, type, trx->id, NULL, &undo, &mtr); #endif /* WITH_OPEN */ if (err != DB_SUCCESS) { goto func_exit; } } if (type == TRX_UNDO_INSERT) { UT_LIST_ADD_FIRST(undo_list, rseg->insert_undo_list, undo); ut_ad(trx->insert_undo == NULL); trx->insert_undo = undo; } else { UT_LIST_ADD_FIRST(undo_list, rseg->update_undo_list, undo); ut_ad(trx->update_undo == NULL); trx->update_undo = undo; } if (trx_get_dict_operation(trx) != TRX_DICT_OP_NONE) { trx_undo_mark_as_dict_operation(trx, undo, &mtr); } func_exit: mutex_exit(&(rseg->mutex)); mtr_commit(&mtr); return err; } /******************************************************************//** Sets the state of the undo log segment at a transaction finish. @return undo log segment header page, x-latched */ UNIV_INTERN page_t* trx_undo_set_state_at_finish( /*=========================*/ trx_rseg_t* rseg, /*!< in: rollback segment memory object */ trx_t* trx __attribute__((unused)), /*!< in: transaction */ trx_undo_t* undo, /*!< in: undo log memory copy */ mtr_t* mtr) /*!< in: mtr */ { trx_usegf_t* seg_hdr; trx_upagef_t* page_hdr; page_t* undo_page; ulint state; ut_ad(trx); ut_ad(undo); ut_ad(mtr); ut_ad(mutex_own(&rseg->mutex)); if (undo->id >= TRX_RSEG_N_SLOTS) { ib_logger(ib_stream, "InnoDB: Error: undo->id is %lu\n", (ulong) undo->id); ut_error; } undo_page = trx_undo_page_get(undo->space, undo->zip_size, undo->hdr_page_no, mtr); seg_hdr = undo_page + TRX_UNDO_SEG_HDR; page_hdr = undo_page + TRX_UNDO_PAGE_HDR; if (undo->size == 1 && mach_read_from_2(page_hdr + TRX_UNDO_PAGE_FREE) < TRX_UNDO_PAGE_REUSE_LIMIT) { /* This is a heuristic to avoid the problem of all UNDO slots ending up in one of the UNDO lists. Previously if the server crashed with all the slots in one of the lists, transactions that required the slots of a different type would fail for lack of slots. */ if (UT_LIST_GET_LEN(rseg->update_undo_list) < 500 && UT_LIST_GET_LEN(rseg->insert_undo_list) < 500) { state = TRX_UNDO_CACHED; } else { state = TRX_UNDO_TO_FREE; } } else if (undo->type == TRX_UNDO_INSERT) { state = TRX_UNDO_TO_FREE; } else { state = TRX_UNDO_TO_PURGE; } undo->state = state; mlog_write_ulint(seg_hdr + TRX_UNDO_STATE, state, MLOG_2BYTES, mtr); return(undo_page); } /******************************************************************//** Sets the state of the undo log segment at a transaction prepare. @return undo log segment header page, x-latched */ UNIV_INTERN page_t* trx_undo_set_state_at_prepare( /*==========================*/ trx_t* trx, /*!< in: transaction */ trx_undo_t* undo, /*!< in: undo log memory copy */ mtr_t* mtr) /*!< in: mtr */ { trx_usegf_t* seg_hdr; trx_upagef_t* page_hdr; trx_ulogf_t* undo_header; page_t* undo_page; ulint offset; ut_ad(trx && undo && mtr); if (undo->id >= TRX_RSEG_N_SLOTS) { ib_logger(ib_stream, "InnoDB: Error: undo->id is %lu\n", (ulong) undo->id); ut_error; } undo_page = trx_undo_page_get(undo->space, undo->zip_size, undo->hdr_page_no, mtr); seg_hdr = undo_page + TRX_UNDO_SEG_HDR; page_hdr = undo_page + TRX_UNDO_PAGE_HDR; /*------------------------------*/ undo->state = TRX_UNDO_PREPARED; #ifdef WITH_XOPEN undo->xid = trx->xid; #endif /* WITH_XOPEN */ /*------------------------------*/ mlog_write_ulint(seg_hdr + TRX_UNDO_STATE, undo->state, MLOG_2BYTES, mtr); offset = mach_read_from_2(seg_hdr + TRX_UNDO_LAST_LOG); undo_header = undo_page + offset; mlog_write_ulint(undo_header + TRX_UNDO_XID_EXISTS, TRUE, MLOG_1BYTE, mtr); #ifdef WITH_XOPEN trx_undo_write_xid(undo_header, &undo->xid, mtr); #else trx_undo_write_xid(undo_header, mtr); #endif /* WITH_XOPEN */ return(undo_page); } /**********************************************************************//** Adds the update undo log header as the first in the history list, and frees the memory object, or puts it to the list of cached update undo log segments. */ UNIV_INTERN void trx_undo_update_cleanup( /*====================*/ trx_t* trx, /*!< in: trx owning the update undo log */ page_t* undo_page, /*!< in: update undo log header page, x-latched */ mtr_t* mtr) /*!< in: mtr */ { trx_rseg_t* rseg; trx_undo_t* undo; undo = trx->update_undo; rseg = trx->rseg; ut_ad(mutex_own(&(rseg->mutex))); trx_purge_add_update_undo_to_history(trx, undo_page, mtr); UT_LIST_REMOVE(undo_list, rseg->update_undo_list, undo); trx->update_undo = NULL; if (undo->state == TRX_UNDO_CACHED) { UT_LIST_ADD_FIRST(undo_list, rseg->update_undo_cached, undo); } else { ut_ad(undo->state == TRX_UNDO_TO_PURGE); trx_undo_mem_free(undo); } } /******************************************************************//** Frees or caches an insert undo log after a transaction commit or rollback. Knowledge of inserts is not needed after a commit or rollback, therefore the data can be discarded. */ UNIV_INTERN void trx_undo_insert_cleanup( /*====================*/ trx_t* trx) /*!< in: transaction handle */ { trx_undo_t* undo; trx_rseg_t* rseg; undo = trx->insert_undo; ut_ad(undo); rseg = trx->rseg; mutex_enter(&(rseg->mutex)); UT_LIST_REMOVE(undo_list, rseg->insert_undo_list, undo); trx->insert_undo = NULL; if (undo->state == TRX_UNDO_CACHED) { UT_LIST_ADD_FIRST(undo_list, rseg->insert_undo_cached, undo); } else { ut_ad(undo->state == TRX_UNDO_TO_FREE); /* Delete first the undo log segment in the file */ mutex_exit(&(rseg->mutex)); trx_undo_seg_free(undo); mutex_enter(&(rseg->mutex)); ut_ad(rseg->curr_size > undo->size); rseg->curr_size -= undo->size; trx_undo_mem_free(undo); } mutex_exit(&(rseg->mutex)); } #endif /* !UNIV_HOTBACKUP */ haildb-2.3.2/trx/trx0trx.c0000644000175000017500000014054511513177357016270 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2010, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file trx/trx0trx.c The transaction Created 3/26/1996 Heikki Tuuri *******************************************************/ #include "trx0trx.h" #ifdef UNIV_NONINL #include "trx0trx.ic" #endif #include "trx0undo.h" #include "trx0rseg.h" #include "log0log.h" #include "que0que.h" #include "lock0lock.h" #include "trx0roll.h" #include "usr0sess.h" #include "read0read.h" #include "srv0srv.h" #include "thr0loc.h" #include "btr0sea.h" #include "os0proc.h" #include "trx0xa.h" #include "api0ucode.h" #include "api0ucode.h" /* TODO: Can we remove this? */ /* Dummy session used currently in client interface */ UNIV_INTERN sess_t* trx_dummy_sess = NULL; /* Number of transactions currently allocated for the client: protected by the kernel mutex */ UNIV_INTERN ulint trx_n_transactions = 0; /* Threads with unknown id. */ UNIV_INTERN os_thread_id_t NULL_THREAD_ID; /************************************************************************** Reset global variables. */ UNIV_INTERN void trx_var_init(void) /*==============*/ { trx_dummy_sess = NULL; trx_n_transactions = 0; } /************************************************************************** Determines if the currently running transaction is in strict_mode. @return TRUE if strict */ UNIV_INTERN ibool trx_is_strict( /*==========*/ trx_t* trx) /*!< in: transaction */ { return(FALSE); } /***************************************************************** Set detailed error message for the transaction. */ UNIV_INTERN void trx_set_detailed_error( /*===================*/ trx_t* trx, /*!< in: transaction struct */ const char* msg) /*!< in: detailed error message */ { ut_strlcpy(trx->detailed_error, msg, sizeof(trx->detailed_error)); } /****************************************************************//** Creates and initializes a transaction object. @return own: the transaction */ UNIV_INTERN trx_t* trx_create( /*=======*/ sess_t* sess) /*!< in: session */ { trx_t* trx; ut_ad(mutex_own(&kernel_mutex)); ut_ad(sess); trx = mem_alloc(sizeof(trx_t)); trx->magic_n = TRX_MAGIC_N; trx->op_info = ""; trx->is_purge = 0; trx->is_recovered = 0; trx->conc_state = TRX_NOT_STARTED; trx->start_time = time(NULL); trx->isolation_level = TRX_ISO_REPEATABLE_READ; trx->id = ut_dulint_zero; trx->no = ut_dulint_max; #ifdef WITH_XOPEN trx->support_xa = FALSE; trx->flush_log_later = FALSE; trx->must_flush_log_later = FALSE; #endif /* WITH_XOPEN */ trx->check_foreigns = TRUE; trx->check_unique_secondary = TRUE; trx->dict_operation = TRX_DICT_OP_NONE; trx->table_id = ut_dulint_zero; trx->client_thd = NULL; trx->client_query_str = NULL; trx->duplicates = 0; trx->n_client_tables_in_use = 0; trx->client_n_tables_locked = 0; mutex_create(&trx->undo_mutex, SYNC_TRX_UNDO); trx->rseg = NULL; trx->undo_no = ut_dulint_zero; trx->last_sql_stat_start.least_undo_no = ut_dulint_zero; trx->insert_undo = NULL; trx->update_undo = NULL; trx->undo_no_arr = NULL; trx->error_state = DB_SUCCESS; trx->error_key_num = 0; trx->detailed_error[0] = '\0'; trx->sess = sess; trx->que_state = TRX_QUE_RUNNING; trx->n_active_thrs = 0; trx->handling_signals = FALSE; UT_LIST_INIT(trx->signals); UT_LIST_INIT(trx->reply_signals); trx->graph = NULL; trx->wait_lock = NULL; trx->was_chosen_as_deadlock_victim = FALSE; UT_LIST_INIT(trx->wait_thrs); trx->lock_heap = mem_heap_create_in_buffer(256); UT_LIST_INIT(trx->trx_locks); UT_LIST_INIT(trx->trx_savepoints); trx->dict_operation_lock_mode = 0; trx->has_search_latch = FALSE; trx->search_latch_timeout = BTR_SEA_TIMEOUT; trx->global_read_view_heap = mem_heap_create(256); trx->global_read_view = NULL; trx->read_view = NULL; #ifdef WITH_XOPEN /* Set X/Open XA transaction identification to NULL */ memset(&trx->xid, 0, sizeof(trx->xid)); trx->xid.formatID = -1; #endif /* WITH_XOPEN */ return(trx); } /********************************************************************//** Creates a transaction object for client. @return own: transaction object */ UNIV_INTERN trx_t* trx_allocate_for_client( /*====================*/ void* arg) /*!< in: pointer to client data */ { trx_t* trx; mutex_enter(&kernel_mutex); trx = trx_create(trx_dummy_sess); trx_n_transactions++; UT_LIST_ADD_FIRST(client_trx_list, trx_sys->client_trx_list, trx); mutex_exit(&kernel_mutex); trx->client_thread_id = os_thread_get_curr_id(); trx->client_process_no = os_proc_get_number(); return(trx); } /********************************************************************//** Creates a transaction object for background operations by the master thread. @return own: transaction object */ UNIV_INTERN trx_t* trx_allocate_for_background(void) /*=============================*/ { trx_t* trx; mutex_enter(&kernel_mutex); trx = trx_create(trx_dummy_sess); mutex_exit(&kernel_mutex); trx->client_process_no = 0; trx->client_thread_id = NULL_THREAD_ID; return(trx); } /********************************************************************//** Releases the search latch if trx has reserved it. */ UNIV_INTERN void trx_search_latch_release_if_reserved( /*=================================*/ trx_t* trx) /*!< in: transaction */ { if (trx->has_search_latch) { rw_lock_s_unlock(&btr_search_latch); trx->has_search_latch = FALSE; } } /********************************************************************//** Frees a transaction object. */ static void trx_free( /*=====*/ trx_t* trx) /*!< in, own: trx object */ { ut_ad(mutex_own(&kernel_mutex)); if (trx->n_client_tables_in_use != 0 || trx->client_n_tables_locked != 0) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Error: Client is freeing a trx instance\n" "InnoDB: though trx->n_client_tables_in_use is %lu\n" "InnoDB: and trx->client_n_tables_locked is %lu.\n", (ulong)trx->n_client_tables_in_use, (ulong)trx->client_n_tables_locked); trx_print(ib_stream, trx, 600); ut_print_buf(ib_stream, trx, sizeof(trx_t)); ib_logger(ib_stream, "\n"); } ut_a(trx->magic_n == TRX_MAGIC_N); trx->magic_n = 11112222; ut_a(trx->conc_state == TRX_NOT_STARTED); mutex_free(&(trx->undo_mutex)); ut_a(trx->insert_undo == NULL); ut_a(trx->update_undo == NULL); if (trx->undo_no_arr) { trx_undo_arr_free(trx->undo_no_arr); } ut_a(UT_LIST_GET_LEN(trx->signals) == 0); ut_a(UT_LIST_GET_LEN(trx->reply_signals) == 0); ut_a(trx->wait_lock == NULL); ut_a(UT_LIST_GET_LEN(trx->wait_thrs) == 0); ut_a(!trx->has_search_latch); ut_a(trx->dict_operation_lock_mode == 0); if (trx->lock_heap) { mem_heap_free(trx->lock_heap); } ut_a(UT_LIST_GET_LEN(trx->trx_locks) == 0); if (trx->global_read_view_heap) { mem_heap_free(trx->global_read_view_heap); } trx->global_read_view = NULL; ut_a(trx->read_view == NULL); mem_free(trx); } /********************************************************************//** Frees a transaction object for client. */ UNIV_INTERN void trx_free_for_client( /*================*/ trx_t* trx) /*!< in, own: trx object */ { mutex_enter(&kernel_mutex); UT_LIST_REMOVE(client_trx_list, trx_sys->client_trx_list, trx); trx_free(trx); ut_a(trx_n_transactions > 0); trx_n_transactions--; mutex_exit(&kernel_mutex); } /********************************************************************//** Frees a transaction object of a background operation of the master thread. */ UNIV_INTERN void trx_free_for_background( /*====================*/ trx_t* trx) /*!< in, own: trx object */ { mutex_enter(&kernel_mutex); trx_free(trx); mutex_exit(&kernel_mutex); } /****************************************************************//** Inserts the trx handle in the trx system trx list in the right position. The list is sorted on the trx id so that the biggest id is at the list start. This function is used at the database startup to insert incomplete transactions to the list. */ static void trx_list_insert_ordered( /*====================*/ trx_t* trx) /*!< in: trx handle */ { trx_t* trx2; ut_ad(mutex_own(&kernel_mutex)); trx2 = UT_LIST_GET_FIRST(trx_sys->trx_list); while (trx2 != NULL) { if (ut_dulint_cmp(trx->id, trx2->id) >= 0) { ut_ad(ut_dulint_cmp(trx->id, trx2->id) == 1); break; } trx2 = UT_LIST_GET_NEXT(trx_list, trx2); } if (trx2 != NULL) { trx2 = UT_LIST_GET_PREV(trx_list, trx2); if (trx2 == NULL) { UT_LIST_ADD_FIRST(trx_list, trx_sys->trx_list, trx); } else { UT_LIST_INSERT_AFTER(trx_list, trx_sys->trx_list, trx2, trx); } } else { UT_LIST_ADD_LAST(trx_list, trx_sys->trx_list, trx); } } /****************************************************************//** Creates trx objects for transactions and initializes the trx list of trx_sys at database start. Rollback segment and undo log lists must already exist when this function is called, because the lists of transactions to be rolled back or cleaned up are built based on the undo log lists. */ UNIV_INTERN void trx_lists_init_at_db_start( /*=======================*/ ib_recovery_t recovery) /*!< in: recovery flag */ { trx_rseg_t* rseg; trx_undo_t* undo; trx_t* trx; ut_ad(mutex_own(&kernel_mutex)); UT_LIST_INIT(trx_sys->trx_list); /* Look from the rollback segments if there exist undo logs for transactions */ rseg = UT_LIST_GET_FIRST(trx_sys->rseg_list); while (rseg != NULL) { undo = UT_LIST_GET_FIRST(rseg->insert_undo_list); while (undo != NULL) { trx = trx_create(trx_dummy_sess); trx->is_recovered = TRUE; trx->id = undo->trx_id; #ifdef WITH_XOPEN trx->xid = undo->xid; #endif /* WITH_XOPEN */ trx->insert_undo = undo; trx->rseg = rseg; if (undo->state != TRX_UNDO_ACTIVE) { /* Prepared transactions are left in the prepared state waiting for a commit or abort decision from the client. */ if (undo->state == TRX_UNDO_PREPARED) { ib_logger(ib_stream, "InnoDB: Transaction " TRX_ID_FMT " was in the" " XA prepared state.\n", TRX_ID_PREP_PRINTF(trx->id)); if (recovery == IB_RECOVERY_DEFAULT) { trx->conc_state = TRX_PREPARED; } else { ib_logger(ib_stream, "InnoDB: Since" " force_recovery" " > 0, we will do a" " rollback" " anyway.\n"); trx->conc_state = TRX_ACTIVE; } } else { trx->conc_state = TRX_COMMITTED_IN_MEMORY; } /* We give a dummy value for the trx no; this should have no relevance since purge is not interested in committed transaction numbers, unless they are in the history list, in which case it looks the number from the disk based undo log structure */ trx->no = trx->id; } else { trx->conc_state = TRX_ACTIVE; /* A running transaction always has the number field inited to ut_dulint_max */ trx->no = ut_dulint_max; } if (undo->dict_operation) { trx_set_dict_operation(trx, TRX_DICT_OP_TABLE); trx->table_id = undo->table_id; } if (!undo->empty) { trx->undo_no = ut_dulint_add(undo->top_undo_no, 1); } trx_list_insert_ordered(trx); undo = UT_LIST_GET_NEXT(undo_list, undo); } undo = UT_LIST_GET_FIRST(rseg->update_undo_list); while (undo != NULL) { trx = trx_get_on_id(undo->trx_id); if (NULL == trx) { trx = trx_create(trx_dummy_sess); trx->is_recovered = TRUE; trx->id = undo->trx_id; #ifdef WITH_XOPEN trx->xid = undo->xid; #endif /* WITH_XOPEN */ if (undo->state != TRX_UNDO_ACTIVE) { /* Prepared transactions are left in the prepared state waiting for a commit or abort decision from the client. */ if (undo->state == TRX_UNDO_PREPARED) { ib_logger(ib_stream, "InnoDB: Transaction " TRX_ID_FMT " was in the" " XA prepared state.\n", TRX_ID_PREP_PRINTF( trx->id)); if (recovery == IB_RECOVERY_DEFAULT) { trx->conc_state = TRX_PREPARED; } else { ib_logger(ib_stream, "InnoDB: Since" " force_recovery" " > 0, we will" " do a rollback" " anyway.\n"); trx->conc_state = TRX_ACTIVE; } } else { trx->conc_state = TRX_COMMITTED_IN_MEMORY; } /* We give a dummy value for the trx number */ trx->no = trx->id; } else { trx->conc_state = TRX_ACTIVE; /* A running transaction always has the number field inited to ut_dulint_max */ trx->no = ut_dulint_max; } trx->rseg = rseg; trx_list_insert_ordered(trx); if (undo->dict_operation) { trx_set_dict_operation( trx, TRX_DICT_OP_TABLE); trx->table_id = undo->table_id; } } trx->update_undo = undo; if ((!undo->empty) && (ut_dulint_cmp(undo->top_undo_no, trx->undo_no) >= 0)) { trx->undo_no = ut_dulint_add(undo->top_undo_no, 1); } undo = UT_LIST_GET_NEXT(undo_list, undo); } rseg = UT_LIST_GET_NEXT(rseg_list, rseg); } } /******************************************************************//** Assigns a rollback segment to a transaction in a round-robin fashion. Skips the SYSTEM rollback segment if another is available. @return assigned rollback segment id */ UNIV_INLINE ulint trx_assign_rseg(void) /*=================*/ { trx_rseg_t* rseg = trx_sys->latest_rseg; ut_ad(mutex_own(&kernel_mutex)); loop: /* Get next rseg in a round-robin fashion */ rseg = UT_LIST_GET_NEXT(rseg_list, rseg); if (rseg == NULL) { rseg = UT_LIST_GET_FIRST(trx_sys->rseg_list); } /* If it is the SYSTEM rollback segment, and there exist others, skip it */ if ((rseg->id == TRX_SYS_SYSTEM_RSEG_ID) && (UT_LIST_GET_LEN(trx_sys->rseg_list) > 1)) { goto loop; } trx_sys->latest_rseg = rseg; return(rseg->id); } /****************************************************************//** Starts a new transaction. @return TRUE */ UNIV_INTERN ibool trx_start_low( /*==========*/ trx_t* trx, /*!< in: transaction */ ulint rseg_id)/*!< in: rollback segment id; if ULINT_UNDEFINED is passed, the system chooses the rollback segment automatically in a round-robin fashion */ { trx_rseg_t* rseg; ut_ad(mutex_own(&kernel_mutex)); ut_ad(trx->rseg == NULL); ut_ad(trx->magic_n == TRX_MAGIC_N); if (trx->is_purge) { trx->id = ut_dulint_zero; trx->conc_state = TRX_ACTIVE; trx->start_time = time(NULL); return(TRUE); } ut_ad(trx->conc_state != TRX_ACTIVE); if (rseg_id == ULINT_UNDEFINED) { rseg_id = trx_assign_rseg(); } rseg = trx_sys_get_nth_rseg(trx_sys, rseg_id); trx->id = trx_sys_get_new_trx_id(); /* The initial value for trx->no: ut_dulint_max is used in read_view_open_now: */ trx->no = ut_dulint_max; trx->rseg = rseg; trx->conc_state = TRX_ACTIVE; trx->start_time = time(NULL); #ifdef WITH_XOPEN trx->flush_log_later = FALSE; trx->must_flush_log_later = FALSE; #endif /* WITH_XOPEN */ UT_LIST_ADD_FIRST(trx_list, trx_sys->trx_list, trx); return(TRUE); } /****************************************************************//** Starts a new transaction. @return TRUE */ UNIV_INTERN ibool trx_start( /*======*/ trx_t* trx, /*!< in: transaction */ ulint rseg_id)/*!< in: rollback segment id; if ULINT_UNDEFINED is passed, the system chooses the rollback segment automatically in a round-robin fashion */ { ibool ret; /* Update the info whether we should skip XA steps that eat CPU time For the duration of the transaction trx->support_xa is not reread from thd so any changes in the value take effect in the next transaction. This is to avoid a scenario where some undo generated by a transaction, has XA stuff, and other undo, generated by the same transaction, doesn't. */ /* FIXME: This requires an API change to support */ /* trx->support_xa = ib_supports_xa(trx->client_thd); */ mutex_enter(&kernel_mutex); ret = trx_start_low(trx, rseg_id); mutex_exit(&kernel_mutex); return(ret); } /****************************************************************//** Commits a transaction. */ UNIV_INTERN void trx_commit_off_kernel( /*==================*/ trx_t* trx) /*!< in: transaction */ { page_t* update_hdr_page; ib_uint64_t lsn = 0; trx_rseg_t* rseg; trx_undo_t* undo; mtr_t mtr; ut_ad(mutex_own(&kernel_mutex)); rseg = trx->rseg; if (trx->insert_undo != NULL || trx->update_undo != NULL) { mutex_exit(&kernel_mutex); mtr_start(&mtr); /* Change the undo log segment states from TRX_UNDO_ACTIVE to some other state: these modifications to the file data structure define the transaction as committed in the file based world, at the serialization point of the log sequence number lsn obtained below. */ mutex_enter(&(rseg->mutex)); if (trx->insert_undo != NULL) { trx_undo_set_state_at_finish( rseg, trx, trx->insert_undo, &mtr); } undo = trx->update_undo; if (undo) { mutex_enter(&kernel_mutex); trx->no = trx_sys_get_new_trx_no(); mutex_exit(&kernel_mutex); /* It is not necessary to obtain trx->undo_mutex here because only a single OS thread is allowed to do the transaction commit for this transaction. */ update_hdr_page = trx_undo_set_state_at_finish( rseg, trx, undo, &mtr); /* We have to do the cleanup for the update log while holding the rseg mutex because update log headers have to be put to the history list in the order of the trx number. */ trx_undo_update_cleanup(trx, update_hdr_page, &mtr); } mutex_exit(&(rseg->mutex)); /* The following call commits the mini-transaction, making the whole transaction committed in the file-based world, at this log sequence number. The transaction becomes 'durable' when we write the log to disk, but in the logical sense the commit in the file-based data structures (undo logs etc.) happens here. NOTE that transaction numbers, which are assigned only to transactions with an update undo log, do not necessarily come in exactly the same order as commit lsn's, if the transactions have different rollback segments. To get exactly the same order we should hold the kernel mutex up to this point, adding to the contention of the kernel mutex. However, if a transaction T2 is able to see modifications made by a transaction T1, T2 will always get a bigger transaction number and a bigger commit lsn than T1. */ /*--------------*/ mtr_commit(&mtr); /*--------------*/ lsn = mtr.end_lsn; mutex_enter(&kernel_mutex); } ut_ad(trx->conc_state == TRX_ACTIVE || trx->conc_state == TRX_PREPARED); ut_ad(mutex_own(&kernel_mutex)); /* The following assignment makes the transaction committed in memory and makes its changes to data visible to other transactions. NOTE that there is a small discrepancy from the strict formal visibility rules here: a human user of the database can see modifications made by another transaction T even before the necessary log segment has been flushed to the disk. If the database happens to crash before the flush, the user has seen modifications from T which will never be a committed transaction. However, any transaction T2 which sees the modifications of the committing transaction T, and which also itself makes modifications to the database, will get an lsn larger than the committing transaction T. In the case where the log flush fails, and T never gets committed, also T2 will never get committed. */ /*--------------------------------------*/ trx->conc_state = TRX_COMMITTED_IN_MEMORY; /*--------------------------------------*/ /* If we release kernel_mutex below and we are still doing recovery i.e.: back ground rollback thread is still active then there is a chance that the rollback thread may see this trx as COMMITTED_IN_MEMORY and goes adhead to clean it up calling trx_cleanup_at_db_startup(). This can happen in the case we are committing a trx here that is left in PREPARED state during the crash. Note that commit of the rollback of a PREPARED trx happens in the recovery thread while the rollback of other transactions happen in the background thread. To avoid this race we unconditionally unset the is_recovered flag from the trx. */ trx->is_recovered = FALSE; lock_release_off_kernel(trx); if (trx->global_read_view) { read_view_close(trx->global_read_view); mem_heap_empty(trx->global_read_view_heap); trx->global_read_view = NULL; } trx->read_view = NULL; if (lsn) { mutex_exit(&kernel_mutex); if (trx->insert_undo != NULL) { trx_undo_insert_cleanup(trx); } /* NOTE that we could possibly make a group commit more efficient here: call os_thread_yield here to allow also other trxs to come to commit! */ /*-------------------------------------*/ /* Depending on the config options, we may now write the log buffer to the log files, making the transaction durable if the OS does not crash. We may also flush the log files to disk, making the transaction durable also at an OS crash or a power outage. The idea in InnoDB's group commit is that a group of transactions gather behind a trx doing a physical disk write to log files, and when that physical write has been completed, one of those transactions does a write which commits the whole group. Note that this group commit will only bring benefit if there are > 2 users in the database. Then at least 2 users can gather behind one doing the physical log write to disk. */ /* If we are calling trx_commit() under prepare_commit_mutex, we will delay possible log write and flush to a separate function trx_commit_flush_log(), which is only called when the thread has released the mutex. This is to make the group commit algorithm to work. Otherwise, the prepare_commit mutex would serialize all commits and prevent a group of transactions from gathering. */ #ifdef WITH_XOPEN if (trx->flush_log_later) { /* Do nothing yet */ trx->must_flush_log_later = TRUE; } else #endif /* WITH_XOPEN */ if (srv_flush_log_at_trx_commit == 0) { /* Do nothing */ } else if (srv_flush_log_at_trx_commit == 1) { if (srv_unix_file_flush_method == SRV_UNIX_NOSYNC) { /* Write the log but do not flush it to disk */ log_write_up_to(lsn, LOG_WAIT_ONE_GROUP, FALSE); } else { /* Write the log to the log files AND flush them to disk */ log_write_up_to(lsn, LOG_WAIT_ONE_GROUP, TRUE); } } else if (srv_flush_log_at_trx_commit == 2) { /* Write the log but do not flush it to disk */ log_write_up_to(lsn, LOG_WAIT_ONE_GROUP, FALSE); } else { ut_error; } trx->commit_lsn = lsn; /*-------------------------------------*/ mutex_enter(&kernel_mutex); } /* Free all savepoints */ trx_roll_free_all_savepoints(trx); trx->conc_state = TRX_NOT_STARTED; trx->rseg = NULL; trx->undo_no = ut_dulint_zero; trx->last_sql_stat_start.least_undo_no = ut_dulint_zero; trx->client_query_str = NULL; ut_ad(UT_LIST_GET_LEN(trx->wait_thrs) == 0); ut_ad(UT_LIST_GET_LEN(trx->trx_locks) == 0); UT_LIST_REMOVE(trx_list, trx_sys->trx_list, trx); } /****************************************************************//** Cleans up a transaction at database startup. The cleanup is needed if the transaction already got to the middle of a commit when the database crashed, and we cannot roll it back. */ UNIV_INTERN void trx_cleanup_at_db_startup( /*======================*/ trx_t* trx) /*!< in: transaction */ { if (trx->insert_undo != NULL) { trx_undo_insert_cleanup(trx); } trx->conc_state = TRX_NOT_STARTED; trx->rseg = NULL; trx->undo_no = ut_dulint_zero; trx->last_sql_stat_start.least_undo_no = ut_dulint_zero; UT_LIST_REMOVE(trx_list, trx_sys->trx_list, trx); } /********************************************************************//** Assigns a read view for a consistent read query. All the consistent reads within the same transaction will get the same read view, which is created when this function is first called for a new started transaction. @return consistent read view */ UNIV_INTERN read_view_t* trx_assign_read_view( /*=================*/ trx_t* trx) /*!< in: active transaction */ { ut_ad(trx->conc_state == TRX_ACTIVE); if (trx->read_view) { return(trx->read_view); } mutex_enter(&kernel_mutex); if (!trx->read_view) { trx->read_view = read_view_open_now( trx->id, trx->global_read_view_heap); trx->global_read_view = trx->read_view; } mutex_exit(&kernel_mutex); return(trx->read_view); } /****************************************************************//** Commits a transaction. NOTE that the kernel mutex is temporarily released. */ static void trx_handle_commit_sig_off_kernel( /*=============================*/ trx_t* trx, /*!< in: transaction */ que_thr_t** next_thr) /*!< in/out: next query thread to run; if the value which is passed in is a pointer to a NULL pointer, then the calling function can start running a new query thread */ { trx_sig_t* sig; trx_sig_t* next_sig; ut_ad(mutex_own(&kernel_mutex)); trx->que_state = TRX_QUE_COMMITTING; trx_commit_off_kernel(trx); ut_ad(UT_LIST_GET_LEN(trx->wait_thrs) == 0); /* Remove all TRX_SIG_COMMIT signals from the signal queue and send reply messages to them */ sig = UT_LIST_GET_FIRST(trx->signals); while (sig != NULL) { next_sig = UT_LIST_GET_NEXT(signals, sig); if (sig->type == TRX_SIG_COMMIT) { trx_sig_reply(sig, next_thr); trx_sig_remove(trx, sig); } sig = next_sig; } trx->que_state = TRX_QUE_RUNNING; } /***********************************************************//** The transaction must be in the TRX_QUE_LOCK_WAIT state. Puts it to the TRX_QUE_RUNNING state and releases query threads which were waiting for a lock in the wait_thrs list. */ UNIV_INTERN void trx_end_lock_wait( /*==============*/ trx_t* trx) /*!< in: transaction */ { que_thr_t* thr; ut_ad(mutex_own(&kernel_mutex)); ut_ad(trx->que_state == TRX_QUE_LOCK_WAIT); thr = UT_LIST_GET_FIRST(trx->wait_thrs); while (thr != NULL) { que_thr_end_wait_no_next_thr(thr); UT_LIST_REMOVE(trx_thrs, trx->wait_thrs, thr); thr = UT_LIST_GET_FIRST(trx->wait_thrs); } trx->que_state = TRX_QUE_RUNNING; } /***********************************************************//** Moves the query threads in the lock wait list to the SUSPENDED state and puts the transaction to the TRX_QUE_RUNNING state. */ static void trx_lock_wait_to_suspended( /*=======================*/ trx_t* trx) /*!< in: transaction in the TRX_QUE_LOCK_WAIT state */ { que_thr_t* thr; ut_ad(mutex_own(&kernel_mutex)); ut_ad(trx->que_state == TRX_QUE_LOCK_WAIT); thr = UT_LIST_GET_FIRST(trx->wait_thrs); while (thr != NULL) { thr->state = QUE_THR_SUSPENDED; UT_LIST_REMOVE(trx_thrs, trx->wait_thrs, thr); thr = UT_LIST_GET_FIRST(trx->wait_thrs); } trx->que_state = TRX_QUE_RUNNING; } /***********************************************************//** Moves the query threads in the sig reply wait list of trx to the SUSPENDED state. */ static void trx_sig_reply_wait_to_suspended( /*============================*/ trx_t* trx) /*!< in: transaction */ { trx_sig_t* sig; que_thr_t* thr; ut_ad(mutex_own(&kernel_mutex)); sig = UT_LIST_GET_FIRST(trx->reply_signals); while (sig != NULL) { thr = sig->receiver; ut_ad(thr->state == QUE_THR_SIG_REPLY_WAIT); thr->state = QUE_THR_SUSPENDED; sig->receiver = NULL; UT_LIST_REMOVE(reply_signals, trx->reply_signals, sig); sig = UT_LIST_GET_FIRST(trx->reply_signals); } } /*****************************************************************//** Checks the compatibility of a new signal with the other signals in the queue. @return TRUE if the signal can be queued */ static ibool trx_sig_is_compatible( /*==================*/ trx_t* trx, /*!< in: trx handle */ ulint type, /*!< in: signal type */ ulint sender) /*!< in: TRX_SIG_SELF or TRX_SIG_OTHER_SESS */ { trx_sig_t* sig; ut_ad(mutex_own(&kernel_mutex)); if (UT_LIST_GET_LEN(trx->signals) == 0) { return(TRUE); } if (sender == TRX_SIG_SELF) { if (type == TRX_SIG_ERROR_OCCURRED) { return(TRUE); } else if (type == TRX_SIG_BREAK_EXECUTION) { return(TRUE); } else { return(FALSE); } } ut_ad(sender == TRX_SIG_OTHER_SESS); sig = UT_LIST_GET_FIRST(trx->signals); if (type == TRX_SIG_COMMIT) { while (sig != NULL) { if (sig->type == TRX_SIG_TOTAL_ROLLBACK) { return(FALSE); } sig = UT_LIST_GET_NEXT(signals, sig); } return(TRUE); } else if (type == TRX_SIG_TOTAL_ROLLBACK) { while (sig != NULL) { if (sig->type == TRX_SIG_COMMIT) { return(FALSE); } sig = UT_LIST_GET_NEXT(signals, sig); } return(TRUE); } else if (type == TRX_SIG_BREAK_EXECUTION) { return(TRUE); } else { ut_error; return(FALSE); } } /****************************************************************//** Sends a signal to a trx object. */ UNIV_INTERN void trx_sig_send( /*=========*/ trx_t* trx, /*!< in: trx handle */ ulint type, /*!< in: signal type */ ulint sender, /*!< in: TRX_SIG_SELF or TRX_SIG_OTHER_SESS */ que_thr_t* receiver_thr, /*!< in: query thread which wants the reply, or NULL; if type is TRX_SIG_END_WAIT, this must be NULL */ trx_savept_t* savept, /*!< in: possible rollback savepoint, or NULL */ que_thr_t** next_thr) /*!< in/out: next query thread to run; if the value which is passed in is a pointer to a NULL pointer, then the calling function can start running a new query thread; if the parameter is NULL, it is ignored */ { trx_sig_t* sig; trx_t* receiver_trx; ut_ad(trx); ut_ad(mutex_own(&kernel_mutex)); if (!trx_sig_is_compatible(trx, type, sender)) { /* The signal is not compatible with the other signals in the queue: die */ ut_error; } /* Queue the signal object */ if (UT_LIST_GET_LEN(trx->signals) == 0) { /* The signal list is empty: the 'sig' slot must be unused (we improve performance a bit by avoiding mem_alloc) */ sig = &(trx->sig); } else { /* It might be that the 'sig' slot is unused also in this case, but we choose the easy way of using mem_alloc */ sig = mem_alloc(sizeof(trx_sig_t)); } UT_LIST_ADD_LAST(signals, trx->signals, sig); sig->type = type; sig->sender = sender; sig->receiver = receiver_thr; if (savept) { sig->savept = *savept; } if (receiver_thr) { receiver_trx = thr_get_trx(receiver_thr); UT_LIST_ADD_LAST(reply_signals, receiver_trx->reply_signals, sig); } if (trx->sess->state == SESS_ERROR) { trx_sig_reply_wait_to_suspended(trx); } if ((sender != TRX_SIG_SELF) || (type == TRX_SIG_BREAK_EXECUTION)) { ut_error; } /* If there were no other signals ahead in the queue, try to start handling of the signal */ if (UT_LIST_GET_FIRST(trx->signals) == sig) { trx_sig_start_handle(trx, next_thr); } } /****************************************************************//** Ends signal handling. If the session is in the error state, and trx->graph_before_signal_handling != NULL, then returns control to the error handling routine of the graph (currently just returns the control to the graph root which then will send an error message to the client). */ UNIV_INTERN void trx_end_signal_handling( /*====================*/ trx_t* trx) /*!< in: trx */ { ut_ad(mutex_own(&kernel_mutex)); ut_ad(trx->handling_signals == TRUE); trx->handling_signals = FALSE; trx->graph = trx->graph_before_signal_handling; if (trx->graph && (trx->sess->state == SESS_ERROR)) { que_fork_error_handle(trx, trx->graph); } } /****************************************************************//** Starts handling of a trx signal. */ UNIV_INTERN void trx_sig_start_handle( /*=================*/ trx_t* trx, /*!< in: trx handle */ que_thr_t** next_thr) /*!< in/out: next query thread to run; if the value which is passed in is a pointer to a NULL pointer, then the calling function can start running a new query thread; if the parameter is NULL, it is ignored */ { trx_sig_t* sig; ulint type; loop: /* We loop in this function body as long as there are queued signals we can process immediately */ ut_ad(trx); ut_ad(mutex_own(&kernel_mutex)); if (trx->handling_signals && (UT_LIST_GET_LEN(trx->signals) == 0)) { trx_end_signal_handling(trx); return; } if (trx->conc_state == TRX_NOT_STARTED) { trx_start_low(trx, ULINT_UNDEFINED); } /* If the trx is in a lock wait state, moves the waiting query threads to the suspended state */ if (trx->que_state == TRX_QUE_LOCK_WAIT) { trx_lock_wait_to_suspended(trx); } /* If the session is in the error state and this trx has threads waiting for reply from signals, moves these threads to the suspended state, canceling wait reservations; note that if the transaction has sent a commit or rollback signal to itself, and its session is not in the error state, then nothing is done here. */ if (trx->sess->state == SESS_ERROR) { trx_sig_reply_wait_to_suspended(trx); } /* If there are no running query threads, we can start processing of a signal, otherwise we have to wait until all query threads of this transaction are aware of the arrival of the signal. */ if (trx->n_active_thrs > 0) { return; } if (trx->handling_signals == FALSE) { trx->graph_before_signal_handling = trx->graph; trx->handling_signals = TRUE; } sig = UT_LIST_GET_FIRST(trx->signals); type = sig->type; if (type == TRX_SIG_COMMIT) { trx_handle_commit_sig_off_kernel(trx, next_thr); } else if ((type == TRX_SIG_TOTAL_ROLLBACK) || (type == TRX_SIG_ROLLBACK_TO_SAVEPT)) { trx_rollback(trx, sig, next_thr); /* No further signals can be handled until the rollback completes, therefore we return */ return; } else if (type == TRX_SIG_ERROR_OCCURRED) { trx_rollback(trx, sig, next_thr); /* No further signals can be handled until the rollback completes, therefore we return */ return; } else if (type == TRX_SIG_BREAK_EXECUTION) { trx_sig_reply(sig, next_thr); trx_sig_remove(trx, sig); } else { ut_error; } goto loop; } /****************************************************************//** Send the reply message when a signal in the queue of the trx has been handled. */ UNIV_INTERN void trx_sig_reply( /*==========*/ trx_sig_t* sig, /*!< in: signal */ que_thr_t** next_thr) /*!< in/out: next query thread to run; if the value which is passed in is a pointer to a NULL pointer, then the calling function can start running a new query thread */ { trx_t* receiver_trx; ut_ad(sig); ut_ad(mutex_own(&kernel_mutex)); if (sig->receiver != NULL) { ut_ad((sig->receiver)->state == QUE_THR_SIG_REPLY_WAIT); receiver_trx = thr_get_trx(sig->receiver); UT_LIST_REMOVE(reply_signals, receiver_trx->reply_signals, sig); ut_ad(receiver_trx->sess->state != SESS_ERROR); que_thr_end_wait(sig->receiver, next_thr); sig->receiver = NULL; } } /****************************************************************//** Removes a signal object from the trx signal queue. */ UNIV_INTERN void trx_sig_remove( /*===========*/ trx_t* trx, /*!< in: trx handle */ trx_sig_t* sig) /*!< in, own: signal */ { ut_ad(trx && sig); ut_ad(mutex_own(&kernel_mutex)); ut_ad(sig->receiver == NULL); UT_LIST_REMOVE(signals, trx->signals, sig); sig->type = 0; /* reset the field to catch possible bugs */ if (sig != &(trx->sig)) { mem_free(sig); } } /*********************************************************************//** Creates a commit command node struct. @return own: commit node struct */ UNIV_INTERN commit_node_t* commit_node_create( /*===============*/ mem_heap_t* heap) /*!< in: mem heap where created */ { commit_node_t* node; node = mem_heap_alloc(heap, sizeof(commit_node_t)); node->common.type = QUE_NODE_COMMIT; node->state = COMMIT_NODE_SEND; return(node); } /***********************************************************//** Performs an execution step for a commit type node in a query graph. @return query thread to run next, or NULL */ UNIV_INTERN que_thr_t* trx_commit_step( /*============*/ que_thr_t* thr) /*!< in: query thread */ { commit_node_t* node; que_thr_t* next_thr; node = thr->run_node; ut_ad(que_node_get_type(node) == QUE_NODE_COMMIT); if (thr->prev_node == que_node_get_parent(node)) { node->state = COMMIT_NODE_SEND; } if (node->state == COMMIT_NODE_SEND) { mutex_enter(&kernel_mutex); node->state = COMMIT_NODE_WAIT; next_thr = NULL; thr->state = QUE_THR_SIG_REPLY_WAIT; /* Send the commit signal to the transaction */ trx_sig_send(thr_get_trx(thr), TRX_SIG_COMMIT, TRX_SIG_SELF, thr, NULL, &next_thr); mutex_exit(&kernel_mutex); return(next_thr); } ut_ad(node->state == COMMIT_NODE_WAIT); node->state = COMMIT_NODE_SEND; thr->run_node = que_node_get_parent(node); return(thr); } /**********************************************************************//** Does the transaction commit for client. @return DB_SUCCESS or error number */ UNIV_INTERN ulint trx_commit( /*=======*/ trx_t* trx) /*!< in: trx handle */ { /* Because we do not do the commit by sending an Innobase sig to the transaction, we must here make sure that trx has been started. */ ut_a(trx); trx->op_info = "committing"; mutex_enter(&kernel_mutex); trx_commit_off_kernel(trx); mutex_exit(&kernel_mutex); trx->op_info = ""; return(DB_SUCCESS); } #ifdef WITH_XOPEN /**********************************************************************//** If required, flushes the log to disk if we called trx_commit_for_client() with trx->flush_log_later == TRUE. @return 0 or error number */ UNIV_INTERN ulint trx_commit_flush_log( /*=================*/ trx_t* trx) /*!< in: trx handle */ { ib_uint64_t lsn = trx->commit_lsn; ut_a(trx); trx->op_info = "flushing log"; if (!trx->must_flush_log_later) { /* Do nothing */ } else if (srv_flush_log_at_trx_commit == 0) { /* Do nothing */ } else if (srv_flush_log_at_trx_commit == 1) { if (srv_unix_file_flush_method == SRV_UNIX_NOSYNC) { /* Write the log but do not flush it to disk */ log_write_up_to(lsn, LOG_WAIT_ONE_GROUP, FALSE); } else { /* Write the log to the log files AND flush them to disk */ log_write_up_to(lsn, LOG_WAIT_ONE_GROUP, TRUE); } } else if (srv_flush_log_at_trx_commit == 2) { /* Write the log but do not flush it to disk */ log_write_up_to(lsn, LOG_WAIT_ONE_GROUP, FALSE); } else { ut_error; } trx->must_flush_log_later = FALSE; trx->op_info = ""; return(0); } #endif /**********************************************************************//** Marks the latest SQL statement ended. */ UNIV_INTERN void trx_mark_sql_stat_end( /*==================*/ trx_t* trx) /*!< in: trx handle */ { ut_a(trx); if (trx->conc_state == TRX_NOT_STARTED) { trx->undo_no = ut_dulint_zero; } trx->last_sql_stat_start.least_undo_no = trx->undo_no; } /**********************************************************************//** Prints info about a transaction to the given file. The caller must own the kernel mutex. */ UNIV_INTERN void trx_print( /*======*/ ib_stream_t ib_stream, /*!< in: output stream */ trx_t* trx, /*!< in: transaction */ ulint max_query_len) /*!< in: max query length to print, or 0 to use the default max length */ { ibool newline; ib_logger(ib_stream, "TRANSACTION " TRX_ID_FMT, TRX_ID_PREP_PRINTF(trx->id)); switch (trx->conc_state) { case TRX_NOT_STARTED: ib_logger(ib_stream, ", not started"); break; case TRX_ACTIVE: ib_logger(ib_stream, ", ACTIVE %lu sec", (ulong)difftime(time(NULL), trx->start_time)); break; case TRX_PREPARED: ib_logger(ib_stream, ", ACTIVE (PREPARED) %lu sec", (ulong)difftime(time(NULL), trx->start_time)); break; case TRX_COMMITTED_IN_MEMORY: ib_logger(ib_stream, ", COMMITTED IN MEMORY"); break; default: ib_logger(ib_stream, " state %lu", (ulong) trx->conc_state); } #ifdef UNIV_LINUX ib_logger(ib_stream, ", process no %lu", trx->client_process_no); #endif ib_logger(ib_stream, ", OS thread id %lu", (ulong) os_thread_pf(trx->client_thread_id)); if (*trx->op_info) { ib_logger(ib_stream, " %s", trx->op_info); } if (trx->is_recovered) { ib_logger(ib_stream, " recovered trx"); } if (trx->is_purge) { ib_logger(ib_stream, " purge trx"); } ib_logger(ib_stream, "\n"); if (trx->n_client_tables_in_use > 0 || trx->client_n_tables_locked > 0) { ib_logger(ib_stream, "Client tables in use %lu, locked %lu\n", (ulong) trx->n_client_tables_in_use, (ulong) trx->client_n_tables_locked); } newline = TRUE; switch (trx->que_state) { case TRX_QUE_RUNNING: newline = FALSE; break; case TRX_QUE_LOCK_WAIT: ib_logger(ib_stream, "LOCK WAIT "); break; case TRX_QUE_ROLLING_BACK: ib_logger(ib_stream, "ROLLING BACK "); break; case TRX_QUE_COMMITTING: ib_logger(ib_stream, "COMMITTING "); break; default: ib_logger(ib_stream, "que state %lu ", (ulong) trx->que_state); } if (0 < UT_LIST_GET_LEN(trx->trx_locks) || mem_heap_get_size(trx->lock_heap) > 400) { newline = TRUE; ib_logger(ib_stream, "%lu lock struct(s), heap size %lu," " %lu row lock(s)", (ulong) UT_LIST_GET_LEN(trx->trx_locks), (ulong) mem_heap_get_size(trx->lock_heap), (ulong) lock_number_of_rows_locked(trx)); } if (trx->has_search_latch) { newline = TRUE; ib_logger(ib_stream, ", holds adaptive hash latch"); } if (!ut_dulint_is_zero(trx->undo_no)) { newline = TRUE; ib_logger(ib_stream, ", undo log entries %lu", (ulong) ut_dulint_get_low(trx->undo_no)); } if (newline) { ib_logger(ib_stream, "\n"); } } /*******************************************************************//** Compares the "weight" (or size) of two transactions. @return <0, 0 or >0; similar to strcmp(3) */ UNIV_INTERN int trx_weight_cmp( /*===========*/ const trx_t* a, /*!< in: the first transaction to be compared */ const trx_t* b) /*!< in: the second transaction to be compared */ { /* We compare the number of altered/locked rows. */ #if 0 ib_logger(ib_stream, "%s TRX_WEIGHT(a): %lld+%lu, TRX_WEIGHT(b): %lld+%lu\n", __func__, ut_conv_dulint_to_longlong(a->undo_no), UT_LIST_GET_LEN(a->trx_locks), ut_conv_dulint_to_longlong(b->undo_no), UT_LIST_GET_LEN(b->trx_locks)); #endif return(ut_dulint_cmp(TRX_WEIGHT(a), TRX_WEIGHT(b))); } /****************************************************************//** Prepares a transaction. */ UNIV_INTERN void trx_prepare_off_kernel( /*===================*/ trx_t* trx) /*!< in: transaction */ { page_t* update_hdr_page; trx_rseg_t* rseg; ib_uint64_t lsn = 0; mtr_t mtr; ut_ad(mutex_own(&kernel_mutex)); rseg = trx->rseg; if (trx->insert_undo != NULL || trx->update_undo != NULL) { mutex_exit(&kernel_mutex); mtr_start(&mtr); /* Change the undo log segment states from TRX_UNDO_ACTIVE to TRX_UNDO_PREPARED: these modifications to the file data structure define the transaction as prepared in the file-based world, at the serialization point of lsn. */ mutex_enter(&(rseg->mutex)); if (trx->insert_undo != NULL) { /* It is not necessary to obtain trx->undo_mutex here because only a single OS thread is allowed to do the transaction prepare for this transaction. */ trx_undo_set_state_at_prepare(trx, trx->insert_undo, &mtr); } if (trx->update_undo) { update_hdr_page = trx_undo_set_state_at_prepare( trx, trx->update_undo, &mtr); } mutex_exit(&(rseg->mutex)); /*--------------*/ mtr_commit(&mtr); /* This mtr commit makes the transaction prepared in the file-based world */ /*--------------*/ lsn = mtr.end_lsn; mutex_enter(&kernel_mutex); } ut_ad(mutex_own(&kernel_mutex)); /*--------------------------------------*/ trx->conc_state = TRX_PREPARED; /*--------------------------------------*/ if (lsn) { /* Depending on the config options, we may now write the log buffer to the log files, making the prepared state of the transaction durable if the OS does not crash. We may also flush the log files to disk, making the prepared state of the transaction durable also at an OS crash or a power outage. The idea in InnoDB's group prepare is that a group of transactions gather behind a trx doing a physical disk write to log files, and when that physical write has been completed, one of those transactions does a write which prepares the whole group. Note that this group prepare will only bring benefit if there are > 2 users in the database. Then at least 2 users can gather behind one doing the physical log write to disk. */ mutex_exit(&kernel_mutex); if (srv_flush_log_at_trx_commit == 0) { /* Do nothing */ } else if (srv_flush_log_at_trx_commit == 1) { if (srv_unix_file_flush_method == SRV_UNIX_NOSYNC) { /* Write the log but do not flush it to disk */ log_write_up_to(lsn, LOG_WAIT_ONE_GROUP, FALSE); } else { /* Write the log to the log files AND flush them to disk */ log_write_up_to(lsn, LOG_WAIT_ONE_GROUP, TRUE); } } else if (srv_flush_log_at_trx_commit == 2) { /* Write the log but do not flush it to disk */ log_write_up_to(lsn, LOG_WAIT_ONE_GROUP, FALSE); } else { ut_error; } mutex_enter(&kernel_mutex); } } /**********************************************************************//** Does the transaction prepare for client. @return 0 or error number */ UNIV_INTERN ulint trx_prepare( /*========*/ trx_t* trx) /*!< in: trx handle */ { /* Because we do not do the prepare by sending an Innobase sig to the transaction, we must here make sure that trx has been started. */ ut_a(trx); trx->op_info = "preparing"; mutex_enter(&kernel_mutex); trx_prepare_off_kernel(trx); mutex_exit(&kernel_mutex); trx->op_info = ""; return(0); } /**********************************************************************//** This function is used to find number of prepared transactions and their transaction objects for a recovery. This function is used to recover any X/Open XA distributed transactions @return number of prepared transactions stored in xid_list */ UNIV_INTERN int trx_recover( /*========*/ XID* xid_list, /*!< in/out: prepared transactions */ ulint len) /*!< in: number of slots in xid_list */ { trx_t* trx; ulint count = 0; ut_ad(xid_list); ut_ad(len); /* We should set those transactions which are in the prepared state to the xid_list */ mutex_enter(&kernel_mutex); trx = UT_LIST_GET_FIRST(trx_sys->trx_list); while (trx) { if (trx->conc_state == TRX_PREPARED) { #ifdef WITH_XOPEN xid_list[count] = trx->xid; #endif /* WITH_XOPEN */ if (count == 0) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Starting recovery for" " XA transactions...\n"); } ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Transaction " TRX_ID_FMT " in" " prepared state after recovery\n", TRX_ID_PREP_PRINTF(trx->id)); ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Transaction contains changes" " to %lu rows\n", (ulong) ut_conv_dulint_to_longlong( trx->undo_no)); count++; if (count == len) { break; } } trx = UT_LIST_GET_NEXT(trx_list, trx); } mutex_exit(&kernel_mutex); if (count > 0){ ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: %lu transactions in prepared state" " after recovery\n", (ulong) count); } return ((int) count); } #ifdef WITH_XOPEN /*******************************************************************//** This function is used to find one X/Open XA distributed transaction which is in the prepared state @return trx or NULL */ UNIV_INTERN trx_t* trx_get_trx_by_xid( /*===============*/ XID* xid) /*!< in: X/Open XA transaction identification */ { trx_t* trx; if (xid == NULL) { return (NULL); } mutex_enter(&kernel_mutex); trx = UT_LIST_GET_FIRST(trx_sys->trx_list); while (trx) { /* Compare two X/Open XA transaction id's: their length should be the same and binary comparison of gtrid_lenght+bqual_length bytes should be the same */ if (xid->gtrid_length == trx->xid.gtrid_length && xid->bqual_length == trx->xid.bqual_length && memcmp(xid->data, trx->xid.data, xid->gtrid_length + xid->bqual_length) == 0) { break; } trx = UT_LIST_GET_NEXT(trx_list, trx); } mutex_exit(&kernel_mutex); if (trx) { if (trx->conc_state != TRX_PREPARED) { return(NULL); } return(trx); } else { return(NULL); } } #endif /* WITH_XOPEN */ haildb-2.3.2/trx/trx0sys.c0000644000175000017500000011307011513177357016262 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2010, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file trx/trx0sys.c Transaction system Created 3/26/1996 Heikki Tuuri *******************************************************/ #include "trx0sys.h" #ifdef UNIV_NONINL #include "trx0sys.ic" #endif #ifndef UNIV_HOTBACKUP #include "fsp0fsp.h" #include "mtr0log.h" #include "mtr0log.h" #include "trx0trx.h" #include "trx0rseg.h" #include "trx0undo.h" #include "srv0srv.h" #include "trx0purge.h" #include "log0log.h" #include "os0file.h" #include "read0read.h" /** The file format tag structure with id and name. All changes to this data structure are covered by the dictionary mutex. */ struct file_format_struct { ulint id; /*!< id of the file format */ const char* name; /*!< text representation of the file format */ }; /** The file format tag */ typedef struct file_format_struct file_format_t; /** The transaction system */ UNIV_INTERN trx_sys_t* trx_sys = NULL; /** The doublewrite buffer */ UNIV_INTERN trx_doublewrite_t* trx_doublewrite = NULL; /** The following is set to TRUE when we are upgrading from pre-4.1 format data files to the multiple tablespaces format data files */ UNIV_INTERN ibool trx_doublewrite_must_reset_space_ids = FALSE; /** Set to TRUE when the doublewrite buffer is being created */ UNIV_INTERN ibool trx_doublewrite_buf_is_being_created = FALSE; /** The following is TRUE when we are using the database in the post-4.1 format, i.e., we have successfully upgraded, or have created a new database installation */ UNIV_INTERN ibool trx_sys_multiple_tablespace_format = FALSE; /** List of animal names representing file format. */ static const char* file_format_name_map[] = { "Antelope", "Barracuda", "Cheetah", "Dragon", "Elk", "Fox", "Gazelle", "Hornet", "Impala", "Jaguar", "Kangaroo", "Leopard", "Moose", "Nautilus", "Ocelot", "Porpoise", "Quail", "Rabbit", "Shark", "Tiger", "Urchin", "Viper", "Whale", "Xenops", "Yak", "Zebra" }; /** The number of elements in the file format name array. */ static const ulint FILE_FORMAT_NAME_N = sizeof(file_format_name_map) / sizeof(file_format_name_map[0]); /** This is used to track the maximum file format id known to InnoDB. It's updated via SET GLOBAL file_format_check = 'x' or when we open or create a table. */ static file_format_t file_format_max; /****************************************************************//** Reset the variables. */ UNIV_INTERN void trx_sys_var_init(void) /*==================*/ { trx_sys = NULL; trx_doublewrite = NULL; trx_doublewrite_must_reset_space_ids = FALSE; trx_sys_multiple_tablespace_format = FALSE; memset(&file_format_max, 0x0, sizeof(file_format_max)); } /******************************************************************** Determines if a page number is located inside the doublewrite buffer. @return TRUE if the location is inside the two blocks of the doublewrite buffer */ UNIV_INTERN ibool trx_doublewrite_page_inside( /*========================*/ ulint page_no) /*!< in: page number */ { if (trx_doublewrite == NULL) { return(FALSE); } if (page_no >= trx_doublewrite->block1 && page_no < trx_doublewrite->block1 + TRX_SYS_DOUBLEWRITE_BLOCK_SIZE) { return(TRUE); } if (page_no >= trx_doublewrite->block2 && page_no < trx_doublewrite->block2 + TRX_SYS_DOUBLEWRITE_BLOCK_SIZE) { return(TRUE); } return(FALSE); } /****************************************************************//** Creates or initialializes the doublewrite buffer at a database start. */ static void trx_doublewrite_init( /*=================*/ byte* doublewrite) /*!< in: pointer to the doublewrite buf header on trx sys page */ { trx_doublewrite = mem_alloc(sizeof(trx_doublewrite_t)); /* Since we now start to use the doublewrite buffer, no need to call fsync() after every write to a data file */ #ifdef UNIV_DO_FLUSH os_do_not_call_flush_at_each_write = TRUE; #endif /* UNIV_DO_FLUSH */ mutex_create(&trx_doublewrite->mutex, SYNC_DOUBLEWRITE); trx_doublewrite->first_free = 0; trx_doublewrite->block1 = mach_read_from_4( doublewrite + TRX_SYS_DOUBLEWRITE_BLOCK1); trx_doublewrite->block2 = mach_read_from_4( doublewrite + TRX_SYS_DOUBLEWRITE_BLOCK2); trx_doublewrite->write_buf_unaligned = ut_malloc( (1 + 2 * TRX_SYS_DOUBLEWRITE_BLOCK_SIZE) * UNIV_PAGE_SIZE); trx_doublewrite->write_buf = ut_align( trx_doublewrite->write_buf_unaligned, UNIV_PAGE_SIZE); trx_doublewrite->buf_block_arr = mem_alloc( 2 * TRX_SYS_DOUBLEWRITE_BLOCK_SIZE * sizeof(void*)); } /****************************************************************//** Marks the trx sys header when we have successfully upgraded to the >= 4.1.x multiple tablespace format. */ UNIV_INTERN void trx_sys_mark_upgraded_to_multiple_tablespaces(void) /*===============================================*/ { buf_block_t* block; byte* doublewrite; mtr_t mtr; /* We upgraded to 4.1.x and reset the space id fields in the doublewrite buffer. Let us mark to the trx_sys header that the upgrade has been done. */ mtr_start(&mtr); block = buf_page_get(TRX_SYS_SPACE, 0, TRX_SYS_PAGE_NO, RW_X_LATCH, &mtr); buf_block_dbg_add_level(block, SYNC_NO_ORDER_CHECK); doublewrite = buf_block_get_frame(block) + TRX_SYS_DOUBLEWRITE; mlog_write_ulint(doublewrite + TRX_SYS_DOUBLEWRITE_SPACE_ID_STORED, TRX_SYS_DOUBLEWRITE_SPACE_ID_STORED_N, MLOG_4BYTES, &mtr); mtr_commit(&mtr); /* Flush the modified pages to disk and make a checkpoint */ log_make_checkpoint_at(IB_UINT64_T_MAX, TRUE); trx_sys_multiple_tablespace_format = TRUE; } /****************************************************************//** Creates the doublewrite buffer to a new InnoDB installation. The header of the doublewrite buffer is placed on the trx system header page. */ UNIV_INTERN enum db_err trx_sys_create_doublewrite_buf(void) /*================================*/ { buf_block_t* block; buf_block_t* block2; buf_block_t* new_block; byte* doublewrite; byte* fseg_header; ulint page_no; ulint prev_page_no; ulint i; mtr_t mtr; if (trx_doublewrite) { /* Already inited */ return(DB_SUCCESS); } start_again: mtr_start(&mtr); trx_doublewrite_buf_is_being_created = TRUE; block = buf_page_get(TRX_SYS_SPACE, 0, TRX_SYS_PAGE_NO, RW_X_LATCH, &mtr); buf_block_dbg_add_level(block, SYNC_NO_ORDER_CHECK); doublewrite = buf_block_get_frame(block) + TRX_SYS_DOUBLEWRITE; if (mach_read_from_4(doublewrite + TRX_SYS_DOUBLEWRITE_MAGIC) == TRX_SYS_DOUBLEWRITE_MAGIC_N) { /* The doublewrite buffer has already been created: just read in some numbers */ trx_doublewrite_init(doublewrite); mtr_commit(&mtr); trx_doublewrite_buf_is_being_created = FALSE; } else { ib_logger(ib_stream, "InnoDB: Doublewrite buffer not found:" " creating new\n"); if (buf_pool_get_curr_size() < ((2 * TRX_SYS_DOUBLEWRITE_BLOCK_SIZE + FSP_EXTENT_SIZE / 2 + 100) * UNIV_PAGE_SIZE)) { ib_logger(ib_stream, "InnoDB: Cannot create doublewrite buffer:" " you must\n" "InnoDB: increase your buffer pool size.\n" "InnoDB: Cannot continue operation.\n"); return(DB_FATAL); } block2 = fseg_create(TRX_SYS_SPACE, TRX_SYS_PAGE_NO, TRX_SYS_DOUBLEWRITE + TRX_SYS_DOUBLEWRITE_FSEG, &mtr); /* fseg_create acquires a second latch on the page, therefore we must declare it: */ buf_block_dbg_add_level(block2, SYNC_NO_ORDER_CHECK); if (block2 == NULL) { ib_logger(ib_stream, "InnoDB: Cannot create doublewrite buffer:" " you must\n" "InnoDB: increase your tablespace size.\n" "InnoDB: Cannot continue operation.\n"); /* We need to exit without committing the mtr to prevent its modifications to the database getting to disk */ return(DB_FATAL); } fseg_header = buf_block_get_frame(block) + TRX_SYS_DOUBLEWRITE + TRX_SYS_DOUBLEWRITE_FSEG; prev_page_no = 0; for (i = 0; i < 2 * TRX_SYS_DOUBLEWRITE_BLOCK_SIZE + FSP_EXTENT_SIZE / 2; i++) { page_no = fseg_alloc_free_page(fseg_header, prev_page_no + 1, FSP_UP, &mtr); if (page_no == FIL_NULL) { ib_logger(ib_stream, "InnoDB: Cannot create doublewrite" " buffer: you must\n" "InnoDB: increase your" " tablespace size.\n" "InnoDB: Cannot continue operation.\n" ); return(DB_FATAL); } /* We read the allocated pages to the buffer pool; when they are written to disk in a flush, the space id and page number fields are also written to the pages. When we at database startup read pages from the doublewrite buffer, we know that if the space id and page number in them are the same as the page position in the tablespace, then the page has not been written to in doublewrite. */ new_block = buf_page_get(TRX_SYS_SPACE, 0, page_no, RW_X_LATCH, &mtr); buf_block_dbg_add_level(new_block, SYNC_NO_ORDER_CHECK); if (i == FSP_EXTENT_SIZE / 2) { ut_a(page_no == FSP_EXTENT_SIZE); mlog_write_ulint(doublewrite + TRX_SYS_DOUBLEWRITE_BLOCK1, page_no, MLOG_4BYTES, &mtr); mlog_write_ulint(doublewrite + TRX_SYS_DOUBLEWRITE_REPEAT + TRX_SYS_DOUBLEWRITE_BLOCK1, page_no, MLOG_4BYTES, &mtr); } else if (i == FSP_EXTENT_SIZE / 2 + TRX_SYS_DOUBLEWRITE_BLOCK_SIZE) { ut_a(page_no == 2 * FSP_EXTENT_SIZE); mlog_write_ulint(doublewrite + TRX_SYS_DOUBLEWRITE_BLOCK2, page_no, MLOG_4BYTES, &mtr); mlog_write_ulint(doublewrite + TRX_SYS_DOUBLEWRITE_REPEAT + TRX_SYS_DOUBLEWRITE_BLOCK2, page_no, MLOG_4BYTES, &mtr); } else if (i > FSP_EXTENT_SIZE / 2) { ut_a(page_no == prev_page_no + 1); } prev_page_no = page_no; } mlog_write_ulint(doublewrite + TRX_SYS_DOUBLEWRITE_MAGIC, TRX_SYS_DOUBLEWRITE_MAGIC_N, MLOG_4BYTES, &mtr); mlog_write_ulint(doublewrite + TRX_SYS_DOUBLEWRITE_MAGIC + TRX_SYS_DOUBLEWRITE_REPEAT, TRX_SYS_DOUBLEWRITE_MAGIC_N, MLOG_4BYTES, &mtr); mlog_write_ulint(doublewrite + TRX_SYS_DOUBLEWRITE_SPACE_ID_STORED, TRX_SYS_DOUBLEWRITE_SPACE_ID_STORED_N, MLOG_4BYTES, &mtr); mtr_commit(&mtr); /* Flush the modified pages to disk and make a checkpoint */ log_make_checkpoint_at(IB_UINT64_T_MAX, TRUE); ib_logger(ib_stream, "InnoDB: Doublewrite buffer created\n"); trx_sys_multiple_tablespace_format = TRUE; goto start_again; } return(DB_SUCCESS); } /****************************************************************//** At a database startup initializes the doublewrite buffer memory structure if we already have a doublewrite buffer created in the data files. If we are upgrading to an InnoDB version which supports multiple tablespaces, then this function performs the necessary update operations. If we are in a crash recovery, this function uses a possible doublewrite buffer to restore half-written pages in the data files. */ UNIV_INTERN void trx_sys_doublewrite_init_or_restore_pages( /*======================================*/ ibool restore_corrupt_pages) /*!< in: TRUE=restore pages */ { byte* buf; byte* read_buf; byte* unaligned_read_buf; ulint block1; ulint block2; ulint source_page_no; byte* page; byte* doublewrite; ulint space_id; ulint page_no; ulint i; /* We do the file i/o past the buffer pool */ unaligned_read_buf = ut_malloc(2 * UNIV_PAGE_SIZE); read_buf = ut_align(unaligned_read_buf, UNIV_PAGE_SIZE); /* Read the trx sys header to check if we are using the doublewrite buffer */ fil_io(OS_FILE_READ, TRUE, TRX_SYS_SPACE, 0, TRX_SYS_PAGE_NO, 0, UNIV_PAGE_SIZE, read_buf, NULL); doublewrite = read_buf + TRX_SYS_DOUBLEWRITE; if (mach_read_from_4(doublewrite + TRX_SYS_DOUBLEWRITE_MAGIC) == TRX_SYS_DOUBLEWRITE_MAGIC_N) { /* The doublewrite buffer has been created */ trx_doublewrite_init(doublewrite); block1 = trx_doublewrite->block1; block2 = trx_doublewrite->block2; buf = trx_doublewrite->write_buf; } else { goto leave_func; } if (mach_read_from_4(doublewrite + TRX_SYS_DOUBLEWRITE_SPACE_ID_STORED) != TRX_SYS_DOUBLEWRITE_SPACE_ID_STORED_N) { /* We are upgrading from a version < 4.1.x to a version where multiple tablespaces are supported. We must reset the space id field in the pages in the doublewrite buffer because starting from this version the space id is stored to FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID. */ trx_doublewrite_must_reset_space_ids = TRUE; ib_logger(ib_stream, "InnoDB: Resetting space id's in the" " doublewrite buffer\n"); } else { trx_sys_multiple_tablespace_format = TRUE; } /* Read the pages from the doublewrite buffer to memory */ fil_io(OS_FILE_READ, TRUE, TRX_SYS_SPACE, 0, block1, 0, TRX_SYS_DOUBLEWRITE_BLOCK_SIZE * UNIV_PAGE_SIZE, buf, NULL); fil_io(OS_FILE_READ, TRUE, TRX_SYS_SPACE, 0, block2, 0, TRX_SYS_DOUBLEWRITE_BLOCK_SIZE * UNIV_PAGE_SIZE, buf + TRX_SYS_DOUBLEWRITE_BLOCK_SIZE * UNIV_PAGE_SIZE, NULL); /* Check if any of these pages is half-written in data files, in the intended position */ page = buf; for (i = 0; i < TRX_SYS_DOUBLEWRITE_BLOCK_SIZE * 2; i++) { page_no = mach_read_from_4(page + FIL_PAGE_OFFSET); if (trx_doublewrite_must_reset_space_ids) { space_id = 0; mach_write_to_4(page + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID, 0); /* We do not need to calculate new checksums for the pages because the field .._SPACE_ID does not affect them. Write the page back to where we read it from. */ if (i < TRX_SYS_DOUBLEWRITE_BLOCK_SIZE) { source_page_no = block1 + i; } else { source_page_no = block2 + i - TRX_SYS_DOUBLEWRITE_BLOCK_SIZE; } fil_io(OS_FILE_WRITE, TRUE, 0, 0, source_page_no, 0, UNIV_PAGE_SIZE, page, NULL); /* printf("Resetting space id in page %lu\n", source_page_no); */ } else { space_id = mach_read_from_4( page + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID); } if (!restore_corrupt_pages) { /* The database was shut down gracefully: no need to restore pages */ } else if (!fil_tablespace_exists_in_mem(space_id)) { /* Maybe we have dropped the single-table tablespace and this page once belonged to it: do nothing */ } else if (!fil_check_adress_in_tablespace(space_id, page_no)) { ib_logger(ib_stream, "InnoDB: Warning: a page in the" " doublewrite buffer is not within space\n" "InnoDB: bounds; space id %lu" " page number %lu, page %lu in" " doublewrite buf.\n", (ulong) space_id, (ulong) page_no, (ulong) i); } else if (space_id == TRX_SYS_SPACE && ((page_no >= block1 && page_no < block1 + TRX_SYS_DOUBLEWRITE_BLOCK_SIZE) || (page_no >= block2 && page_no < (block2 + TRX_SYS_DOUBLEWRITE_BLOCK_SIZE)))) { /* It is an unwritten doublewrite buffer page: do nothing */ } else { ulint zip_size = fil_space_get_zip_size(space_id); #ifndef WITH_ZIP if (zip_size > 0) { srv_panic(DB_UNSUPPORTED, "InnoDB: Warning: attempt to " "restore compressed table page, " "InnoDB not compiled\n" "InnoDB: with support for compressed " "tables.\n"); } #endif /* WITH_ZIP */ /* Read in the actual page from the file */ fil_io(OS_FILE_READ, TRUE, space_id, zip_size, page_no, 0, zip_size ? zip_size : UNIV_PAGE_SIZE, read_buf, NULL); /* Check if the page is corrupt */ if (UNIV_UNLIKELY (buf_page_is_corrupted(read_buf, zip_size))) { ib_logger(ib_stream, "InnoDB: Warning: database page" " corruption or a failed\n" "InnoDB: file read of" " space %lu page %lu.\n" "InnoDB: Trying to recover it from" " the doublewrite buffer.\n", (ulong) space_id, (ulong) page_no); if (buf_page_is_corrupted(page, zip_size)) { ib_logger(ib_stream, "InnoDB: Dump of the page:\n"); buf_page_print(read_buf, zip_size); ib_logger(ib_stream, "InnoDB: Dump of" " corresponding page" " in doublewrite buffer:\n"); buf_page_print(page, zip_size); ib_logger(ib_stream, "InnoDB: Also the page in the" " doublewrite buffer" " is corrupt.\n" "InnoDB: Cannot continue" " operation.\n" "InnoDB: You can try to" " recover the database\n" "InnoDB: with the option:\n" "InnoDB: " "force_recovery=6\n"); srv_panic(DB_CORRUPTION, "Corrupt page"); } /* Write the good page from the doublewrite buffer to the intended position */ fil_io(OS_FILE_WRITE, TRUE, space_id, zip_size, page_no, 0, zip_size ? zip_size : UNIV_PAGE_SIZE, page, NULL); ib_logger(ib_stream, "InnoDB: Recovered the page from" " the doublewrite buffer.\n"); } } page += UNIV_PAGE_SIZE; } fil_flush_file_spaces(FIL_TABLESPACE); leave_func: ut_free(unaligned_read_buf); } /****************************************************************//** Checks that trx is in the trx list. @return TRUE if is in */ UNIV_INTERN ibool trx_in_trx_list( /*============*/ trx_t* in_trx) /*!< in: trx */ { trx_t* trx; ut_ad(mutex_own(&(kernel_mutex))); trx = UT_LIST_GET_FIRST(trx_sys->trx_list); while (trx != NULL) { if (trx == in_trx) { return(TRUE); } trx = UT_LIST_GET_NEXT(trx_list, trx); } return(FALSE); } /*****************************************************************//** Writes the value of max_trx_id to the file based trx system header. */ UNIV_INTERN void trx_sys_flush_max_trx_id(void) /*==========================*/ { trx_sysf_t* sys_header; mtr_t mtr; ut_ad(mutex_own(&kernel_mutex)); mtr_start(&mtr); sys_header = trx_sysf_get(&mtr); mlog_write_dulint(sys_header + TRX_SYS_TRX_ID_STORE, trx_sys->max_trx_id, &mtr); mtr_commit(&mtr); } /****************************************************************//** Looks for a free slot for a rollback segment in the trx system file copy. @return slot index or ULINT_UNDEFINED if not found */ UNIV_INTERN ulint trx_sysf_rseg_find_free( /*====================*/ mtr_t* mtr) /*!< in: mtr */ { trx_sysf_t* sys_header; ulint page_no; ulint i; ut_ad(mutex_own(&(kernel_mutex))); sys_header = trx_sysf_get(mtr); for (i = 0; i < TRX_SYS_N_RSEGS; i++) { page_no = trx_sysf_rseg_get_page_no(sys_header, i, mtr); if (page_no == FIL_NULL) { return(i); } } return(ULINT_UNDEFINED); } /*****************************************************************//** Creates the file page for the transaction system. This function is called only at the database creation, before trx_sys_init. */ static void trx_sysf_create( /*============*/ mtr_t* mtr) /*!< in: mtr */ { trx_sysf_t* sys_header; ulint slot_no; buf_block_t* block; page_t* page; ulint page_no; ulint i; ut_ad(mtr); /* Note that below we first reserve the file space x-latch, and then enter the kernel: we must do it in this order to conform to the latching order rules. */ mtr_x_lock(fil_space_get_latch(TRX_SYS_SPACE, NULL), mtr); mutex_enter(&kernel_mutex); /* Create the trx sys file block in a new allocated file segment */ block = fseg_create(TRX_SYS_SPACE, 0, TRX_SYS + TRX_SYS_FSEG_HEADER, mtr); buf_block_dbg_add_level(block, SYNC_TRX_SYS_HEADER); ut_a(buf_block_get_page_no(block) == TRX_SYS_PAGE_NO); page = buf_block_get_frame(block); mlog_write_ulint(page + FIL_PAGE_TYPE, FIL_PAGE_TYPE_TRX_SYS, MLOG_2BYTES, mtr); /* Reset the doublewrite buffer magic number to zero so that we know that the doublewrite buffer has not yet been created (this suppresses a Valgrind warning) */ mlog_write_ulint(page + TRX_SYS_DOUBLEWRITE + TRX_SYS_DOUBLEWRITE_MAGIC, 0, MLOG_4BYTES, mtr); sys_header = trx_sysf_get(mtr); /* Start counting transaction ids from number 1 up */ mlog_write_dulint(sys_header + TRX_SYS_TRX_ID_STORE, ut_dulint_create(0, 1), mtr); /* Reset the rollback segment slots */ for (i = 0; i < TRX_SYS_N_RSEGS; i++) { trx_sysf_rseg_set_space(sys_header, i, ULINT_UNDEFINED, mtr); trx_sysf_rseg_set_page_no(sys_header, i, FIL_NULL, mtr); } /* The remaining area (up to the page trailer) is uninitialized. Silence Valgrind warnings about it. */ UNIV_MEM_VALID(sys_header + (TRX_SYS_RSEGS + TRX_SYS_N_RSEGS * TRX_SYS_RSEG_SLOT_SIZE + TRX_SYS_RSEG_SPACE), (UNIV_PAGE_SIZE - FIL_PAGE_DATA_END - (TRX_SYS_RSEGS + TRX_SYS_N_RSEGS * TRX_SYS_RSEG_SLOT_SIZE + TRX_SYS_RSEG_SPACE)) + page - sys_header); /* Create the first rollback segment in the SYSTEM tablespace */ page_no = trx_rseg_header_create(TRX_SYS_SPACE, 0, ULINT_MAX, &slot_no, mtr); ut_a(slot_no == TRX_SYS_SYSTEM_RSEG_ID); ut_a(page_no != FIL_NULL); mutex_exit(&kernel_mutex); } /*****************************************************************//** Creates and initializes the central memory structures for the transaction system. This is called when the database is started. */ UNIV_INTERN void trx_sys_init_at_db_start( /*=====================*/ ib_recovery_t recovery) /*!< in: recovery flag */ { trx_sysf_t* sys_header; ib_int64_t rows_to_undo = 0; const char* unit = ""; trx_t* trx; mtr_t mtr; mtr_start(&mtr); ut_ad(trx_sys == NULL); mutex_enter(&kernel_mutex); trx_sys = mem_alloc(sizeof(trx_sys_t)); sys_header = trx_sysf_get(&mtr); trx_rseg_list_and_array_init(recovery, sys_header, &mtr); trx_sys->latest_rseg = UT_LIST_GET_FIRST(trx_sys->rseg_list); /* VERY important: after the database is started, max_trx_id value is divisible by TRX_SYS_TRX_ID_WRITE_MARGIN, and the 'if' in trx_sys_get_new_trx_id will evaluate to TRUE when the function is first time called, and the value for trx id will be written to the disk-based header! Thus trx id values will not overlap when the database is repeatedly started! */ trx_sys->max_trx_id = ut_dulint_add( ut_dulint_align_up(mtr_read_dulint( sys_header + TRX_SYS_TRX_ID_STORE, &mtr), TRX_SYS_TRX_ID_WRITE_MARGIN), 2 * TRX_SYS_TRX_ID_WRITE_MARGIN); UT_LIST_INIT(trx_sys->client_trx_list); trx_dummy_sess = sess_open(); trx_lists_init_at_db_start(recovery); if (UT_LIST_GET_LEN(trx_sys->trx_list) > 0) { trx = UT_LIST_GET_FIRST(trx_sys->trx_list); for (;;) { if ( trx->conc_state != TRX_PREPARED) { rows_to_undo += ut_conv_dulint_to_longlong( trx->undo_no); } trx = UT_LIST_GET_NEXT(trx_list, trx); if (!trx) { break; } } if (rows_to_undo > 1000000000) { unit = "M"; rows_to_undo = rows_to_undo / 1000000; } ib_logger(ib_stream, "InnoDB: %lu transaction(s) which must be" " rolled back or cleaned up\n" "InnoDB: in total %lu%s row operations to undo\n", (ulong) UT_LIST_GET_LEN(trx_sys->trx_list), (ulong) rows_to_undo, unit); ib_logger(ib_stream, "InnoDB: Trx id counter is " TRX_ID_FMT "\n", TRX_ID_PREP_PRINTF(trx_sys->max_trx_id)); } UT_LIST_INIT(trx_sys->view_list); trx_purge_sys_create(); mutex_exit(&kernel_mutex); mtr_commit(&mtr); } /*****************************************************************//** Creates and initializes the transaction system at the database creation. */ UNIV_INTERN void trx_sys_create( /*===========*/ ib_recovery_t recovery) /*!< in: recovery flag */ { mtr_t mtr; mtr_start(&mtr); trx_sysf_create(&mtr); mtr_commit(&mtr); trx_sys_init_at_db_start(recovery); } /*****************************************************************//** Update the file format tag. @return always TRUE */ static ibool trx_sys_file_format_max_write( /*==========================*/ ulint format_id, /*!< in: file format id */ const char** name) /*!< out: max file format name, can be NULL */ { mtr_t mtr; byte* ptr; buf_block_t* block; ulint tag_value_low; mtr_start(&mtr); block = buf_page_get( TRX_SYS_SPACE, 0, TRX_SYS_PAGE_NO, RW_X_LATCH, &mtr); file_format_max.id = format_id; file_format_max.name = trx_sys_file_format_id_to_name(format_id); ptr = buf_block_get_frame(block) + TRX_SYS_FILE_FORMAT_TAG; tag_value_low = format_id + TRX_SYS_FILE_FORMAT_TAG_MAGIC_N_LOW; if (name) { *name = file_format_max.name; } mlog_write_dulint( ptr, ut_dulint_create(TRX_SYS_FILE_FORMAT_TAG_MAGIC_N_HIGH, tag_value_low), &mtr); mtr_commit(&mtr); return(TRUE); } /*****************************************************************//** Read the file format tag. @return the file format */ static ulint trx_sys_file_format_max_read(void) /*==============================*/ { mtr_t mtr; const byte* ptr; const buf_block_t* block; ulint format_id; dulint file_format_id; /* Since this is called during the startup phase it's safe to read the value without a covering mutex. */ mtr_start(&mtr); block = buf_page_get( TRX_SYS_SPACE, 0, TRX_SYS_PAGE_NO, RW_X_LATCH, &mtr); ptr = buf_block_get_frame(block) + TRX_SYS_FILE_FORMAT_TAG; file_format_id = mach_read_from_8(ptr); mtr_commit(&mtr); format_id = file_format_id.low - TRX_SYS_FILE_FORMAT_TAG_MAGIC_N_LOW; if (file_format_id.high != TRX_SYS_FILE_FORMAT_TAG_MAGIC_N_HIGH || format_id >= FILE_FORMAT_NAME_N) { /* Either it has never been tagged, or garbage in it. Reset the tag in either case. */ format_id = DICT_TF_FORMAT_51; trx_sys_file_format_max_write(format_id, NULL); } return(format_id); } /*****************************************************************//** Get the name representation of the file format from its id. @return pointer to the name */ UNIV_INTERN const char* trx_sys_file_format_id_to_name( /*===========================*/ const ulint id) /*!< in: id of the file format */ { if (!(id < FILE_FORMAT_NAME_N)) { /* unknown id */ return ("Unknown"); } return(file_format_name_map[id]); } /**************************************************************** Validate the file format name and return its corresponding id. @return valid file format id or DICT_TF_FORMAT_MAX + 1 */ UNIV_INTERN ulint trx_sys_file_format_name_to_id( /*===========================*/ const char* format_name) /*!< in: pointer to file format name */ { char* endp; ulint format_id; ut_a(format_name != NULL); /* The format name can contain the format id itself instead of the name and we check for that. */ format_id = (ulint) strtoul(format_name, &endp, 10); /* Check for valid parse. */ if (*endp == '\0' && *format_name != '\0') { if (format_id <= DICT_TF_FORMAT_MAX) { return(format_id); } } else { for (format_id = 0; format_id <= DICT_TF_FORMAT_MAX; format_id++) { const char* name; name = trx_sys_file_format_id_to_name(format_id); if (!strcasecmp(format_name, name)) { return(format_id); } } } return(DICT_TF_FORMAT_MAX + 1); } /*****************************************************************//** Check for the max file format tag stored on disk. Note: If max_format_id is == DICT_TF_FORMAT_MAX + 1 then we only print a warning. @return DB_SUCCESS or error code */ UNIV_INTERN ulint trx_sys_file_format_max_check( /*==========================*/ ulint max_format_id) /*!< in: max format id to check */ { ulint format_id; /* Check the file format in the tablespace. Do not try to recover if the file format is not supported by the engine unless forced by the user. */ format_id = trx_sys_file_format_max_read(); ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: highest supported file format is %s.\n", trx_sys_file_format_id_to_name(DICT_TF_FORMAT_MAX)); if (format_id > DICT_TF_FORMAT_MAX) { ut_a(format_id < FILE_FORMAT_NAME_N); ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: %s: the system tablespace is in a file " "format that this version doesn't support - %s\n", ((max_format_id <= DICT_TF_FORMAT_MAX) ? "Error" : "Warning"), trx_sys_file_format_id_to_name(format_id)); if (max_format_id <= DICT_TF_FORMAT_MAX) { return(DB_ERROR); } } format_id = (format_id > max_format_id) ? format_id : max_format_id; /* We don't need a mutex here, as this function should only be called once at start up. */ file_format_max.id = format_id; file_format_max.name = trx_sys_file_format_id_to_name(format_id); return(DB_SUCCESS); } /*****************************************************************//** Set the file format id unconditionally except if it's already the same value. @return TRUE if value updated */ UNIV_INTERN ibool trx_sys_file_format_max_set( /*========================*/ ulint format_id, /*!< in: file format id */ const char** name) /*!< out: max file format name or NULL if not needed. */ { ibool ret = FALSE; ut_a(name); ut_a(format_id <= DICT_TF_FORMAT_MAX); /* Only update if not already same value. */ if (format_id != file_format_max.id) { ret = trx_sys_file_format_max_write(format_id, name); } return(ret); } /********************************************************************//** Tags the system table space with minimum format id if it has not been tagged yet. WARNING: This function is only called during the startup and AFTER the redo log application during recovery has finished. */ UNIV_INTERN void trx_sys_file_format_tag_init(void) /*==============================*/ { ulint format_id; format_id = trx_sys_file_format_max_read(); /* If format_id is not set then set it to the minimum. */ if (format_id == ULINT_UNDEFINED) { trx_sys_file_format_max_set(DICT_TF_FORMAT_51, NULL); } } /********************************************************************//** Update the file format tag in the system tablespace only if the given format id is greater than the known max id. @return TRUE if format_id was bigger than the known max id */ UNIV_INTERN ibool trx_sys_file_format_max_upgrade( /*============================*/ const char** name, /*!< out: max file format name */ ulint format_id) /*!< in: file format identifier */ { ibool ret = FALSE; ut_a(name); ut_a(file_format_max.name != NULL); ut_a(format_id <= DICT_TF_FORMAT_MAX); if (format_id > file_format_max.id) { ret = trx_sys_file_format_max_write(format_id, name); } return(ret); } /*****************************************************************//** Get the name representation of the file format from its id. @return pointer to the max format name */ UNIV_INTERN const char* trx_sys_file_format_max_get(void) /*=============================*/ { return(file_format_max.name); } /*****************************************************************//** Initializes the tablespace tag system. */ UNIV_INTERN void trx_sys_file_format_init(void) /*==========================*/ { /* We don't need a mutex here, as this function should only be called once at start up. */ file_format_max.id = DICT_TF_FORMAT_51; file_format_max.name = trx_sys_file_format_id_to_name( file_format_max.id); } /*****************************************************************//** Closes the tablespace tag system. */ UNIV_INTERN void trx_sys_file_format_close(void) /*===========================*/ { /* Does nothing at the moment */ } /*****************************************************************//** Reads the file format id from the first system table space file. Even if the call succeeds and returns TRUE, the returned format id may be ULINT_UNDEFINED signalling that the format id was not present in the data file. @return TRUE if call succeeds */ UNIV_INTERN ibool trx_sys_read_file_format_id( /*========================*/ const char *pathname, /*!< in: pathname of the first system table space file */ ulint *format_id) /*!< out: file format of the system table space */ { os_file_t file; ibool success; byte buf[UNIV_PAGE_SIZE * 2]; page_t* page = ut_align(buf, UNIV_PAGE_SIZE); const byte* ptr; dulint file_format_id; *format_id = ULINT_UNDEFINED; file = os_file_create_simple_no_error_handling( pathname, OS_FILE_OPEN, OS_FILE_READ_ONLY, &success ); if (!success) { /* The following call prints an error message */ os_file_get_last_error(TRUE); ut_print_timestamp(ib_stream); ib_logger(ib_stream, " ibbackup: Error: trying to read system tablespace file format,\n" " ibbackup: but could not open the tablespace file %s!\n", pathname ); return(FALSE); } /* Read the page on which file format is stored */ success = os_file_read_no_error_handling( file, page, TRX_SYS_PAGE_NO * UNIV_PAGE_SIZE, 0, UNIV_PAGE_SIZE ); if (!success) { /* The following call prints an error message */ os_file_get_last_error(TRUE); ut_print_timestamp(ib_stream); ib_logger(ib_stream, " ibbackup: Error: trying to read system table space file format,\n" " ibbackup: but failed to read the tablespace file %s!\n", pathname ); os_file_close(file); return(FALSE); } os_file_close(file); /* get the file format from the page */ ptr = page + TRX_SYS_FILE_FORMAT_TAG; file_format_id = mach_read_from_8(ptr); *format_id = file_format_id.low - TRX_SYS_FILE_FORMAT_TAG_MAGIC_N_LOW; if (file_format_id.high != TRX_SYS_FILE_FORMAT_TAG_MAGIC_N_HIGH || *format_id >= FILE_FORMAT_NAME_N) { /* Either it has never been tagged, or garbage in it. */ *format_id = ULINT_UNDEFINED; return(TRUE); } return(TRUE); } /*****************************************************************//** Reads the file format id from the given per-table data file. @return TRUE if call succeeds */ UNIV_INTERN ibool trx_sys_read_pertable_file_format_id( /*=================================*/ const char *pathname, /*!< in: pathname of a per-table datafile */ ulint *format_id) /*!< out: file format of the per-table data file */ { os_file_t file; ibool success; byte buf[UNIV_PAGE_SIZE * 2]; page_t* page = ut_align(buf, UNIV_PAGE_SIZE); const byte* ptr; ib_uint32_t flags; *format_id = ULINT_UNDEFINED; file = os_file_create_simple_no_error_handling( pathname, OS_FILE_OPEN, OS_FILE_READ_ONLY, &success ); if (!success) { /* The following call prints an error message */ os_file_get_last_error(TRUE); ut_print_timestamp(ib_stream); ib_logger(ib_stream, " ibbackup: Error: trying to read per-table tablespace format,\n" " ibbackup: but could not open the tablespace file %s!\n", pathname ); return(FALSE); } /* Read the first page of the per-table datafile */ success = os_file_read_no_error_handling( file, page, 0, 0, UNIV_PAGE_SIZE ); if (!success) { /* The following call prints an error message */ os_file_get_last_error(TRUE); ut_print_timestamp(ib_stream); ib_logger(ib_stream, " ibbackup: Error: trying to per-table data file format,\n" " ibbackup: but failed to read the tablespace file %s!\n", pathname ); os_file_close(file); return(FALSE); } os_file_close(file); /* get the file format from the page */ ptr = page + 54; flags = mach_read_from_4(ptr); if (flags == 0) { /* file format is Antelope */ *format_id = 0; return (TRUE); } else if (flags & 1) { /* tablespace flags are ok */ *format_id = (flags / 32) % 128; return (TRUE); } else { /* bad tablespace flags */ return(FALSE); } } #endif /* !UNIV_HOTBACKUP */ #ifndef UNIV_HOTBACKUP /********************************************************************* Shutdown/Close the transaction system. */ UNIV_INTERN void trx_sys_close(void) /*===============*/ { trx_rseg_t* rseg; read_view_t* view; ut_ad(trx_sys != NULL); /* Check that all read views are closed except read view owned by a purge. */ if (UT_LIST_GET_LEN(trx_sys->view_list) > 1) { ib_logger(ib_stream, "InnoDB: Error: all read views were not closed" " before shutdown:\n" "InnoDB: %lu read views open \n", UT_LIST_GET_LEN(trx_sys->view_list) - 1); } sess_close(trx_dummy_sess); trx_dummy_sess = NULL; trx_purge_sys_close(); /* This is required only because it's a pre-condition for many of the functions that we need to call. */ mutex_enter(&kernel_mutex); #ifdef UNIV_DO_FLUSH os_do_not_call_flush_at_each_write = TRUE; #endif /* UNIV_DO_FLUSH */ /* Free the double write data structures. */ ut_a(trx_doublewrite != NULL); ut_free(trx_doublewrite->write_buf_unaligned); trx_doublewrite->write_buf_unaligned = NULL; mem_free(trx_doublewrite->buf_block_arr); trx_doublewrite->buf_block_arr = NULL; mutex_free(&trx_doublewrite->mutex); #ifdef UNIV_DEBUG memset(&trx_doublewrite->mutex, 0x0, sizeof(trx_doublewrite->mutex)); #endif mem_free(trx_doublewrite); trx_doublewrite = NULL; /* End freeing of doublewrite buffer data structures. */ /* There can't be any active transactions. */ rseg = UT_LIST_GET_FIRST(trx_sys->rseg_list); while (rseg != NULL) { trx_rseg_t* prev_rseg = rseg; rseg = UT_LIST_GET_NEXT(rseg_list, prev_rseg); UT_LIST_REMOVE(rseg_list, trx_sys->rseg_list, prev_rseg); trx_rseg_mem_free(prev_rseg); } view = UT_LIST_GET_FIRST(trx_sys->view_list); while (view != NULL) { read_view_t* prev_view = view; view = UT_LIST_GET_NEXT(view_list, prev_view); /* Views are allocated from the trx_sys->global_read_view_heap. So, we simply remove the element here. */ UT_LIST_REMOVE(view_list, trx_sys->view_list, prev_view); } ut_a(UT_LIST_GET_LEN(trx_sys->trx_list) == 0); ut_a(UT_LIST_GET_LEN(trx_sys->rseg_list) == 0); ut_a(UT_LIST_GET_LEN(trx_sys->view_list) == 0); ut_a(UT_LIST_GET_LEN(trx_sys->client_trx_list) == 0); mem_free(trx_sys); trx_sys = NULL; mutex_exit(&kernel_mutex); } #endif /* !UNIV_HOTBACKUP */ haildb-2.3.2/trx/trx0roll.c0000644000175000017500000006462011513177357016422 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file trx/trx0roll.c Transaction rollback Created 3/26/1996 Heikki Tuuri *******************************************************/ #include "trx0roll.h" #ifdef UNIV_NONINL #include "trx0roll.ic" #endif #include "fsp0fsp.h" #include "mach0data.h" #include "trx0rseg.h" #include "trx0trx.h" #include "trx0undo.h" #include "trx0rec.h" #include "que0que.h" #include "usr0sess.h" #include "srv0que.h" #include "srv0start.h" #include "ddl0ddl.h" #include "row0undo.h" #include "lock0lock.h" #include "pars0pars.h" /** This many pages must be undone before a truncate is tried within rollback */ #define TRX_ROLL_TRUNC_THRESHOLD 1 /** In crash recovery, the current trx to be rolled back */ static trx_t* trx_roll_crash_recv_trx = NULL; /** In crash recovery we set this to the undo n:o of the current trx to be rolled back. Then we can print how many % the rollback has progressed. */ static ib_int64_t trx_roll_max_undo_no; /** Auxiliary variable which tells the previous progress % we printed */ static ulint trx_roll_progress_printed_pct; /*******************************************************************//** Rollback a transaction. @return error code or DB_SUCCESS */ UNIV_INTERN int trx_general_rollback( /*==================*/ trx_t* trx, /*!< in: transaction handle */ ibool partial,/*!< in: TRUE if partial rollback requested */ trx_savept_t* savept) /*!< in: pointer to savepoint undo number, if partial rollback requested */ { mem_heap_t* heap; que_thr_t* thr; roll_node_t* roll_node; /* Tell Innobase server that there might be work for utility threads: */ srv_active_wake_master_thread(); heap = mem_heap_create(512); roll_node = roll_node_create(heap); roll_node->partial = partial; if (partial) { roll_node->savept = *savept; } trx->error_state = DB_SUCCESS; thr = pars_complete_graph_for_exec(roll_node, trx, heap); ut_a(thr == que_fork_start_command(que_node_get_parent(thr))); que_run_threads(thr); mutex_enter(&kernel_mutex); while (trx->que_state != TRX_QUE_RUNNING) { mutex_exit(&kernel_mutex); os_thread_sleep(100000); mutex_enter(&kernel_mutex); } mutex_exit(&kernel_mutex); mem_heap_free(heap); ut_a(trx->error_state == DB_SUCCESS); /* Tell Innobase server that there might be work for utility threads: */ srv_active_wake_master_thread(); return((int) trx->error_state); } /*******************************************************************//** Frees savepoint structs starting from savep, if savep == NULL then free all savepoints. */ UNIV_INTERN void trx_roll_savepoints_free( /*=====================*/ trx_t* trx, /*!< in: transaction handle */ trx_named_savept_t* savep) /*!< in: free all savepoints > this one; if this is NULL, free all savepoints of trx */ { trx_named_savept_t* next_savep; if (savep == NULL) { savep = UT_LIST_GET_FIRST(trx->trx_savepoints); } else { savep = UT_LIST_GET_NEXT(trx_savepoints, savep); } while (savep != NULL) { next_savep = UT_LIST_GET_NEXT(trx_savepoints, savep); UT_LIST_REMOVE(trx_savepoints, trx->trx_savepoints, savep); mem_free(savep); savep = next_savep; } } /*******************************************************************//** Determines if this transaction is rolling back an incomplete transaction in crash recovery. @return TRUE if trx is an incomplete transaction that is being rolled back in crash recovery */ UNIV_INTERN ibool trx_is_recv( /*========*/ const trx_t* trx) /*!< in: transaction */ { return(trx == trx_roll_crash_recv_trx); } /*******************************************************************//** Returns a transaction savepoint taken at this point in time. @return savepoint */ UNIV_INTERN trx_savept_t trx_savept_take( /*============*/ trx_t* trx) /*!< in: transaction */ { trx_savept_t savept; savept.least_undo_no = trx->undo_no; return(savept); } /*******************************************************************//** Roll back an active transaction. */ static void trx_rollback_active( /*================*/ ib_recovery_t recovery, /*!< in: recovery flag */ trx_t* trx) /*!< in/out: transaction */ { mem_heap_t* heap; que_fork_t* fork; que_thr_t* thr; roll_node_t* roll_node; dict_table_t* table; ib_int64_t rows_to_undo; const char* unit = ""; ibool dictionary_locked = FALSE; heap = mem_heap_create(512); fork = que_fork_create(NULL, NULL, QUE_FORK_RECOVERY, heap); fork->trx = trx; thr = que_thr_create(fork, heap); roll_node = roll_node_create(heap); thr->child = roll_node; roll_node->common.parent = thr; mutex_enter(&kernel_mutex); trx->graph = fork; ut_a(thr == que_fork_start_command(fork)); trx_roll_crash_recv_trx = trx; trx_roll_max_undo_no = ut_conv_dulint_to_longlong(trx->undo_no); trx_roll_progress_printed_pct = 0; rows_to_undo = trx_roll_max_undo_no; if (rows_to_undo > 1000000000) { rows_to_undo = rows_to_undo / 1000000; unit = "M"; } ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Rolling back trx with id " TRX_ID_FMT ", %lu%s" " rows to undo\n", TRX_ID_PREP_PRINTF(trx->id), (ulong) rows_to_undo, unit); mutex_exit(&kernel_mutex); trx->client_thread_id = os_thread_get_curr_id(); trx->client_process_no = os_proc_get_number(); if (trx_get_dict_operation(trx) != TRX_DICT_OP_NONE) { dict_lock_data_dictionary(trx); dictionary_locked = TRUE; } que_run_threads(thr); mutex_enter(&kernel_mutex); while (trx->que_state != TRX_QUE_RUNNING) { mutex_exit(&kernel_mutex); ib_logger(ib_stream, "InnoDB: Waiting for rollback of trx id %lu to end\n", (ulong) ut_dulint_get_low(trx->id)); os_thread_sleep(100000); mutex_enter(&kernel_mutex); } mutex_exit(&kernel_mutex); if (trx_get_dict_operation(trx) != TRX_DICT_OP_NONE && !ut_dulint_is_zero(trx->table_id)) { /* If the transaction was for a dictionary operation, we drop the relevant table, if it still exists */ ib_logger(ib_stream, "InnoDB: Dropping table with id %lu %lu" " in recovery if it exists\n", (ulong) ut_dulint_get_high(trx->table_id), (ulong) ut_dulint_get_low(trx->table_id)); table = dict_table_get_on_id_low(recovery, trx->table_id); if (table) { ulint err; ib_logger(ib_stream, "InnoDB: Table found: dropping table "); ut_print_name(ib_stream, trx, TRUE, table->name); ib_logger(ib_stream, " in recovery\n"); err = ddl_drop_table(table->name, trx, TRUE); trx_commit(trx); ut_a(err == (int) DB_SUCCESS); } } if (dictionary_locked) { dict_unlock_data_dictionary(trx); } ib_logger(ib_stream, "\nInnoDB: Rolling back of trx id " TRX_ID_FMT " completed\n", TRX_ID_PREP_PRINTF(trx->id)); mem_heap_free(heap); trx_roll_crash_recv_trx = NULL; } /*******************************************************************//** Rollback or clean up any incomplete transactions which were encountered in crash recovery. If the transaction already was committed, then we clean up a possible insert undo log. If the transaction was not yet committed, then we roll it back. */ UNIV_INTERN void trx_rollback_or_clean_recovered( /*============================*/ ibool all) /*!< in: FALSE=roll back dictionary transactions; TRUE=roll back all non-PREPARED transactions */ { trx_t* trx; mutex_enter(&kernel_mutex); if (!UT_LIST_GET_FIRST(trx_sys->trx_list)) { goto leave_function; } if (all) { ib_logger(ib_stream, "InnoDB: Starting in background the rollback" " of uncommitted transactions\n"); } mutex_exit(&kernel_mutex); loop: mutex_enter(&kernel_mutex); for (trx = UT_LIST_GET_FIRST(trx_sys->trx_list); trx; trx = UT_LIST_GET_NEXT(trx_list, trx)) { if (!trx->is_recovered) { continue; } switch (trx->conc_state) { case TRX_NOT_STARTED: case TRX_PREPARED: continue; case TRX_COMMITTED_IN_MEMORY: mutex_exit(&kernel_mutex); ib_logger(ib_stream, "InnoDB: Cleaning up trx with id " TRX_ID_FMT "\n", TRX_ID_PREP_PRINTF(trx->id)); trx_cleanup_at_db_startup(trx); goto loop; case TRX_ACTIVE: if (all || trx_get_dict_operation(trx) != TRX_DICT_OP_NONE) { mutex_exit(&kernel_mutex); // FIXME: Need to get rid of this global access trx_rollback_active(srv_force_recovery, trx); goto loop; } } } if (all) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Rollback of non-prepared" " transactions completed\n"); } leave_function: mutex_exit(&kernel_mutex); } /*******************************************************************//** Rollback or clean up any incomplete transactions which were encountered in crash recovery. If the transaction already was committed, then we clean up a possible insert undo log. If the transaction was not yet committed, then we roll it back. Note: this is done in a background thread. @return a dummy parameter */ UNIV_INTERN os_thread_ret_t trx_rollback_or_clean_all_recovered( /*================================*/ void* arg __attribute__((unused))) /*!< in: a dummy parameter required by os_thread_create */ { trx_rollback_or_clean_recovered(TRUE); /* We count the number of threads in os_thread_exit(). A created thread should always use that to exit and not use return() to exit. */ os_thread_exit(NULL); OS_THREAD_DUMMY_RETURN; } /*******************************************************************//** Creates an undo number array. @return own: undo number array */ UNIV_INTERN trx_undo_arr_t* trx_undo_arr_create(void) /*=====================*/ { trx_undo_arr_t* arr; mem_heap_t* heap; ulint i; heap = mem_heap_create(1024); arr = mem_heap_alloc(heap, sizeof(trx_undo_arr_t)); arr->infos = mem_heap_alloc(heap, sizeof(trx_undo_inf_t) * UNIV_MAX_PARALLELISM); arr->n_cells = UNIV_MAX_PARALLELISM; arr->n_used = 0; arr->heap = heap; for (i = 0; i < UNIV_MAX_PARALLELISM; i++) { (trx_undo_arr_get_nth_info(arr, i))->in_use = FALSE; } return(arr); } /*******************************************************************//** Frees an undo number array. */ UNIV_INTERN void trx_undo_arr_free( /*==============*/ trx_undo_arr_t* arr) /*!< in: undo number array */ { ut_ad(arr->n_used == 0); mem_heap_free(arr->heap); } /*******************************************************************//** Stores info of an undo log record to the array if it is not stored yet. @return FALSE if the record already existed in the array */ static ibool trx_undo_arr_store_info( /*====================*/ trx_t* trx, /*!< in: transaction */ undo_no_t undo_no)/*!< in: undo number */ { trx_undo_inf_t* cell; trx_undo_inf_t* stored_here; trx_undo_arr_t* arr; ulint n_used; ulint n; ulint i; n = 0; arr = trx->undo_no_arr; n_used = arr->n_used; stored_here = NULL; for (i = 0;; i++) { cell = trx_undo_arr_get_nth_info(arr, i); if (!cell->in_use) { if (!stored_here) { /* Not in use, we may store here */ cell->undo_no = undo_no; cell->in_use = TRUE; arr->n_used++; stored_here = cell; } } else { n++; if (0 == ut_dulint_cmp(cell->undo_no, undo_no)) { if (stored_here) { stored_here->in_use = FALSE; ut_ad(arr->n_used > 0); arr->n_used--; } ut_ad(arr->n_used == n_used); return(FALSE); } } if (n == n_used && stored_here) { ut_ad(arr->n_used == 1 + n_used); return(TRUE); } } } /*******************************************************************//** Removes an undo number from the array. */ static void trx_undo_arr_remove_info( /*=====================*/ trx_undo_arr_t* arr, /*!< in: undo number array */ undo_no_t undo_no)/*!< in: undo number */ { trx_undo_inf_t* cell; ulint n_used; ulint n; ulint i; n_used = arr->n_used; n = 0; for (i = 0;; i++) { cell = trx_undo_arr_get_nth_info(arr, i); if (cell->in_use && 0 == ut_dulint_cmp(cell->undo_no, undo_no)) { cell->in_use = FALSE; ut_ad(arr->n_used > 0); arr->n_used--; return; } } } /*******************************************************************//** Gets the biggest undo number in an array. @return biggest value, ut_dulint_zero if the array is empty */ static undo_no_t trx_undo_arr_get_biggest( /*=====================*/ trx_undo_arr_t* arr) /*!< in: undo number array */ { trx_undo_inf_t* cell; ulint n_used; undo_no_t biggest; ulint n; ulint i; n = 0; n_used = arr->n_used; biggest = ut_dulint_zero; for (i = 0;; i++) { cell = trx_undo_arr_get_nth_info(arr, i); if (cell->in_use) { n++; if (ut_dulint_cmp(cell->undo_no, biggest) > 0) { biggest = cell->undo_no; } } if (n == n_used) { return(biggest); } } } /***********************************************************************//** Tries truncate the undo logs. */ UNIV_INTERN void trx_roll_try_truncate( /*==================*/ trx_t* trx) /*!< in/out: transaction */ { trx_undo_arr_t* arr; undo_no_t limit; undo_no_t biggest; ut_ad(mutex_own(&(trx->undo_mutex))); ut_ad(mutex_own(&((trx->rseg)->mutex))); trx->pages_undone = 0; arr = trx->undo_no_arr; limit = trx->undo_no; if (arr->n_used > 0) { biggest = trx_undo_arr_get_biggest(arr); if (ut_dulint_cmp(biggest, limit) >= 0) { limit = ut_dulint_add(biggest, 1); } } if (trx->insert_undo) { trx_undo_truncate_end(trx, trx->insert_undo, limit); } if (trx->update_undo) { trx_undo_truncate_end(trx, trx->update_undo, limit); } } /***********************************************************************//** Pops the topmost undo log record in a single undo log and updates the info about the topmost record in the undo log memory struct. @return undo log record, the page s-latched */ static trx_undo_rec_t* trx_roll_pop_top_rec( /*=================*/ trx_t* trx, /*!< in: transaction */ trx_undo_t* undo, /*!< in: undo log */ mtr_t* mtr) /*!< in: mtr */ { page_t* undo_page; ulint offset; trx_undo_rec_t* prev_rec; page_t* prev_rec_page; ut_ad(mutex_own(&(trx->undo_mutex))); undo_page = trx_undo_page_get_s_latched(undo->space, undo->zip_size, undo->top_page_no, mtr); offset = undo->top_offset; /*ib_logger(ib_stream, "Thread %lu undoing trx %lu undo record %lu\n", os_thread_get_curr_id(), ut_dulint_get_low(trx->id), ut_dulint_get_low(undo->top_undo_no)); */ prev_rec = trx_undo_get_prev_rec(undo_page + offset, undo->hdr_page_no, undo->hdr_offset, mtr); if (prev_rec == NULL) { undo->empty = TRUE; } else { prev_rec_page = page_align(prev_rec); if (prev_rec_page != undo_page) { trx->pages_undone++; } undo->top_page_no = page_get_page_no(prev_rec_page); undo->top_offset = prev_rec - prev_rec_page; undo->top_undo_no = trx_undo_rec_get_undo_no(prev_rec); } return(undo_page + offset); } /********************************************************************//** Pops the topmost record when the two undo logs of a transaction are seen as a single stack of records ordered by their undo numbers. Inserts the undo number of the popped undo record to the array of currently processed undo numbers in the transaction. When the query thread finishes processing of this undo record, it must be released with trx_undo_rec_release. @return undo log record copied to heap, NULL if none left, or if the undo number of the top record would be less than the limit */ UNIV_INTERN trx_undo_rec_t* trx_roll_pop_top_rec_of_trx( /*========================*/ trx_t* trx, /*!< in: transaction */ undo_no_t limit, /*!< in: least undo number we need */ roll_ptr_t* roll_ptr,/*!< out: roll pointer to undo record */ mem_heap_t* heap) /*!< in: memory heap where copied */ { trx_undo_t* undo; trx_undo_t* ins_undo; trx_undo_t* upd_undo; trx_undo_rec_t* undo_rec; trx_undo_rec_t* undo_rec_copy; undo_no_t undo_no; ibool is_insert; trx_rseg_t* rseg; ulint progress_pct; mtr_t mtr; rseg = trx->rseg; try_again: mutex_enter(&(trx->undo_mutex)); if (trx->pages_undone >= TRX_ROLL_TRUNC_THRESHOLD) { mutex_enter(&(rseg->mutex)); trx_roll_try_truncate(trx); mutex_exit(&(rseg->mutex)); } ins_undo = trx->insert_undo; upd_undo = trx->update_undo; if (!ins_undo || ins_undo->empty) { undo = upd_undo; } else if (!upd_undo || upd_undo->empty) { undo = ins_undo; } else if (ut_dulint_cmp(upd_undo->top_undo_no, ins_undo->top_undo_no) > 0) { undo = upd_undo; } else { undo = ins_undo; } if (!undo || undo->empty || (ut_dulint_cmp(limit, undo->top_undo_no) > 0)) { if ((trx->undo_no_arr)->n_used == 0) { /* Rollback is ending */ mutex_enter(&(rseg->mutex)); trx_roll_try_truncate(trx); mutex_exit(&(rseg->mutex)); } mutex_exit(&(trx->undo_mutex)); return(NULL); } if (undo == ins_undo) { is_insert = TRUE; } else { is_insert = FALSE; } *roll_ptr = trx_undo_build_roll_ptr(is_insert, (undo->rseg)->id, undo->top_page_no, undo->top_offset); mtr_start(&mtr); undo_rec = trx_roll_pop_top_rec(trx, undo, &mtr); undo_no = trx_undo_rec_get_undo_no(undo_rec); ut_ad(ut_dulint_cmp(ut_dulint_add(undo_no, 1), trx->undo_no) == 0); /* We print rollback progress info if we are in a crash recovery and the transaction has at least 1000 row operations to undo. */ if (trx == trx_roll_crash_recv_trx && trx_roll_max_undo_no > 1000) { progress_pct = 100 - (ulint) ((ut_conv_dulint_to_longlong(undo_no) * 100) / trx_roll_max_undo_no); if (progress_pct != trx_roll_progress_printed_pct) { if (trx_roll_progress_printed_pct == 0) { ib_logger(ib_stream, "\nInnoDB: Progress in percents:" " %lu", (ulong) progress_pct); } else { ib_logger(ib_stream, " %lu", (ulong) progress_pct); } trx_roll_progress_printed_pct = progress_pct; } } trx->undo_no = undo_no; if (!trx_undo_arr_store_info(trx, undo_no)) { /* A query thread is already processing this undo log record */ mutex_exit(&(trx->undo_mutex)); mtr_commit(&mtr); goto try_again; } undo_rec_copy = trx_undo_rec_copy(undo_rec, heap); mutex_exit(&(trx->undo_mutex)); mtr_commit(&mtr); return(undo_rec_copy); } /********************************************************************//** Reserves an undo log record for a query thread to undo. This should be called if the query thread gets the undo log record not using the pop function above. @return TRUE if succeeded */ UNIV_INTERN ibool trx_undo_rec_reserve( /*=================*/ trx_t* trx, /*!< in/out: transaction */ undo_no_t undo_no)/*!< in: undo number of the record */ { ibool ret; mutex_enter(&(trx->undo_mutex)); ret = trx_undo_arr_store_info(trx, undo_no); mutex_exit(&(trx->undo_mutex)); return(ret); } /*******************************************************************//** Releases a reserved undo record. */ UNIV_INTERN void trx_undo_rec_release( /*=================*/ trx_t* trx, /*!< in/out: transaction */ undo_no_t undo_no)/*!< in: undo number */ { trx_undo_arr_t* arr; mutex_enter(&(trx->undo_mutex)); arr = trx->undo_no_arr; trx_undo_arr_remove_info(arr, undo_no); mutex_exit(&(trx->undo_mutex)); } /*********************************************************************//** Starts a rollback operation. */ UNIV_INTERN void trx_rollback( /*=========*/ trx_t* trx, /*!< in: transaction */ trx_sig_t* sig, /*!< in: signal starting the rollback */ que_thr_t** next_thr)/*!< in/out: next query thread to run; if the value which is passed in is a pointer to a NULL pointer, then the calling function can start running a new query thread; if the passed value is NULL, the parameter is ignored */ { que_t* roll_graph; que_thr_t* thr; /* que_thr_t* thr2; */ ut_ad(mutex_own(&kernel_mutex)); ut_ad((trx->undo_no_arr == NULL) || ((trx->undo_no_arr)->n_used == 0)); /* Initialize the rollback field in the transaction */ if (sig->type == TRX_SIG_TOTAL_ROLLBACK) { trx->roll_limit = ut_dulint_zero; } else if (sig->type == TRX_SIG_ROLLBACK_TO_SAVEPT) { trx->roll_limit = (sig->savept).least_undo_no; } else if (sig->type == TRX_SIG_ERROR_OCCURRED) { trx->roll_limit = trx->last_sql_stat_start.least_undo_no; } else { ut_error; } ut_a(ut_dulint_cmp(trx->roll_limit, trx->undo_no) <= 0); trx->pages_undone = 0; if (trx->undo_no_arr == NULL) { trx->undo_no_arr = trx_undo_arr_create(); } /* Build a 'query' graph which will perform the undo operations */ roll_graph = trx_roll_graph_build(trx); trx->graph = roll_graph; trx->que_state = TRX_QUE_ROLLING_BACK; thr = que_fork_start_command(roll_graph); ut_ad(thr); /* thr2 = que_fork_start_command(roll_graph); ut_ad(thr2); */ if (next_thr && (*next_thr == NULL)) { *next_thr = thr; /* srv_que_task_enqueue_low(thr2); */ } else { srv_que_task_enqueue_low(thr); /* srv_que_task_enqueue_low(thr2); */ } } /****************************************************************//** Builds an undo 'query' graph for a transaction. The actual rollback is performed by executing this query graph like a query subprocedure call. The reply about the completion of the rollback will be sent by this graph. @return own: the query graph */ UNIV_INTERN que_t* trx_roll_graph_build( /*=================*/ trx_t* trx) /*!< in: trx handle */ { mem_heap_t* heap; que_fork_t* fork; que_thr_t* thr; /* que_thr_t* thr2; */ ut_ad(mutex_own(&kernel_mutex)); heap = mem_heap_create(512); fork = que_fork_create(NULL, NULL, QUE_FORK_ROLLBACK, heap); fork->trx = trx; thr = que_thr_create(fork, heap); /* thr2 = que_thr_create(fork, heap); */ thr->child = row_undo_node_create(trx, thr, heap); /* thr2->child = row_undo_node_create(trx, thr2, heap); */ return(fork); } /*********************************************************************//** Finishes error processing after the necessary partial rollback has been done. */ static void trx_finish_error_processing( /*========================*/ trx_t* trx) /*!< in: transaction */ { trx_sig_t* sig; trx_sig_t* next_sig; ut_ad(mutex_own(&kernel_mutex)); sig = UT_LIST_GET_FIRST(trx->signals); while (sig != NULL) { next_sig = UT_LIST_GET_NEXT(signals, sig); if (sig->type == TRX_SIG_ERROR_OCCURRED) { trx_sig_remove(trx, sig); } sig = next_sig; } trx->que_state = TRX_QUE_RUNNING; } /*********************************************************************//** Finishes a partial rollback operation. */ static void trx_finish_partial_rollback_off_kernel( /*===================================*/ trx_t* trx, /*!< in: transaction */ que_thr_t** next_thr)/*!< in/out: next query thread to run; if the value which is passed in is a pointer to a NULL pointer, then the calling function can start running a new query thread; if this parameter is NULL, it is ignored */ { trx_sig_t* sig; ut_ad(mutex_own(&kernel_mutex)); sig = UT_LIST_GET_FIRST(trx->signals); /* Remove the signal from the signal queue and send reply message to it */ trx_sig_reply(sig, next_thr); trx_sig_remove(trx, sig); trx->que_state = TRX_QUE_RUNNING; } /****************************************************************//** Finishes a transaction rollback. */ UNIV_INTERN void trx_finish_rollback_off_kernel( /*===========================*/ que_t* graph, /*!< in: undo graph which can now be freed */ trx_t* trx, /*!< in: transaction */ que_thr_t** next_thr)/*!< in/out: next query thread to run; if the value which is passed in is a pointer to a NULL pointer, then the calling function can start running a new query thread; if this parameter is NULL, it is ignored */ { trx_sig_t* sig; trx_sig_t* next_sig; ut_ad(mutex_own(&kernel_mutex)); ut_a(trx->undo_no_arr == NULL || trx->undo_no_arr->n_used == 0); /* Free the memory reserved by the undo graph */ que_graph_free(graph); sig = UT_LIST_GET_FIRST(trx->signals); if (sig->type == TRX_SIG_ROLLBACK_TO_SAVEPT) { trx_finish_partial_rollback_off_kernel(trx, next_thr); return; } else if (sig->type == TRX_SIG_ERROR_OCCURRED) { trx_finish_error_processing(trx); return; } #ifdef UNIV_DEBUG if (lock_print_waits) { ib_logger(ib_stream, "Trx %lu rollback finished\n", (ulong) ut_dulint_get_low(trx->id)); } #endif /* UNIV_DEBUG */ trx_commit_off_kernel(trx); /* Remove all TRX_SIG_TOTAL_ROLLBACK signals from the signal queue and send reply messages to them */ trx->que_state = TRX_QUE_RUNNING; while (sig != NULL) { next_sig = UT_LIST_GET_NEXT(signals, sig); if (sig->type == TRX_SIG_TOTAL_ROLLBACK) { trx_sig_reply(sig, next_thr); trx_sig_remove(trx, sig); } sig = next_sig; } } /*********************************************************************//** Creates a rollback command node struct. @return own: rollback node struct */ UNIV_INTERN roll_node_t* roll_node_create( /*=============*/ mem_heap_t* heap) /*!< in: mem heap where created */ { roll_node_t* node; node = mem_heap_alloc(heap, sizeof(roll_node_t)); node->common.type = QUE_NODE_ROLLBACK; node->state = ROLL_NODE_SEND; node->partial = FALSE; return(node); } /***********************************************************//** Performs an execution step for a rollback command node in a query graph. @return query thread to run next, or NULL */ UNIV_INTERN que_thr_t* trx_rollback_step( /*==============*/ que_thr_t* thr) /*!< in: query thread */ { roll_node_t* node; ulint sig_no; trx_savept_t* savept; node = thr->run_node; ut_ad(que_node_get_type(node) == QUE_NODE_ROLLBACK); if (thr->prev_node == que_node_get_parent(node)) { node->state = ROLL_NODE_SEND; } if (node->state == ROLL_NODE_SEND) { mutex_enter(&kernel_mutex); node->state = ROLL_NODE_WAIT; if (node->partial) { sig_no = TRX_SIG_ROLLBACK_TO_SAVEPT; savept = &(node->savept); } else { sig_no = TRX_SIG_TOTAL_ROLLBACK; savept = NULL; } /* Send a rollback signal to the transaction */ trx_sig_send(thr_get_trx(thr), sig_no, TRX_SIG_SELF, thr, savept, NULL); thr->state = QUE_THR_SIG_REPLY_WAIT; mutex_exit(&kernel_mutex); return(NULL); } ut_ad(node->state == ROLL_NODE_WAIT); thr->run_node = que_node_get_parent(node); return(thr); } haildb-2.3.2/trx/trx0purge.c0000644000175000017500000007424311513177357016576 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file trx/trx0purge.c Purge old versions Created 3/26/1996 Heikki Tuuri *******************************************************/ #include "trx0purge.h" #ifdef UNIV_NONINL #include "trx0purge.ic" #endif #include "fsp0fsp.h" #include "mach0data.h" #include "mtr0log.h" #include "trx0rseg.h" #include "trx0trx.h" #include "trx0roll.h" #include "read0read.h" #include "fut0fut.h" #include "que0que.h" #include "row0purge.h" #include "row0upd.h" #include "trx0rec.h" #include "srv0que.h" #include "os0thread.h" /** The global data structure coordinating a purge */ UNIV_INTERN trx_purge_t* purge_sys = NULL; /** A dummy undo record used as a return value when we have a whole undo log which needs no purge */ UNIV_INTERN trx_undo_rec_t trx_purge_dummy_rec; /*****************************************************************//** Reset the variables. */ UNIV_INTERN void trx_purge_var_init(void) /*====================*/ { purge_sys = NULL; memset(&trx_purge_dummy_rec, 0x0, sizeof(trx_purge_dummy_rec)); } /********************************************************************* Checks if trx_id is >= purge_view: then it is guaranteed that its update undo log still exists in the system. @return TRUE if is sure that it is preserved, also if the function returns FALSE, it is possible that the undo log still exists in the system */ UNIV_INTERN ibool trx_purge_update_undo_must_exist( /*=============================*/ trx_id_t trx_id) /*!< in: transaction id */ { #ifdef UNIV_SYNC_DEBUG ut_ad(rw_lock_own(&(purge_sys->latch), RW_LOCK_SHARED)); #endif /* UNIV_SYNC_DEBUG */ if (!read_view_sees_trx_id(purge_sys->view, trx_id)) { return(TRUE); } return(FALSE); } /*=================== PURGE RECORD ARRAY =============================*/ /*******************************************************************//** Stores info of an undo log record during a purge. @return pointer to the storage cell */ static trx_undo_inf_t* trx_purge_arr_store_info( /*=====================*/ trx_id_t trx_no, /*!< in: transaction number */ undo_no_t undo_no)/*!< in: undo number */ { trx_undo_inf_t* cell; trx_undo_arr_t* arr; ulint i; arr = purge_sys->arr; for (i = 0;; i++) { cell = trx_undo_arr_get_nth_info(arr, i); if (!(cell->in_use)) { /* Not in use, we may store here */ cell->undo_no = undo_no; cell->trx_no = trx_no; cell->in_use = TRUE; arr->n_used++; return(cell); } } } /*******************************************************************//** Removes info of an undo log record during a purge. */ UNIV_INLINE void trx_purge_arr_remove_info( /*======================*/ trx_undo_inf_t* cell) /*!< in: pointer to the storage cell */ { trx_undo_arr_t* arr; arr = purge_sys->arr; cell->in_use = FALSE; ut_ad(arr->n_used > 0); arr->n_used--; } /*******************************************************************//** Gets the biggest pair of a trx number and an undo number in a purge array. */ static void trx_purge_arr_get_biggest( /*======================*/ trx_undo_arr_t* arr, /*!< in: purge array */ trx_id_t* trx_no, /*!< out: transaction number: ut_dulint_zero if array is empty */ undo_no_t* undo_no)/*!< out: undo number */ { trx_undo_inf_t* cell; trx_id_t pair_trx_no; undo_no_t pair_undo_no; int trx_cmp; ulint n_used; ulint i; ulint n; n = 0; n_used = arr->n_used; pair_trx_no = ut_dulint_zero; pair_undo_no = ut_dulint_zero; for (i = 0;; i++) { cell = trx_undo_arr_get_nth_info(arr, i); if (cell->in_use) { n++; trx_cmp = ut_dulint_cmp(cell->trx_no, pair_trx_no); if ((trx_cmp > 0) || ((trx_cmp == 0) && (ut_dulint_cmp(cell->undo_no, pair_undo_no) >= 0))) { pair_trx_no = cell->trx_no; pair_undo_no = cell->undo_no; } } if (n == n_used) { *trx_no = pair_trx_no; *undo_no = pair_undo_no; return; } } } /****************************************************************//** Builds a purge 'query' graph. The actual purge is performed by executing this query graph. @return own: the query graph */ static que_t* trx_purge_graph_build(void) /*=======================*/ { mem_heap_t* heap; que_fork_t* fork; que_thr_t* thr; /* que_thr_t* thr2; */ heap = mem_heap_create(512); fork = que_fork_create(NULL, NULL, QUE_FORK_PURGE, heap); fork->trx = purge_sys->trx; thr = que_thr_create(fork, heap); thr->child = row_purge_node_create(thr, heap); /* thr2 = que_thr_create(fork, fork, heap); thr2->child = row_purge_node_create(fork, thr2, heap); */ return(fork); } /********************************************************************//** Creates the global purge system control structure and inits the history mutex. */ UNIV_INTERN void trx_purge_sys_create(void) /*======================*/ { ut_ad(mutex_own(&kernel_mutex)); purge_sys = mem_alloc(sizeof(trx_purge_t)); purge_sys->state = TRX_STOP_PURGE; purge_sys->n_pages_handled = 0; purge_sys->purge_trx_no = ut_dulint_zero; purge_sys->purge_undo_no = ut_dulint_zero; purge_sys->next_stored = FALSE; rw_lock_create(&purge_sys->latch, SYNC_PURGE_LATCH); mutex_create(&purge_sys->mutex, SYNC_PURGE_SYS); purge_sys->heap = mem_heap_create(256); purge_sys->arr = trx_undo_arr_create(); purge_sys->sess = sess_open(); purge_sys->trx = purge_sys->sess->trx; purge_sys->trx->is_purge = 1; ut_a(trx_start_low(purge_sys->trx, ULINT_UNDEFINED)); purge_sys->query = trx_purge_graph_build(); purge_sys->view = read_view_oldest_copy_or_open_new(ut_dulint_zero, purge_sys->heap); } /************************************************************************ Frees the global purge system control structure. */ UNIV_INTERN void trx_purge_sys_close(void) /*======================*/ { ut_ad(!mutex_own(&kernel_mutex)); que_graph_free(purge_sys->query); ut_a(purge_sys->sess->trx->is_purge); purge_sys->sess->trx->conc_state = TRX_NOT_STARTED; sess_close(purge_sys->sess); purge_sys->sess = NULL; if (purge_sys->view != NULL) { /* Because acquiring the kernel mutex is a pre-condition of read_view_close(). We don't really need it here. */ mutex_enter(&kernel_mutex); read_view_close(purge_sys->view); purge_sys->view = NULL; mutex_exit(&kernel_mutex); } trx_undo_arr_free(purge_sys->arr); rw_lock_free(&purge_sys->latch); mutex_free(&purge_sys->mutex); mem_heap_free(purge_sys->heap); mem_free(purge_sys); purge_sys = NULL; } /*================ UNDO LOG HISTORY LIST =============================*/ /********************************************************************//** Adds the update undo log as the first log in the history list. Removes the update undo log segment from the rseg slot if it is too big for reuse. */ UNIV_INTERN void trx_purge_add_update_undo_to_history( /*=================================*/ trx_t* trx, /*!< in: transaction */ page_t* undo_page, /*!< in: update undo log header page, x-latched */ mtr_t* mtr) /*!< in: mtr */ { trx_undo_t* undo; trx_rseg_t* rseg; trx_rsegf_t* rseg_header; trx_usegf_t* seg_header; trx_ulogf_t* undo_header; trx_upagef_t* page_header; ulint hist_size; undo = trx->update_undo; ut_ad(undo); rseg = undo->rseg; ut_ad(mutex_own(&(rseg->mutex))); rseg_header = trx_rsegf_get(rseg->space, rseg->zip_size, rseg->page_no, mtr); undo_header = undo_page + undo->hdr_offset; seg_header = undo_page + TRX_UNDO_SEG_HDR; page_header = undo_page + TRX_UNDO_PAGE_HDR; if (undo->state != TRX_UNDO_CACHED) { /* The undo log segment will not be reused */ if (undo->id >= TRX_RSEG_N_SLOTS) { ib_logger(ib_stream, "InnoDB: Error: undo->id is %lu\n", (ulong) undo->id); ut_error; } trx_rsegf_set_nth_undo(rseg_header, undo->id, FIL_NULL, mtr); hist_size = mtr_read_ulint(rseg_header + TRX_RSEG_HISTORY_SIZE, MLOG_4BYTES, mtr); ut_ad(undo->size == flst_get_len( seg_header + TRX_UNDO_PAGE_LIST, mtr)); mlog_write_ulint(rseg_header + TRX_RSEG_HISTORY_SIZE, hist_size + undo->size, MLOG_4BYTES, mtr); } /* Add the log as the first in the history list */ flst_add_first(rseg_header + TRX_RSEG_HISTORY, undo_header + TRX_UNDO_HISTORY_NODE, mtr); mutex_enter(&kernel_mutex); trx_sys->rseg_history_len++; mutex_exit(&kernel_mutex); /* Write the trx number to the undo log header */ mlog_write_dulint(undo_header + TRX_UNDO_TRX_NO, trx->no, mtr); /* Write information about delete markings to the undo log header */ if (!undo->del_marks) { mlog_write_ulint(undo_header + TRX_UNDO_DEL_MARKS, FALSE, MLOG_2BYTES, mtr); } if (rseg->last_page_no == FIL_NULL) { rseg->last_page_no = undo->hdr_page_no; rseg->last_offset = undo->hdr_offset; rseg->last_trx_no = trx->no; rseg->last_del_marks = undo->del_marks; } } /**********************************************************************//** Frees an undo log segment which is in the history list. Cuts the end of the history list at the youngest undo log in this segment. */ static void trx_purge_free_segment( /*===================*/ trx_rseg_t* rseg, /*!< in: rollback segment */ fil_addr_t hdr_addr, /*!< in: the file address of log_hdr */ ulint n_removed_logs) /*!< in: count of how many undo logs we will cut off from the end of the history list */ { page_t* undo_page; trx_rsegf_t* rseg_hdr; trx_ulogf_t* log_hdr; trx_usegf_t* seg_hdr; ibool freed; ulint seg_size; ulint hist_size; ibool marked = FALSE; mtr_t mtr; /* ib_logger("Freeing an update undo log segment\n", ib_stream); */ ut_ad(mutex_own(&(purge_sys->mutex))); loop: mtr_start(&mtr); mutex_enter(&(rseg->mutex)); rseg_hdr = trx_rsegf_get(rseg->space, rseg->zip_size, rseg->page_no, &mtr); undo_page = trx_undo_page_get(rseg->space, rseg->zip_size, hdr_addr.page, &mtr); seg_hdr = undo_page + TRX_UNDO_SEG_HDR; log_hdr = undo_page + hdr_addr.boffset; /* Mark the last undo log totally purged, so that if the system crashes, the tail of the undo log will not get accessed again. The list of pages in the undo log tail gets inconsistent during the freeing of the segment, and therefore purge should not try to access them again. */ if (!marked) { mlog_write_ulint(log_hdr + TRX_UNDO_DEL_MARKS, FALSE, MLOG_2BYTES, &mtr); marked = TRUE; } freed = fseg_free_step_not_header(seg_hdr + TRX_UNDO_FSEG_HEADER, &mtr); if (!freed) { mutex_exit(&(rseg->mutex)); mtr_commit(&mtr); goto loop; } /* The page list may now be inconsistent, but the length field stored in the list base node tells us how big it was before we started the freeing. */ seg_size = flst_get_len(seg_hdr + TRX_UNDO_PAGE_LIST, &mtr); /* We may free the undo log segment header page; it must be freed within the same mtr as the undo log header is removed from the history list: otherwise, in case of a database crash, the segment could become inaccessible garbage in the file space. */ flst_cut_end(rseg_hdr + TRX_RSEG_HISTORY, log_hdr + TRX_UNDO_HISTORY_NODE, n_removed_logs, &mtr); mutex_enter(&kernel_mutex); ut_ad(trx_sys->rseg_history_len >= n_removed_logs); trx_sys->rseg_history_len -= n_removed_logs; mutex_exit(&kernel_mutex); freed = FALSE; while (!freed) { /* Here we assume that a file segment with just the header page can be freed in a few steps, so that the buffer pool is not flooded with bufferfixed pages: see the note in fsp0fsp.c. */ freed = fseg_free_step(seg_hdr + TRX_UNDO_FSEG_HEADER, &mtr); } hist_size = mtr_read_ulint(rseg_hdr + TRX_RSEG_HISTORY_SIZE, MLOG_4BYTES, &mtr); ut_ad(hist_size >= seg_size); mlog_write_ulint(rseg_hdr + TRX_RSEG_HISTORY_SIZE, hist_size - seg_size, MLOG_4BYTES, &mtr); ut_ad(rseg->curr_size >= seg_size); rseg->curr_size -= seg_size; mutex_exit(&(rseg->mutex)); mtr_commit(&mtr); } /********************************************************************//** Removes unnecessary history data from a rollback segment. */ static void trx_purge_truncate_rseg_history( /*============================*/ trx_rseg_t* rseg, /*!< in: rollback segment */ trx_id_t limit_trx_no, /*!< in: remove update undo logs whose trx number is < limit_trx_no */ undo_no_t limit_undo_no) /*!< in: if transaction number is equal to limit_trx_no, truncate undo records with undo number < limit_undo_no */ { fil_addr_t hdr_addr; fil_addr_t prev_hdr_addr; trx_rsegf_t* rseg_hdr; page_t* undo_page; trx_ulogf_t* log_hdr; trx_usegf_t* seg_hdr; int cmp; ulint n_removed_logs = 0; mtr_t mtr; ut_ad(mutex_own(&(purge_sys->mutex))); mtr_start(&mtr); mutex_enter(&(rseg->mutex)); rseg_hdr = trx_rsegf_get(rseg->space, rseg->zip_size, rseg->page_no, &mtr); hdr_addr = trx_purge_get_log_from_hist( flst_get_last(rseg_hdr + TRX_RSEG_HISTORY, &mtr)); loop: if (hdr_addr.page == FIL_NULL) { mutex_exit(&(rseg->mutex)); mtr_commit(&mtr); return; } undo_page = trx_undo_page_get(rseg->space, rseg->zip_size, hdr_addr.page, &mtr); log_hdr = undo_page + hdr_addr.boffset; cmp = ut_dulint_cmp(mach_read_from_8(log_hdr + TRX_UNDO_TRX_NO), limit_trx_no); if (cmp == 0) { trx_undo_truncate_start(rseg, rseg->space, hdr_addr.page, hdr_addr.boffset, limit_undo_no); } if (cmp >= 0) { mutex_enter(&kernel_mutex); ut_a(trx_sys->rseg_history_len >= n_removed_logs); trx_sys->rseg_history_len -= n_removed_logs; mutex_exit(&kernel_mutex); flst_truncate_end(rseg_hdr + TRX_RSEG_HISTORY, log_hdr + TRX_UNDO_HISTORY_NODE, n_removed_logs, &mtr); mutex_exit(&(rseg->mutex)); mtr_commit(&mtr); return; } prev_hdr_addr = trx_purge_get_log_from_hist( flst_get_prev_addr(log_hdr + TRX_UNDO_HISTORY_NODE, &mtr)); n_removed_logs++; seg_hdr = undo_page + TRX_UNDO_SEG_HDR; if ((mach_read_from_2(seg_hdr + TRX_UNDO_STATE) == TRX_UNDO_TO_PURGE) && (mach_read_from_2(log_hdr + TRX_UNDO_NEXT_LOG) == 0)) { /* We can free the whole log segment */ mutex_exit(&(rseg->mutex)); mtr_commit(&mtr); trx_purge_free_segment(rseg, hdr_addr, n_removed_logs); n_removed_logs = 0; } else { mutex_exit(&(rseg->mutex)); mtr_commit(&mtr); } mtr_start(&mtr); mutex_enter(&(rseg->mutex)); rseg_hdr = trx_rsegf_get(rseg->space, rseg->zip_size, rseg->page_no, &mtr); hdr_addr = prev_hdr_addr; goto loop; } /********************************************************************//** Removes unnecessary history data from rollback segments. NOTE that when this function is called, the caller must not have any latches on undo log pages! */ static void trx_purge_truncate_history(void) /*============================*/ { trx_rseg_t* rseg; trx_id_t limit_trx_no; undo_no_t limit_undo_no; ut_ad(mutex_own(&(purge_sys->mutex))); trx_purge_arr_get_biggest(purge_sys->arr, &limit_trx_no, &limit_undo_no); if (ut_dulint_is_zero(limit_trx_no)) { limit_trx_no = purge_sys->purge_trx_no; limit_undo_no = purge_sys->purge_undo_no; } /* We play safe and set the truncate limit at most to the purge view low_limit number, though this is not necessary */ if (ut_dulint_cmp(limit_trx_no, purge_sys->view->low_limit_no) >= 0) { limit_trx_no = purge_sys->view->low_limit_no; limit_undo_no = ut_dulint_zero; } ut_ad((ut_dulint_cmp(limit_trx_no, purge_sys->view->low_limit_no) <= 0)); rseg = UT_LIST_GET_FIRST(trx_sys->rseg_list); while (rseg) { trx_purge_truncate_rseg_history(rseg, limit_trx_no, limit_undo_no); rseg = UT_LIST_GET_NEXT(rseg_list, rseg); } } /********************************************************************//** Does a truncate if the purge array is empty. NOTE that when this function is called, the caller must not have any latches on undo log pages! @return TRUE if array empty */ UNIV_INLINE ibool trx_purge_truncate_if_arr_empty(void) /*=================================*/ { ut_ad(mutex_own(&(purge_sys->mutex))); if (purge_sys->arr->n_used == 0) { trx_purge_truncate_history(); return(TRUE); } return(FALSE); } /***********************************************************************//** Updates the last not yet purged history log info in rseg when we have purged a whole undo log. Advances also purge_sys->purge_trx_no past the purged log. */ static void trx_purge_rseg_get_next_history_log( /*================================*/ trx_rseg_t* rseg) /*!< in: rollback segment */ { page_t* undo_page; trx_ulogf_t* log_hdr; trx_usegf_t* seg_hdr; fil_addr_t prev_log_addr; trx_id_t trx_no; ibool del_marks; mtr_t mtr; ut_ad(mutex_own(&(purge_sys->mutex))); mutex_enter(&(rseg->mutex)); ut_a(rseg->last_page_no != FIL_NULL); purge_sys->purge_trx_no = ut_dulint_add(rseg->last_trx_no, 1); purge_sys->purge_undo_no = ut_dulint_zero; purge_sys->next_stored = FALSE; mtr_start(&mtr); undo_page = trx_undo_page_get_s_latched(rseg->space, rseg->zip_size, rseg->last_page_no, &mtr); log_hdr = undo_page + rseg->last_offset; seg_hdr = undo_page + TRX_UNDO_SEG_HDR; /* Increase the purge page count by one for every handled log */ purge_sys->n_pages_handled++; prev_log_addr = trx_purge_get_log_from_hist( flst_get_prev_addr(log_hdr + TRX_UNDO_HISTORY_NODE, &mtr)); if (prev_log_addr.page == FIL_NULL) { /* No logs left in the history list */ rseg->last_page_no = FIL_NULL; mutex_exit(&(rseg->mutex)); mtr_commit(&mtr); mutex_enter(&kernel_mutex); /* Add debug code to track history list corruption reported on the MySQL mailing list on Nov 9, 2004. The fut0lst.c file-based list was corrupt. The prev node pointer was FIL_NULL, even though the list length was over 8 million nodes! We assume that purge truncates the history list in moderate size pieces, and if we here reach the head of the list, the list cannot be longer than 20 000 undo logs now. */ if (trx_sys->rseg_history_len > 20000) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Warning: purge reached the" " head of the history list,\n" "InnoDB: but its length is still" " reported as %lu! Make a detailed bug\n" "InnoDB: report, and submit it.\n" "InnoDB: Check the InnoDB website for " "details\n", (ulong) trx_sys->rseg_history_len); } mutex_exit(&kernel_mutex); return; } mutex_exit(&(rseg->mutex)); mtr_commit(&mtr); /* Read the trx number and del marks from the previous log header */ mtr_start(&mtr); log_hdr = trx_undo_page_get_s_latched(rseg->space, rseg->zip_size, prev_log_addr.page, &mtr) + prev_log_addr.boffset; trx_no = mach_read_from_8(log_hdr + TRX_UNDO_TRX_NO); del_marks = mach_read_from_2(log_hdr + TRX_UNDO_DEL_MARKS); mtr_commit(&mtr); mutex_enter(&(rseg->mutex)); rseg->last_page_no = prev_log_addr.page; rseg->last_offset = prev_log_addr.boffset; rseg->last_trx_no = trx_no; rseg->last_del_marks = del_marks; mutex_exit(&(rseg->mutex)); } /***********************************************************************//** Chooses the next undo log to purge and updates the info in purge_sys. This function is used to initialize purge_sys when the next record to purge is not known, and also to update the purge system info on the next record when purge has handled the whole undo log for a transaction. */ static void trx_purge_choose_next_log(void) /*===========================*/ { trx_undo_rec_t* rec; trx_rseg_t* rseg; trx_rseg_t* min_rseg; trx_id_t min_trx_no; ulint space = 0; /* remove warning (??? bug ???) */ ulint zip_size = 0; ulint page_no = 0; /* remove warning (??? bug ???) */ ulint offset = 0; /* remove warning (??? bug ???) */ mtr_t mtr; ut_ad(mutex_own(&(purge_sys->mutex))); ut_ad(purge_sys->next_stored == FALSE); rseg = UT_LIST_GET_FIRST(trx_sys->rseg_list); min_trx_no = ut_dulint_max; min_rseg = NULL; while (rseg) { mutex_enter(&(rseg->mutex)); if (rseg->last_page_no != FIL_NULL) { if ((min_rseg == NULL) || (ut_dulint_cmp(min_trx_no, rseg->last_trx_no) > 0)) { min_rseg = rseg; min_trx_no = rseg->last_trx_no; space = rseg->space; zip_size = rseg->zip_size; ut_a(space == 0); /* We assume in purge of externally stored fields that space id == 0 */ page_no = rseg->last_page_no; offset = rseg->last_offset; } } mutex_exit(&(rseg->mutex)); rseg = UT_LIST_GET_NEXT(rseg_list, rseg); } if (min_rseg == NULL) { return; } mtr_start(&mtr); if (!min_rseg->last_del_marks) { /* No need to purge this log */ rec = &trx_purge_dummy_rec; } else { rec = trx_undo_get_first_rec(space, zip_size, page_no, offset, RW_S_LATCH, &mtr); if (rec == NULL) { /* Undo log empty */ rec = &trx_purge_dummy_rec; } } purge_sys->next_stored = TRUE; purge_sys->rseg = min_rseg; purge_sys->hdr_page_no = page_no; purge_sys->hdr_offset = offset; purge_sys->purge_trx_no = min_trx_no; if (rec == &trx_purge_dummy_rec) { purge_sys->purge_undo_no = ut_dulint_zero; purge_sys->page_no = page_no; purge_sys->offset = 0; } else { purge_sys->purge_undo_no = trx_undo_rec_get_undo_no(rec); purge_sys->page_no = page_get_page_no(page_align(rec)); purge_sys->offset = page_offset(rec); } mtr_commit(&mtr); } /***********************************************************************//** Gets the next record to purge and updates the info in the purge system. @return copy of an undo log record or pointer to the dummy undo log record */ static trx_undo_rec_t* trx_purge_get_next_rec( /*===================*/ mem_heap_t* heap) /*!< in: memory heap where copied */ { trx_undo_rec_t* rec; trx_undo_rec_t* rec_copy; trx_undo_rec_t* rec2; trx_undo_rec_t* next_rec; page_t* undo_page; page_t* page; ulint offset; ulint page_no; ulint space; ulint zip_size; ulint type; ulint cmpl_info; mtr_t mtr; ut_ad(mutex_own(&(purge_sys->mutex))); ut_ad(purge_sys->next_stored); space = purge_sys->rseg->space; zip_size = purge_sys->rseg->zip_size; page_no = purge_sys->page_no; offset = purge_sys->offset; if (offset == 0) { /* It is the dummy undo log record, which means that there is no need to purge this undo log */ trx_purge_rseg_get_next_history_log(purge_sys->rseg); /* Look for the next undo log and record to purge */ trx_purge_choose_next_log(); return(&trx_purge_dummy_rec); } mtr_start(&mtr); undo_page = trx_undo_page_get_s_latched(space, zip_size, page_no, &mtr); rec = undo_page + offset; rec2 = rec; for (;;) { /* Try first to find the next record which requires a purge operation from the same page of the same undo log */ next_rec = trx_undo_page_get_next_rec(rec2, purge_sys->hdr_page_no, purge_sys->hdr_offset); if (next_rec == NULL) { rec2 = trx_undo_get_next_rec( rec2, purge_sys->hdr_page_no, purge_sys->hdr_offset, &mtr); break; } rec2 = next_rec; type = trx_undo_rec_get_type(rec2); if (type == TRX_UNDO_DEL_MARK_REC) { break; } cmpl_info = trx_undo_rec_get_cmpl_info(rec2); if (trx_undo_rec_get_extern_storage(rec2)) { break; } if ((type == TRX_UNDO_UPD_EXIST_REC) && !(cmpl_info & UPD_NODE_NO_ORD_CHANGE)) { break; } } if (rec2 == NULL) { mtr_commit(&mtr); trx_purge_rseg_get_next_history_log(purge_sys->rseg); /* Look for the next undo log and record to purge */ trx_purge_choose_next_log(); mtr_start(&mtr); undo_page = trx_undo_page_get_s_latched(space, zip_size, page_no, &mtr); rec = undo_page + offset; } else { page = page_align(rec2); purge_sys->purge_undo_no = trx_undo_rec_get_undo_no(rec2); purge_sys->page_no = page_get_page_no(page); purge_sys->offset = rec2 - page; if (undo_page != page) { /* We advance to a new page of the undo log: */ purge_sys->n_pages_handled++; } } rec_copy = trx_undo_rec_copy(rec, heap); mtr_commit(&mtr); return(rec_copy); } /********************************************************************//** Fetches the next undo log record from the history list to purge. It must be released with the corresponding release function. @return copy of an undo log record or pointer to trx_purge_dummy_rec, if the whole undo log can skipped in purge; NULL if none left */ UNIV_INTERN trx_undo_rec_t* trx_purge_fetch_next_rec( /*=====================*/ roll_ptr_t* roll_ptr,/*!< out: roll pointer to undo record */ trx_undo_inf_t** cell, /*!< out: storage cell for the record in the purge array */ mem_heap_t* heap) /*!< in: memory heap where copied */ { trx_undo_rec_t* undo_rec; mutex_enter(&(purge_sys->mutex)); if (purge_sys->state == TRX_STOP_PURGE) { trx_purge_truncate_if_arr_empty(); mutex_exit(&(purge_sys->mutex)); return(NULL); } if (!purge_sys->next_stored) { trx_purge_choose_next_log(); if (!purge_sys->next_stored) { purge_sys->state = TRX_STOP_PURGE; trx_purge_truncate_if_arr_empty(); if (srv_print_thread_releases) { ib_logger(ib_stream, "Purge: No logs left in the" " history list; pages handled %lu\n", (ulong) purge_sys->n_pages_handled); } mutex_exit(&(purge_sys->mutex)); return(NULL); } } if (purge_sys->n_pages_handled >= purge_sys->handle_limit) { purge_sys->state = TRX_STOP_PURGE; trx_purge_truncate_if_arr_empty(); mutex_exit(&(purge_sys->mutex)); return(NULL); } if (ut_dulint_cmp(purge_sys->purge_trx_no, purge_sys->view->low_limit_no) >= 0) { purge_sys->state = TRX_STOP_PURGE; trx_purge_truncate_if_arr_empty(); mutex_exit(&(purge_sys->mutex)); return(NULL); } /* ib_logger(ib_stream, "Thread %lu purging trx %lu undo record %lu\n", os_thread_get_curr_id(), ut_dulint_get_low(purge_sys->purge_trx_no), ut_dulint_get_low(purge_sys->purge_undo_no)); */ *roll_ptr = trx_undo_build_roll_ptr(FALSE, (purge_sys->rseg)->id, purge_sys->page_no, purge_sys->offset); *cell = trx_purge_arr_store_info(purge_sys->purge_trx_no, purge_sys->purge_undo_no); ut_ad(ut_dulint_cmp(purge_sys->purge_trx_no, (purge_sys->view)->low_limit_no) < 0); /* The following call will advance the stored values of purge_trx_no and purge_undo_no, therefore we had to store them first */ undo_rec = trx_purge_get_next_rec(heap); mutex_exit(&(purge_sys->mutex)); return(undo_rec); } /*******************************************************************//** Releases a reserved purge undo record. */ UNIV_INTERN void trx_purge_rec_release( /*==================*/ trx_undo_inf_t* cell) /*!< in: storage cell */ { trx_undo_arr_t* arr; mutex_enter(&(purge_sys->mutex)); arr = purge_sys->arr; trx_purge_arr_remove_info(cell); mutex_exit(&(purge_sys->mutex)); } /*******************************************************************//** This function runs a purge batch. @return number of undo log pages handled in the batch */ UNIV_INTERN ulint trx_purge(void) /*===========*/ { que_thr_t* thr; /* que_thr_t* thr2; */ ulint old_pages_handled; mutex_enter(&(purge_sys->mutex)); if (purge_sys->trx->n_active_thrs > 0) { mutex_exit(&(purge_sys->mutex)); /* Should not happen */ ut_error; return(0); } rw_lock_x_lock(&(purge_sys->latch)); mutex_enter(&kernel_mutex); /* Close and free the old purge view */ read_view_close(purge_sys->view); purge_sys->view = NULL; mem_heap_empty(purge_sys->heap); /* Determine how much data manipulation language (DML) statements need to be delayed in order to reduce the lagging of the purge thread. */ srv_dml_needed_delay = 0; /* in microseconds; default: no delay */ /* If we cannot advance the 'purge view' because of an old 'consistent read view', then the DML statements cannot be delayed. Also, srv_max_purge_lag <= 0 means 'infinity'. */ if (srv_max_purge_lag > 0 && !UT_LIST_GET_LAST(trx_sys->view_list)) { float ratio = (float) trx_sys->rseg_history_len / srv_max_purge_lag; if (ratio > ULINT_MAX / 10000) { /* Avoid overflow: maximum delay is 4295 seconds */ srv_dml_needed_delay = ULINT_MAX; } else if (ratio > 1) { /* If the history list length exceeds the innodb_max_purge_lag, the data manipulation statements are delayed by at least 5000 microseconds. */ srv_dml_needed_delay = (ulint) ((ratio - .5) * 10000); } } purge_sys->view = read_view_oldest_copy_or_open_new(ut_dulint_zero, purge_sys->heap); mutex_exit(&kernel_mutex); rw_lock_x_unlock(&(purge_sys->latch)); purge_sys->state = TRX_PURGE_ON; /* Handle at most 20 undo log pages in one purge batch */ purge_sys->handle_limit = purge_sys->n_pages_handled + 20; old_pages_handled = purge_sys->n_pages_handled; mutex_exit(&(purge_sys->mutex)); mutex_enter(&kernel_mutex); thr = que_fork_start_command(purge_sys->query); ut_ad(thr); /* thr2 = que_fork_start_command(purge_sys->query); ut_ad(thr2); */ mutex_exit(&kernel_mutex); /* srv_que_task_enqueue(thr2); */ if (srv_print_thread_releases) { ib_logger("Starting purge\n", ib_stream); } que_run_threads(thr); if (srv_print_thread_releases) { ib_logger(ib_stream, "Purge ends; pages handled %lu\n", (ulong) purge_sys->n_pages_handled); } return(purge_sys->n_pages_handled - old_pages_handled); } /******************************************************************//** Prints information of the purge system to ib_stream. */ UNIV_INTERN void trx_purge_sys_print(void) /*=====================*/ { ib_logger(ib_stream, "InnoDB: Purge system view:\n"); read_view_print(purge_sys->view); ib_logger(ib_stream, "InnoDB: Purge trx n:o " TRX_ID_FMT ", undo n:o " TRX_ID_FMT "\n", TRX_ID_PREP_PRINTF(purge_sys->purge_trx_no), TRX_ID_PREP_PRINTF(purge_sys->purge_undo_no)); ib_logger(ib_stream, "InnoDB: Purge next stored %lu, page_no %lu, offset %lu,\n" "InnoDB: Purge hdr_page_no %lu, hdr_offset %lu\n", (ulong) purge_sys->next_stored, (ulong) purge_sys->page_no, (ulong) purge_sys->offset, (ulong) purge_sys->hdr_page_no, (ulong) purge_sys->hdr_offset); } haildb-2.3.2/btr/0000755000175000017500000000000011513177437014431 5ustar00pcrewspcrews00000000000000haildb-2.3.2/btr/btr0pcur.c0000644000175000017500000004033411513177357016343 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2010, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file btr/btr0pcur.c The index tree persistent cursor Created 2/23/1996 Heikki Tuuri *******************************************************/ #include "btr0pcur.h" #ifdef UNIV_NONINL #include "btr0pcur.ic" #endif #include "ut0byte.h" #include "rem0cmp.h" #include "trx0trx.h" /**************************************************************//** Allocates memory for a persistent cursor object and initializes the cursor. @return own: persistent cursor */ UNIV_INTERN btr_pcur_t* btr_pcur_create(void) /*=================*/ { btr_pcur_t* pcur; pcur = mem_alloc(sizeof(btr_pcur_t)); pcur->btr_cur.index = NULL; btr_pcur_init(pcur); return(pcur); } /**************************************************************//** Frees the memory for a persistent cursor object. */ UNIV_INTERN void btr_pcur_free( /*==========*/ btr_pcur_t* cursor) /*!< in, own: persistent cursor */ { if (cursor->old_rec_buf != NULL) { mem_free(cursor->old_rec_buf); cursor->old_rec_buf = NULL; } cursor->btr_cur.page_cur.rec = NULL; cursor->old_rec = NULL; cursor->old_n_fields = 0; cursor->old_stored = BTR_PCUR_OLD_NOT_STORED; cursor->latch_mode = BTR_NO_LATCHES; cursor->pos_state = BTR_PCUR_NOT_POSITIONED; mem_free(cursor); } /**************************************************************//** The position of the cursor is stored by taking an initial segment of the record the cursor is positioned on, before, or after, and copying it to the cursor data structure, or just setting a flag if the cursor id before the first in an EMPTY tree, or after the last in an EMPTY tree. NOTE that the page where the cursor is positioned must not be empty if the index tree is not totally empty! */ UNIV_INTERN void btr_pcur_store_position( /*====================*/ btr_pcur_t* cursor, /*!< in: persistent cursor */ mtr_t* mtr) /*!< in: mtr */ { page_cur_t* page_cursor; buf_block_t* block; rec_t* rec; dict_index_t* index; page_t* page; ulint offs; ut_a(cursor->pos_state == BTR_PCUR_IS_POSITIONED); ut_ad(cursor->latch_mode != BTR_NO_LATCHES); block = btr_pcur_get_block(cursor); index = btr_cur_get_index(btr_pcur_get_btr_cur(cursor)); page_cursor = btr_pcur_get_page_cur(cursor); rec = page_cur_get_rec(page_cursor); page = page_align(rec); offs = page_offset(rec); ut_ad(mtr_memo_contains(mtr, block, MTR_MEMO_PAGE_S_FIX) || mtr_memo_contains(mtr, block, MTR_MEMO_PAGE_X_FIX)); ut_a(cursor->latch_mode != BTR_NO_LATCHES); if (UNIV_UNLIKELY(page_get_n_recs(page) == 0)) { /* It must be an empty index tree; NOTE that in this case we do not store the modify_clock, but always do a search if we restore the cursor position */ ut_a(btr_page_get_next(page, mtr) == FIL_NULL); ut_a(btr_page_get_prev(page, mtr) == FIL_NULL); cursor->old_stored = BTR_PCUR_OLD_STORED; if (page_rec_is_supremum_low(offs)) { cursor->rel_pos = BTR_PCUR_AFTER_LAST_IN_TREE; } else { cursor->rel_pos = BTR_PCUR_BEFORE_FIRST_IN_TREE; } return; } if (page_rec_is_supremum_low(offs)) { rec = page_rec_get_prev(rec); cursor->rel_pos = BTR_PCUR_AFTER; } else if (page_rec_is_infimum_low(offs)) { rec = page_rec_get_next(rec); cursor->rel_pos = BTR_PCUR_BEFORE; } else { cursor->rel_pos = BTR_PCUR_ON; } cursor->old_stored = BTR_PCUR_OLD_STORED; cursor->old_rec = dict_index_copy_rec_order_prefix( index, rec, &cursor->old_n_fields, &cursor->old_rec_buf, &cursor->buf_size); cursor->block_when_stored = block; cursor->modify_clock = buf_block_get_modify_clock(block); } /**************************************************************//** Copies the stored position of a pcur to another pcur. */ UNIV_INTERN void btr_pcur_copy_stored_position( /*==========================*/ btr_pcur_t* pcur_receive, /*!< in: pcur which will receive the position info */ btr_pcur_t* pcur_donate) /*!< in: pcur from which the info is copied */ { if (pcur_receive->old_rec_buf) { mem_free(pcur_receive->old_rec_buf); } ut_memcpy(pcur_receive, pcur_donate, sizeof(btr_pcur_t)); if (pcur_donate->old_rec_buf) { pcur_receive->old_rec_buf = mem_alloc(pcur_donate->buf_size); ut_memcpy(pcur_receive->old_rec_buf, pcur_donate->old_rec_buf, pcur_donate->buf_size); pcur_receive->old_rec = pcur_receive->old_rec_buf + (pcur_donate->old_rec - pcur_donate->old_rec_buf); } pcur_receive->old_n_fields = pcur_donate->old_n_fields; } /**************************************************************//** Restores the stored position of a persistent cursor bufferfixing the page and obtaining the specified latches. If the cursor position was saved when the (1) cursor was positioned on a user record: this function restores the position to the last record LESS OR EQUAL to the stored record; (2) cursor was positioned on a page infimum record: restores the position to the last record LESS than the user record which was the successor of the page infimum; (3) cursor was positioned on the page supremum: restores to the first record GREATER than the user record which was the predecessor of the supremum. (4) cursor was positioned before the first or after the last in an empty tree: restores to before first or after the last in the tree. @return TRUE if the cursor position was stored when it was on a user record and it can be restored on a user record whose ordering fields are identical to the ones of the original user record */ UNIV_INTERN ibool btr_pcur_restore_position_func( /*===========================*/ ulint latch_mode, /*!< in: BTR_SEARCH_LEAF, ... */ btr_pcur_t* cursor, /*!< in: detached persistent cursor */ const char* file, /*!< in: file name */ ulint line, /*!< in: line where called */ mtr_t* mtr) /*!< in: mtr */ { dict_index_t* index; dtuple_t* tuple; ulint mode; ulint old_mode; mem_heap_t* heap; ut_ad(mtr); ut_ad(mtr->state == MTR_ACTIVE); index = btr_cur_get_index(btr_pcur_get_btr_cur(cursor)); if (UNIV_UNLIKELY(cursor->old_stored != BTR_PCUR_OLD_STORED) || UNIV_UNLIKELY(cursor->pos_state != BTR_PCUR_WAS_POSITIONED && cursor->pos_state != BTR_PCUR_IS_POSITIONED)) { ut_print_buf(ib_stream, cursor, sizeof(btr_pcur_t)); ib_logger(ib_stream, "\n"); if (cursor->trx_if_known) { trx_print(ib_stream, cursor->trx_if_known, 0); } ut_error; } if (UNIV_UNLIKELY (cursor->rel_pos == BTR_PCUR_AFTER_LAST_IN_TREE || cursor->rel_pos == BTR_PCUR_BEFORE_FIRST_IN_TREE)) { /* In these cases we do not try an optimistic restoration, but always do a search */ btr_cur_open_at_index_side( cursor->rel_pos == BTR_PCUR_BEFORE_FIRST_IN_TREE, index, latch_mode, btr_pcur_get_btr_cur(cursor), mtr); cursor->block_when_stored = btr_pcur_get_block(cursor); return(FALSE); } ut_a(cursor->old_rec); ut_a(cursor->old_n_fields); if (UNIV_LIKELY(latch_mode == BTR_SEARCH_LEAF) || UNIV_LIKELY(latch_mode == BTR_MODIFY_LEAF)) { /* Try optimistic restoration */ if (UNIV_LIKELY(buf_page_optimistic_get( latch_mode, cursor->block_when_stored, cursor->modify_clock, file, line, mtr))) { cursor->pos_state = BTR_PCUR_IS_POSITIONED; buf_block_dbg_add_level(btr_pcur_get_block(cursor), SYNC_TREE_NODE); if (cursor->rel_pos == BTR_PCUR_ON) { #ifdef UNIV_DEBUG const rec_t* rec; const ulint* offsets1; const ulint* offsets2; #endif /* UNIV_DEBUG */ cursor->latch_mode = latch_mode; #ifdef UNIV_DEBUG rec = btr_pcur_get_rec(cursor); heap = mem_heap_create(256); offsets1 = rec_get_offsets( cursor->old_rec, index, NULL, cursor->old_n_fields, &heap); offsets2 = rec_get_offsets( rec, index, NULL, cursor->old_n_fields, &heap); ut_ad(!cmp_rec_rec(cursor->old_rec, rec, offsets1, offsets2, index)); mem_heap_free(heap); #endif /* UNIV_DEBUG */ return(TRUE); } return(FALSE); } } /* If optimistic restoration did not succeed, open the cursor anew */ heap = mem_heap_create(256); tuple = dict_index_build_data_tuple(index, cursor->old_rec, cursor->old_n_fields, heap); /* Save the old search mode of the cursor */ old_mode = cursor->search_mode; if (UNIV_LIKELY(cursor->rel_pos == BTR_PCUR_ON)) { mode = PAGE_CUR_LE; } else if (cursor->rel_pos == BTR_PCUR_AFTER) { mode = PAGE_CUR_G; } else { ut_ad(cursor->rel_pos == BTR_PCUR_BEFORE); mode = PAGE_CUR_L; } btr_pcur_open_with_no_init_func(index, tuple, mode, latch_mode, cursor, 0, file, line, mtr); /* Restore the old search mode */ cursor->search_mode = old_mode; if (cursor->rel_pos == BTR_PCUR_ON && btr_pcur_is_on_user_rec(cursor) && 0 == cmp_dtuple_rec( index->cmp_ctx, tuple, btr_pcur_get_rec(cursor), rec_get_offsets( btr_pcur_get_rec(cursor), index, NULL, ULINT_UNDEFINED, &heap))) { /* We have to store the NEW value for the modify clock, since the cursor can now be on a different page! But we can retain the value of old_rec */ cursor->block_when_stored = btr_pcur_get_block(cursor); cursor->modify_clock = buf_block_get_modify_clock( cursor->block_when_stored); cursor->old_stored = BTR_PCUR_OLD_STORED; mem_heap_free(heap); return(TRUE); } mem_heap_free(heap); /* We have to store new position information, modify_clock etc., to the cursor because it can now be on a different page, the record under it may have been removed, etc. */ btr_pcur_store_position(cursor, mtr); return(FALSE); } /**************************************************************//** If the latch mode of the cursor is BTR_LEAF_SEARCH or BTR_LEAF_MODIFY, releases the page latch and bufferfix reserved by the cursor. NOTE! In the case of BTR_LEAF_MODIFY, there should not exist changes made by the current mini-transaction to the data protected by the cursor latch, as then the latch must not be released until mtr_commit. */ UNIV_INTERN void btr_pcur_release_leaf( /*==================*/ btr_pcur_t* cursor, /*!< in: persistent cursor */ mtr_t* mtr) /*!< in: mtr */ { buf_block_t* block; ut_a(cursor->pos_state == BTR_PCUR_IS_POSITIONED); ut_ad(cursor->latch_mode != BTR_NO_LATCHES); block = btr_pcur_get_block(cursor); btr_leaf_page_release(block, cursor->latch_mode, mtr); cursor->latch_mode = BTR_NO_LATCHES; cursor->pos_state = BTR_PCUR_WAS_POSITIONED; } /*********************************************************//** Moves the persistent cursor to the first record on the next page. Releases the latch on the current page, and bufferunfixes it. Note that there must not be modifications on the current page, as then the x-latch can be released only in mtr_commit. */ UNIV_INTERN void btr_pcur_move_to_next_page( /*=======================*/ btr_pcur_t* cursor, /*!< in: persistent cursor; must be on the last record of the current page */ mtr_t* mtr) /*!< in: mtr */ { ulint next_page_no; ulint space; ulint zip_size; page_t* page; buf_block_t* next_block; page_t* next_page; ut_a(cursor->pos_state == BTR_PCUR_IS_POSITIONED); ut_ad(cursor->latch_mode != BTR_NO_LATCHES); ut_ad(btr_pcur_is_after_last_on_page(cursor)); cursor->old_stored = BTR_PCUR_OLD_NOT_STORED; page = btr_pcur_get_page(cursor); next_page_no = btr_page_get_next(page, mtr); space = buf_block_get_space(btr_pcur_get_block(cursor)); zip_size = buf_block_get_zip_size(btr_pcur_get_block(cursor)); ut_ad(next_page_no != FIL_NULL); next_block = btr_block_get(space, zip_size, next_page_no, cursor->latch_mode, mtr); next_page = buf_block_get_frame(next_block); #ifdef UNIV_BTR_DEBUG ut_a(page_is_comp(next_page) == page_is_comp(page)); ut_a(btr_page_get_prev(next_page, mtr) == buf_block_get_page_no(btr_pcur_get_block(cursor))); #endif /* UNIV_BTR_DEBUG */ next_block->check_index_page_at_flush = TRUE; btr_leaf_page_release(btr_pcur_get_block(cursor), cursor->latch_mode, mtr); page_cur_set_before_first(next_block, btr_pcur_get_page_cur(cursor)); page_check_dir(next_page); } /*********************************************************//** Moves the persistent cursor backward if it is on the first record of the page. Commits mtr. Note that to prevent a possible deadlock, the operation first stores the position of the cursor, commits mtr, acquires the necessary latches and restores the cursor position again before returning. The alphabetical position of the cursor is guaranteed to be sensible on return, but it may happen that the cursor is not positioned on the last record of any page, because the structure of the tree may have changed during the time when the cursor had no latches. */ UNIV_INTERN void btr_pcur_move_backward_from_page( /*=============================*/ btr_pcur_t* cursor, /*!< in: persistent cursor, must be on the first record of the current page */ mtr_t* mtr) /*!< in: mtr */ { ulint prev_page_no; ulint space; page_t* page; buf_block_t* prev_block; ulint latch_mode; ulint latch_mode2; ut_a(cursor->pos_state == BTR_PCUR_IS_POSITIONED); ut_ad(cursor->latch_mode != BTR_NO_LATCHES); ut_ad(btr_pcur_is_before_first_on_page(cursor)); ut_ad(!btr_pcur_is_before_first_in_tree(cursor, mtr)); latch_mode = cursor->latch_mode; if (latch_mode == BTR_SEARCH_LEAF) { latch_mode2 = BTR_SEARCH_PREV; } else if (latch_mode == BTR_MODIFY_LEAF) { latch_mode2 = BTR_MODIFY_PREV; } else { latch_mode2 = 0; /* To eliminate compiler warning */ ut_error; } btr_pcur_store_position(cursor, mtr); mtr_commit(mtr); mtr_start(mtr); btr_pcur_restore_position(latch_mode2, cursor, mtr); page = btr_pcur_get_page(cursor); prev_page_no = btr_page_get_prev(page, mtr); space = buf_block_get_space(btr_pcur_get_block(cursor)); if (prev_page_no == FIL_NULL) { } else if (btr_pcur_is_before_first_on_page(cursor)) { prev_block = btr_pcur_get_btr_cur(cursor)->left_block; btr_leaf_page_release(btr_pcur_get_block(cursor), latch_mode, mtr); page_cur_set_after_last(prev_block, btr_pcur_get_page_cur(cursor)); } else { /* The repositioned cursor did not end on an infimum record on a page. Cursor repositioning acquired a latch also on the previous page, but we do not need the latch: release it. */ prev_block = btr_pcur_get_btr_cur(cursor)->left_block; btr_leaf_page_release(prev_block, latch_mode, mtr); } cursor->latch_mode = latch_mode; cursor->old_stored = BTR_PCUR_OLD_NOT_STORED; } /**************************************************************//** If mode is PAGE_CUR_G or PAGE_CUR_GE, opens a persistent cursor on the first user record satisfying the search condition, in the case PAGE_CUR_L or PAGE_CUR_LE, on the last user record. If no such user record exists, then in the first case sets the cursor after last in tree, and in the latter case before first in tree. The latching mode must be BTR_SEARCH_LEAF or BTR_MODIFY_LEAF. */ UNIV_INTERN void btr_pcur_open_on_user_rec_func( /*===========================*/ dict_index_t* index, /*!< in: index */ const dtuple_t* tuple, /*!< in: tuple on which search done */ ulint mode, /*!< in: PAGE_CUR_L, ... */ ulint latch_mode, /*!< in: BTR_SEARCH_LEAF or BTR_MODIFY_LEAF */ btr_pcur_t* cursor, /*!< in: memory buffer for persistent cursor */ const char* file, /*!< in: file name */ ulint line, /*!< in: line where called */ mtr_t* mtr) /*!< in: mtr */ { btr_pcur_open_func(index, tuple, mode, latch_mode, cursor, file, line, mtr); if ((mode == PAGE_CUR_GE) || (mode == PAGE_CUR_G)) { if (btr_pcur_is_after_last_on_page(cursor)) { btr_pcur_move_to_next_user_rec(cursor, mtr); } } else { ut_ad((mode == PAGE_CUR_LE) || (mode == PAGE_CUR_L)); /* Not implemented yet */ ut_error; } } haildb-2.3.2/btr/btr0cur.c0000644000175000017500000041701511513177357016167 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1994, 2010, Innobase Oy. All Rights Reserved. Copyright (c) 2008, Google Inc. Portions of this file contain modifications contributed and copyrighted by Google, Inc. Those modifications are gratefully acknowledged and are described briefly in the InnoDB documentation. The contributions by Google are incorporated with their permission, and subject to the conditions contained in the file COPYING.Google. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file btr/btr0cur.c The index tree cursor All changes that row operations make to a B-tree or the records there must go through this module! Undo log records are written here of every modify or insert of a clustered index record. NOTE!!! To make sure we do not run out of disk space during a pessimistic insert or update, we have to reserve 2 x the height of the index tree many pages in the tablespace before we start the operation, because if leaf splitting has been started, it is difficult to undo, except by crashing the database and doing a roll-forward. Created 10/16/1994 Heikki Tuuri *******************************************************/ #include "univ.i" #include #include "btr0cur.h" #ifdef UNIV_NONINL #include "btr0cur.ic" #endif #include "row0upd.h" #ifndef UNIV_HOTBACKUP #include "mtr0log.h" #include "page0page.h" #include "page0zip.h" #include "rem0rec.h" #include "rem0cmp.h" #include "buf0lru.h" #include "btr0btr.h" #include "btr0sea.h" #include "trx0rec.h" #include "trx0roll.h" /* trx_is_recv() */ #include "que0que.h" #include "row0row.h" #include "srv0srv.h" #include "ibuf0ibuf.h" #include "lock0lock.h" #ifdef UNIV_DEBUG /** If the following is set to TRUE, this module prints a lot of trace information of individual record operations */ UNIV_INTERN ibool btr_cur_print_record_ops = FALSE; #endif /* UNIV_DEBUG */ /** Number of searches down the B-tree in btr_cur_search_to_nth_level(). */ UNIV_INTERN ulint btr_cur_n_non_sea = 0; /** Number of successful adaptive hash index lookups in btr_cur_search_to_nth_level(). */ UNIV_INTERN ulint btr_cur_n_sea = 0; /** Old value of btr_cur_n_non_sea. Copied by srv_refresh_innodb_monitor_stats(). Referenced by srv_printf_innodb_monitor(). */ UNIV_INTERN ulint btr_cur_n_non_sea_old = 0; /** Old value of btr_cur_n_sea. Copied by srv_refresh_innodb_monitor_stats(). Referenced by srv_printf_innodb_monitor(). */ UNIV_INTERN ulint btr_cur_n_sea_old = 0; /** In the optimistic insert, if the insert does not fit, but this much space can be released by page reorganize, then it is reorganized */ #define BTR_CUR_PAGE_REORGANIZE_LIMIT (UNIV_PAGE_SIZE / 32) /** The structure of a BLOB part header */ /* @{ */ /*--------------------------------------*/ #define BTR_BLOB_HDR_PART_LEN 0 /*!< BLOB part len on this page */ #define BTR_BLOB_HDR_NEXT_PAGE_NO 4 /*!< next BLOB part page no, FIL_NULL if none */ /*--------------------------------------*/ #define BTR_BLOB_HDR_SIZE 8 /*!< Size of a BLOB part header, in bytes */ /* @} */ #endif /* !UNIV_HOTBACKUP */ /** A BLOB field reference full of zero, for use in assertions and tests. Initially, BLOB field references are set to zero, in dtuple_convert_big_rec(). */ UNIV_INTERN const byte field_ref_zero[BTR_EXTERN_FIELD_REF_SIZE]; #ifndef UNIV_HOTBACKUP /*******************************************************************//** Marks all extern fields in a record as owned by the record. This function should be called if the delete mark of a record is removed: a not delete marked record always owns all its extern fields. */ static void btr_cur_unmark_extern_fields( /*=========================*/ page_zip_des_t* page_zip,/*!< in/out: compressed page whose uncompressed part will be updated, or NULL */ rec_t* rec, /*!< in/out: record in a clustered index */ dict_index_t* index, /*!< in: index of the page */ const ulint* offsets,/*!< in: array returned by rec_get_offsets() */ mtr_t* mtr); /*!< in: mtr, or NULL if not logged */ /*******************************************************************//** Adds path information to the cursor for the current page, for which the binary search has been performed. */ static void btr_cur_add_path_info( /*==================*/ btr_cur_t* cursor, /*!< in: cursor positioned on a page */ ulint height, /*!< in: height of the page in tree; 0 means leaf node */ ulint root_height); /*!< in: root node height in tree */ /***********************************************************//** Frees the externally stored fields for a record, if the field is mentioned in the update vector. */ static void btr_rec_free_updated_extern_fields( /*===============================*/ dict_index_t* index, /*!< in: index of rec; the index tree MUST be X-latched */ rec_t* rec, /*!< in: record */ page_zip_des_t* page_zip,/*!< in: compressed page whose uncompressed part will be updated, or NULL */ const ulint* offsets,/*!< in: rec_get_offsets(rec, index) */ const upd_t* update, /*!< in: update vector */ enum trx_rb_ctx rb_ctx, /*!< in: rollback context */ mtr_t* mtr); /*!< in: mini-transaction handle which contains an X-latch to record page and to the tree */ /***********************************************************//** Frees the externally stored fields for a record. */ static void btr_rec_free_externally_stored_fields( /*==================================*/ dict_index_t* index, /*!< in: index of the data, the index tree MUST be X-latched */ rec_t* rec, /*!< in: record */ const ulint* offsets,/*!< in: rec_get_offsets(rec, index) */ page_zip_des_t* page_zip,/*!< in: compressed page whose uncompressed part will be updated, or NULL */ enum trx_rb_ctx rb_ctx, /*!< in: rollback context */ mtr_t* mtr); /*!< in: mini-transaction handle which contains an X-latch to record page and to the index tree */ /***********************************************************//** Gets the externally stored size of a record, in units of a database page. @return externally stored part, in units of a database page */ static ulint btr_rec_get_externally_stored_len( /*==============================*/ rec_t* rec, /*!< in: record */ const ulint* offsets);/*!< in: array returned by rec_get_offsets() */ #endif /* !UNIV_HOTBACKUP */ /******************************************************//** The following function is used to set the deleted bit of a record. */ UNIV_INLINE void btr_rec_set_deleted_flag( /*=====================*/ rec_t* rec, /*!< in/out: physical record */ page_zip_des_t* page_zip,/*!< in/out: compressed page (or NULL) */ ulint flag) /*!< in: nonzero if delete marked */ { if (page_rec_is_comp(rec)) { rec_set_deleted_flag_new(rec, page_zip, flag); } else { ut_ad(!page_zip); rec_set_deleted_flag_old(rec, flag); } } #ifndef UNIV_HOTBACKUP /*==================== B-TREE SEARCH =========================*/ /********************************************************************//** Reset global configuration variables. */ UNIV_INTERN void btr_cur_var_init(void) /*==================*/ { btr_cur_n_non_sea = 0; btr_cur_n_sea = 0; btr_cur_n_non_sea_old = 0; btr_cur_n_sea_old = 0; #ifdef UNIV_DEBUG btr_cur_print_record_ops = FALSE; #endif /* UNIV_DEBUG */ } /************************************************************************ Latches the leaf page or pages requested. */ static void btr_cur_latch_leaves( /*=================*/ page_t* page, /*!< in: leaf page where the search converged */ ulint space, /*!< in: space id */ ulint zip_size, /*!< in: compressed page size in bytes or 0 for uncompressed pages */ ulint page_no, /*!< in: page number of the leaf */ ulint latch_mode, /*!< in: BTR_SEARCH_LEAF, ... */ btr_cur_t* cursor, /*!< in: cursor */ mtr_t* mtr) /*!< in: mtr */ { ulint mode; ulint left_page_no; ulint right_page_no; buf_block_t* get_block; ut_ad(page && mtr); switch (latch_mode) { case BTR_SEARCH_LEAF: case BTR_MODIFY_LEAF: mode = latch_mode == BTR_SEARCH_LEAF ? RW_S_LATCH : RW_X_LATCH; get_block = btr_block_get(space, zip_size, page_no, mode, mtr); #ifdef UNIV_BTR_DEBUG ut_a(page_is_comp(get_block->frame) == page_is_comp(page)); #endif /* UNIV_BTR_DEBUG */ get_block->check_index_page_at_flush = TRUE; return; case BTR_MODIFY_TREE: /* x-latch also brothers from left to right */ left_page_no = btr_page_get_prev(page, mtr); if (left_page_no != FIL_NULL) { get_block = btr_block_get(space, zip_size, left_page_no, RW_X_LATCH, mtr); #ifdef UNIV_BTR_DEBUG ut_a(page_is_comp(get_block->frame) == page_is_comp(page)); ut_a(btr_page_get_next(get_block->frame, mtr) == page_get_page_no(page)); #endif /* UNIV_BTR_DEBUG */ get_block->check_index_page_at_flush = TRUE; } get_block = btr_block_get(space, zip_size, page_no, RW_X_LATCH, mtr); #ifdef UNIV_BTR_DEBUG ut_a(page_is_comp(get_block->frame) == page_is_comp(page)); #endif /* UNIV_BTR_DEBUG */ get_block->check_index_page_at_flush = TRUE; right_page_no = btr_page_get_next(page, mtr); if (right_page_no != FIL_NULL) { get_block = btr_block_get(space, zip_size, right_page_no, RW_X_LATCH, mtr); #ifdef UNIV_BTR_DEBUG ut_a(page_is_comp(get_block->frame) == page_is_comp(page)); ut_a(btr_page_get_prev(get_block->frame, mtr) == page_get_page_no(page)); #endif /* UNIV_BTR_DEBUG */ get_block->check_index_page_at_flush = TRUE; } return; case BTR_SEARCH_PREV: case BTR_MODIFY_PREV: mode = latch_mode == BTR_SEARCH_PREV ? RW_S_LATCH : RW_X_LATCH; /* latch also left brother */ left_page_no = btr_page_get_prev(page, mtr); if (left_page_no != FIL_NULL) { get_block = btr_block_get(space, zip_size, left_page_no, mode, mtr); cursor->left_block = get_block; #ifdef UNIV_BTR_DEBUG ut_a(page_is_comp(get_block->frame) == page_is_comp(page)); ut_a(btr_page_get_next(get_block->frame, mtr) == page_get_page_no(page)); #endif /* UNIV_BTR_DEBUG */ get_block->check_index_page_at_flush = TRUE; } get_block = btr_block_get(space, zip_size, page_no, mode, mtr); #ifdef UNIV_BTR_DEBUG ut_a(page_is_comp(get_block->frame) == page_is_comp(page)); #endif /* UNIV_BTR_DEBUG */ get_block->check_index_page_at_flush = TRUE; return; } ut_error; } /********************************************************************//** Searches an index tree and positions a tree cursor on a given level. NOTE: n_fields_cmp in tuple must be set so that it cannot be compared to node pointer page number fields on the upper levels of the tree! Note that if mode is PAGE_CUR_LE, which is used in inserts, then cursor->up_match and cursor->low_match both will have sensible values. If mode is PAGE_CUR_GE, then up_match will a have a sensible value. If mode is PAGE_CUR_LE , cursor is left at the place where an insert of the search tuple should be performed in the B-tree. InnoDB does an insert immediately after the cursor. Thus, the cursor may end up on a user record, or on a page infimum record. */ UNIV_INTERN void btr_cur_search_to_nth_level( /*========================*/ dict_index_t* dict_index, /*!< in: index */ ulint level, /*!< in: the tree level of search */ const dtuple_t* tuple, /*!< in: data tuple; NOTE: n_fields_cmp in tuple must be set so that it cannot get compared to the node ptr page number field! */ ulint mode, /*!< in: PAGE_CUR_L, ...; Inserts should always be made using PAGE_CUR_LE to search the position! */ ulint latch_mode, /*!< in: BTR_SEARCH_LEAF, ..., ORed with BTR_INSERT and BTR_ESTIMATE; cursor->left_block is used to store a pointer to the left neighbor page, in the cases BTR_SEARCH_PREV and BTR_MODIFY_PREV; NOTE that if has_search_latch is != 0, we maybe do not have a latch set on the cursor page, we assume the caller uses his search latch to protect the record! */ btr_cur_t* cursor, /*!< in/out: tree cursor; the cursor page is s- or x-latched, but see also above! */ ulint has_search_latch,/*!< in: info on the latch mode the caller currently has on btr_search_latch: RW_S_LATCH, or 0 */ const char* file, /*!< in: file name */ ulint line, /*!< in: line where called */ mtr_t* mtr) /*!< in: mtr */ { page_cur_t* page_cursor; page_t* page; buf_block_t* guess; rec_t* node_ptr; ulint page_no; ulint space; ulint up_match; ulint up_bytes; ulint low_match; ulint low_bytes; ulint height; ulint savepoint; ulint page_mode; ulint insert_planned; ulint estimate; ulint ignore_sec_unique; ulint root_height = 0; /* remove warning */ #ifdef BTR_CUR_ADAPT btr_search_t* info; #endif mem_heap_t* heap = NULL; ulint offsets_[REC_OFFS_NORMAL_SIZE]; ulint* offsets = offsets_; rec_offs_init(offsets_); /* Currently, PAGE_CUR_LE is the only search mode used for searches ending to upper levels */ ut_ad(level == 0 || mode == PAGE_CUR_LE); ut_ad(dict_index_check_search_tuple(dict_index, tuple)); ut_ad(!dict_index_is_ibuf(dict_index) || ibuf_inside()); ut_ad(dtuple_check_typed(tuple)); #ifdef UNIV_DEBUG cursor->up_match = ULINT_UNDEFINED; cursor->low_match = ULINT_UNDEFINED; #endif insert_planned = latch_mode & BTR_INSERT; estimate = latch_mode & BTR_ESTIMATE; ignore_sec_unique = latch_mode & BTR_IGNORE_SEC_UNIQUE; latch_mode = latch_mode & ~(BTR_INSERT | BTR_ESTIMATE | BTR_IGNORE_SEC_UNIQUE); ut_ad(!insert_planned || (mode == PAGE_CUR_LE)); cursor->flag = BTR_CUR_BINARY; cursor->index = dict_index; #ifndef BTR_CUR_ADAPT guess = NULL; #else info = btr_search_get_info(dict_index); guess = info->root_guess; #ifdef BTR_CUR_HASH_ADAPT #ifdef UNIV_SEARCH_PERF_STAT info->n_searches++; #endif if (rw_lock_get_writer(&btr_search_latch) == RW_LOCK_NOT_LOCKED && latch_mode <= BTR_MODIFY_LEAF && info->last_hash_succ && !estimate #ifdef PAGE_CUR_LE_OR_EXTENDS && mode != PAGE_CUR_LE_OR_EXTENDS #endif /* PAGE_CUR_LE_OR_EXTENDS */ /* If !has_search_latch, we do a dirty read of btr_search_enabled below, and btr_search_guess_on_hash() will have to check it again. */ && UNIV_LIKELY(btr_search_enabled) && btr_search_guess_on_hash(dict_index, info, tuple, mode, latch_mode, cursor, has_search_latch, mtr)) { /* Search using the hash index succeeded */ ut_ad(cursor->up_match != ULINT_UNDEFINED || mode != PAGE_CUR_GE); ut_ad(cursor->up_match != ULINT_UNDEFINED || mode != PAGE_CUR_LE); ut_ad(cursor->low_match != ULINT_UNDEFINED || mode != PAGE_CUR_LE); btr_cur_n_sea++; return; } #endif /* BTR_CUR_HASH_ADAPT */ #endif /* BTR_CUR_ADAPT */ btr_cur_n_non_sea++; /* If the hash search did not succeed, do binary search down the tree */ if (has_search_latch) { /* Release possible search latch to obey latching order */ rw_lock_s_unlock(&btr_search_latch); } /* Store the position of the tree latch we push to mtr so that we know how to release it when we have latched leaf node(s) */ savepoint = mtr_set_savepoint(mtr); if (latch_mode == BTR_MODIFY_TREE) { mtr_x_lock(dict_index_get_lock(dict_index), mtr); } else if (latch_mode == BTR_CONT_MODIFY_TREE) { /* Do nothing */ ut_ad(mtr_memo_contains(mtr, dict_index_get_lock(dict_index), MTR_MEMO_X_LOCK)); } else { mtr_s_lock(dict_index_get_lock(dict_index), mtr); } page_cursor = btr_cur_get_page_cur(cursor); space = dict_index_get_space(dict_index); page_no = dict_index_get_page(dict_index); up_match = 0; up_bytes = 0; low_match = 0; low_bytes = 0; height = ULINT_UNDEFINED; /* We use these modified search modes on non-leaf levels of the B-tree. These let us end up in the right B-tree leaf. In that leaf we use the original search mode. */ switch (mode) { case PAGE_CUR_GE: page_mode = PAGE_CUR_L; break; case PAGE_CUR_G: page_mode = PAGE_CUR_LE; break; default: #ifdef PAGE_CUR_LE_OR_EXTENDS ut_ad(mode == PAGE_CUR_L || mode == PAGE_CUR_LE || mode == PAGE_CUR_LE_OR_EXTENDS); #else /* PAGE_CUR_LE_OR_EXTENDS */ ut_ad(mode == PAGE_CUR_L || mode == PAGE_CUR_LE); #endif /* PAGE_CUR_LE_OR_EXTENDS */ page_mode = mode; break; } /* Loop and search until we arrive at the desired level */ for (;;) { ulint zip_size; buf_block_t* block; ulint rw_latch; ulint buf_mode; zip_size = dict_table_zip_size(dict_index->table); rw_latch = RW_NO_LATCH; buf_mode = BUF_GET; if (height == 0 && latch_mode <= BTR_MODIFY_LEAF) { rw_latch = latch_mode; if (insert_planned && ibuf_should_try(dict_index, ignore_sec_unique)) { /* Try insert to the insert buffer if the page is not in the buffer pool */ buf_mode = BUF_GET_IF_IN_POOL; } } retry_page_get: block = buf_page_get_gen(space, zip_size, page_no, rw_latch, guess, buf_mode, file, line, mtr); if (block == NULL) { /* This must be a search to perform an insert; try insert to the insert buffer */ ut_ad(buf_mode == BUF_GET_IF_IN_POOL); ut_ad(insert_planned); ut_ad(cursor->thr); if (ibuf_insert(tuple, dict_index, space, zip_size, page_no, cursor->thr)) { /* Insertion to the insert buffer succeeded */ cursor->flag = BTR_CUR_INSERT_TO_IBUF; if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } goto func_exit; } /* Insert to the insert buffer did not succeed: retry page get */ buf_mode = BUF_GET; goto retry_page_get; } page = buf_block_get_frame(block); block->check_index_page_at_flush = TRUE; if (rw_latch != RW_NO_LATCH) { #ifdef UNIV_ZIP_DEBUG const page_zip_des_t* page_zip = buf_block_get_page_zip(block); ut_a(!page_zip || page_zip_validate(page_zip, page)); #endif /* UNIV_ZIP_DEBUG */ buf_block_dbg_add_level(block, SYNC_TREE_NODE); } ut_ad(0 == ut_dulint_cmp(dict_index->id, btr_page_get_index_id(page))); if (UNIV_UNLIKELY(height == ULINT_UNDEFINED)) { /* We are in the root node */ height = btr_page_get_level(page, mtr); root_height = height; cursor->tree_height = root_height + 1; #ifdef BTR_CUR_ADAPT if (block != guess) { info->root_guess = block; } #endif } if (height == 0) { if (rw_latch == RW_NO_LATCH) { btr_cur_latch_leaves(page, space, zip_size, page_no, latch_mode, cursor, mtr); } if ((latch_mode != BTR_MODIFY_TREE) && (latch_mode != BTR_CONT_MODIFY_TREE)) { /* Release the tree s-latch */ mtr_release_s_latch_at_savepoint( mtr, savepoint, dict_index_get_lock(dict_index)); } page_mode = mode; } page_cur_search_with_match(block, dict_index, tuple, page_mode, &up_match, &up_bytes, &low_match, &low_bytes, page_cursor); if (estimate) { btr_cur_add_path_info(cursor, height, root_height); } /* If this is the desired level, leave the loop */ ut_ad(height == btr_page_get_level( page_cur_get_page(page_cursor), mtr)); if (level == height) { if (level > 0) { /* x-latch the page */ page = btr_page_get(space, zip_size, page_no, RW_X_LATCH, mtr); ut_a((ibool)!!page_is_comp(page) == dict_table_is_comp(dict_index->table)); } break; } ut_ad(height > 0); height--; guess = NULL; node_ptr = page_cur_get_rec(page_cursor); offsets = rec_get_offsets(node_ptr, cursor->index, offsets, ULINT_UNDEFINED, &heap); /* Go to the child node */ page_no = btr_node_ptr_get_child_page_no(node_ptr, offsets); } if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } if (level == 0) { cursor->low_match = low_match; cursor->low_bytes = low_bytes; cursor->up_match = up_match; cursor->up_bytes = up_bytes; #ifdef BTR_CUR_ADAPT /* We do a dirty read of btr_search_enabled here. We will properly check btr_search_enabled again in btr_search_build_page_hash_index() before building a page hash index, while holding btr_search_latch. */ if (UNIV_LIKELY(btr_search_enabled)) { btr_search_info_update(dict_index, cursor); } #endif ut_ad(cursor->up_match != ULINT_UNDEFINED || mode != PAGE_CUR_GE); ut_ad(cursor->up_match != ULINT_UNDEFINED || mode != PAGE_CUR_LE); ut_ad(cursor->low_match != ULINT_UNDEFINED || mode != PAGE_CUR_LE); } func_exit: if (has_search_latch) { rw_lock_s_lock(&btr_search_latch); } } /*****************************************************************//** Opens a cursor at either end of an index. */ UNIV_INTERN void btr_cur_open_at_index_side_func( /*============================*/ ibool from_left, /*!< in: TRUE if open to the low end, FALSE if to the high end */ dict_index_t* dict_index, /*!< in: index */ ulint latch_mode, /*!< in: latch mode */ btr_cur_t* cursor, /*!< in: cursor */ const char* file, /*!< in: file name */ ulint line, /*!< in: line where called */ mtr_t* mtr) /*!< in: mtr */ { page_cur_t* page_cursor; ulint page_no; ulint space; ulint zip_size; ulint height; ulint root_height = 0; /* remove warning */ rec_t* node_ptr; ulint estimate; ulint savepoint; mem_heap_t* heap = NULL; ulint offsets_[REC_OFFS_NORMAL_SIZE]; ulint* offsets = offsets_; rec_offs_init(offsets_); estimate = latch_mode & BTR_ESTIMATE; latch_mode = latch_mode & ~BTR_ESTIMATE; /* Store the position of the tree latch we push to mtr so that we know how to release it when we have latched the leaf node */ savepoint = mtr_set_savepoint(mtr); if (latch_mode == BTR_MODIFY_TREE) { mtr_x_lock(dict_index_get_lock(dict_index), mtr); } else { mtr_s_lock(dict_index_get_lock(dict_index), mtr); } page_cursor = btr_cur_get_page_cur(cursor); cursor->index = dict_index; space = dict_index_get_space(dict_index); zip_size = dict_table_zip_size(dict_index->table); page_no = dict_index_get_page(dict_index); height = ULINT_UNDEFINED; for (;;) { buf_block_t* block; page_t* page; block = buf_page_get_gen(space, zip_size, page_no, RW_NO_LATCH, NULL, BUF_GET, file, line, mtr); page = buf_block_get_frame(block); ut_ad(0 == ut_dulint_cmp(dict_index->id, btr_page_get_index_id(page))); block->check_index_page_at_flush = TRUE; if (height == ULINT_UNDEFINED) { /* We are in the root node */ height = btr_page_get_level(page, mtr); root_height = height; } if (height == 0) { btr_cur_latch_leaves(page, space, zip_size, page_no, latch_mode, cursor, mtr); /* In versions <= 3.23.52 we had forgotten to release the tree latch here. If in an index scan we had to scan far to find a record visible to the current transaction, that could starve others waiting for the tree latch. */ if ((latch_mode != BTR_MODIFY_TREE) && (latch_mode != BTR_CONT_MODIFY_TREE)) { /* Release the tree s-latch */ mtr_release_s_latch_at_savepoint( mtr, savepoint, dict_index_get_lock(dict_index)); } } if (from_left) { page_cur_set_before_first(block, page_cursor); } else { page_cur_set_after_last(block, page_cursor); } if (height == 0) { if (estimate) { btr_cur_add_path_info(cursor, height, root_height); } break; } ut_ad(height > 0); if (from_left) { page_cur_move_to_next(page_cursor); } else { page_cur_move_to_prev(page_cursor); } if (estimate) { btr_cur_add_path_info(cursor, height, root_height); } height--; node_ptr = page_cur_get_rec(page_cursor); offsets = rec_get_offsets(node_ptr, cursor->index, offsets, ULINT_UNDEFINED, &heap); /* Go to the child node */ page_no = btr_node_ptr_get_child_page_no(node_ptr, offsets); } if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } } /**********************************************************************//** Positions a cursor at a randomly chosen position within a B-tree. */ UNIV_INTERN void btr_cur_open_at_rnd_pos_func( /*=========================*/ dict_index_t* dict_index, /*!< in: index */ ulint latch_mode, /*!< in: BTR_SEARCH_LEAF, ... */ btr_cur_t* cursor, /*!< in/out: B-tree cursor */ const char* file, /*!< in: file name */ ulint line, /*!< in: line where called */ mtr_t* mtr) /*!< in: mtr */ { page_cur_t* page_cursor; ulint page_no; ulint space; ulint zip_size; ulint height; rec_t* node_ptr; mem_heap_t* heap = NULL; ulint offsets_[REC_OFFS_NORMAL_SIZE]; ulint* offsets = offsets_; rec_offs_init(offsets_); if (latch_mode == BTR_MODIFY_TREE) { mtr_x_lock(dict_index_get_lock(dict_index), mtr); } else { mtr_s_lock(dict_index_get_lock(dict_index), mtr); } page_cursor = btr_cur_get_page_cur(cursor); cursor->index = dict_index; space = dict_index_get_space(dict_index); zip_size = dict_table_zip_size(dict_index->table); page_no = dict_index_get_page(dict_index); height = ULINT_UNDEFINED; for (;;) { buf_block_t* block; page_t* page; block = buf_page_get_gen(space, zip_size, page_no, RW_NO_LATCH, NULL, BUF_GET, file, line, mtr); page = buf_block_get_frame(block); ut_ad(0 == ut_dulint_cmp(dict_index->id, btr_page_get_index_id(page))); if (height == ULINT_UNDEFINED) { /* We are in the root node */ height = btr_page_get_level(page, mtr); } if (height == 0) { btr_cur_latch_leaves(page, space, zip_size, page_no, latch_mode, cursor, mtr); } page_cur_open_on_rnd_user_rec(block, page_cursor); if (height == 0) { break; } ut_ad(height > 0); height--; node_ptr = page_cur_get_rec(page_cursor); offsets = rec_get_offsets(node_ptr, cursor->index, offsets, ULINT_UNDEFINED, &heap); /* Go to the child node */ page_no = btr_node_ptr_get_child_page_no(node_ptr, offsets); } if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } } /*==================== B-TREE INSERT =========================*/ /*************************************************************//** Inserts a record if there is enough space, or if enough space can be freed by reorganizing. Differs from btr_cur_optimistic_insert because no heuristics is applied to whether it pays to use CPU time for reorganizing the page or not. @return pointer to inserted record if succeed, else NULL */ static rec_t* btr_cur_insert_if_possible( /*=======================*/ btr_cur_t* cursor, /*!< in: cursor on page after which to insert; cursor stays valid */ const dtuple_t* tuple, /*!< in: tuple to insert; the size info need not have been stored to tuple */ ulint n_ext, /*!< in: number of externally stored columns */ mtr_t* mtr) /*!< in: mtr */ { page_cur_t* page_cursor; buf_block_t* block; rec_t* rec; ut_ad(dtuple_check_typed(tuple)); block = btr_cur_get_block(cursor); ut_ad(mtr_memo_contains(mtr, block, MTR_MEMO_PAGE_X_FIX)); page_cursor = btr_cur_get_page_cur(cursor); /* Now, try the insert */ rec = page_cur_tuple_insert(page_cursor, tuple, cursor->index, n_ext, mtr); if (UNIV_UNLIKELY(!rec)) { /* If record did not fit, reorganize */ if (btr_page_reorganize(block, cursor->index, mtr)) { page_cur_search(block, cursor->index, tuple, PAGE_CUR_LE, page_cursor); rec = page_cur_tuple_insert(page_cursor, tuple, cursor->index, n_ext, mtr); } } return(rec); } /*************************************************************//** For an insert, checks the locks and does the undo logging if desired. @return DB_SUCCESS, DB_WAIT_LOCK, DB_FAIL, or error number */ UNIV_INLINE ulint btr_cur_ins_lock_and_undo( /*======================*/ ulint flags, /*!< in: undo logging and locking flags: if not zero, the parameters index and thr should be specified */ btr_cur_t* cursor, /*!< in: cursor on page after which to insert */ const dtuple_t* entry, /*!< in: entry to insert */ que_thr_t* thr, /*!< in: query thread or NULL */ mtr_t* mtr, /*!< in/out: mini-transaction */ ibool* inherit)/*!< out: TRUE if the inserted new record maybe should inherit LOCK_GAP type locks from the successor record */ { dict_index_t* dict_index; ulint err; rec_t* rec; roll_ptr_t roll_ptr; /* Check if we have to wait for a lock: enqueue an explicit lock request if yes */ rec = btr_cur_get_rec(cursor); dict_index = cursor->index; err = lock_rec_insert_check_and_lock(flags, rec, btr_cur_get_block(cursor), dict_index, thr, mtr, inherit); if (err != DB_SUCCESS) { return(err); } if (dict_index_is_clust(dict_index) && !dict_index_is_ibuf(dict_index)) { err = trx_undo_report_row_operation(flags, TRX_UNDO_INSERT_OP, thr, dict_index, entry, NULL, 0, NULL, &roll_ptr); if (err != DB_SUCCESS) { return(err); } /* Now we can fill in the roll ptr field in entry */ if (!(flags & BTR_KEEP_SYS_FLAG)) { row_upd_index_entry_sys_field(entry, dict_index, DATA_ROLL_PTR, roll_ptr); } } return(DB_SUCCESS); } #ifdef UNIV_DEBUG /*************************************************************//** Report information about a transaction. */ static void btr_cur_trx_report( /*===============*/ trx_t* trx, /*!< in: transaction */ const dict_index_t* index, /*!< in: index */ const char* op) /*!< in: operation */ { ib_logger(ib_stream, "Trx with id " TRX_ID_FMT " going to ", TRX_ID_PREP_PRINTF(trx->id)); ib_logger(ib_stream, op); dict_index_name_print(ib_stream, trx, index); ib_logger(ib_stream, "\n"); } #endif /* UNIV_DEBUG */ /*************************************************************//** Tries to perform an insert to a page in an index tree, next to cursor. It is assumed that mtr holds an x-latch on the page. The operation does not succeed if there is too little space on the page. If there is just one record on the page, the insert will always succeed; this is to prevent trying to split a page with just one record. @return DB_SUCCESS, DB_WAIT_LOCK, DB_FAIL, or error number */ UNIV_INTERN ulint btr_cur_optimistic_insert( /*======================*/ ulint flags, /*!< in: undo logging and locking flags: if not zero, the parameters index and thr should be specified */ btr_cur_t* cursor, /*!< in: cursor on page after which to insert; cursor stays valid */ dtuple_t* entry, /*!< in/out: entry to insert */ rec_t** rec, /*!< out: pointer to inserted record if succeed */ big_rec_t** big_rec,/*!< out: big rec vector whose fields have to be stored externally by the caller, or NULL */ ulint n_ext, /*!< in: number of externally stored columns */ que_thr_t* thr, /*!< in: query thread or NULL */ mtr_t* mtr) /*!< in: mtr; if this function returns DB_SUCCESS on a leaf page of a secondary index in a compressed tablespace, the mtr must be committed before latching any further pages */ { big_rec_t* big_rec_vec = NULL; dict_index_t* dict_index; page_cur_t* page_cursor; buf_block_t* block; page_t* page; ulint max_size; rec_t* dummy_rec; ibool leaf; ibool reorg; ibool inherit; ulint zip_size; ulint rec_size; ulint err; *big_rec = NULL; block = btr_cur_get_block(cursor); page = buf_block_get_frame(block); dict_index = cursor->index; zip_size = buf_block_get_zip_size(block); #ifdef UNIV_DEBUG_VALGRIND if (zip_size) { UNIV_MEM_ASSERT_RW(page, UNIV_PAGE_SIZE); UNIV_MEM_ASSERT_RW(block->page.zip.data, zip_size); } #endif /* UNIV_DEBUG_VALGRIND */ if (!dtuple_check_typed_no_assert(entry)) { ib_logger(ib_stream, "InnoDB: Error in a tuple to insert into "); dict_index_name_print(ib_stream, thr_get_trx(thr), dict_index); } #ifdef UNIV_DEBUG if (btr_cur_print_record_ops && thr) { btr_cur_trx_report(thr_get_trx(thr), dict_index, "insert into "); dtuple_print(ib_stream, entry); } #endif /* UNIV_DEBUG */ ut_ad(mtr_memo_contains(mtr, block, MTR_MEMO_PAGE_X_FIX)); max_size = page_get_max_insert_size_after_reorganize(page, 1); leaf = page_is_leaf(page); /* Calculate the record size when entry is converted to a record */ rec_size = rec_get_converted_size(dict_index, entry, n_ext); if (page_rec_needs_ext(rec_size, page_is_comp(page), dtuple_get_n_fields(entry), zip_size)) { /* The record is so big that we have to store some fields externally on separate database pages */ big_rec_vec = dtuple_convert_big_rec(dict_index, entry, &n_ext); if (UNIV_UNLIKELY(big_rec_vec == NULL)) { return(DB_TOO_BIG_RECORD); } rec_size = rec_get_converted_size(dict_index, entry, n_ext); } if (UNIV_UNLIKELY(zip_size)) { /* Estimate the free space of an empty compressed page. Subtract one byte for the encoded heap_no in the modification log. */ ulint free_space_zip = page_zip_empty_size( cursor->index->n_fields, zip_size) - 1; ulint n_uniq = dict_index_get_n_unique_in_tree(dict_index); ut_ad(dict_table_is_comp(dict_index->table)); /* There should be enough room for two node pointer records on an empty non-leaf page. This prevents infinite page splits. */ if (UNIV_LIKELY(entry->n_fields >= n_uniq) && UNIV_UNLIKELY(REC_NODE_PTR_SIZE + rec_get_converted_size_comp_prefix( dict_index, entry->fields, n_uniq, NULL) /* On a compressed page, there is a two-byte entry in the dense page directory for every record. But there is no record header. */ - (REC_N_NEW_EXTRA_BYTES - 2) > free_space_zip / 2)) { if (big_rec_vec) { dtuple_convert_back_big_rec( dict_index, entry, big_rec_vec); } return(DB_TOO_BIG_RECORD); } } /* If there have been many consecutive inserts, and we are on the leaf level, check if we have to split the page to reserve enough free space for future updates of records. */ if (dict_index_is_clust(dict_index) && (page_get_n_recs(page) >= 2) && UNIV_LIKELY(leaf) && (dict_index_get_space_reserve() + rec_size > max_size) && (btr_page_get_split_rec_to_right(cursor, &dummy_rec) || btr_page_get_split_rec_to_left(cursor, &dummy_rec))) { fail: err = DB_FAIL; fail_err: if (big_rec_vec) { dtuple_convert_back_big_rec(dict_index, entry, big_rec_vec); } return(err); } if (UNIV_UNLIKELY(max_size < BTR_CUR_PAGE_REORGANIZE_LIMIT || max_size < rec_size) && UNIV_LIKELY(page_get_n_recs(page) > 1) && page_get_max_insert_size(page, 1) < rec_size) { goto fail; } /* Check locks and write to the undo log, if specified */ err = btr_cur_ins_lock_and_undo(flags, cursor, entry, thr, mtr, &inherit); if (UNIV_UNLIKELY(err != DB_SUCCESS)) { goto fail_err; } page_cursor = btr_cur_get_page_cur(cursor); /* Now, try the insert */ { const rec_t* page_cursor_rec = page_cur_get_rec(page_cursor); *rec = page_cur_tuple_insert(page_cursor, entry, dict_index, n_ext, mtr); reorg = page_cursor_rec != page_cur_get_rec(page_cursor); if (UNIV_UNLIKELY(reorg)) { ut_a(zip_size); ut_a(*rec); } } if (UNIV_UNLIKELY(!*rec) && UNIV_LIKELY(!reorg)) { /* If the record did not fit, reorganize */ if (UNIV_UNLIKELY(!btr_page_reorganize(block, dict_index, mtr))) { ut_a(zip_size); goto fail; } ut_ad(zip_size || page_get_max_insert_size(page, 1) == max_size); reorg = TRUE; page_cur_search(block, dict_index, entry, PAGE_CUR_LE, page_cursor); *rec = page_cur_tuple_insert(page_cursor, entry, dict_index, n_ext, mtr); if (UNIV_UNLIKELY(!*rec)) { if (UNIV_LIKELY(zip_size != 0)) { goto fail; } ib_logger(ib_stream, "InnoDB: Error: cannot insert tuple "); dtuple_print(ib_stream, entry); ib_logger(ib_stream, " into "); dict_index_name_print(ib_stream, thr_get_trx(thr), dict_index); ib_logger(ib_stream, "\nInnoDB: max insert size %lu\n", (ulong) max_size); ut_error; } } #ifdef BTR_CUR_HASH_ADAPT if (!reorg && leaf && (cursor->flag == BTR_CUR_HASH)) { btr_search_update_hash_node_on_insert(cursor); } else { btr_search_update_hash_on_insert(cursor); } #endif if (!(flags & BTR_NO_LOCKING_FLAG) && inherit) { lock_update_insert(block, *rec); } #if 0 ib_logger(ib_stream, "Insert into page %lu, max ins size %lu," " rec %lu ind type %lu\n", buf_block_get_page_no(block), max_size, rec_size + PAGE_DIR_SLOT_SIZE, dict_index->type); #endif if (leaf && !dict_index_is_clust(dict_index)) { /* Update the free bits of the B-tree page in the insert buffer bitmap. */ /* The free bits in the insert buffer bitmap must never exceed the free space on a page. It is safe to decrement or reset the bits in the bitmap in a mini-transaction that is committed before the mini-transaction that affects the free space. */ /* It is unsafe to increment the bits in a separately committed mini-transaction, because in crash recovery, the free bits could momentarily be set too high. */ if (zip_size) { /* Update the bits in the same mini-transaction. */ ibuf_update_free_bits_zip(block, mtr); } else { /* Decrement the bits in a separate mini-transaction. */ ibuf_update_free_bits_if_full( block, max_size, rec_size + PAGE_DIR_SLOT_SIZE); } } *big_rec = big_rec_vec; return(DB_SUCCESS); } /*************************************************************//** Performs an insert on a page of an index tree. It is assumed that mtr holds an x-latch on the tree and on the cursor page. If the insert is made on the leaf level, to avoid deadlocks, mtr must also own x-latches to brothers of page, if those brothers exist. @return DB_SUCCESS or error number */ UNIV_INTERN ulint btr_cur_pessimistic_insert( /*=======================*/ ulint flags, /*!< in: undo logging and locking flags: if not zero, the parameter thr should be specified; if no undo logging is specified, then the caller must have reserved enough free extents in the file space so that the insertion will certainly succeed */ btr_cur_t* cursor, /*!< in: cursor after which to insert; cursor stays valid */ dtuple_t* entry, /*!< in/out: entry to insert */ rec_t** rec, /*!< out: pointer to inserted record if succeed */ big_rec_t** big_rec,/*!< out: big rec vector whose fields have to be stored externally by the caller, or NULL */ ulint n_ext, /*!< in: number of externally stored columns */ que_thr_t* thr, /*!< in: query thread or NULL */ mtr_t* mtr) /*!< in: mtr */ { dict_index_t* dict_index = cursor->index; ulint zip_size = dict_table_zip_size(dict_index->table); big_rec_t* big_rec_vec = NULL; mem_heap_t* heap = NULL; ulint err; ibool dummy_inh; ibool success; ulint n_extents = 0; ulint n_reserved; ut_ad(dtuple_check_typed(entry)); *big_rec = NULL; ut_ad(mtr_memo_contains(mtr, dict_index_get_lock(btr_cur_get_index(cursor)), MTR_MEMO_X_LOCK)); ut_ad(mtr_memo_contains(mtr, btr_cur_get_block(cursor), MTR_MEMO_PAGE_X_FIX)); /* Try first an optimistic insert; reset the cursor flag: we do not assume anything of how it was positioned */ cursor->flag = BTR_CUR_BINARY; err = btr_cur_optimistic_insert(flags, cursor, entry, rec, big_rec, n_ext, thr, mtr); if (err != DB_FAIL) { return(err); } /* Retry with a pessimistic insert. Check locks and write to undo log, if specified */ err = btr_cur_ins_lock_and_undo(flags, cursor, entry, thr, mtr, &dummy_inh); if (err != DB_SUCCESS) { return(err); } if (!(flags & BTR_NO_UNDO_LOG_FLAG)) { /* First reserve enough free space for the file segments of the index tree, so that the insert will not fail because of lack of space */ n_extents = cursor->tree_height / 16 + 3; success = fsp_reserve_free_extents(&n_reserved, dict_index->space, n_extents, FSP_NORMAL, mtr); if (!success) { return(DB_OUT_OF_FILE_SPACE); } } if (page_rec_needs_ext(rec_get_converted_size(dict_index, entry, n_ext), dict_table_is_comp(dict_index->table), dict_index_get_n_fields(dict_index), zip_size)) { /* The record is so big that we have to store some fields externally on separate database pages */ if (UNIV_LIKELY_NULL(big_rec_vec)) { /* This should never happen, but we handle the situation in a robust manner. */ ut_ad(0); dtuple_convert_back_big_rec(dict_index, entry, big_rec_vec); } big_rec_vec = dtuple_convert_big_rec(dict_index, entry, &n_ext); if (big_rec_vec == NULL) { if (n_extents > 0) { fil_space_release_free_extents(dict_index->space, n_reserved); } return(DB_TOO_BIG_RECORD); } } if (dict_index_get_page(dict_index) == buf_block_get_page_no(btr_cur_get_block(cursor))) { /* The page is the root page */ *rec = btr_root_raise_and_insert(cursor, entry, n_ext, mtr); } else { *rec = btr_page_split_and_insert(cursor, entry, n_ext, mtr); } if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } ut_ad(page_rec_get_next(btr_cur_get_rec(cursor)) == *rec); #ifdef BTR_CUR_ADAPT btr_search_update_hash_on_insert(cursor); #endif if (!(flags & BTR_NO_LOCKING_FLAG)) { lock_update_insert(btr_cur_get_block(cursor), *rec); } if (n_extents > 0) { fil_space_release_free_extents(dict_index->space, n_reserved); } *big_rec = big_rec_vec; return(DB_SUCCESS); } /*==================== B-TREE UPDATE =========================*/ /*************************************************************//** For an update, checks the locks and does the undo logging. @return DB_SUCCESS, DB_WAIT_LOCK, or error number */ UNIV_INLINE ulint btr_cur_upd_lock_and_undo( /*======================*/ ulint flags, /*!< in: undo logging and locking flags */ btr_cur_t* cursor, /*!< in: cursor on record to update */ const upd_t* update, /*!< in: update vector */ ulint cmpl_info,/*!< in: compiler info on secondary index updates */ que_thr_t* thr, /*!< in: query thread */ mtr_t* mtr, /*!< in/out: mini-transaction */ roll_ptr_t* roll_ptr)/*!< out: roll pointer */ { dict_index_t* dict_index; rec_t* rec; ulint err; ut_ad(cursor && update && thr && roll_ptr); rec = btr_cur_get_rec(cursor); dict_index = cursor->index; if (!dict_index_is_clust(dict_index)) { /* We do undo logging only when we update a clustered index record */ return(lock_sec_rec_modify_check_and_lock( flags, btr_cur_get_block(cursor), rec, dict_index, thr, mtr)); } /* Check if we have to wait for a lock: enqueue an explicit lock request if yes */ err = DB_SUCCESS; if (!(flags & BTR_NO_LOCKING_FLAG)) { mem_heap_t* heap = NULL; ulint offsets_[REC_OFFS_NORMAL_SIZE]; rec_offs_init(offsets_); err = lock_clust_rec_modify_check_and_lock( flags, btr_cur_get_block(cursor), rec, dict_index, rec_get_offsets(rec, dict_index, offsets_, ULINT_UNDEFINED, &heap), thr); if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } if (err != DB_SUCCESS) { return(err); } } /* Append the info about the update in the undo log */ err = trx_undo_report_row_operation(flags, TRX_UNDO_MODIFY_OP, thr, dict_index, NULL, update, cmpl_info, rec, roll_ptr); return(err); } /***********************************************************//** Writes a redo log record of updating a record in-place. */ UNIV_INLINE void btr_cur_update_in_place_log( /*========================*/ ulint flags, /*!< in: flags */ rec_t* rec, /*!< in: record */ dict_index_t* dict_index, /*!< in: index where cursor positioned */ const upd_t* update, /*!< in: update vector */ trx_t* trx, /*!< in: transaction */ roll_ptr_t roll_ptr, /*!< in: roll ptr */ mtr_t* mtr) /*!< in: mtr */ { byte* log_ptr; page_t* page = page_align(rec); ut_ad(flags < 256); ut_ad(!!page_is_comp(page) == dict_table_is_comp(dict_index->table)); log_ptr = mlog_open_and_write_index(mtr, rec, dict_index, page_is_comp(page) ? MLOG_COMP_REC_UPDATE_IN_PLACE : MLOG_REC_UPDATE_IN_PLACE, 1 + DATA_ROLL_PTR_LEN + 14 + 2 + MLOG_BUF_MARGIN); if (!log_ptr) { /* Logging in mtr is switched off during crash recovery */ return; } /* The code below assumes index is a clustered index: change index to the clustered index if we are updating a secondary index record (or we could as well skip writing the sys col values to the log in this case because they are not needed for a secondary index record update) */ dict_index = dict_table_get_first_index(dict_index->table); mach_write_to_1(log_ptr, flags); log_ptr++; log_ptr = row_upd_write_sys_vals_to_log(dict_index, trx, roll_ptr, log_ptr, mtr); mach_write_to_2(log_ptr, page_offset(rec)); log_ptr += 2; row_upd_index_write_log(update, log_ptr, mtr); } #endif /* UNIV_HOTBACKUP */ /***********************************************************//** Parses a redo log record of updating a record in-place. @return end of log record or NULL */ UNIV_INTERN byte* btr_cur_parse_update_in_place( /*==========================*/ byte* ptr, /*!< in: buffer */ byte* end_ptr,/*!< in: buffer end */ page_t* page, /*!< in/out: page or NULL */ page_zip_des_t* page_zip,/*!< in/out: compressed page, or NULL */ dict_index_t* dict_index)/*!< in: index corresponding to page */ { ulint flags; rec_t* rec; upd_t* update; ulint pos; trx_id_t trx_id; roll_ptr_t roll_ptr; ulint rec_offset; mem_heap_t* heap; ulint* offsets; if (end_ptr < ptr + 1) { return(NULL); } flags = mach_read_from_1(ptr); ptr++; ptr = row_upd_parse_sys_vals(ptr, end_ptr, &pos, &trx_id, &roll_ptr); if (ptr == NULL) { return(NULL); } if (end_ptr < ptr + 2) { return(NULL); } rec_offset = mach_read_from_2(ptr); ptr += 2; ut_a(rec_offset <= UNIV_PAGE_SIZE); heap = mem_heap_create(256); ptr = row_upd_index_parse(ptr, end_ptr, heap, &update); if (!ptr || !page) { goto func_exit; } ut_a((ibool)!!page_is_comp(page) == dict_table_is_comp(dict_index->table)); rec = page + rec_offset; /* We do not need to reserve btr_search_latch, as the page is only being recovered, and there cannot be a hash index to it. */ offsets = rec_get_offsets(rec, dict_index, NULL, ULINT_UNDEFINED, &heap); if (!(flags & BTR_KEEP_SYS_FLAG)) { row_upd_rec_sys_fields_in_recovery(rec, page_zip, offsets, pos, trx_id, roll_ptr); } row_upd_rec_in_place(rec, dict_index, offsets, update, page_zip); func_exit: mem_heap_free(heap); return(ptr); } #ifndef UNIV_HOTBACKUP /*************************************************************//** See if there is enough place in the page modification log to log an update-in-place. @return TRUE if enough place */ static ibool btr_cur_update_alloc_zip( /*=====================*/ page_zip_des_t* page_zip,/*!< in/out: compressed page */ buf_block_t* block, /*!< in/out: buffer page */ dict_index_t* dict_index,/*!< in: the index corresponding to the block */ ulint length, /*!< in: size needed */ ibool create, /*!< in: TRUE=delete-and-insert, FALSE=update-in-place */ mtr_t* mtr) /*!< in: mini-transaction */ { ut_a(page_zip == buf_block_get_page_zip(block)); ut_ad(page_zip); ut_ad(!dict_index_is_ibuf(dict_index)); if (page_zip_available(page_zip, dict_index_is_clust(dict_index), length, create)) { return(TRUE); } if (!page_zip->m_nonempty) { /* The page has been freshly compressed, so recompressing it will not help. */ return(FALSE); } if (!page_zip_compress(page_zip, buf_block_get_frame(block), dict_index, mtr)) { /* Unable to compress the page */ return(FALSE); } /* After recompressing a page, we must make sure that the free bits in the insert buffer bitmap will not exceed the free space on the page. Because this function will not attempt recompression unless page_zip_available() fails above, it is safe to reset the free bits if page_zip_available() fails again, below. The free bits can safely be reset in a separate mini-transaction. If page_zip_available() succeeds below, we can be sure that the page_zip_compress() above did not reduce the free space available on the page. */ if (!page_zip_available(page_zip, dict_index_is_clust(dict_index), length, create)) { /* Out of space: reset the free bits. */ if (!dict_index_is_clust(dict_index) && page_is_leaf(buf_block_get_frame(block))) { ibuf_reset_free_bits(block); } return(FALSE); } return(TRUE); } /*************************************************************//** Updates a record when the update causes no size changes in its fields. We assume here that the ordering fields of the record do not change. @return DB_SUCCESS or error number */ UNIV_INTERN ulint btr_cur_update_in_place( /*====================*/ ulint flags, /*!< in: undo logging and locking flags */ btr_cur_t* cursor, /*!< in: cursor on the record to update; cursor stays valid and positioned on the same record */ const upd_t* update, /*!< in: update vector */ ulint cmpl_info,/*!< in: compiler info on secondary index updates */ que_thr_t* thr, /*!< in: query thread */ mtr_t* mtr) /*!< in: mtr; must be committed before latching any further pages */ { dict_index_t* dict_index; buf_block_t* block; page_zip_des_t* page_zip; ulint err; rec_t* rec; roll_ptr_t roll_ptr = ut_dulint_zero; trx_t* trx; ulint was_delete_marked; mem_heap_t* heap = NULL; ulint offsets_[REC_OFFS_NORMAL_SIZE]; ulint* offsets = offsets_; rec_offs_init(offsets_); rec = btr_cur_get_rec(cursor); dict_index = cursor->index; ut_ad(!!page_rec_is_comp(rec) == dict_table_is_comp(dict_index->table)); /* The insert buffer tree should never be updated in place. */ ut_ad(!dict_index_is_ibuf(dict_index)); trx = thr_get_trx(thr); offsets = rec_get_offsets(rec, dict_index, offsets, ULINT_UNDEFINED, &heap); #ifdef UNIV_DEBUG if (btr_cur_print_record_ops && thr) { btr_cur_trx_report(trx, dict_index, "update "); rec_print_new(ib_stream, rec, offsets); } #endif /* UNIV_DEBUG */ block = btr_cur_get_block(cursor); page_zip = buf_block_get_page_zip(block); /* Check that enough space is available on the compressed page. */ if (UNIV_LIKELY_NULL(page_zip) && !btr_cur_update_alloc_zip(page_zip, block, dict_index, rec_offs_size(offsets), FALSE, mtr)) { return(DB_ZIP_OVERFLOW); } /* Do lock checking and undo logging */ err = btr_cur_upd_lock_and_undo(flags, cursor, update, cmpl_info, thr, mtr, &roll_ptr); if (UNIV_UNLIKELY(err != DB_SUCCESS)) { if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } return(err); } if (block->is_hashed) { /* The function row_upd_changes_ord_field_binary works only if the update vector was built for a clustered index, we must NOT call it if index is secondary */ if (!dict_index_is_clust(dict_index) || row_upd_changes_ord_field_binary(NULL, dict_index, update)) { /* Remove possible hash index pointer to this record */ btr_search_update_hash_on_delete(cursor); } rw_lock_x_lock(&btr_search_latch); } if (!(flags & BTR_KEEP_SYS_FLAG)) { row_upd_rec_sys_fields(rec, NULL, dict_index, offsets, trx, roll_ptr); } was_delete_marked = rec_get_deleted_flag( rec, page_is_comp(buf_block_get_frame(block))); row_upd_rec_in_place(rec, dict_index, offsets, update, page_zip); if (block->is_hashed) { rw_lock_x_unlock(&btr_search_latch); } if (page_zip && !dict_index_is_clust(dict_index) && page_is_leaf(buf_block_get_frame(block))) { /* Update the free bits in the insert buffer. */ ibuf_update_free_bits_zip(block, mtr); } btr_cur_update_in_place_log(flags, rec, dict_index, update, trx, roll_ptr, mtr); if (was_delete_marked && !rec_get_deleted_flag(rec, page_is_comp( buf_block_get_frame(block)))) { /* The new updated record owns its possible externally stored fields */ btr_cur_unmark_extern_fields(page_zip, rec, dict_index, offsets, mtr); } if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } return(DB_SUCCESS); } /*************************************************************//** Tries to update a record on a page in an index tree. It is assumed that mtr holds an x-latch on the page. The operation does not succeed if there is too little space on the page or if the update would result in too empty a page, so that tree compression is recommended. We assume here that the ordering fields of the record do not change. @return DB_SUCCESS, or DB_OVERFLOW if the updated record does not fit, DB_UNDERFLOW if the page would become too empty, or DB_ZIP_OVERFLOW if there is not enough space left on the compressed page */ UNIV_INTERN ulint btr_cur_optimistic_update( /*======================*/ ulint flags, /*!< in: undo logging and locking flags */ btr_cur_t* cursor, /*!< in: cursor on the record to update; cursor stays valid and positioned on the same record */ const upd_t* update, /*!< in: update vector; this must also contain trx id and roll ptr fields */ ulint cmpl_info,/*!< in: compiler info on secondary index updates */ que_thr_t* thr, /*!< in: query thread */ mtr_t* mtr) /*!< in: mtr; must be committed before latching any further pages */ { dict_index_t* index; page_cur_t* page_cursor; ulint err; buf_block_t* block; page_t* page; page_zip_des_t* page_zip; rec_t* rec; rec_t* orig_rec; ulint max_size; ulint new_rec_size; ulint old_rec_size; dtuple_t* new_entry; roll_ptr_t roll_ptr; trx_t* trx; mem_heap_t* heap; ulint i; ulint n_ext; ulint* offsets; block = btr_cur_get_block(cursor); page = buf_block_get_frame(block); orig_rec = rec = btr_cur_get_rec(cursor); index = cursor->index; ut_ad(!!page_rec_is_comp(rec) == dict_table_is_comp(index->table)); ut_ad(mtr_memo_contains(mtr, block, MTR_MEMO_PAGE_X_FIX)); /* The insert buffer tree should never be updated in place. */ ut_ad(!dict_index_is_ibuf(index)); heap = mem_heap_create(1024); offsets = rec_get_offsets(rec, index, NULL, ULINT_UNDEFINED, &heap); #ifdef UNIV_DEBUG if (btr_cur_print_record_ops && thr) { btr_cur_trx_report(thr_get_trx(thr), index, "update "); rec_print_new(ib_stream, rec, offsets); } #endif /* UNIV_DEBUG */ if (!row_upd_changes_field_size_or_external(index, offsets, update)) { /* The simplest and the most common case: the update does not change the size of any field and none of the updated fields is externally stored in rec or update, and there is enough space on the compressed page to log the update. */ mem_heap_free(heap); return(btr_cur_update_in_place(flags, cursor, update, cmpl_info, thr, mtr)); } if (rec_offs_any_extern(offsets)) { any_extern: /* Externally stored fields are treated in pessimistic update */ mem_heap_free(heap); return(DB_OVERFLOW); } for (i = 0; i < upd_get_n_fields(update); i++) { if (dfield_is_ext(&upd_get_nth_field(update, i)->new_val)) { goto any_extern; } } page_cursor = btr_cur_get_page_cur(cursor); new_entry = row_rec_to_index_entry(ROW_COPY_DATA, rec, index, offsets, &n_ext, heap); /* We checked above that there are no externally stored fields. */ ut_a(!n_ext); /* The page containing the clustered index record corresponding to new_entry is latched in mtr. Thus the following call is safe. */ row_upd_index_replace_new_col_vals_index_pos(new_entry, index, update, FALSE, heap); old_rec_size = rec_offs_size(offsets); new_rec_size = rec_get_converted_size(index, new_entry, 0); page_zip = buf_block_get_page_zip(block); #ifdef UNIV_ZIP_DEBUG ut_a(!page_zip || page_zip_validate(page_zip, page)); #endif /* UNIV_ZIP_DEBUG */ if (UNIV_LIKELY_NULL(page_zip) && !btr_cur_update_alloc_zip(page_zip, block, index, new_rec_size, TRUE, mtr)) { err = DB_ZIP_OVERFLOW; goto err_exit; } if (UNIV_UNLIKELY(new_rec_size >= (page_get_free_space_of_empty(page_is_comp(page)) / 2))) { err = DB_OVERFLOW; goto err_exit; } if (UNIV_UNLIKELY(page_get_data_size(page) - old_rec_size + new_rec_size < BTR_CUR_PAGE_COMPRESS_LIMIT)) { /* The page would become too empty */ err = DB_UNDERFLOW; goto err_exit; } max_size = old_rec_size + page_get_max_insert_size_after_reorganize(page, 1); if (!(((max_size >= BTR_CUR_PAGE_REORGANIZE_LIMIT) && (max_size >= new_rec_size)) || (page_get_n_recs(page) <= 1))) { /* There was not enough space, or it did not pay to reorganize: for simplicity, we decide what to do assuming a reorganization is needed, though it might not be necessary */ err = DB_OVERFLOW; goto err_exit; } /* Do lock checking and undo logging */ err = btr_cur_upd_lock_and_undo(flags, cursor, update, cmpl_info, thr, mtr, &roll_ptr); if (err != DB_SUCCESS) { err_exit: mem_heap_free(heap); return(err); } /* Ok, we may do the replacement. Store on the page infimum the explicit locks on rec, before deleting rec (see the comment in btr_cur_pessimistic_update). */ lock_rec_store_on_page_infimum(block, rec); btr_search_update_hash_on_delete(cursor); /* The call to row_rec_to_index_entry(ROW_COPY_DATA, ...) above invokes rec_offs_make_valid() to point to the copied record that the fields of new_entry point to. We have to undo it here. */ ut_ad(rec_offs_validate(NULL, index, offsets)); rec_offs_make_valid(page_cur_get_rec(page_cursor), index, offsets); page_cur_delete_rec(page_cursor, index, offsets, mtr); page_cur_move_to_prev(page_cursor); trx = thr_get_trx(thr); if (!(flags & BTR_KEEP_SYS_FLAG)) { row_upd_index_entry_sys_field(new_entry, index, DATA_ROLL_PTR, roll_ptr); row_upd_index_entry_sys_field(new_entry, index, DATA_TRX_ID, trx->id); } /* There are no externally stored columns in new_entry */ rec = btr_cur_insert_if_possible(cursor, new_entry, 0/*n_ext*/, mtr); ut_a(rec); /* <- We calculated above the insert would fit */ if (page_zip && !dict_index_is_clust(index) && page_is_leaf(page)) { /* Update the free bits in the insert buffer. */ ibuf_update_free_bits_zip(block, mtr); } /* Restore the old explicit lock state on the record */ lock_rec_restore_from_page_infimum(block, rec, block); page_cur_move_to_next(page_cursor); mem_heap_free(heap); return(DB_SUCCESS); } /*************************************************************//** If, in a split, a new supremum record was created as the predecessor of the updated record, the supremum record must inherit exactly the locks on the updated record. In the split it may have inherited locks from the successor of the updated record, which is not correct. This function restores the right locks for the new supremum. */ static void btr_cur_pess_upd_restore_supremum( /*==============================*/ buf_block_t* block, /*!< in: buffer block of rec */ const rec_t* rec, /*!< in: updated record */ mtr_t* mtr) /*!< in: mtr */ { page_t* page; buf_block_t* prev_block; ulint space; ulint zip_size; ulint prev_page_no; page = buf_block_get_frame(block); if (page_rec_get_next(page_get_infimum_rec(page)) != rec) { /* Updated record is not the first user record on its page */ return; } space = buf_block_get_space(block); zip_size = buf_block_get_zip_size(block); prev_page_no = btr_page_get_prev(page, mtr); ut_ad(prev_page_no != FIL_NULL); prev_block = buf_page_get_with_no_latch(space, zip_size, prev_page_no, mtr); #ifdef UNIV_BTR_DEBUG ut_a(btr_page_get_next(prev_block->frame, mtr) == page_get_page_no(page)); #endif /* UNIV_BTR_DEBUG */ /* We must already have an x-latch on prev_block! */ ut_ad(mtr_memo_contains(mtr, prev_block, MTR_MEMO_PAGE_X_FIX)); lock_rec_reset_and_inherit_gap_locks(prev_block, block, PAGE_HEAP_NO_SUPREMUM, page_rec_get_heap_no(rec)); } /*************************************************************//** Performs an update of a record on a page of a tree. It is assumed that mtr holds an x-latch on the tree and on the cursor page. If the update is made on the leaf level, to avoid deadlocks, mtr must also own x-latches to brothers of page, if those brothers exist. We assume here that the ordering fields of the record do not change. @return DB_SUCCESS or error code */ UNIV_INTERN ulint btr_cur_pessimistic_update( /*=======================*/ ulint flags, /*!< in: undo logging, locking, and rollback flags */ btr_cur_t* cursor, /*!< in: cursor on the record to update */ mem_heap_t** heap, /*!< in/out: pointer to memory heap, or NULL */ big_rec_t** big_rec,/*!< out: big rec vector whose fields have to be stored externally by the caller, or NULL */ const upd_t* update, /*!< in: update vector; this is allowed also contain trx id and roll ptr fields, but the values in update vector have no effect */ ulint cmpl_info,/*!< in: compiler info on secondary index updates */ que_thr_t* thr, /*!< in: query thread */ mtr_t* mtr) /*!< in: mtr; must be committed before latching any further pages */ { big_rec_t* big_rec_vec = NULL; big_rec_t* dummy_big_rec; dict_index_t* index; buf_block_t* block; page_t* page; page_zip_des_t* page_zip; rec_t* rec; page_cur_t* page_cursor; dtuple_t* new_entry; ulint err; ulint optim_err; roll_ptr_t roll_ptr; trx_t* trx; ibool was_first; ulint n_extents = 0; ulint n_reserved; ulint n_ext; ulint* offsets = NULL; *big_rec = NULL; block = btr_cur_get_block(cursor); page = buf_block_get_frame(block); page_zip = buf_block_get_page_zip(block); rec = btr_cur_get_rec(cursor); index = cursor->index; ut_ad(mtr_memo_contains(mtr, dict_index_get_lock(index), MTR_MEMO_X_LOCK)); ut_ad(mtr_memo_contains(mtr, block, MTR_MEMO_PAGE_X_FIX)); #ifdef UNIV_ZIP_DEBUG ut_a(!page_zip || page_zip_validate(page_zip, page)); #endif /* UNIV_ZIP_DEBUG */ /* The insert buffer tree should never be updated in place. */ ut_ad(!dict_index_is_ibuf(index)); optim_err = btr_cur_optimistic_update(flags, cursor, update, cmpl_info, thr, mtr); switch (optim_err) { case DB_UNDERFLOW: case DB_OVERFLOW: case DB_ZIP_OVERFLOW: break; default: return(optim_err); } /* Do lock checking and undo logging */ err = btr_cur_upd_lock_and_undo(flags, cursor, update, cmpl_info, thr, mtr, &roll_ptr); if (err != DB_SUCCESS) { return(err); } if (optim_err == DB_OVERFLOW) { ulint reserve_flag; /* First reserve enough free space for the file segments of the index tree, so that the update will not fail because of lack of space */ n_extents = cursor->tree_height / 16 + 3; if (flags & BTR_NO_UNDO_LOG_FLAG) { reserve_flag = FSP_CLEANING; } else { reserve_flag = FSP_NORMAL; } if (!fsp_reserve_free_extents(&n_reserved, index->space, n_extents, reserve_flag, mtr)) { return(DB_OUT_OF_FILE_SPACE); } } if (!*heap) { *heap = mem_heap_create(1024); } offsets = rec_get_offsets(rec, index, NULL, ULINT_UNDEFINED, heap); trx = thr_get_trx(thr); new_entry = row_rec_to_index_entry(ROW_COPY_DATA, rec, index, offsets, &n_ext, *heap); /* The call to row_rec_to_index_entry(ROW_COPY_DATA, ...) above invokes rec_offs_make_valid() to point to the copied record that the fields of new_entry point to. We have to undo it here. */ ut_ad(rec_offs_validate(NULL, index, offsets)); rec_offs_make_valid(rec, index, offsets); /* The page containing the clustered index record corresponding to new_entry is latched in mtr. If the clustered index record is delete-marked, then its externally stored fields cannot have been purged yet, because then the purge would also have removed the clustered index record itself. Thus the following call is safe. */ row_upd_index_replace_new_col_vals_index_pos(new_entry, index, update, FALSE, *heap); if (!(flags & BTR_KEEP_SYS_FLAG)) { row_upd_index_entry_sys_field(new_entry, index, DATA_ROLL_PTR, roll_ptr); row_upd_index_entry_sys_field(new_entry, index, DATA_TRX_ID, trx->id); } if ((flags & BTR_NO_UNDO_LOG_FLAG) && rec_offs_any_extern(offsets)) { /* We are in a transaction rollback undoing a row update: we must free possible externally stored fields which got new values in the update, if they are not inherited values. They can be inherited if we have updated the primary key to another value, and then update it back again. */ ut_ad(big_rec_vec == NULL); btr_rec_free_updated_extern_fields( index, rec, page_zip, offsets, update, trx_is_recv(trx) ? RB_RECOVERY : RB_NORMAL, mtr); } /* We have to set appropriate extern storage bits in the new record to be inserted: we have to remember which fields were such */ ut_ad(!page_is_comp(page) || !rec_get_node_ptr_flag(rec)); offsets = rec_get_offsets(rec, index, offsets, ULINT_UNDEFINED, heap); n_ext += btr_push_update_extern_fields(new_entry, update, *heap); if (UNIV_LIKELY_NULL(page_zip)) { ut_ad(page_is_comp(page)); if (page_zip_rec_needs_ext( rec_get_converted_size(index, new_entry, n_ext), TRUE, dict_index_get_n_fields(index), page_zip_get_size(page_zip))) { goto make_external; } } else if (page_rec_needs_ext( rec_get_converted_size(index, new_entry, n_ext), page_is_comp(page), 0, 0)) { make_external: big_rec_vec = dtuple_convert_big_rec(index, new_entry, &n_ext); if (UNIV_UNLIKELY(big_rec_vec == NULL)) { err = DB_TOO_BIG_RECORD; goto return_after_reservations; } } /* Store state of explicit locks on rec on the page infimum record, before deleting rec. The page infimum acts as a dummy carrier of the locks, taking care also of lock releases, before we can move the locks back on the actual record. There is a special case: if we are inserting on the root page and the insert causes a call of btr_root_raise_and_insert. Therefore we cannot in the lock system delete the lock structs set on the root page even if the root page carries just node pointers. */ lock_rec_store_on_page_infimum(block, rec); btr_search_update_hash_on_delete(cursor); #ifdef UNIV_ZIP_DEBUG ut_a(!page_zip || page_zip_validate(page_zip, page)); #endif /* UNIV_ZIP_DEBUG */ page_cursor = btr_cur_get_page_cur(cursor); page_cur_delete_rec(page_cursor, index, offsets, mtr); page_cur_move_to_prev(page_cursor); rec = btr_cur_insert_if_possible(cursor, new_entry, n_ext, mtr); if (rec) { lock_rec_restore_from_page_infimum(btr_cur_get_block(cursor), rec, block); offsets = rec_get_offsets(rec, index, offsets, ULINT_UNDEFINED, heap); if (!rec_get_deleted_flag(rec, rec_offs_comp(offsets))) { /* The new inserted record owns its possible externally stored fields */ btr_cur_unmark_extern_fields(page_zip, rec, index, offsets, mtr); } btr_cur_compress_if_useful(cursor, mtr); if (page_zip && !dict_index_is_clust(index) && page_is_leaf(page)) { /* Update the free bits in the insert buffer. */ ibuf_update_free_bits_zip(block, mtr); } err = DB_SUCCESS; goto return_after_reservations; } else { ut_a(optim_err != DB_UNDERFLOW); /* Out of space: reset the free bits. */ if (!dict_index_is_clust(index) && page_is_leaf(page)) { ibuf_reset_free_bits(block); } } /* Was the record to be updated positioned as the first user record on its page? */ was_first = page_cur_is_before_first(page_cursor); /* The first parameter means that no lock checking and undo logging is made in the insert */ err = btr_cur_pessimistic_insert(BTR_NO_UNDO_LOG_FLAG | BTR_NO_LOCKING_FLAG | BTR_KEEP_SYS_FLAG, cursor, new_entry, &rec, &dummy_big_rec, n_ext, NULL, mtr); ut_a(rec); ut_a(err == DB_SUCCESS); ut_a(dummy_big_rec == NULL); if (dict_index_is_sec_or_ibuf(index)) { /* Update PAGE_MAX_TRX_ID in the index page header. It was not updated by btr_cur_pessimistic_insert() because of BTR_NO_LOCKING_FLAG. */ buf_block_t* rec_block; rec_block = btr_cur_get_block(cursor); page_update_max_trx_id(rec_block, buf_block_get_page_zip(rec_block), trx->id, mtr); } if (!rec_get_deleted_flag(rec, rec_offs_comp(offsets))) { /* The new inserted record owns its possible externally stored fields */ buf_block_t* rec_block = btr_cur_get_block(cursor); #ifdef UNIV_ZIP_DEBUG ut_a(!page_zip || page_zip_validate(page_zip, page)); page = buf_block_get_frame(rec_block); #endif /* UNIV_ZIP_DEBUG */ page_zip = buf_block_get_page_zip(rec_block); offsets = rec_get_offsets(rec, index, offsets, ULINT_UNDEFINED, heap); btr_cur_unmark_extern_fields(page_zip, rec, index, offsets, mtr); } lock_rec_restore_from_page_infimum(btr_cur_get_block(cursor), rec, block); /* If necessary, restore also the correct lock state for a new, preceding supremum record created in a page split. While the old record was nonexistent, the supremum might have inherited its locks from a wrong record. */ if (!was_first) { btr_cur_pess_upd_restore_supremum(btr_cur_get_block(cursor), rec, mtr); } return_after_reservations: #ifdef UNIV_ZIP_DEBUG ut_a(!page_zip || page_zip_validate(page_zip, page)); #endif /* UNIV_ZIP_DEBUG */ if (n_extents > 0) { fil_space_release_free_extents(index->space, n_reserved); } *big_rec = big_rec_vec; return(err); } /*==================== B-TREE DELETE MARK AND UNMARK ===============*/ /****************************************************************//** Writes the redo log record for delete marking or unmarking of an index record. */ UNIV_INLINE void btr_cur_del_mark_set_clust_rec_log( /*===============================*/ ulint flags, /*!< in: flags */ rec_t* rec, /*!< in: record */ dict_index_t* index, /*!< in: index of the record */ ibool val, /*!< in: value to set */ trx_t* trx, /*!< in: deleting transaction */ roll_ptr_t roll_ptr,/*!< in: roll ptr to the undo log record */ mtr_t* mtr) /*!< in: mtr */ { byte* log_ptr; ut_ad(flags < 256); ut_ad(val <= 1); ut_ad(!!page_rec_is_comp(rec) == dict_table_is_comp(index->table)); log_ptr = mlog_open_and_write_index(mtr, rec, index, page_rec_is_comp(rec) ? MLOG_COMP_REC_CLUST_DELETE_MARK : MLOG_REC_CLUST_DELETE_MARK, 1 + 1 + DATA_ROLL_PTR_LEN + 14 + 2); if (!log_ptr) { /* Logging in mtr is switched off during crash recovery */ return; } mach_write_to_1(log_ptr, flags); log_ptr++; mach_write_to_1(log_ptr, val); log_ptr++; log_ptr = row_upd_write_sys_vals_to_log(index, trx, roll_ptr, log_ptr, mtr); mach_write_to_2(log_ptr, page_offset(rec)); log_ptr += 2; mlog_close(mtr, log_ptr); } #endif /* !UNIV_HOTBACKUP */ /****************************************************************//** Parses the redo log record for delete marking or unmarking of a clustered index record. @return end of log record or NULL */ UNIV_INTERN byte* btr_cur_parse_del_mark_set_clust_rec( /*=================================*/ byte* ptr, /*!< in: buffer */ byte* end_ptr,/*!< in: buffer end */ page_t* page, /*!< in/out: page or NULL */ page_zip_des_t* page_zip,/*!< in/out: compressed page, or NULL */ dict_index_t* index) /*!< in: index corresponding to page */ { ulint flags; ulint val; ulint pos; trx_id_t trx_id; roll_ptr_t roll_ptr; ulint offset; rec_t* rec; ut_ad(!page || !!page_is_comp(page) == dict_table_is_comp(index->table)); if (end_ptr < ptr + 2) { return(NULL); } flags = mach_read_from_1(ptr); ptr++; val = mach_read_from_1(ptr); ptr++; ptr = row_upd_parse_sys_vals(ptr, end_ptr, &pos, &trx_id, &roll_ptr); if (ptr == NULL) { return(NULL); } if (end_ptr < ptr + 2) { return(NULL); } offset = mach_read_from_2(ptr); ptr += 2; ut_a(offset <= UNIV_PAGE_SIZE); if (page) { rec = page + offset; /* We do not need to reserve btr_search_latch, as the page is only being recovered, and there cannot be a hash index to it. */ btr_rec_set_deleted_flag(rec, page_zip, val); if (!(flags & BTR_KEEP_SYS_FLAG)) { mem_heap_t* heap = NULL; ulint offsets_[REC_OFFS_NORMAL_SIZE]; rec_offs_init(offsets_); row_upd_rec_sys_fields_in_recovery( rec, page_zip, rec_get_offsets(rec, index, offsets_, ULINT_UNDEFINED, &heap), pos, trx_id, roll_ptr); if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } } } return(ptr); } #ifndef UNIV_HOTBACKUP /***********************************************************//** Marks a clustered index record deleted. Writes an undo log record to undo log on this delete marking. Writes in the trx id field the id of the deleting transaction, and in the roll ptr field pointer to the undo log record created. @return DB_SUCCESS, DB_LOCK_WAIT, or error number */ UNIV_INTERN ulint btr_cur_del_mark_set_clust_rec( /*===========================*/ ulint flags, /*!< in: undo logging and locking flags */ btr_cur_t* cursor, /*!< in: cursor */ ibool val, /*!< in: value to set */ que_thr_t* thr, /*!< in: query thread */ mtr_t* mtr) /*!< in: mtr */ { dict_index_t* index; buf_block_t* block; roll_ptr_t roll_ptr; ulint err; rec_t* rec; page_zip_des_t* page_zip; trx_t* trx; mem_heap_t* heap = NULL; ulint offsets_[REC_OFFS_NORMAL_SIZE]; ulint* offsets = offsets_; rec_offs_init(offsets_); rec = btr_cur_get_rec(cursor); index = cursor->index; ut_ad(!!page_rec_is_comp(rec) == dict_table_is_comp(index->table)); offsets = rec_get_offsets(rec, index, offsets, ULINT_UNDEFINED, &heap); #ifdef UNIV_DEBUG if (btr_cur_print_record_ops && thr) { btr_cur_trx_report(thr_get_trx(thr), index, "del mark "); rec_print_new(ib_stream, rec, offsets); } #endif /* UNIV_DEBUG */ ut_ad(dict_index_is_clust(index)); ut_ad(!rec_get_deleted_flag(rec, rec_offs_comp(offsets))); err = lock_clust_rec_modify_check_and_lock(flags, btr_cur_get_block(cursor), rec, index, offsets, thr); if (err != DB_SUCCESS) { goto func_exit; } err = trx_undo_report_row_operation(flags, TRX_UNDO_MODIFY_OP, thr, index, NULL, NULL, 0, rec, &roll_ptr); if (err != DB_SUCCESS) { goto func_exit; } block = btr_cur_get_block(cursor); if (block->is_hashed) { rw_lock_x_lock(&btr_search_latch); } page_zip = buf_block_get_page_zip(block); btr_rec_set_deleted_flag(rec, page_zip, val); trx = thr_get_trx(thr); if (!(flags & BTR_KEEP_SYS_FLAG)) { row_upd_rec_sys_fields(rec, page_zip, index, offsets, trx, roll_ptr); } if (block->is_hashed) { rw_lock_x_unlock(&btr_search_latch); } btr_cur_del_mark_set_clust_rec_log(flags, rec, index, val, trx, roll_ptr, mtr); func_exit: if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } return(err); } /****************************************************************//** Writes the redo log record for a delete mark setting of a secondary index record. */ UNIV_INLINE void btr_cur_del_mark_set_sec_rec_log( /*=============================*/ rec_t* rec, /*!< in: record */ ibool val, /*!< in: value to set */ mtr_t* mtr) /*!< in: mtr */ { byte* log_ptr; ut_ad(val <= 1); log_ptr = mlog_open(mtr, 11 + 1 + 2); if (!log_ptr) { /* Logging in mtr is switched off during crash recovery: in that case mlog_open returns NULL */ return; } log_ptr = mlog_write_initial_log_record_fast( rec, MLOG_REC_SEC_DELETE_MARK, log_ptr, mtr); mach_write_to_1(log_ptr, val); log_ptr++; mach_write_to_2(log_ptr, page_offset(rec)); log_ptr += 2; mlog_close(mtr, log_ptr); } #endif /* !UNIV_HOTBACKUP */ /****************************************************************//** Parses the redo log record for delete marking or unmarking of a secondary index record. @return end of log record or NULL */ UNIV_INTERN byte* btr_cur_parse_del_mark_set_sec_rec( /*===============================*/ byte* ptr, /*!< in: buffer */ byte* end_ptr,/*!< in: buffer end */ page_t* page, /*!< in/out: page or NULL */ page_zip_des_t* page_zip)/*!< in/out: compressed page, or NULL */ { ulint val; ulint offset; rec_t* rec; if (end_ptr < ptr + 3) { return(NULL); } val = mach_read_from_1(ptr); ptr++; offset = mach_read_from_2(ptr); ptr += 2; ut_a(offset <= UNIV_PAGE_SIZE); if (page) { rec = page + offset; /* We do not need to reserve btr_search_latch, as the page is only being recovered, and there cannot be a hash index to it. */ btr_rec_set_deleted_flag(rec, page_zip, val); } return(ptr); } #ifndef UNIV_HOTBACKUP /***********************************************************//** Sets a secondary index record delete mark to TRUE or FALSE. @return DB_SUCCESS, DB_LOCK_WAIT, or error number */ UNIV_INTERN ulint btr_cur_del_mark_set_sec_rec( /*=========================*/ ulint flags, /*!< in: locking flag */ btr_cur_t* cursor, /*!< in: cursor */ ibool val, /*!< in: value to set */ que_thr_t* thr, /*!< in: query thread */ mtr_t* mtr) /*!< in: mtr */ { buf_block_t* block; rec_t* rec; ulint err; block = btr_cur_get_block(cursor); rec = btr_cur_get_rec(cursor); #ifdef UNIV_DEBUG if (btr_cur_print_record_ops && thr) { btr_cur_trx_report(thr_get_trx(thr), cursor->index, "del mark "); rec_print(ib_stream, rec, cursor->index); } #endif /* UNIV_DEBUG */ err = lock_sec_rec_modify_check_and_lock(flags, btr_cur_get_block(cursor), rec, cursor->index, thr, mtr); if (err != DB_SUCCESS) { return(err); } ut_ad(!!page_rec_is_comp(rec) == dict_table_is_comp(cursor->index->table)); if (block->is_hashed) { rw_lock_x_lock(&btr_search_latch); } btr_rec_set_deleted_flag(rec, buf_block_get_page_zip(block), val); if (block->is_hashed) { rw_lock_x_unlock(&btr_search_latch); } btr_cur_del_mark_set_sec_rec_log(rec, val, mtr); return(DB_SUCCESS); } /***********************************************************//** Clear a secondary index record's delete mark. This function is only used by the insert buffer insert merge mechanism. */ UNIV_INTERN void btr_cur_del_unmark_for_ibuf( /*========================*/ rec_t* rec, /*!< in/out: record to delete unmark */ page_zip_des_t* page_zip, /*!< in/out: compressed page corresponding to rec, or NULL when the tablespace is uncompressed */ mtr_t* mtr) /*!< in: mtr */ { /* We do not need to reserve btr_search_latch, as the page has just been read to the buffer pool and there cannot be a hash index to it. */ btr_rec_set_deleted_flag(rec, page_zip, FALSE); btr_cur_del_mark_set_sec_rec_log(rec, FALSE, mtr); } /*==================== B-TREE RECORD REMOVE =========================*/ /*************************************************************//** Tries to compress a page of the tree if it seems useful. It is assumed that mtr holds an x-latch on the tree and on the cursor page. To avoid deadlocks, mtr must also own x-latches to brothers of page, if those brothers exist. NOTE: it is assumed that the caller has reserved enough free extents so that the compression will always succeed if done! @return TRUE if compression occurred */ UNIV_INTERN ibool btr_cur_compress_if_useful( /*=======================*/ btr_cur_t* cursor, /*!< in: cursor on the page to compress; cursor does not stay valid if compression occurs */ mtr_t* mtr) /*!< in: mtr */ { ut_ad(mtr_memo_contains(mtr, dict_index_get_lock(btr_cur_get_index(cursor)), MTR_MEMO_X_LOCK)); ut_ad(mtr_memo_contains(mtr, btr_cur_get_block(cursor), MTR_MEMO_PAGE_X_FIX)); return(btr_cur_compress_recommendation(cursor, mtr) && btr_compress(cursor, mtr)); } /*******************************************************//** Removes the record on which the tree cursor is positioned on a leaf page. It is assumed that the mtr has an x-latch on the page where the cursor is positioned, but no latch on the whole tree. @return TRUE if success, i.e., the page did not become too empty */ UNIV_INTERN ibool btr_cur_optimistic_delete( /*======================*/ btr_cur_t* cursor, /*!< in: cursor on leaf page, on the record to delete; cursor stays valid: if deletion succeeds, on function exit it points to the successor of the deleted record */ mtr_t* mtr) /*!< in: mtr; if this function returns TRUE on a leaf page of a secondary index, the mtr must be committed before latching any further pages */ { buf_block_t* block; rec_t* rec; mem_heap_t* heap = NULL; ulint offsets_[REC_OFFS_NORMAL_SIZE]; ulint* offsets = offsets_; ibool no_compress_needed; rec_offs_init(offsets_); ut_ad(mtr_memo_contains(mtr, btr_cur_get_block(cursor), MTR_MEMO_PAGE_X_FIX)); /* This is intended only for leaf page deletions */ block = btr_cur_get_block(cursor); ut_ad(page_is_leaf(buf_block_get_frame(block))); rec = btr_cur_get_rec(cursor); offsets = rec_get_offsets(rec, cursor->index, offsets, ULINT_UNDEFINED, &heap); no_compress_needed = !rec_offs_any_extern(offsets) && btr_cur_can_delete_without_compress( cursor, rec_offs_size(offsets), mtr); if (no_compress_needed) { page_t* page = buf_block_get_frame(block); page_zip_des_t* page_zip= buf_block_get_page_zip(block); ulint max_ins = 0; lock_update_delete(block, rec); btr_search_update_hash_on_delete(cursor); if (!page_zip) { max_ins = page_get_max_insert_size_after_reorganize( page, 1); } #ifdef UNIV_ZIP_DEBUG ut_a(!page_zip || page_zip_validate(page_zip, page)); #endif /* UNIV_ZIP_DEBUG */ page_cur_delete_rec(btr_cur_get_page_cur(cursor), cursor->index, offsets, mtr); #ifdef UNIV_ZIP_DEBUG ut_a(!page_zip || page_zip_validate(page_zip, page)); #endif /* UNIV_ZIP_DEBUG */ if (dict_index_is_clust(cursor->index) || dict_index_is_ibuf(cursor->index) || !page_is_leaf(page)) { /* The insert buffer does not handle inserts to clustered indexes, to non-leaf pages of secondary index B-trees, or to the insert buffer. */ } else if (page_zip) { ibuf_update_free_bits_zip(block, mtr); } else { ibuf_update_free_bits_low(block, max_ins, mtr); } } if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } return(no_compress_needed); } /*************************************************************//** Removes the record on which the tree cursor is positioned. Tries to compress the page if its fillfactor drops below a threshold or if it is the only page on the level. It is assumed that mtr holds an x-latch on the tree and on the cursor page. To avoid deadlocks, mtr must also own x-latches to brothers of page, if those brothers exist. @return TRUE if compression occurred */ UNIV_INTERN ibool btr_cur_pessimistic_delete( /*=======================*/ ulint* err, /*!< out: DB_SUCCESS or DB_OUT_OF_FILE_SPACE; the latter may occur because we may have to update node pointers on upper levels, and in the case of variable length keys these may actually grow in size */ ibool has_reserved_extents, /*!< in: TRUE if the caller has already reserved enough free extents so that he knows that the operation will succeed */ btr_cur_t* cursor, /*!< in: cursor on the record to delete; if compression does not occur, the cursor stays valid: it points to successor of deleted record on function exit */ enum trx_rb_ctx rb_ctx, /*!< in: rollback context */ mtr_t* mtr) /*!< in: mtr */ { buf_block_t* block; page_t* page; page_zip_des_t* page_zip; dict_index_t* index; rec_t* rec; dtuple_t* node_ptr; ulint n_extents = 0; ulint n_reserved; ibool success; ibool ret = FALSE; ulint level; mem_heap_t* heap; ulint* offsets; block = btr_cur_get_block(cursor); page = buf_block_get_frame(block); index = btr_cur_get_index(cursor); ut_ad(mtr_memo_contains(mtr, dict_index_get_lock(index), MTR_MEMO_X_LOCK)); ut_ad(mtr_memo_contains(mtr, block, MTR_MEMO_PAGE_X_FIX)); if (!has_reserved_extents) { /* First reserve enough free space for the file segments of the index tree, so that the node pointer updates will not fail because of lack of space */ n_extents = cursor->tree_height / 32 + 1; success = fsp_reserve_free_extents(&n_reserved, index->space, n_extents, FSP_CLEANING, mtr); if (!success) { *err = DB_OUT_OF_FILE_SPACE; return(FALSE); } } heap = mem_heap_create(1024); rec = btr_cur_get_rec(cursor); page_zip = buf_block_get_page_zip(block); #ifdef UNIV_ZIP_DEBUG ut_a(!page_zip || page_zip_validate(page_zip, page)); #endif /* UNIV_ZIP_DEBUG */ offsets = rec_get_offsets(rec, index, NULL, ULINT_UNDEFINED, &heap); if (rec_offs_any_extern(offsets)) { btr_rec_free_externally_stored_fields(index, rec, offsets, page_zip, rb_ctx, mtr); #ifdef UNIV_ZIP_DEBUG ut_a(!page_zip || page_zip_validate(page_zip, page)); #endif /* UNIV_ZIP_DEBUG */ } if (UNIV_UNLIKELY(page_get_n_recs(page) < 2) && UNIV_UNLIKELY(dict_index_get_page(index) != buf_block_get_page_no(block))) { /* If there is only one record, drop the whole page in btr_discard_page, if this is not the root page */ btr_discard_page(cursor, mtr); *err = DB_SUCCESS; ret = TRUE; goto return_after_reservations; } lock_update_delete(block, rec); level = btr_page_get_level(page, mtr); if (level > 0 && UNIV_UNLIKELY(rec == page_rec_get_next( page_get_infimum_rec(page)))) { rec_t* next_rec = page_rec_get_next(rec); if (btr_page_get_prev(page, mtr) == FIL_NULL) { /* If we delete the leftmost node pointer on a non-leaf level, we must mark the new leftmost node pointer as the predefined minimum record */ /* This will make page_zip_validate() fail until page_cur_delete_rec() completes. This is harmless, because everything will take place within a single mini-transaction and because writing to the redo log is an atomic operation (performed by mtr_commit()). */ btr_set_min_rec_mark(next_rec, mtr); } else { /* Otherwise, if we delete the leftmost node pointer on a page, we have to change the father node pointer so that it is equal to the new leftmost node pointer on the page */ btr_node_ptr_delete(index, block, mtr); node_ptr = dict_index_build_node_ptr( index, next_rec, buf_block_get_page_no(block), heap, level); btr_insert_on_non_leaf_level(index, level + 1, node_ptr, mtr); } } btr_search_update_hash_on_delete(cursor); page_cur_delete_rec(btr_cur_get_page_cur(cursor), index, offsets, mtr); #ifdef UNIV_ZIP_DEBUG ut_a(!page_zip || page_zip_validate(page_zip, page)); #endif /* UNIV_ZIP_DEBUG */ ut_ad(btr_check_node_ptr(index, block, mtr)); *err = DB_SUCCESS; return_after_reservations: mem_heap_free(heap); if (ret == FALSE) { ret = btr_cur_compress_if_useful(cursor, mtr); } if (n_extents > 0) { fil_space_release_free_extents(index->space, n_reserved); } return(ret); } /*******************************************************************//** Adds path information to the cursor for the current page, for which the binary search has been performed. */ static void btr_cur_add_path_info( /*==================*/ btr_cur_t* cursor, /*!< in: cursor positioned on a page */ ulint height, /*!< in: height of the page in tree; 0 means leaf node */ ulint root_height) /*!< in: root node height in tree */ { btr_path_t* slot; rec_t* rec; ut_a(cursor->path_arr); if (root_height >= BTR_PATH_ARRAY_N_SLOTS - 1) { /* Do nothing; return empty path */ slot = cursor->path_arr; slot->nth_rec = ULINT_UNDEFINED; return; } if (height == 0) { /* Mark end of slots for path */ slot = cursor->path_arr + root_height + 1; slot->nth_rec = ULINT_UNDEFINED; } rec = btr_cur_get_rec(cursor); slot = cursor->path_arr + (root_height - height); slot->nth_rec = page_rec_get_n_recs_before(rec); slot->n_recs = page_get_n_recs(page_align(rec)); } /*******************************************************************//** Estimates the number of rows in a given index range. @return estimated number of rows */ UNIV_INTERN ib_int64_t btr_estimate_n_rows_in_range( /*=========================*/ dict_index_t* index, /*!< in: index */ const dtuple_t* tuple1, /*!< in: range start, may also be empty tuple */ ulint mode1, /*!< in: search mode for range start */ const dtuple_t* tuple2, /*!< in: range end, may also be empty tuple */ ulint mode2) /*!< in: search mode for range end */ { btr_path_t path1[BTR_PATH_ARRAY_N_SLOTS]; btr_path_t path2[BTR_PATH_ARRAY_N_SLOTS]; btr_cur_t cursor; btr_path_t* slot1; btr_path_t* slot2; ibool diverged; ibool diverged_lot; ulint divergence_level; ib_int64_t n_rows; ulint i; mtr_t mtr; mtr_start(&mtr); cursor.path_arr = path1; if (dtuple_get_n_fields(tuple1) > 0) { btr_cur_search_to_nth_level(index, 0, tuple1, mode1, BTR_SEARCH_LEAF | BTR_ESTIMATE, &cursor, 0, __FILE__, __LINE__, &mtr); } else { btr_cur_open_at_index_side(TRUE, index, BTR_SEARCH_LEAF | BTR_ESTIMATE, &cursor, &mtr); } mtr_commit(&mtr); mtr_start(&mtr); cursor.path_arr = path2; if (dtuple_get_n_fields(tuple2) > 0) { btr_cur_search_to_nth_level(index, 0, tuple2, mode2, BTR_SEARCH_LEAF | BTR_ESTIMATE, &cursor, 0, __FILE__, __LINE__, &mtr); } else { btr_cur_open_at_index_side(FALSE, index, BTR_SEARCH_LEAF | BTR_ESTIMATE, &cursor, &mtr); } mtr_commit(&mtr); /* We have the path information for the range in path1 and path2 */ n_rows = 1; diverged = FALSE; /* This becomes true when the path is not the same any more */ diverged_lot = FALSE; /* This becomes true when the paths are not the same or adjacent any more */ divergence_level = 1000000; /* This is the level where paths diverged a lot */ for (i = 0; ; i++) { ut_ad(i < BTR_PATH_ARRAY_N_SLOTS); slot1 = path1 + i; slot2 = path2 + i; if (slot1->nth_rec == ULINT_UNDEFINED || slot2->nth_rec == ULINT_UNDEFINED) { if (i > divergence_level + 1) { /* In trees whose height is > 1 our algorithm tends to underestimate: multiply the estimate by 2: */ n_rows = n_rows * 2; } /* Do not estimate the number of rows in the range to over 1 / 2 of the estimated rows in the whole table */ if (n_rows > index->table->stat_n_rows / 2) { n_rows = index->table->stat_n_rows / 2; /* If there are just 0 or 1 rows in the table, then we estimate all rows are in the range */ if (n_rows == 0) { n_rows = index->table->stat_n_rows; } } return(n_rows); } if (!diverged && slot1->nth_rec != slot2->nth_rec) { diverged = TRUE; if (slot1->nth_rec < slot2->nth_rec) { n_rows = slot2->nth_rec - slot1->nth_rec; if (n_rows > 1) { diverged_lot = TRUE; divergence_level = i; } } else { /* Maybe the tree has changed between searches */ return(10); } } else if (diverged && !diverged_lot) { if (slot1->nth_rec < slot1->n_recs || slot2->nth_rec > 1) { diverged_lot = TRUE; divergence_level = i; n_rows = 0; if (slot1->nth_rec < slot1->n_recs) { n_rows += slot1->n_recs - slot1->nth_rec; } if (slot2->nth_rec > 1) { n_rows += slot2->nth_rec - 1; } } } else if (diverged_lot) { n_rows = (n_rows * (slot1->n_recs + slot2->n_recs)) / 2; } } } /*******************************************************************//** Estimates the number of different key values in a given index, for each n-column prefix of the index where n <= dict_index_get_n_unique(index). The estimates are stored in the array index->stat_n_diff_key_vals. */ UNIV_INTERN void btr_estimate_number_of_different_key_vals( /*======================================*/ dict_index_t* index) /*!< in: index */ { btr_cur_t cursor; page_t* page; rec_t* rec; ulint n_cols; ulint matched_fields; ulint matched_bytes; ib_int64_t* n_diff; ib_uint64_t n_sample_pages; /* number of pages to sample */ ulint not_empty_flag = 0; ulint total_external_size = 0; ulint i; ulint j; ib_uint64_t add_on; mtr_t mtr; mem_heap_t* heap = NULL; ulint offsets_rec_[REC_OFFS_NORMAL_SIZE]; ulint offsets_next_rec_[REC_OFFS_NORMAL_SIZE]; ulint* offsets_rec = offsets_rec_; ulint* offsets_next_rec= offsets_next_rec_; rec_offs_init(offsets_rec_); rec_offs_init(offsets_next_rec_); n_cols = dict_index_get_n_unique(index); n_diff = mem_zalloc((n_cols + 1) * sizeof(ib_int64_t)); /* It makes no sense to test more pages than are contained in the index, thus we lower the number if it is too high */ if (srv_stats_sample_pages > index->stat_index_size) { if (index->stat_index_size > 0) { n_sample_pages = index->stat_index_size; } else { n_sample_pages = 1; } } else { n_sample_pages = srv_stats_sample_pages; } /* We sample some pages in the index to get an estimate */ for (i = 0; i < n_sample_pages; i++) { rec_t* supremum; mtr_start(&mtr); btr_cur_open_at_rnd_pos(index, BTR_SEARCH_LEAF, &cursor, &mtr); /* Count the number of different key values for each prefix of the key on this index page. If the prefix does not determine the index record uniquely in the B-tree, then we subtract one because otherwise our algorithm would give a wrong estimate for an index where there is just one key value. */ page = btr_cur_get_page(&cursor); supremum = page_get_supremum_rec(page); rec = page_rec_get_next(page_get_infimum_rec(page)); if (rec != supremum) { not_empty_flag = 1; offsets_rec = rec_get_offsets(rec, index, offsets_rec, ULINT_UNDEFINED, &heap); } while (rec != supremum) { rec_t* next_rec = page_rec_get_next(rec); if (next_rec == supremum) { break; } matched_fields = 0; matched_bytes = 0; offsets_next_rec = rec_get_offsets(next_rec, index, offsets_next_rec, n_cols, &heap); cmp_rec_rec_with_match(rec, next_rec, offsets_rec, offsets_next_rec, index, &matched_fields, &matched_bytes); for (j = matched_fields + 1; j <= n_cols; j++) { /* We add one if this index record has a different prefix from the previous */ n_diff[j]++; } total_external_size += btr_rec_get_externally_stored_len( rec, offsets_rec); rec = next_rec; /* Initialize offsets_rec for the next round and assign the old offsets_rec buffer to offsets_next_rec. */ { ulint* offsets_tmp = offsets_rec; offsets_rec = offsets_next_rec; offsets_next_rec = offsets_tmp; } } if (n_cols == dict_index_get_n_unique_in_tree(index)) { /* If there is more than one leaf page in the tree, we add one because we know that the first record on the page certainly had a different prefix than the last record on the previous index page in the alphabetical order. Before this fix, if there was just one big record on each clustered index page, the algorithm grossly underestimated the number of rows in the table. */ if (btr_page_get_prev(page, &mtr) != FIL_NULL || btr_page_get_next(page, &mtr) != FIL_NULL) { n_diff[n_cols]++; } } offsets_rec = rec_get_offsets(rec, index, offsets_rec, ULINT_UNDEFINED, &heap); total_external_size += btr_rec_get_externally_stored_len( rec, offsets_rec); mtr_commit(&mtr); } /* If we saw k borders between different key values on n_sample_pages leaf pages, we can estimate how many there will be in index->stat_n_leaf_pages */ /* We must take into account that our sample actually represents also the pages used for external storage of fields (those pages are included in index->stat_n_leaf_pages) */ dict_index_stat_mutex_enter(index); for (j = 0; j <= n_cols; j++) { index->stat_n_diff_key_vals[j] = ((n_diff[j] * (ib_int64_t)index->stat_n_leaf_pages + n_sample_pages - 1 + total_external_size + not_empty_flag) / (n_sample_pages + total_external_size)); /* If the tree is small, smaller than 10 * n_sample_pages + total_external_size, then the above estimate is ok. For bigger trees it is common that we do not see any borders between key values in the few pages we pick. But still there may be n_sample_pages different key values, or even more. Let us try to approximate that: */ add_on = index->stat_n_leaf_pages / (10 * (n_sample_pages + total_external_size)); if (add_on > n_sample_pages) { add_on = n_sample_pages; } index->stat_n_diff_key_vals[j] += add_on; } dict_index_stat_mutex_exit(index); mem_free(n_diff); if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } } /*================== EXTERNAL STORAGE OF BIG FIELDS ===================*/ /***********************************************************//** Gets the externally stored size of a record, in units of a database page. @return externally stored part, in units of a database page */ static ulint btr_rec_get_externally_stored_len( /*==============================*/ rec_t* rec, /*!< in: record */ const ulint* offsets)/*!< in: array returned by rec_get_offsets() */ { ulint n_fields; byte* data; ulint local_len; ulint extern_len; ulint total_extern_len = 0; ulint i; ut_ad(!rec_offs_comp(offsets) || !rec_get_node_ptr_flag(rec)); n_fields = rec_offs_n_fields(offsets); for (i = 0; i < n_fields; i++) { if (rec_offs_nth_extern(offsets, i)) { data = rec_get_nth_field(rec, offsets, i, &local_len); local_len -= BTR_EXTERN_FIELD_REF_SIZE; extern_len = mach_read_from_4(data + local_len + BTR_EXTERN_LEN + 4); total_extern_len += ut_calc_align(extern_len, UNIV_PAGE_SIZE); } } return(total_extern_len / UNIV_PAGE_SIZE); } /*******************************************************************//** Sets the ownership bit of an externally stored field in a record. */ static void btr_cur_set_ownership_of_extern_field( /*==================================*/ page_zip_des_t* page_zip,/*!< in/out: compressed page whose uncompressed part will be updated, or NULL */ rec_t* rec, /*!< in/out: clustered index record */ dict_index_t* index, /*!< in: index of the page */ const ulint* offsets,/*!< in: array returned by rec_get_offsets() */ ulint i, /*!< in: field number */ ibool val, /*!< in: value to set */ mtr_t* mtr) /*!< in: mtr, or NULL if not logged */ { byte* data; ulint local_len; ulint byte_val; data = rec_get_nth_field(rec, offsets, i, &local_len); ut_a(local_len >= BTR_EXTERN_FIELD_REF_SIZE); local_len -= BTR_EXTERN_FIELD_REF_SIZE; byte_val = mach_read_from_1(data + local_len + BTR_EXTERN_LEN); if (val) { byte_val = byte_val & (~BTR_EXTERN_OWNER_FLAG); } else { byte_val = byte_val | BTR_EXTERN_OWNER_FLAG; } if (UNIV_LIKELY_NULL(page_zip)) { mach_write_to_1(data + local_len + BTR_EXTERN_LEN, byte_val); page_zip_write_blob_ptr(page_zip, rec, index, offsets, i, mtr); } else if (UNIV_LIKELY(mtr != NULL)) { mlog_write_ulint(data + local_len + BTR_EXTERN_LEN, byte_val, MLOG_1BYTE, mtr); } else { mach_write_to_1(data + local_len + BTR_EXTERN_LEN, byte_val); } } /*******************************************************************//** Marks not updated extern fields as not-owned by this record. The ownership is transferred to the updated record which is inserted elsewhere in the index tree. In purge only the owner of externally stored field is allowed to free the field. */ UNIV_INTERN void btr_cur_mark_extern_inherited_fields( /*=================================*/ page_zip_des_t* page_zip,/*!< in/out: compressed page whose uncompressed part will be updated, or NULL */ rec_t* rec, /*!< in/out: record in a clustered index */ dict_index_t* index, /*!< in: index of the page */ const ulint* offsets,/*!< in: array returned by rec_get_offsets() */ const upd_t* update, /*!< in: update vector */ mtr_t* mtr) /*!< in: mtr, or NULL if not logged */ { ulint n; ulint j; ulint i; ut_ad(rec_offs_validate(rec, NULL, offsets)); ut_ad(!rec_offs_comp(offsets) || !rec_get_node_ptr_flag(rec)); if (!rec_offs_any_extern(offsets)) { return; } n = rec_offs_n_fields(offsets); for (i = 0; i < n; i++) { if (rec_offs_nth_extern(offsets, i)) { /* Check it is not in updated fields */ if (update) { for (j = 0; j < upd_get_n_fields(update); j++) { if (upd_get_nth_field(update, j) ->field_no == i) { goto updated; } } } btr_cur_set_ownership_of_extern_field( page_zip, rec, index, offsets, i, FALSE, mtr); updated: ; } } } /*******************************************************************//** The complement of the previous function: in an update entry may inherit some externally stored fields from a record. We must mark them as inherited in entry, so that they are not freed in a rollback. */ UNIV_INTERN void btr_cur_mark_dtuple_inherited_extern( /*=================================*/ dtuple_t* entry, /*!< in/out: updated entry to be inserted to clustered index */ const upd_t* update) /*!< in: update vector */ { ulint i; for (i = 0; i < dtuple_get_n_fields(entry); i++) { dfield_t* dfield = dtuple_get_nth_field(entry, i); byte* data; ulint len; ulint j; if (!dfield_is_ext(dfield)) { continue; } /* Check if it is in updated fields */ for (j = 0; j < upd_get_n_fields(update); j++) { if (upd_get_nth_field(update, j)->field_no == i) { goto is_updated; } } data = dfield_get_data(dfield); len = dfield_get_len(dfield); data[len - BTR_EXTERN_FIELD_REF_SIZE + BTR_EXTERN_LEN] |= BTR_EXTERN_INHERITED_FLAG; is_updated: ; } } /*******************************************************************//** Marks all extern fields in a record as owned by the record. This function should be called if the delete mark of a record is removed: a not delete marked record always owns all its extern fields. */ static void btr_cur_unmark_extern_fields( /*=========================*/ page_zip_des_t* page_zip,/*!< in/out: compressed page whose uncompressed part will be updated, or NULL */ rec_t* rec, /*!< in/out: record in a clustered index */ dict_index_t* index, /*!< in: index of the page */ const ulint* offsets,/*!< in: array returned by rec_get_offsets() */ mtr_t* mtr) /*!< in: mtr, or NULL if not logged */ { ulint n; ulint i; ut_ad(!rec_offs_comp(offsets) || !rec_get_node_ptr_flag(rec)); n = rec_offs_n_fields(offsets); if (!rec_offs_any_extern(offsets)) { return; } for (i = 0; i < n; i++) { if (rec_offs_nth_extern(offsets, i)) { btr_cur_set_ownership_of_extern_field( page_zip, rec, index, offsets, i, TRUE, mtr); } } } /*******************************************************************//** Marks all extern fields in a dtuple as owned by the record. */ UNIV_INTERN void btr_cur_unmark_dtuple_extern_fields( /*================================*/ dtuple_t* entry) /*!< in/out: clustered index entry */ { ulint i; for (i = 0; i < dtuple_get_n_fields(entry); i++) { dfield_t* dfield = dtuple_get_nth_field(entry, i); if (dfield_is_ext(dfield)) { byte* data = dfield_get_data(dfield); ulint len = dfield_get_len(dfield); data[len - BTR_EXTERN_FIELD_REF_SIZE + BTR_EXTERN_LEN] &= ~BTR_EXTERN_OWNER_FLAG; } } } /*******************************************************************//** Flags the data tuple fields that are marked as extern storage in the update vector. We use this function to remember which fields we must mark as extern storage in a record inserted for an update. @return number of flagged external columns */ UNIV_INTERN ulint btr_push_update_extern_fields( /*==========================*/ dtuple_t* tuple, /*!< in/out: data tuple */ const upd_t* update, /*!< in: update vector */ mem_heap_t* heap) /*!< in: memory heap */ { ulint n_pushed = 0; ulint n; const upd_field_t* uf; ut_ad(tuple); ut_ad(update); uf = update->fields; n = upd_get_n_fields(update); for (; n--; uf++) { if (dfield_is_ext(&uf->new_val)) { dfield_t* field = dtuple_get_nth_field(tuple, uf->field_no); if (!dfield_is_ext(field)) { dfield_set_ext(field); n_pushed++; } switch (uf->orig_len) { byte* data; ulint len; byte* buf; case 0: break; case BTR_EXTERN_FIELD_REF_SIZE: /* Restore the original locally stored part of the column. In the undo log, InnoDB writes a longer prefix of externally stored columns, so that column prefixes in secondary indexes can be reconstructed. */ dfield_set_data(field, (byte*) dfield_get_data(field) + dfield_get_len(field) - BTR_EXTERN_FIELD_REF_SIZE, BTR_EXTERN_FIELD_REF_SIZE); dfield_set_ext(field); break; default: /* Reconstruct the original locally stored part of the column. The data will have to be copied. */ ut_a(uf->orig_len > BTR_EXTERN_FIELD_REF_SIZE); data = dfield_get_data(field); len = dfield_get_len(field); buf = mem_heap_alloc(heap, uf->orig_len); /* Copy the locally stored prefix. */ memcpy(buf, data, uf->orig_len - BTR_EXTERN_FIELD_REF_SIZE); /* Copy the BLOB pointer. */ memcpy(buf + uf->orig_len - BTR_EXTERN_FIELD_REF_SIZE, data + len - BTR_EXTERN_FIELD_REF_SIZE, BTR_EXTERN_FIELD_REF_SIZE); dfield_set_data(field, buf, uf->orig_len); dfield_set_ext(field); } } } return(n_pushed); } /*******************************************************************//** Returns the length of a BLOB part stored on the header page. @return part length */ static ulint btr_blob_get_part_len( /*==================*/ const byte* blob_header) /*!< in: blob header */ { return(mach_read_from_4(blob_header + BTR_BLOB_HDR_PART_LEN)); } /*******************************************************************//** Returns the page number where the next BLOB part is stored. @return page number or FIL_NULL if no more pages */ static ulint btr_blob_get_next_page_no( /*======================*/ const byte* blob_header) /*!< in: blob header */ { return(mach_read_from_4(blob_header + BTR_BLOB_HDR_NEXT_PAGE_NO)); } /*******************************************************************//** Deallocate a buffer block that was reserved for a BLOB part. */ static void btr_blob_free( /*==========*/ buf_block_t* block, /*!< in: buffer block */ ibool all, /*!< in: TRUE=remove also the compressed page if there is one */ mtr_t* mtr) /*!< in: mini-transaction to commit */ { ulint space = buf_block_get_space(block); ulint page_no = buf_block_get_page_no(block); ut_ad(mtr_memo_contains(mtr, block, MTR_MEMO_PAGE_X_FIX)); mtr_commit(mtr); buf_pool_mutex_enter(); mutex_enter(&block->mutex); /* Only free the block if it is still allocated to the same file page. */ if (buf_block_get_state(block) == BUF_BLOCK_FILE_PAGE && buf_block_get_space(block) == space && buf_block_get_page_no(block) == page_no) { if (buf_LRU_free_block(&block->page, all, NULL) != BUF_LRU_FREED && all && block->page.zip.data ) { /* Attempt to deallocate the uncompressed page if the whole block cannot be deallocted. */ buf_LRU_free_block(&block->page, FALSE, NULL); } } buf_pool_mutex_exit(); mutex_exit(&block->mutex); } /*******************************************************************//** Stores the fields in big_rec_vec to the tablespace and puts pointers to them in rec. The extern flags in rec will have to be set beforehand. The fields are stored on pages allocated from leaf node file segment of the index tree. @return DB_SUCCESS or error */ UNIV_INTERN ulint btr_store_big_rec_extern_fields( /*============================*/ dict_index_t* index, /*!< in: index of rec; the index tree MUST be X-latched */ buf_block_t* rec_block, /*!< in/out: block containing rec */ rec_t* rec, /*!< in/out: record */ const ulint* offsets, /*!< in: rec_get_offsets(rec, index); the "external storage" flags in offsets will not correspond to rec when this function returns */ big_rec_t* big_rec_vec, /*!< in: vector containing fields to be stored externally */ mtr_t* local_mtr __attribute__((unused))) /*!< in: mtr containing the latch to rec and to the tree */ { ulint rec_page_no; byte* field_ref; ulint extern_len; ulint store_len; ulint page_no; ulint space_id; ulint zip_size; ulint prev_page_no; ulint hint_page_no; ulint i; mtr_t mtr; page_zip_des_t* page_zip; mem_heap_t* heap = NULL; z_stream c_stream; ut_ad(rec_offs_validate(rec, index, offsets)); ut_ad(mtr_memo_contains(local_mtr, dict_index_get_lock(index), MTR_MEMO_X_LOCK)); ut_ad(mtr_memo_contains(local_mtr, rec_block, MTR_MEMO_PAGE_X_FIX)); ut_ad(buf_block_get_frame(rec_block) == page_align(rec)); ut_a(dict_index_is_clust(index)); page_zip = buf_block_get_page_zip(rec_block); ut_a(dict_table_zip_size(index->table) == buf_block_get_zip_size(rec_block)); space_id = buf_block_get_space(rec_block); zip_size = buf_block_get_zip_size(rec_block); rec_page_no = buf_block_get_page_no(rec_block); ut_a(fil_page_get_type(page_align(rec)) == FIL_PAGE_INDEX); if (UNIV_LIKELY_NULL(page_zip)) { int err; /* Zlib deflate needs 128 kilobytes for the default window size, plus 512 << memLevel, plus a few kilobytes for small objects. We use reduced memLevel to limit the memory consumption, and preallocate the heap, hoping to avoid memory fragmentation. */ heap = mem_heap_create(250000); page_zip_set_alloc(&c_stream, heap); err = deflateInit2(&c_stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15, 7, Z_DEFAULT_STRATEGY); ut_a(err == Z_OK); } /* We have to create a file segment to the tablespace for each field and put the pointer to the field in rec */ for (i = 0; i < big_rec_vec->n_fields; i++) { ut_ad(rec_offs_nth_extern(offsets, big_rec_vec->fields[i].field_no)); { ulint local_len; field_ref = rec_get_nth_field( rec, offsets, big_rec_vec->fields[i].field_no, &local_len); ut_a(local_len >= BTR_EXTERN_FIELD_REF_SIZE); local_len -= BTR_EXTERN_FIELD_REF_SIZE; field_ref += local_len; } extern_len = big_rec_vec->fields[i].len; ut_a(extern_len > 0); prev_page_no = FIL_NULL; if (UNIV_LIKELY_NULL(page_zip)) { int err = deflateReset(&c_stream); ut_a(err == Z_OK); c_stream.next_in = (void*) big_rec_vec->fields[i].data; c_stream.avail_in = extern_len; } for (;;) { buf_block_t* block; page_t* page; mtr_start(&mtr); if (prev_page_no == FIL_NULL) { hint_page_no = 1 + rec_page_no; } else { hint_page_no = prev_page_no + 1; } block = btr_page_alloc(index, hint_page_no, FSP_NO_DIR, 0, &mtr); if (UNIV_UNLIKELY(block == NULL)) { mtr_commit(&mtr); if (UNIV_LIKELY_NULL(page_zip)) { deflateEnd(&c_stream); mem_heap_free(heap); } return(DB_OUT_OF_FILE_SPACE); } page_no = buf_block_get_page_no(block); page = buf_block_get_frame(block); if (prev_page_no != FIL_NULL) { buf_block_t* prev_block; page_t* prev_page; prev_block = buf_page_get(space_id, zip_size, prev_page_no, RW_X_LATCH, &mtr); buf_block_dbg_add_level(prev_block, SYNC_EXTERN_STORAGE); prev_page = buf_block_get_frame(prev_block); if (UNIV_LIKELY_NULL(page_zip)) { mlog_write_ulint( prev_page + FIL_PAGE_NEXT, page_no, MLOG_4BYTES, &mtr); memcpy(buf_block_get_page_zip( prev_block) ->data + FIL_PAGE_NEXT, prev_page + FIL_PAGE_NEXT, 4); } else { mlog_write_ulint( prev_page + FIL_PAGE_DATA + BTR_BLOB_HDR_NEXT_PAGE_NO, page_no, MLOG_4BYTES, &mtr); } } if (UNIV_LIKELY_NULL(page_zip)) { int err; page_zip_des_t* blob_page_zip; /* Write FIL_PAGE_TYPE to the redo log separately, before logging any other changes to the page, so that the debug assertions in recv_parse_or_apply_log_rec_body() can be made simpler. Before InnoDB Plugin 1.0.4, the initialization of FIL_PAGE_TYPE was logged as part of the mlog_log_string() below. */ mlog_write_ulint(page + FIL_PAGE_TYPE, prev_page_no == FIL_NULL ? FIL_PAGE_TYPE_ZBLOB : FIL_PAGE_TYPE_ZBLOB2, MLOG_2BYTES, &mtr); c_stream.next_out = page + FIL_PAGE_DATA; c_stream.avail_out = page_zip_get_size(page_zip) - FIL_PAGE_DATA; err = deflate(&c_stream, Z_FINISH); ut_a(err == Z_OK || err == Z_STREAM_END); ut_a(err == Z_STREAM_END || c_stream.avail_out == 0); /* Write the "next BLOB page" pointer */ mlog_write_ulint(page + FIL_PAGE_NEXT, FIL_NULL, MLOG_4BYTES, &mtr); /* Initialize the unused "prev page" pointer */ mlog_write_ulint(page + FIL_PAGE_PREV, FIL_NULL, MLOG_4BYTES, &mtr); /* Write a back pointer to the record into the otherwise unused area. This information could be useful in debugging. Later, we might want to implement the possibility to relocate BLOB pages. Then, we would need to be able to adjust the BLOB pointer in the record. We do not store the heap number of the record, because it can change in page_zip_reorganize() or btr_page_reorganize(). However, also the page number of the record may change when B-tree nodes are split or merged. */ mlog_write_ulint(page + FIL_PAGE_FILE_FLUSH_LSN, space_id, MLOG_4BYTES, &mtr); mlog_write_ulint(page + FIL_PAGE_FILE_FLUSH_LSN + 4, rec_page_no, MLOG_4BYTES, &mtr); /* Zero out the unused part of the page. */ memset(page + page_zip_get_size(page_zip) - c_stream.avail_out, 0, c_stream.avail_out); mlog_log_string(page + FIL_PAGE_FILE_FLUSH_LSN, page_zip_get_size(page_zip) - FIL_PAGE_FILE_FLUSH_LSN, &mtr); /* Copy the page to compressed storage, because it will be flushed to disk from there. */ blob_page_zip = buf_block_get_page_zip(block); ut_ad(blob_page_zip); ut_ad(page_zip_get_size(blob_page_zip) == page_zip_get_size(page_zip)); memcpy(blob_page_zip->data, page, page_zip_get_size(page_zip)); if (err == Z_OK && prev_page_no != FIL_NULL) { goto next_zip_page; } rec_block = buf_page_get(space_id, zip_size, rec_page_no, RW_X_LATCH, &mtr); buf_block_dbg_add_level(rec_block, SYNC_NO_ORDER_CHECK); if (err == Z_STREAM_END) { mach_write_to_4(field_ref + BTR_EXTERN_LEN, 0); mach_write_to_4(field_ref + BTR_EXTERN_LEN + 4, c_stream.total_in); } else { memset(field_ref + BTR_EXTERN_LEN, 0, 8); } if (prev_page_no == FIL_NULL) { mach_write_to_4(field_ref + BTR_EXTERN_SPACE_ID, space_id); mach_write_to_4(field_ref + BTR_EXTERN_PAGE_NO, page_no); mach_write_to_4(field_ref + BTR_EXTERN_OFFSET, FIL_PAGE_NEXT); } page_zip_write_blob_ptr( page_zip, rec, index, offsets, big_rec_vec->fields[i].field_no, &mtr); next_zip_page: prev_page_no = page_no; /* Commit mtr and release the uncompressed page frame to save memory. */ btr_blob_free(block, FALSE, &mtr); if (err == Z_STREAM_END) { break; } } else { mlog_write_ulint(page + FIL_PAGE_TYPE, FIL_PAGE_TYPE_BLOB, MLOG_2BYTES, &mtr); if (extern_len > (UNIV_PAGE_SIZE - FIL_PAGE_DATA - BTR_BLOB_HDR_SIZE - FIL_PAGE_DATA_END)) { store_len = UNIV_PAGE_SIZE - FIL_PAGE_DATA - BTR_BLOB_HDR_SIZE - FIL_PAGE_DATA_END; } else { store_len = extern_len; } mlog_write_string(page + FIL_PAGE_DATA + BTR_BLOB_HDR_SIZE, (const byte*) big_rec_vec->fields[i].data + big_rec_vec->fields[i].len - extern_len, store_len, &mtr); mlog_write_ulint(page + FIL_PAGE_DATA + BTR_BLOB_HDR_PART_LEN, store_len, MLOG_4BYTES, &mtr); mlog_write_ulint(page + FIL_PAGE_DATA + BTR_BLOB_HDR_NEXT_PAGE_NO, FIL_NULL, MLOG_4BYTES, &mtr); extern_len -= store_len; rec_block = buf_page_get(space_id, zip_size, rec_page_no, RW_X_LATCH, &mtr); buf_block_dbg_add_level(rec_block, SYNC_NO_ORDER_CHECK); mlog_write_ulint(field_ref + BTR_EXTERN_LEN, 0, MLOG_4BYTES, &mtr); mlog_write_ulint(field_ref + BTR_EXTERN_LEN + 4, big_rec_vec->fields[i].len - extern_len, MLOG_4BYTES, &mtr); if (prev_page_no == FIL_NULL) { mlog_write_ulint(field_ref + BTR_EXTERN_SPACE_ID, space_id, MLOG_4BYTES, &mtr); mlog_write_ulint(field_ref + BTR_EXTERN_PAGE_NO, page_no, MLOG_4BYTES, &mtr); mlog_write_ulint(field_ref + BTR_EXTERN_OFFSET, FIL_PAGE_DATA, MLOG_4BYTES, &mtr); } prev_page_no = page_no; mtr_commit(&mtr); if (extern_len == 0) { break; } } } } if (UNIV_LIKELY_NULL(page_zip)) { deflateEnd(&c_stream); mem_heap_free(heap); } return(DB_SUCCESS); } /*******************************************************************//** Check the FIL_PAGE_TYPE on an uncompressed BLOB page. */ static void btr_check_blob_fil_page_type( /*=========================*/ ulint space_id, /*!< in: space id */ ulint page_no, /*!< in: page number */ const page_t* page, /*!< in: page */ ibool read) /*!< in: TRUE=read, FALSE=purge */ { ulint type = fil_page_get_type(page); ut_a(space_id == page_get_space_id(page)); ut_a(page_no == page_get_page_no(page)); if (UNIV_UNLIKELY(type != FIL_PAGE_TYPE_BLOB)) { ulint flags = fil_space_get_flags(space_id); if (UNIV_LIKELY ((flags & DICT_TF_FORMAT_MASK) == DICT_TF_FORMAT_51)) { /* Old versions of InnoDB did not initialize FIL_PAGE_TYPE on BLOB pages. Do not print anything about the type mismatch when reading a BLOB page that is in Antelope format.*/ return; } ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: FIL_PAGE_TYPE=%lu" " on BLOB %s space %lu page %lu flags %lx\n", (ulong) type, read ? "read" : "purge", (ulong) space_id, (ulong) page_no, (ulong) flags); ut_error; } } /*******************************************************************//** Frees the space in an externally stored field to the file space management if the field in data is owned by the externally stored field, in a rollback we may have the additional condition that the field must not be inherited. */ UNIV_INTERN void btr_free_externally_stored_field( /*=============================*/ dict_index_t* index, /*!< in: index of the data, the index tree MUST be X-latched; if the tree height is 1, then also the root page must be X-latched! (this is relevant in the case this function is called from purge where 'data' is located on an undo log page, not an index page) */ byte* field_ref, /*!< in/out: field reference */ const rec_t* rec, /*!< in: record containing field_ref, for page_zip_write_blob_ptr(), or NULL */ const ulint* offsets, /*!< in: rec_get_offsets(rec, index), or NULL */ page_zip_des_t* page_zip, /*!< in: compressed page corresponding to rec, or NULL if rec == NULL */ ulint i, /*!< in: field number of field_ref; ignored if rec == NULL */ enum trx_rb_ctx rb_ctx, /*!< in: rollback context */ mtr_t* local_mtr __attribute__((unused))) /*!< in: mtr containing the latch to data an an X-latch to the index tree */ { page_t* page; ulint space_id; ulint rec_zip_size = dict_table_zip_size(index->table); ulint ext_zip_size; ulint page_no; ulint next_page_no; mtr_t mtr; #ifdef UNIV_DEBUG ut_ad(mtr_memo_contains(local_mtr, dict_index_get_lock(index), MTR_MEMO_X_LOCK)); ut_ad(mtr_memo_contains_page(local_mtr, field_ref, MTR_MEMO_PAGE_X_FIX)); ut_ad(!rec || rec_offs_validate(rec, index, offsets)); if (rec) { ulint local_len; const byte* f = rec_get_nth_field(rec, offsets, i, &local_len); ut_a(local_len >= BTR_EXTERN_FIELD_REF_SIZE); local_len -= BTR_EXTERN_FIELD_REF_SIZE; f += local_len; ut_ad(f == field_ref); } #endif /* UNIV_DEBUG */ if (UNIV_UNLIKELY(!memcmp(field_ref, field_ref_zero, BTR_EXTERN_FIELD_REF_SIZE))) { /* In the rollback of uncommitted transactions, we may encounter a clustered index record whose BLOBs have not been written. There is nothing to free then. */ ut_a(rb_ctx == RB_RECOVERY || rb_ctx == RB_RECOVERY_PURGE_REC); return; } space_id = mach_read_from_4(field_ref + BTR_EXTERN_SPACE_ID); if (UNIV_UNLIKELY(space_id != dict_index_get_space(index))) { ext_zip_size = fil_space_get_zip_size(space_id); /* This must be an undo log record in the system tablespace, that is, in row_purge_upd_exist_or_extern(). Currently, externally stored records are stored in the same tablespace as the referring records. */ ut_ad(!page_get_space_id(page_align(field_ref))); ut_ad(!rec); ut_ad(!page_zip); } else { ext_zip_size = rec_zip_size; } if (!rec) { /* This is a call from row_purge_upd_exist_or_extern(). */ ut_ad(!page_zip); rec_zip_size = 0; } for (;;) { buf_block_t* rec_block; buf_block_t* ext_block; mtr_start(&mtr); rec_block = buf_page_get(page_get_space_id( page_align(field_ref)), rec_zip_size, page_get_page_no( page_align(field_ref)), RW_X_LATCH, &mtr); buf_block_dbg_add_level(rec_block, SYNC_NO_ORDER_CHECK); page_no = mach_read_from_4(field_ref + BTR_EXTERN_PAGE_NO); if (/* There is no external storage data */ page_no == FIL_NULL /* This field does not own the externally stored field */ || (mach_read_from_1(field_ref + BTR_EXTERN_LEN) & BTR_EXTERN_OWNER_FLAG) /* Rollback and inherited field */ || ((rb_ctx == RB_NORMAL || rb_ctx == RB_RECOVERY) && (mach_read_from_1(field_ref + BTR_EXTERN_LEN) & BTR_EXTERN_INHERITED_FLAG))) { /* Do not free */ mtr_commit(&mtr); return; } ext_block = buf_page_get(space_id, ext_zip_size, page_no, RW_X_LATCH, &mtr); buf_block_dbg_add_level(ext_block, SYNC_EXTERN_STORAGE); page = buf_block_get_frame(ext_block); if (ext_zip_size) { /* Note that page_zip will be NULL in row_purge_upd_exist_or_extern(). */ switch (fil_page_get_type(page)) { case FIL_PAGE_TYPE_ZBLOB: case FIL_PAGE_TYPE_ZBLOB2: break; default: ut_error; } next_page_no = mach_read_from_4(page + FIL_PAGE_NEXT); btr_page_free_low(index, ext_block, 0, &mtr); if (UNIV_LIKELY(page_zip != NULL)) { mach_write_to_4(field_ref + BTR_EXTERN_PAGE_NO, next_page_no); mach_write_to_4(field_ref + BTR_EXTERN_LEN + 4, 0); page_zip_write_blob_ptr(page_zip, rec, index, offsets, i, &mtr); } else { mlog_write_ulint(field_ref + BTR_EXTERN_PAGE_NO, next_page_no, MLOG_4BYTES, &mtr); mlog_write_ulint(field_ref + BTR_EXTERN_LEN + 4, 0, MLOG_4BYTES, &mtr); } } else { ut_a(!page_zip); btr_check_blob_fil_page_type(space_id, page_no, page, FALSE); next_page_no = mach_read_from_4( page + FIL_PAGE_DATA + BTR_BLOB_HDR_NEXT_PAGE_NO); /* We must supply the page level (= 0) as an argument because we did not store it on the page (we save the space overhead from an index page header. */ btr_page_free_low(index, ext_block, 0, &mtr); mlog_write_ulint(field_ref + BTR_EXTERN_PAGE_NO, next_page_no, MLOG_4BYTES, &mtr); /* Zero out the BLOB length. If the server crashes during the execution of this function, trx_rollback_or_clean_all_recovered() could dereference the half-deleted BLOB, fetching a wrong prefix for the BLOB. */ mlog_write_ulint(field_ref + BTR_EXTERN_LEN + 4, 0, MLOG_4BYTES, &mtr); } /* Commit mtr and release the BLOB block to save memory. */ btr_blob_free(ext_block, TRUE, &mtr); } } /***********************************************************//** Frees the externally stored fields for a record. */ static void btr_rec_free_externally_stored_fields( /*==================================*/ dict_index_t* index, /*!< in: index of the data, the index tree MUST be X-latched */ rec_t* rec, /*!< in/out: record */ const ulint* offsets,/*!< in: rec_get_offsets(rec, index) */ page_zip_des_t* page_zip,/*!< in: compressed page whose uncompressed part will be updated, or NULL */ enum trx_rb_ctx rb_ctx, /*!< in: rollback context */ mtr_t* mtr) /*!< in: mini-transaction handle which contains an X-latch to record page and to the index tree */ { ulint n_fields; ulint i; ut_ad(rec_offs_validate(rec, index, offsets)); ut_ad(mtr_memo_contains_page(mtr, rec, MTR_MEMO_PAGE_X_FIX)); /* Free possible externally stored fields in the record */ ut_ad(dict_table_is_comp(index->table) == !!rec_offs_comp(offsets)); n_fields = rec_offs_n_fields(offsets); for (i = 0; i < n_fields; i++) { if (rec_offs_nth_extern(offsets, i)) { ulint len; byte* data = rec_get_nth_field(rec, offsets, i, &len); ut_a(len >= BTR_EXTERN_FIELD_REF_SIZE); btr_free_externally_stored_field( index, data + len - BTR_EXTERN_FIELD_REF_SIZE, rec, offsets, page_zip, i, rb_ctx, mtr); } } } /***********************************************************//** Frees the externally stored fields for a record, if the field is mentioned in the update vector. */ static void btr_rec_free_updated_extern_fields( /*===============================*/ dict_index_t* index, /*!< in: index of rec; the index tree MUST be X-latched */ rec_t* rec, /*!< in/out: record */ page_zip_des_t* page_zip,/*!< in: compressed page whose uncompressed part will be updated, or NULL */ const ulint* offsets,/*!< in: rec_get_offsets(rec, index) */ const upd_t* update, /*!< in: update vector */ enum trx_rb_ctx rb_ctx, /*!< in: rollback context */ mtr_t* mtr) /*!< in: mini-transaction handle which contains an X-latch to record page and to the tree */ { ulint n_fields; ulint i; ut_ad(rec_offs_validate(rec, index, offsets)); ut_ad(mtr_memo_contains_page(mtr, rec, MTR_MEMO_PAGE_X_FIX)); /* Free possible externally stored fields in the record */ n_fields = upd_get_n_fields(update); for (i = 0; i < n_fields; i++) { const upd_field_t* ufield = upd_get_nth_field(update, i); if (rec_offs_nth_extern(offsets, ufield->field_no)) { ulint len; byte* data = rec_get_nth_field( rec, offsets, ufield->field_no, &len); ut_a(len >= BTR_EXTERN_FIELD_REF_SIZE); btr_free_externally_stored_field( index, data + len - BTR_EXTERN_FIELD_REF_SIZE, rec, offsets, page_zip, ufield->field_no, rb_ctx, mtr); } } } /*******************************************************************//** Copies the prefix of an uncompressed BLOB. The clustered index record that points to this BLOB must be protected by a lock or a page latch. @return number of bytes written to buf */ static ulint btr_copy_blob_prefix( /*=================*/ byte* buf, /*!< out: the externally stored part of the field, or a prefix of it */ ulint len, /*!< in: length of buf, in bytes */ ulint space_id,/*!< in: space id of the BLOB pages */ ulint page_no,/*!< in: page number of the first BLOB page */ ulint offset) /*!< in: offset on the first BLOB page */ { ulint copied_len = 0; for (;;) { mtr_t mtr; buf_block_t* block; const page_t* page; const byte* blob_header; ulint part_len; ulint copy_len; mtr_start(&mtr); block = buf_page_get(space_id, 0, page_no, RW_S_LATCH, &mtr); buf_block_dbg_add_level(block, SYNC_EXTERN_STORAGE); page = buf_block_get_frame(block); btr_check_blob_fil_page_type(space_id, page_no, page, TRUE); blob_header = page + offset; part_len = btr_blob_get_part_len(blob_header); copy_len = ut_min(part_len, len - copied_len); memcpy(buf + copied_len, blob_header + BTR_BLOB_HDR_SIZE, copy_len); copied_len += copy_len; page_no = btr_blob_get_next_page_no(blob_header); mtr_commit(&mtr); if (page_no == FIL_NULL || copy_len != part_len) { return(copied_len); } /* On other BLOB pages except the first the BLOB header always is at the page data start: */ offset = FIL_PAGE_DATA; ut_ad(copied_len <= len); } } /*******************************************************************//** Copies the prefix of a compressed BLOB. The clustered index record that points to this BLOB must be protected by a lock or a page latch. */ static void btr_copy_zblob_prefix( /*==================*/ z_stream* d_stream,/*!< in/out: the decompressing stream */ ulint zip_size,/*!< in: compressed BLOB page size */ ulint space_id,/*!< in: space id of the BLOB pages */ ulint page_no,/*!< in: page number of the first BLOB page */ ulint offset) /*!< in: offset on the first BLOB page */ { ulint page_type = FIL_PAGE_TYPE_ZBLOB; ut_ad(ut_is_2pow(zip_size)); ut_ad(zip_size >= PAGE_ZIP_MIN_SIZE); ut_ad(zip_size <= UNIV_PAGE_SIZE); ut_ad(space_id); for (;;) { buf_page_t* bpage; int err; ulint next_page_no; /* There is no latch on bpage directly. Instead, bpage is protected by the B-tree page latch that is being held on the clustered index record, or, in row_merge_copy_blobs(), by an exclusive table lock. */ bpage = buf_page_get_zip(space_id, zip_size, page_no); if (UNIV_UNLIKELY(!bpage)) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Cannot load" " compressed BLOB" " page %lu space %lu\n", (ulong) page_no, (ulong) space_id); return; } if (UNIV_UNLIKELY (fil_page_get_type(bpage->zip.data) != page_type)) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Unexpected type %lu of" " compressed BLOB" " page %lu space %lu\n", (ulong) fil_page_get_type(bpage->zip.data), (ulong) page_no, (ulong) space_id); goto end_of_blob; } next_page_no = mach_read_from_4(bpage->zip.data + offset); if (UNIV_LIKELY(offset == FIL_PAGE_NEXT)) { /* When the BLOB begins at page header, the compressed data payload does not immediately follow the next page pointer. */ offset = FIL_PAGE_DATA; } else { offset += 4; } d_stream->next_in = bpage->zip.data + offset; d_stream->avail_in = zip_size - offset; err = inflate(d_stream, Z_NO_FLUSH); switch (err) { case Z_OK: if (!d_stream->avail_out) { goto end_of_blob; } break; case Z_STREAM_END: if (next_page_no == FIL_NULL) { goto end_of_blob; } /* fall through */ default: inflate_error: ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: inflate() of" " compressed BLOB" " page %lu space %lu returned %d (%s)\n", (ulong) page_no, (ulong) space_id, err, d_stream->msg); case Z_BUF_ERROR: goto end_of_blob; } if (next_page_no == FIL_NULL) { if (!d_stream->avail_in) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: unexpected end of" " compressed BLOB" " page %lu space %lu\n", (ulong) page_no, (ulong) space_id); } else { err = inflate(d_stream, Z_FINISH); switch (err) { case Z_STREAM_END: case Z_BUF_ERROR: break; default: goto inflate_error; } } end_of_blob: buf_page_release_zip(bpage); return; } buf_page_release_zip(bpage); /* On other BLOB pages except the first the BLOB header always is at the page header: */ page_no = next_page_no; offset = FIL_PAGE_NEXT; page_type = FIL_PAGE_TYPE_ZBLOB2; } } /*******************************************************************//** Copies the prefix of an externally stored field of a record. The clustered index record that points to this BLOB must be protected by a lock or a page latch. @return number of bytes written to buf */ static ulint btr_copy_externally_stored_field_prefix_low( /*========================================*/ byte* buf, /*!< out: the externally stored part of the field, or a prefix of it */ ulint len, /*!< in: length of buf, in bytes */ ulint zip_size,/*!< in: nonzero=compressed BLOB page size, zero for uncompressed BLOBs */ ulint space_id,/*!< in: space id of the first BLOB page */ ulint page_no,/*!< in: page number of the first BLOB page */ ulint offset) /*!< in: offset on the first BLOB page */ { if (UNIV_UNLIKELY(len == 0)) { return(0); } if (UNIV_UNLIKELY(zip_size)) { int err; z_stream d_stream; mem_heap_t* heap; /* Zlib inflate needs 32 kilobytes for the default window size, plus a few kilobytes for small objects. */ heap = mem_heap_create(40000); page_zip_set_alloc(&d_stream, heap); err = inflateInit(&d_stream); ut_a(err == Z_OK); d_stream.next_out = buf; d_stream.avail_out = len; d_stream.avail_in = 0; btr_copy_zblob_prefix(&d_stream, zip_size, space_id, page_no, offset); inflateEnd(&d_stream); mem_heap_free(heap); return(d_stream.total_out); } else { return(btr_copy_blob_prefix(buf, len, space_id, page_no, offset)); } } /*******************************************************************//** Copies the prefix of an externally stored field of a record. The clustered index record must be protected by a lock or a page latch. @return the length of the copied field, or 0 if the column was being or has been deleted */ UNIV_INTERN ulint btr_copy_externally_stored_field_prefix( /*====================================*/ byte* buf, /*!< out: the field, or a prefix of it */ ulint len, /*!< in: length of buf, in bytes */ ulint zip_size,/*!< in: nonzero=compressed BLOB page size, zero for uncompressed BLOBs */ const byte* data, /*!< in: 'internally' stored part of the field containing also the reference to the external part; must be protected by a lock or a page latch */ ulint local_len)/*!< in: length of data, in bytes */ { ulint space_id; ulint page_no; ulint offset; ut_a(local_len >= BTR_EXTERN_FIELD_REF_SIZE); local_len -= BTR_EXTERN_FIELD_REF_SIZE; if (UNIV_UNLIKELY(local_len >= len)) { memcpy(buf, data, len); return(len); } memcpy(buf, data, local_len); data += local_len; ut_a(memcmp(data, field_ref_zero, BTR_EXTERN_FIELD_REF_SIZE)); if (!mach_read_from_4(data + BTR_EXTERN_LEN + 4)) { /* The externally stored part of the column has been (partially) deleted. Signal the half-deleted BLOB to the caller. */ return(0); } space_id = mach_read_from_4(data + BTR_EXTERN_SPACE_ID); page_no = mach_read_from_4(data + BTR_EXTERN_PAGE_NO); offset = mach_read_from_4(data + BTR_EXTERN_OFFSET); return(local_len + btr_copy_externally_stored_field_prefix_low(buf + local_len, len - local_len, zip_size, space_id, page_no, offset)); } /*******************************************************************//** Copies an externally stored field of a record to mem heap. The clustered index record must be protected by a lock or a page latch. @return the whole field copied to heap */ static byte* btr_copy_externally_stored_field( /*=============================*/ ulint* len, /*!< out: length of the whole field */ const byte* data, /*!< in: 'internally' stored part of the field containing also the reference to the external part; must be protected by a lock or a page latch */ ulint zip_size,/*!< in: nonzero=compressed BLOB page size, zero for uncompressed BLOBs */ ulint local_len,/*!< in: length of data */ mem_heap_t* heap) /*!< in: mem heap */ { ulint space_id; ulint page_no; ulint offset; ulint extern_len; byte* buf; ut_a(local_len >= BTR_EXTERN_FIELD_REF_SIZE); local_len -= BTR_EXTERN_FIELD_REF_SIZE; space_id = mach_read_from_4(data + local_len + BTR_EXTERN_SPACE_ID); page_no = mach_read_from_4(data + local_len + BTR_EXTERN_PAGE_NO); offset = mach_read_from_4(data + local_len + BTR_EXTERN_OFFSET); /* Currently a BLOB cannot be bigger than 4 GB; we leave the 4 upper bytes in the length field unused */ extern_len = mach_read_from_4(data + local_len + BTR_EXTERN_LEN + 4); buf = mem_heap_alloc(heap, local_len + extern_len); memcpy(buf, data, local_len); *len = local_len + btr_copy_externally_stored_field_prefix_low(buf + local_len, extern_len, zip_size, space_id, page_no, offset); return(buf); } /*******************************************************************//** Copies an externally stored field of a record to mem heap. @return the field copied to heap */ UNIV_INTERN byte* btr_rec_copy_externally_stored_field( /*=================================*/ const rec_t* rec, /*!< in: record in a clustered index; must be protected by a lock or a page latch */ const ulint* offsets,/*!< in: array returned by rec_get_offsets() */ ulint zip_size,/*!< in: nonzero=compressed BLOB page size, zero for uncompressed BLOBs */ ulint no, /*!< in: field number */ ulint* len, /*!< out: length of the field */ mem_heap_t* heap) /*!< in: mem heap */ { ulint local_len; const byte* data; ut_a(rec_offs_nth_extern(offsets, no)); /* An externally stored field can contain some initial data from the field, and in the last 20 bytes it has the space id, page number, and offset where the rest of the field data is stored, and the data length in addition to the data stored locally. We may need to store some data locally to get the local record length above the 128 byte limit so that field offsets are stored in two bytes, and the extern bit is available in those two bytes. */ data = rec_get_nth_field(rec, offsets, no, &local_len); return(btr_copy_externally_stored_field(len, data, zip_size, local_len, heap)); } #endif /* !UNIV_HOTBACKUP */ haildb-2.3.2/btr/btr0sea.c0000644000175000017500000014156311513177357016150 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. Copyright (c) 2008, Google Inc. Portions of this file contain modifications contributed and copyrighted by Google, Inc. Those modifications are gratefully acknowledged and are described briefly in the InnoDB documentation. The contributions by Google are incorporated with their permission, and subject to the conditions contained in the file COPYING.Google. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /********************************************************************//** @file btr/btr0sea.c The index tree adaptive search Created 2/17/1996 Heikki Tuuri *************************************************************************/ #include "btr0sea.h" #ifdef UNIV_NONINL #include "btr0sea.ic" #endif #include "buf0buf.h" #include "page0page.h" #include "page0cur.h" #include "btr0cur.h" #include "btr0pcur.h" #include "btr0btr.h" #include "ha0ha.h" /** Flag: has the search system been enabled? Protected by btr_search_latch and btr_search_enabled_mutex. */ UNIV_INTERN char btr_search_enabled = TRUE; /** Mutex protecting btr_search_enabled */ static mutex_t btr_search_enabled_mutex; /** A dummy variable to fool the compiler */ UNIV_INTERN ulint btr_search_this_is_zero = 0; #ifdef UNIV_SEARCH_PERF_STAT /** Number of successful adaptive hash index lookups */ UNIV_INTERN ulint btr_search_n_succ = 0; /** Number of failed adaptive hash index lookups */ UNIV_INTERN ulint btr_search_n_hash_fail = 0; #endif /* UNIV_SEARCH_PERF_STAT */ /** padding to prevent other memory update hotspots from residing on the same memory cache line as btr_search_latch */ UNIV_INTERN byte btr_sea_pad1[64]; /** The latch protecting the adaptive search system: this latch protects the (1) positions of records on those pages where a hash index has been built. NOTE: It does not protect values of non-ordering fields within a record from being updated in-place! We can use fact (1) to perform unique searches to indexes. */ /* We will allocate the latch from dynamic memory to get it to the same DRAM page as other hotspot semaphores */ UNIV_INTERN rw_lock_t* btr_search_latch_temp; /** padding to prevent other memory update hotspots from residing on the same memory cache line */ UNIV_INTERN byte btr_sea_pad2[64]; /** The adaptive hash index */ UNIV_INTERN btr_search_sys_t* btr_search_sys; /** If the number of records on the page divided by this parameter would have been successfully accessed using a hash index, the index is then built on the page, assuming the global limit has been reached */ #define BTR_SEARCH_PAGE_BUILD_LIMIT 16 /** The global limit for consecutive potentially successful hash searches, before hash index building is started */ #define BTR_SEARCH_BUILD_LIMIT 100 /********************************************************************//** Builds a hash index on a page with the given parameters. If the page already has a hash index with different parameters, the old hash index is removed. If index is non-NULL, this function checks if n_fields and n_bytes are sensible values, and does not build a hash index if not. */ static void btr_search_build_page_hash_index( /*=============================*/ dict_index_t* index, /*!< in: index for which to build, or NULL if not known */ buf_block_t* block, /*!< in: index page, s- or x-latched */ ulint n_fields,/*!< in: hash this many full fields */ ulint n_bytes,/*!< in: hash this many bytes from the next field */ ibool left_side);/*!< in: hash for searches from left side? */ /*****************************************************************//** This function should be called before reserving any btr search mutex, if the intended operation might add nodes to the search system hash table. Because of the latching order, once we have reserved the btr search system latch, we cannot allocate a free frame from the buffer pool. Checks that there is a free buffer frame allocated for hash table heap in the btr search system. If not, allocates a free frames for the heap. This check makes it probable that, when have reserved the btr search system latch and we need to allocate a new node to the hash table, it will succeed. However, the check will not guarantee success. */ static void btr_search_check_free_space_in_heap(void) /*=====================================*/ { hash_table_t* table; mem_heap_t* heap; #ifdef UNIV_SYNC_DEBUG ut_ad(!rw_lock_own(&btr_search_latch, RW_LOCK_SHARED)); ut_ad(!rw_lock_own(&btr_search_latch, RW_LOCK_EX)); #endif /* UNIV_SYNC_DEBUG */ table = btr_search_sys->hash_index; heap = table->heap; /* Note that we peek the value of heap->free_block without reserving the latch: this is ok, because we will not guarantee that there will be enough free space in the hash table. */ if (heap->free_block == NULL) { buf_block_t* block = buf_block_alloc(0); rw_lock_x_lock(&btr_search_latch); if (heap->free_block == NULL) { heap->free_block = block; } else { buf_block_free(block); } rw_lock_x_unlock(&btr_search_latch); } } /*****************************************************************//** Reset global configuration variables. */ UNIV_INTERN void btr_search_var_init(void) /*=====================*/ { btr_search_this_is_zero = 0; #ifdef UNIV_SEARCH_PERF_STAT btr_search_n_succ = 0; btr_search_n_hash_fail = 0; #endif /* UNIV_SEARCH_PERF_STAT */ btr_search_latch_temp = NULL; btr_search_sys = NULL; } /********************************************************************* Creates and initializes the adaptive search system at a database start. */ UNIV_INTERN void btr_search_sys_create( /*==================*/ ulint hash_size) /*!< in: hash index hash table size */ { /* We allocate the search latch from dynamic memory: see above at the global variable definition */ btr_search_latch_temp = mem_alloc(sizeof(rw_lock_t)); rw_lock_create(&btr_search_latch, SYNC_SEARCH_SYS); mutex_create(&btr_search_enabled_mutex, SYNC_SEARCH_SYS_CONF); btr_search_sys = mem_alloc(sizeof(btr_search_sys_t)); btr_search_sys->hash_index = ha_create(hash_size, 0, 0); } /********************************************************************* Closes the adaptive search system at a database shutdown. */ UNIV_INTERN void btr_search_sys_close(void) /*======================*/ { /* This can happen if we abort during the startup phase. */ if (btr_search_sys == NULL) { return; } mem_heap_free(btr_search_sys->hash_index->heap); hash_table_free(btr_search_sys->hash_index); rw_lock_free(&btr_search_latch); mutex_free(&btr_search_enabled_mutex); memset(&btr_search_enabled_mutex, 0x0, sizeof(btr_search_enabled_mutex)); mem_free(btr_search_latch_temp); btr_search_latch_temp = NULL; mem_free(btr_search_sys); btr_search_sys = NULL; } /************************************************************************ Disable the adaptive hash search system and empty the index. */ UNIV_INTERN void btr_search_disable(void) /*====================*/ { mutex_enter(&btr_search_enabled_mutex); rw_lock_x_lock(&btr_search_latch); btr_search_enabled = FALSE; /* Clear all block->is_hashed flags and remove all entries from btr_search_sys->hash_index. */ buf_pool_drop_hash_index(); /* btr_search_enabled_mutex should guarantee this. */ ut_ad(!btr_search_enabled); rw_lock_x_unlock(&btr_search_latch); mutex_exit(&btr_search_enabled_mutex); } /********************************************************************//** Enable the adaptive hash search system. */ UNIV_INTERN void btr_search_enable(void) /*====================*/ { mutex_enter(&btr_search_enabled_mutex); rw_lock_x_lock(&btr_search_latch); btr_search_enabled = TRUE; rw_lock_x_unlock(&btr_search_latch); mutex_exit(&btr_search_enabled_mutex); } /*****************************************************************//** Creates and initializes a search info struct. @return own: search info struct */ UNIV_INTERN btr_search_t* btr_search_info_create( /*===================*/ mem_heap_t* heap) /*!< in: heap where created */ { btr_search_t* info; info = mem_heap_alloc(heap, sizeof(btr_search_t)); #ifdef UNIV_DEBUG info->magic_n = BTR_SEARCH_MAGIC_N; #endif /* UNIV_DEBUG */ info->ref_count = 0; info->root_guess = NULL; info->hash_analysis = 0; info->n_hash_potential = 0; info->last_hash_succ = FALSE; #ifdef UNIV_SEARCH_PERF_STAT info->n_hash_succ = 0; info->n_hash_fail = 0; info->n_patt_succ = 0; info->n_searches = 0; #endif /* UNIV_SEARCH_PERF_STAT */ /* Set some sensible values */ info->n_fields = 1; info->n_bytes = 0; info->left_side = TRUE; return(info); } /*****************************************************************//** Returns the value of ref_count. The value is protected by btr_search_latch. @return ref_count value. */ UNIV_INTERN ulint btr_search_info_get_ref_count( /*==========================*/ btr_search_t* info) /*!< in: search info. */ { ulint ret; ut_ad(info); #ifdef UNIV_SYNC_DEBUG ut_ad(!rw_lock_own(&btr_search_latch, RW_LOCK_SHARED)); ut_ad(!rw_lock_own(&btr_search_latch, RW_LOCK_EX)); #endif /* UNIV_SYNC_DEBUG */ rw_lock_s_lock(&btr_search_latch); ret = info->ref_count; rw_lock_s_unlock(&btr_search_latch); return(ret); } /*********************************************************************//** Updates the search info of an index about hash successes. NOTE that info is NOT protected by any semaphore, to save CPU time! Do not assume its fields are consistent. */ static void btr_search_info_update_hash( /*========================*/ btr_search_t* info, /*!< in/out: search info */ btr_cur_t* cursor) /*!< in: cursor which was just positioned */ { dict_index_t* index; ulint n_unique; int cmp; #ifdef UNIV_SYNC_DEBUG ut_ad(!rw_lock_own(&btr_search_latch, RW_LOCK_SHARED)); ut_ad(!rw_lock_own(&btr_search_latch, RW_LOCK_EX)); #endif /* UNIV_SYNC_DEBUG */ index = cursor->index; if (dict_index_is_ibuf(index)) { /* So many deletes are performed on an insert buffer tree that we do not consider a hash index useful on it: */ return; } n_unique = dict_index_get_n_unique_in_tree(index); if (info->n_hash_potential == 0) { goto set_new_recomm; } /* Test if the search would have succeeded using the recommended hash prefix */ if (info->n_fields >= n_unique && cursor->up_match >= n_unique) { increment_potential: info->n_hash_potential++; return; } cmp = ut_pair_cmp(info->n_fields, info->n_bytes, cursor->low_match, cursor->low_bytes); if (info->left_side ? cmp <= 0 : cmp > 0) { goto set_new_recomm; } cmp = ut_pair_cmp(info->n_fields, info->n_bytes, cursor->up_match, cursor->up_bytes); if (info->left_side ? cmp <= 0 : cmp > 0) { goto increment_potential; } set_new_recomm: /* We have to set a new recommendation; skip the hash analysis for a while to avoid unnecessary CPU time usage when there is no chance for success */ info->hash_analysis = 0; cmp = ut_pair_cmp(cursor->up_match, cursor->up_bytes, cursor->low_match, cursor->low_bytes); if (cmp == 0) { info->n_hash_potential = 0; /* For extra safety, we set some sensible values here */ info->n_fields = 1; info->n_bytes = 0; info->left_side = TRUE; } else if (cmp > 0) { info->n_hash_potential = 1; if (cursor->up_match >= n_unique) { info->n_fields = n_unique; info->n_bytes = 0; } else if (cursor->low_match < cursor->up_match) { info->n_fields = cursor->low_match + 1; info->n_bytes = 0; } else { info->n_fields = cursor->low_match; info->n_bytes = cursor->low_bytes + 1; } info->left_side = TRUE; } else { info->n_hash_potential = 1; if (cursor->low_match >= n_unique) { info->n_fields = n_unique; info->n_bytes = 0; } else if (cursor->low_match > cursor->up_match) { info->n_fields = cursor->up_match + 1; info->n_bytes = 0; } else { info->n_fields = cursor->up_match; info->n_bytes = cursor->up_bytes + 1; } info->left_side = FALSE; } } /*********************************************************************//** Updates the block search info on hash successes. NOTE that info and block->n_hash_helps, n_fields, n_bytes, side are NOT protected by any semaphore, to save CPU time! Do not assume the fields are consistent. @return TRUE if building a (new) hash index on the block is recommended */ static ibool btr_search_update_block_hash_info( /*==============================*/ btr_search_t* info, /*!< in: search info */ buf_block_t* block, /*!< in: buffer block */ btr_cur_t* cursor __attribute__((unused))) /*!< in: cursor */ { #ifdef UNIV_SYNC_DEBUG ut_ad(!rw_lock_own(&btr_search_latch, RW_LOCK_SHARED)); ut_ad(!rw_lock_own(&btr_search_latch, RW_LOCK_EX)); ut_ad(rw_lock_own(&block->lock, RW_LOCK_SHARED) || rw_lock_own(&block->lock, RW_LOCK_EX)); #endif /* UNIV_SYNC_DEBUG */ ut_ad(cursor); info->last_hash_succ = FALSE; ut_a(buf_block_state_valid(block)); ut_ad(info->magic_n == BTR_SEARCH_MAGIC_N); if ((block->n_hash_helps > 0) && (info->n_hash_potential > 0) && (block->n_fields == info->n_fields) && (block->n_bytes == info->n_bytes) && (block->left_side == info->left_side)) { if ((block->is_hashed) && (block->curr_n_fields == info->n_fields) && (block->curr_n_bytes == info->n_bytes) && (block->curr_left_side == info->left_side)) { /* The search would presumably have succeeded using the hash index */ info->last_hash_succ = TRUE; } block->n_hash_helps++; } else { block->n_hash_helps = 1; block->n_fields = info->n_fields; block->n_bytes = info->n_bytes; block->left_side = info->left_side; } #ifdef UNIV_DEBUG if (cursor->index->table->does_not_fit_in_memory) { block->n_hash_helps = 0; } #endif /* UNIV_DEBUG */ if ((block->n_hash_helps > page_get_n_recs(block->frame) / BTR_SEARCH_PAGE_BUILD_LIMIT) && (info->n_hash_potential >= BTR_SEARCH_BUILD_LIMIT)) { if ((!block->is_hashed) || (block->n_hash_helps > 2 * page_get_n_recs(block->frame)) || (block->n_fields != block->curr_n_fields) || (block->n_bytes != block->curr_n_bytes) || (block->left_side != block->curr_left_side)) { /* Build a new hash index on the page */ return(TRUE); } } return(FALSE); } /*********************************************************************//** Updates a hash node reference when it has been unsuccessfully used in a search which could have succeeded with the used hash parameters. This can happen because when building a hash index for a page, we do not check what happens at page boundaries, and therefore there can be misleading hash nodes. Also, collisions in the fold value can lead to misleading references. This function lazily fixes these imperfections in the hash index. */ static void btr_search_update_hash_ref( /*=======================*/ btr_search_t* info, /*!< in: search info */ buf_block_t* block, /*!< in: buffer block where cursor positioned */ btr_cur_t* cursor) /*!< in: cursor */ { ulint fold; rec_t* rec; dulint index_id; ut_ad(cursor->flag == BTR_CUR_HASH_FAIL); #ifdef UNIV_SYNC_DEBUG ut_ad(rw_lock_own(&btr_search_latch, RW_LOCK_EX)); ut_ad(rw_lock_own(&(block->lock), RW_LOCK_SHARED) || rw_lock_own(&(block->lock), RW_LOCK_EX)); #endif /* UNIV_SYNC_DEBUG */ ut_ad(page_align(btr_cur_get_rec(cursor)) == buf_block_get_frame(block)); if (!block->is_hashed) { return; } ut_a(block->index == cursor->index); ut_a(!dict_index_is_ibuf(cursor->index)); if ((info->n_hash_potential > 0) && (block->curr_n_fields == info->n_fields) && (block->curr_n_bytes == info->n_bytes) && (block->curr_left_side == info->left_side)) { mem_heap_t* heap = NULL; ulint offsets_[REC_OFFS_NORMAL_SIZE]; rec_offs_init(offsets_); rec = btr_cur_get_rec(cursor); if (!page_rec_is_user_rec(rec)) { return; } index_id = cursor->index->id; fold = rec_fold(rec, rec_get_offsets(rec, cursor->index, offsets_, ULINT_UNDEFINED, &heap), block->curr_n_fields, block->curr_n_bytes, index_id); if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } #ifdef UNIV_SYNC_DEBUG ut_ad(rw_lock_own(&btr_search_latch, RW_LOCK_EX)); #endif /* UNIV_SYNC_DEBUG */ ha_insert_for_fold(btr_search_sys->hash_index, fold, block, rec); } } /*********************************************************************//** Updates the search info. */ UNIV_INTERN void btr_search_info_update_slow( /*========================*/ btr_search_t* info, /*!< in/out: search info */ btr_cur_t* cursor) /*!< in: cursor which was just positioned */ { buf_block_t* block; ibool build_index; ulint* params; ulint* params2; #ifdef UNIV_SYNC_DEBUG ut_ad(!rw_lock_own(&btr_search_latch, RW_LOCK_SHARED)); ut_ad(!rw_lock_own(&btr_search_latch, RW_LOCK_EX)); #endif /* UNIV_SYNC_DEBUG */ block = btr_cur_get_block(cursor); /* NOTE that the following two function calls do NOT protect info or block->n_fields etc. with any semaphore, to save CPU time! We cannot assume the fields are consistent when we return from those functions! */ btr_search_info_update_hash(info, cursor); build_index = btr_search_update_block_hash_info(info, block, cursor); if (build_index || (cursor->flag == BTR_CUR_HASH_FAIL)) { btr_search_check_free_space_in_heap(); } if (cursor->flag == BTR_CUR_HASH_FAIL) { /* Update the hash node reference, if appropriate */ #ifdef UNIV_SEARCH_PERF_STAT btr_search_n_hash_fail++; #endif /* UNIV_SEARCH_PERF_STAT */ rw_lock_x_lock(&btr_search_latch); btr_search_update_hash_ref(info, block, cursor); rw_lock_x_unlock(&btr_search_latch); } if (build_index) { /* Note that since we did not protect block->n_fields etc. with any semaphore, the values can be inconsistent. We have to check inside the function call that they make sense. We also malloc an array and store the values there to make sure the compiler does not let the function call parameters change inside the called function. It might be that the compiler would optimize the call just to pass pointers to block. */ params = mem_alloc(3 * sizeof(ulint)); params[0] = block->n_fields; params[1] = block->n_bytes; params[2] = block->left_side; /* Make sure the compiler cannot deduce the values and do optimizations */ params2 = params + btr_search_this_is_zero; btr_search_build_page_hash_index(cursor->index, block, params2[0], params2[1], params2[2]); mem_free(params); } } /******************************************************************//** Checks if a guessed position for a tree cursor is right. Note that if mode is PAGE_CUR_LE, which is used in inserts, and the function returns TRUE, then cursor->up_match and cursor->low_match both have sensible values. @return TRUE if success */ static ibool btr_search_check_guess( /*===================*/ btr_cur_t* cursor, /*!< in: guessed cursor position */ ibool can_only_compare_to_cursor_rec, /*!< in: if we do not have a latch on the page of cursor, but only a latch on btr_search_latch, then ONLY the columns of the record UNDER the cursor are protected, not the next or previous record in the chain: we cannot look at the next or previous record to check our guess! */ const dtuple_t* tuple, /*!< in: data tuple */ ulint mode, /*!< in: PAGE_CUR_L, PAGE_CUR_LE, PAGE_CUR_G, or PAGE_CUR_GE */ mtr_t* mtr) /*!< in: mtr */ { rec_t* rec; ulint n_unique; ulint match; ulint bytes; int cmp; mem_heap_t* heap = NULL; ulint offsets_[REC_OFFS_NORMAL_SIZE]; ulint* offsets = offsets_; ibool success = FALSE; rec_offs_init(offsets_); n_unique = dict_index_get_n_unique_in_tree(cursor->index); rec = btr_cur_get_rec(cursor); ut_ad(page_rec_is_user_rec(rec)); match = 0; bytes = 0; offsets = rec_get_offsets(rec, cursor->index, offsets, n_unique, &heap); cmp = page_cmp_dtuple_rec_with_match( cursor->index->cmp_ctx, tuple, rec, offsets, &match, &bytes); if (mode == PAGE_CUR_GE) { if (cmp == 1) { goto exit_func; } cursor->up_match = match; if (match >= n_unique) { success = TRUE; goto exit_func; } } else if (mode == PAGE_CUR_LE) { if (cmp == -1) { goto exit_func; } cursor->low_match = match; } else if (mode == PAGE_CUR_G) { if (cmp != -1) { goto exit_func; } } else if (mode == PAGE_CUR_L) { if (cmp != 1) { goto exit_func; } } if (can_only_compare_to_cursor_rec) { /* Since we could not determine if our guess is right just by looking at the record under the cursor, return FALSE */ goto exit_func; } match = 0; bytes = 0; if ((mode == PAGE_CUR_G) || (mode == PAGE_CUR_GE)) { rec_t* prev_rec; ut_ad(!page_rec_is_infimum(rec)); prev_rec = page_rec_get_prev(rec); if (page_rec_is_infimum(prev_rec)) { success = btr_page_get_prev(page_align(prev_rec), mtr) == FIL_NULL; goto exit_func; } offsets = rec_get_offsets(prev_rec, cursor->index, offsets, n_unique, &heap); cmp = page_cmp_dtuple_rec_with_match( cursor->index->cmp_ctx, tuple, prev_rec, offsets, &match, &bytes); if (mode == PAGE_CUR_GE) { success = cmp == 1; } else { success = cmp != -1; } goto exit_func; } else { rec_t* next_rec; ut_ad(!page_rec_is_supremum(rec)); next_rec = page_rec_get_next(rec); if (page_rec_is_supremum(next_rec)) { if (btr_page_get_next(page_align(next_rec), mtr) == FIL_NULL) { cursor->up_match = 0; success = TRUE; } goto exit_func; } offsets = rec_get_offsets(next_rec, cursor->index, offsets, n_unique, &heap); cmp = page_cmp_dtuple_rec_with_match( cursor->index->cmp_ctx, tuple, next_rec, offsets, &match, &bytes); if (mode == PAGE_CUR_LE) { success = cmp == -1; cursor->up_match = match; } else { success = cmp != 1; } } exit_func: if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } return(success); } /******************************************************************//** Tries to guess the right search position based on the hash search info of the index. Note that if mode is PAGE_CUR_LE, which is used in inserts, and the function returns TRUE, then cursor->up_match and cursor->low_match both have sensible values. @return TRUE if succeeded */ UNIV_INTERN ibool btr_search_guess_on_hash( /*=====================*/ dict_index_t* index, /*!< in: index */ btr_search_t* info, /*!< in: index search info */ const dtuple_t* tuple, /*!< in: logical record */ ulint mode, /*!< in: PAGE_CUR_L, ... */ ulint latch_mode, /*!< in: BTR_SEARCH_LEAF, ...; NOTE that only if has_search_latch is 0, we will have a latch set on the cursor page, otherwise we assume the caller uses his search latch to protect the record! */ btr_cur_t* cursor, /*!< out: tree cursor */ ulint has_search_latch,/*!< in: latch mode the caller currently has on btr_search_latch: RW_S_LATCH, RW_X_LATCH, or 0 */ mtr_t* mtr) /*!< in: mtr */ { buf_block_t* block; rec_t* rec; ulint fold; dulint index_id; #ifdef notdefined btr_cur_t cursor2; btr_pcur_t pcur; #endif ut_ad(index && info && tuple && cursor && mtr); ut_ad((latch_mode == BTR_SEARCH_LEAF) || (latch_mode == BTR_MODIFY_LEAF)); /* Note that, for efficiency, the struct info may not be protected by any latch here! */ if (UNIV_UNLIKELY(info->n_hash_potential == 0)) { return(FALSE); } cursor->n_fields = info->n_fields; cursor->n_bytes = info->n_bytes; if (UNIV_UNLIKELY(dtuple_get_n_fields(tuple) < cursor->n_fields + (cursor->n_bytes > 0))) { return(FALSE); } index_id = index->id; #ifdef UNIV_SEARCH_PERF_STAT info->n_hash_succ++; #endif fold = dtuple_fold(tuple, cursor->n_fields, cursor->n_bytes, index_id); cursor->fold = fold; cursor->flag = BTR_CUR_HASH; if (UNIV_LIKELY(!has_search_latch)) { rw_lock_s_lock(&btr_search_latch); if (UNIV_UNLIKELY(!btr_search_enabled)) { goto failure_unlock; } } ut_ad(rw_lock_get_writer(&btr_search_latch) != RW_LOCK_EX); ut_ad(rw_lock_get_reader_count(&btr_search_latch) > 0); rec = ha_search_and_get_data(btr_search_sys->hash_index, fold); if (UNIV_UNLIKELY(!rec)) { goto failure_unlock; } block = buf_block_align(rec); if (UNIV_LIKELY(!has_search_latch)) { if (UNIV_UNLIKELY( !buf_page_get_known_nowait(latch_mode, block, BUF_MAKE_YOUNG, __FILE__, __LINE__, mtr))) { goto failure_unlock; } rw_lock_s_unlock(&btr_search_latch); buf_block_dbg_add_level(block, SYNC_TREE_NODE_FROM_HASH); } if (UNIV_UNLIKELY(buf_block_get_state(block) != BUF_BLOCK_FILE_PAGE)) { ut_ad(buf_block_get_state(block) == BUF_BLOCK_REMOVE_HASH); if (UNIV_LIKELY(!has_search_latch)) { btr_leaf_page_release(block, latch_mode, mtr); } goto failure; } ut_ad(page_rec_is_user_rec(rec)); btr_cur_position(index, rec, block, cursor); /* Check the validity of the guess within the page */ /* If we only have the latch on btr_search_latch, not on the page, it only protects the columns of the record the cursor is positioned on. We cannot look at the next of the previous record to determine if our guess for the cursor position is right. */ if (UNIV_EXPECT (ut_dulint_cmp(index_id, btr_page_get_index_id(block->frame)), 0) || !btr_search_check_guess(cursor, has_search_latch, tuple, mode, mtr)) { if (UNIV_LIKELY(!has_search_latch)) { btr_leaf_page_release(block, latch_mode, mtr); } goto failure; } if (UNIV_LIKELY(info->n_hash_potential < BTR_SEARCH_BUILD_LIMIT + 5)) { info->n_hash_potential++; } #ifdef notdefined /* These lines of code can be used in a debug version to check the correctness of the searched cursor position: */ info->last_hash_succ = FALSE; /* Currently, does not work if the following fails: */ ut_ad(!has_search_latch); btr_leaf_page_release(block, latch_mode, mtr); btr_cur_search_to_nth_level(index, 0, tuple, mode, latch_mode, &cursor2, 0, mtr); if (mode == PAGE_CUR_GE && page_rec_is_supremum(btr_cur_get_rec(&cursor2))) { /* If mode is PAGE_CUR_GE, then the binary search in the index tree may actually take us to the supremum of the previous page */ info->last_hash_succ = FALSE; btr_pcur_open_on_user_rec(index, tuple, mode, latch_mode, &pcur, mtr); ut_ad(btr_pcur_get_rec(&pcur) == btr_cur_get_rec(cursor)); } else { ut_ad(btr_cur_get_rec(&cursor2) == btr_cur_get_rec(cursor)); } /* NOTE that it is theoretically possible that the above assertions fail if the page of the cursor gets removed from the buffer pool meanwhile! Thus it might not be a bug. */ #endif info->last_hash_succ = TRUE; #ifdef UNIV_SEARCH_PERF_STAT btr_search_n_succ++; #endif if (UNIV_LIKELY(!has_search_latch) && buf_page_peek_if_too_old(&block->page)) { buf_page_make_young(&block->page); } /* Increment the page get statistics though we did not really fix the page: for user info only */ buf_pool->stat.n_page_gets++; return(TRUE); /*-------------------------------------------*/ failure_unlock: if (UNIV_LIKELY(!has_search_latch)) { rw_lock_s_unlock(&btr_search_latch); } failure: cursor->flag = BTR_CUR_HASH_FAIL; #ifdef UNIV_SEARCH_PERF_STAT info->n_hash_fail++; if (info->n_hash_succ > 0) { info->n_hash_succ--; } #endif info->last_hash_succ = FALSE; return(FALSE); } /********************************************************************//** Drops a page hash index. */ UNIV_INTERN void btr_search_drop_page_hash_index( /*============================*/ buf_block_t* block) /*!< in: block containing index page, s- or x-latched, or an index page for which we know that block->buf_fix_count == 0 */ { hash_table_t* table; ulint n_fields; ulint n_bytes; const page_t* page; const rec_t* rec; ulint fold; ulint prev_fold; dulint index_id; ulint n_cached; ulint n_recs; ulint* folds; ulint i; mem_heap_t* heap; const dict_index_t* index; ulint* offsets; #ifdef UNIV_SYNC_DEBUG ut_ad(!rw_lock_own(&btr_search_latch, RW_LOCK_SHARED)); ut_ad(!rw_lock_own(&btr_search_latch, RW_LOCK_EX)); #endif /* UNIV_SYNC_DEBUG */ retry: rw_lock_s_lock(&btr_search_latch); page = block->frame; if (UNIV_LIKELY(!block->is_hashed)) { rw_lock_s_unlock(&btr_search_latch); return; } table = btr_search_sys->hash_index; #ifdef UNIV_SYNC_DEBUG ut_ad(rw_lock_own(&(block->lock), RW_LOCK_SHARED) || rw_lock_own(&(block->lock), RW_LOCK_EX) || (block->page.buf_fix_count == 0)); #endif /* UNIV_SYNC_DEBUG */ n_fields = block->curr_n_fields; n_bytes = block->curr_n_bytes; index = block->index; ut_a(!dict_index_is_ibuf(index)); /* NOTE: The fields of block must not be accessed after releasing btr_search_latch, as the index page might only be s-latched! */ rw_lock_s_unlock(&btr_search_latch); ut_a(n_fields + n_bytes > 0); n_recs = page_get_n_recs(page); /* Calculate and cache fold values into an array for fast deletion from the hash index */ folds = mem_alloc(n_recs * sizeof(ulint)); n_cached = 0; rec = page_get_infimum_rec(page); rec = page_rec_get_next_low(rec, page_is_comp(page)); index_id = btr_page_get_index_id(page); ut_a(0 == ut_dulint_cmp(index_id, index->id)); prev_fold = 0; heap = NULL; offsets = NULL; while (!page_rec_is_supremum(rec)) { offsets = rec_get_offsets(rec, index, offsets, n_fields + (n_bytes > 0), &heap); ut_a(rec_offs_n_fields(offsets) == n_fields + (n_bytes > 0)); fold = rec_fold(rec, offsets, n_fields, n_bytes, index_id); if (fold == prev_fold && prev_fold != 0) { goto next_rec; } /* Remove all hash nodes pointing to this page from the hash chain */ folds[n_cached] = fold; n_cached++; next_rec: rec = page_rec_get_next_low(rec, page_rec_is_comp(rec)); prev_fold = fold; } if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } rw_lock_x_lock(&btr_search_latch); if (UNIV_UNLIKELY(!block->is_hashed)) { /* Someone else has meanwhile dropped the hash index */ goto cleanup; } ut_a(block->index == index); if (UNIV_UNLIKELY(block->curr_n_fields != n_fields) || UNIV_UNLIKELY(block->curr_n_bytes != n_bytes)) { /* Someone else has meanwhile built a new hash index on the page, with different parameters */ rw_lock_x_unlock(&btr_search_latch); mem_free(folds); goto retry; } for (i = 0; i < n_cached; i++) { ha_remove_all_nodes_to_page(table, folds[i], page); } ut_a(index->search_info->ref_count > 0); index->search_info->ref_count--; block->is_hashed = FALSE; block->index = NULL; cleanup: #if defined UNIV_AHI_DEBUG || defined UNIV_DEBUG if (UNIV_UNLIKELY(block->n_pointers)) { /* Corruption */ ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Corruption of adaptive hash index." " After dropping\n" "InnoDB: the hash index to a page of %s," " still %lu hash nodes remain.\n", index->name, (ulong) block->n_pointers); rw_lock_x_unlock(&btr_search_latch); btr_search_validate(); } else { rw_lock_x_unlock(&btr_search_latch); } #else /* UNIV_AHI_DEBUG || UNIV_DEBUG */ rw_lock_x_unlock(&btr_search_latch); #endif /* UNIV_AHI_DEBUG || UNIV_DEBUG */ mem_free(folds); } /********************************************************************//** Drops a page hash index when a page is freed from a fseg to the file system. Drops possible hash index if the page happens to be in the buffer pool. */ UNIV_INTERN void btr_search_drop_page_hash_when_freed( /*=================================*/ ulint space, /*!< in: space id */ ulint zip_size, /*!< in: compressed page size in bytes or 0 for uncompressed pages */ ulint page_no) /*!< in: page number */ { buf_block_t* block; mtr_t mtr; if (!buf_page_peek_if_search_hashed(space, page_no)) { return; } mtr_start(&mtr); /* We assume that if the caller has a latch on the page, then the caller has already dropped the hash index for the page, and we never get here. Therefore we can acquire the s-latch to the page without having to fear a deadlock. */ block = buf_page_get_gen(space, zip_size, page_no, RW_S_LATCH, NULL, BUF_GET_IF_IN_POOL, __FILE__, __LINE__, &mtr); /* Because the buffer pool mutex was released by buf_page_peek_if_search_hashed(), it is possible that the block was removed from the buffer pool by another thread before buf_page_get_gen() got a chance to acquire the buffer pool mutex again. Thus, we must check for a NULL return. */ if (UNIV_LIKELY(block != NULL)) { buf_block_dbg_add_level(block, SYNC_TREE_NODE_FROM_HASH); btr_search_drop_page_hash_index(block); } mtr_commit(&mtr); } /********************************************************************//** Builds a hash index on a page with the given parameters. If the page already has a hash index with different parameters, the old hash index is removed. If index is non-NULL, this function checks if n_fields and n_bytes are sensible values, and does not build a hash index if not. */ static void btr_search_build_page_hash_index( /*=============================*/ dict_index_t* index, /*!< in: index for which to build */ buf_block_t* block, /*!< in: index page, s- or x-latched */ ulint n_fields,/*!< in: hash this many full fields */ ulint n_bytes,/*!< in: hash this many bytes from the next field */ ibool left_side)/*!< in: hash for searches from left side? */ { hash_table_t* table; page_t* page; rec_t* rec; rec_t* next_rec; ulint fold; ulint next_fold; dulint index_id; ulint n_cached; ulint n_recs; ulint* folds; rec_t** recs; ulint i; mem_heap_t* heap = NULL; ulint offsets_[REC_OFFS_NORMAL_SIZE]; ulint* offsets = offsets_; rec_offs_init(offsets_); ut_ad(index); ut_a(!dict_index_is_ibuf(index)); table = btr_search_sys->hash_index; page = buf_block_get_frame(block); #ifdef UNIV_SYNC_DEBUG ut_ad(!rw_lock_own(&btr_search_latch, RW_LOCK_EX)); ut_ad(rw_lock_own(&(block->lock), RW_LOCK_SHARED) || rw_lock_own(&(block->lock), RW_LOCK_EX)); #endif /* UNIV_SYNC_DEBUG */ rw_lock_s_lock(&btr_search_latch); if (block->is_hashed && ((block->curr_n_fields != n_fields) || (block->curr_n_bytes != n_bytes) || (block->curr_left_side != left_side))) { rw_lock_s_unlock(&btr_search_latch); btr_search_drop_page_hash_index(block); } else { rw_lock_s_unlock(&btr_search_latch); } n_recs = page_get_n_recs(page); if (n_recs == 0) { return; } /* Check that the values for hash index build are sensible */ if (n_fields + n_bytes == 0) { return; } if (dict_index_get_n_unique_in_tree(index) < n_fields || (dict_index_get_n_unique_in_tree(index) == n_fields && n_bytes > 0)) { return; } /* Calculate and cache fold values and corresponding records into an array for fast insertion to the hash index */ folds = mem_alloc(n_recs * sizeof(ulint)); recs = mem_alloc(n_recs * sizeof(rec_t*)); n_cached = 0; index_id = btr_page_get_index_id(page); rec = page_rec_get_next(page_get_infimum_rec(page)); offsets = rec_get_offsets(rec, index, offsets, n_fields + (n_bytes > 0), &heap); if (!page_rec_is_supremum(rec)) { ut_a(n_fields <= rec_offs_n_fields(offsets)); if (n_bytes > 0) { ut_a(n_fields < rec_offs_n_fields(offsets)); } } fold = rec_fold(rec, offsets, n_fields, n_bytes, index_id); if (left_side) { folds[n_cached] = fold; recs[n_cached] = rec; n_cached++; } for (;;) { next_rec = page_rec_get_next(rec); if (page_rec_is_supremum(next_rec)) { if (!left_side) { folds[n_cached] = fold; recs[n_cached] = rec; n_cached++; } break; } offsets = rec_get_offsets(next_rec, index, offsets, n_fields + (n_bytes > 0), &heap); next_fold = rec_fold(next_rec, offsets, n_fields, n_bytes, index_id); if (fold != next_fold) { /* Insert an entry into the hash index */ if (left_side) { folds[n_cached] = next_fold; recs[n_cached] = next_rec; n_cached++; } else { folds[n_cached] = fold; recs[n_cached] = rec; n_cached++; } } rec = next_rec; fold = next_fold; } btr_search_check_free_space_in_heap(); rw_lock_x_lock(&btr_search_latch); if (UNIV_UNLIKELY(!btr_search_enabled)) { goto exit_func; } if (block->is_hashed && ((block->curr_n_fields != n_fields) || (block->curr_n_bytes != n_bytes) || (block->curr_left_side != left_side))) { goto exit_func; } /* This counter is decremented every time we drop page hash index entries and is incremented here. Since we can rebuild hash index for a page that is already hashed, we have to take care not to increment the counter in that case. */ if (!block->is_hashed) { index->search_info->ref_count++; } block->is_hashed = TRUE; block->n_hash_helps = 0; block->curr_n_fields = n_fields; block->curr_n_bytes = n_bytes; block->curr_left_side = left_side; block->index = index; for (i = 0; i < n_cached; i++) { ha_insert_for_fold(table, folds[i], block, recs[i]); } exit_func: rw_lock_x_unlock(&btr_search_latch); mem_free(folds); mem_free(recs); if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } } /********************************************************************//** Moves or deletes hash entries for moved records. If new_page is already hashed, then the hash index for page, if any, is dropped. If new_page is not hashed, and page is hashed, then a new hash index is built to new_page with the same parameters as page (this often happens when a page is split). */ UNIV_INTERN void btr_search_move_or_delete_hash_entries( /*===================================*/ buf_block_t* new_block, /*!< in: records are copied to this page */ buf_block_t* block, /*!< in: index page from which records were copied, and the copied records will be deleted from this page */ dict_index_t* index) /*!< in: record descriptor */ { ulint n_fields; ulint n_bytes; ibool left_side; #ifdef UNIV_SYNC_DEBUG ut_ad(rw_lock_own(&(block->lock), RW_LOCK_EX)); ut_ad(rw_lock_own(&(new_block->lock), RW_LOCK_EX)); #endif /* UNIV_SYNC_DEBUG */ ut_a(!new_block->is_hashed || new_block->index == index); ut_a(!block->is_hashed || block->index == index); ut_a(!(new_block->is_hashed || block->is_hashed) || !dict_index_is_ibuf(index)); rw_lock_s_lock(&btr_search_latch); if (new_block->is_hashed) { rw_lock_s_unlock(&btr_search_latch); btr_search_drop_page_hash_index(block); return; } if (block->is_hashed) { n_fields = block->curr_n_fields; n_bytes = block->curr_n_bytes; left_side = block->curr_left_side; new_block->n_fields = block->curr_n_fields; new_block->n_bytes = block->curr_n_bytes; new_block->left_side = left_side; rw_lock_s_unlock(&btr_search_latch); ut_a(n_fields + n_bytes > 0); btr_search_build_page_hash_index(index, new_block, n_fields, n_bytes, left_side); ut_ad(n_fields == block->curr_n_fields); ut_ad(n_bytes == block->curr_n_bytes); ut_ad(left_side == block->curr_left_side); return; } rw_lock_s_unlock(&btr_search_latch); } /********************************************************************//** Updates the page hash index when a single record is deleted from a page. */ UNIV_INTERN void btr_search_update_hash_on_delete( /*=============================*/ btr_cur_t* cursor) /*!< in: cursor which was positioned on the record to delete using btr_cur_search_..., the record is not yet deleted */ { hash_table_t* table; buf_block_t* block; rec_t* rec; ulint fold; dulint index_id; ibool found; ulint offsets_[REC_OFFS_NORMAL_SIZE]; mem_heap_t* heap = NULL; rec_offs_init(offsets_); rec = btr_cur_get_rec(cursor); block = btr_cur_get_block(cursor); #ifdef UNIV_SYNC_DEBUG ut_ad(rw_lock_own(&(block->lock), RW_LOCK_EX)); #endif /* UNIV_SYNC_DEBUG */ if (!block->is_hashed) { return; } ut_a(block->index == cursor->index); ut_a(block->curr_n_fields + block->curr_n_bytes > 0); ut_a(!dict_index_is_ibuf(cursor->index)); table = btr_search_sys->hash_index; index_id = cursor->index->id; fold = rec_fold(rec, rec_get_offsets(rec, cursor->index, offsets_, ULINT_UNDEFINED, &heap), block->curr_n_fields, block->curr_n_bytes, index_id); if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } rw_lock_x_lock(&btr_search_latch); found = ha_search_and_delete_if_found(table, fold, rec); rw_lock_x_unlock(&btr_search_latch); } /********************************************************************//** Updates the page hash index when a single record is inserted on a page. */ UNIV_INTERN void btr_search_update_hash_node_on_insert( /*==================================*/ btr_cur_t* cursor) /*!< in: cursor which was positioned to the place to insert using btr_cur_search_..., and the new record has been inserted next to the cursor */ { hash_table_t* table; buf_block_t* block; rec_t* rec; rec = btr_cur_get_rec(cursor); block = btr_cur_get_block(cursor); #ifdef UNIV_SYNC_DEBUG ut_ad(rw_lock_own(&(block->lock), RW_LOCK_EX)); #endif /* UNIV_SYNC_DEBUG */ if (!block->is_hashed) { return; } ut_a(block->index == cursor->index); ut_a(!dict_index_is_ibuf(cursor->index)); rw_lock_x_lock(&btr_search_latch); if ((cursor->flag == BTR_CUR_HASH) && (cursor->n_fields == block->curr_n_fields) && (cursor->n_bytes == block->curr_n_bytes) && !block->curr_left_side) { table = btr_search_sys->hash_index; ha_search_and_update_if_found(table, cursor->fold, rec, block, page_rec_get_next(rec)); rw_lock_x_unlock(&btr_search_latch); } else { rw_lock_x_unlock(&btr_search_latch); btr_search_update_hash_on_insert(cursor); } } /********************************************************************//** Updates the page hash index when a single record is inserted on a page. */ UNIV_INTERN void btr_search_update_hash_on_insert( /*=============================*/ btr_cur_t* cursor) /*!< in: cursor which was positioned to the place to insert using btr_cur_search_..., and the new record has been inserted next to the cursor */ { hash_table_t* table; buf_block_t* block; rec_t* rec; rec_t* ins_rec; rec_t* next_rec; dulint index_id; ulint fold; ulint ins_fold; ulint next_fold = 0; /* remove warning (??? bug ???) */ ulint n_fields; ulint n_bytes; ibool left_side; ibool locked = FALSE; mem_heap_t* heap = NULL; ulint offsets_[REC_OFFS_NORMAL_SIZE]; ulint* offsets = offsets_; rec_offs_init(offsets_); table = btr_search_sys->hash_index; btr_search_check_free_space_in_heap(); rec = btr_cur_get_rec(cursor); block = btr_cur_get_block(cursor); #ifdef UNIV_SYNC_DEBUG ut_ad(rw_lock_own(&(block->lock), RW_LOCK_EX)); #endif /* UNIV_SYNC_DEBUG */ if (!block->is_hashed) { return; } ut_a(block->index == cursor->index); ut_a(!dict_index_is_ibuf(cursor->index)); index_id = cursor->index->id; n_fields = block->curr_n_fields; n_bytes = block->curr_n_bytes; left_side = block->curr_left_side; ins_rec = page_rec_get_next(rec); next_rec = page_rec_get_next(ins_rec); offsets = rec_get_offsets(ins_rec, cursor->index, offsets, ULINT_UNDEFINED, &heap); ins_fold = rec_fold(ins_rec, offsets, n_fields, n_bytes, index_id); if (!page_rec_is_supremum(next_rec)) { offsets = rec_get_offsets(next_rec, cursor->index, offsets, n_fields + (n_bytes > 0), &heap); next_fold = rec_fold(next_rec, offsets, n_fields, n_bytes, index_id); } if (!page_rec_is_infimum(rec)) { offsets = rec_get_offsets(rec, cursor->index, offsets, n_fields + (n_bytes > 0), &heap); fold = rec_fold(rec, offsets, n_fields, n_bytes, index_id); } else { if (left_side) { rw_lock_x_lock(&btr_search_latch); locked = TRUE; ha_insert_for_fold(table, ins_fold, block, ins_rec); } goto check_next_rec; } if (fold != ins_fold) { if (!locked) { rw_lock_x_lock(&btr_search_latch); locked = TRUE; } if (!left_side) { ha_insert_for_fold(table, fold, block, rec); } else { ha_insert_for_fold(table, ins_fold, block, ins_rec); } } check_next_rec: if (page_rec_is_supremum(next_rec)) { if (!left_side) { if (!locked) { rw_lock_x_lock(&btr_search_latch); locked = TRUE; } ha_insert_for_fold(table, ins_fold, block, ins_rec); } goto function_exit; } if (ins_fold != next_fold) { if (!locked) { rw_lock_x_lock(&btr_search_latch); locked = TRUE; } if (!left_side) { ha_insert_for_fold(table, ins_fold, block, ins_rec); /* ib_logger(ib_stream, "Hash insert for "); dict_index_name_print(ib_stream, cursor->index); ib_logger(ib_stream, " fold %lu\n", ins_fold); */ } else { ha_insert_for_fold(table, next_fold, block, next_rec); } } function_exit: if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } if (locked) { rw_lock_x_unlock(&btr_search_latch); } } /********************************************************************//** Validates the search system. @return TRUE if ok */ UNIV_INTERN ibool btr_search_validate(void) /*=====================*/ { ha_node_t* node; ulint n_page_dumps = 0; ibool ok = TRUE; ulint i; ulint cell_count; mem_heap_t* heap = NULL; ulint offsets_[REC_OFFS_NORMAL_SIZE]; ulint* offsets = offsets_; /* How many cells to check before temporarily releasing btr_search_latch. */ ulint chunk_size = 10000; rec_offs_init(offsets_); rw_lock_x_lock(&btr_search_latch); buf_pool_mutex_enter(); cell_count = hash_get_n_cells(btr_search_sys->hash_index); for (i = 0; i < cell_count; i++) { /* We release btr_search_latch every once in a while to give other queries a chance to run. */ if ((i != 0) && ((i % chunk_size) == 0)) { buf_pool_mutex_exit(); rw_lock_x_unlock(&btr_search_latch); os_thread_yield(); rw_lock_x_lock(&btr_search_latch); buf_pool_mutex_enter(); } node = hash_get_nth_cell(btr_search_sys->hash_index, i)->node; for (; node != NULL; node = node->next) { const buf_block_t* block = buf_block_align(node->data); const buf_block_t* hash_block; if (UNIV_LIKELY(buf_block_get_state(block) == BUF_BLOCK_FILE_PAGE)) { /* The space and offset are only valid for file blocks. It is possible that the block is being freed (BUF_BLOCK_REMOVE_HASH, see the assertion and the comment below) */ hash_block = buf_block_hash_get( buf_block_get_space(block), buf_block_get_page_no(block)); } else { hash_block = NULL; } if (hash_block) { ut_a(hash_block == block); } else { /* When a block is being freed, buf_LRU_search_and_free_block() first removes the block from buf_pool->page_hash by calling buf_LRU_block_remove_hashed_page(). After that, it invokes btr_search_drop_page_hash_index() to remove the block from btr_search_sys->hash_index. */ ut_a(buf_block_get_state(block) == BUF_BLOCK_REMOVE_HASH); } ut_a(!dict_index_is_ibuf(block->index)); offsets = rec_get_offsets((const rec_t*) node->data, block->index, offsets, block->curr_n_fields + (block->curr_n_bytes > 0), &heap); if (!block->is_hashed || node->fold != rec_fold((rec_t*)(node->data), offsets, block->curr_n_fields, block->curr_n_bytes, btr_page_get_index_id(block->frame))) { const page_t* page = block->frame; ok = FALSE; ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Error in an adaptive hash" " index pointer to page %lu\n" "InnoDB: ptr mem address %p" " index id %lu %lu," " node fold %lu, rec fold %lu\n", (ulong) page_get_page_no(page), node->data, (ulong) ut_dulint_get_high( btr_page_get_index_id(page)), (ulong) ut_dulint_get_low( btr_page_get_index_id(page)), (ulong) node->fold, (ulong) rec_fold((rec_t*)(node->data), offsets, block->curr_n_fields, block->curr_n_bytes, btr_page_get_index_id( page))); ib_logger(ib_stream, "InnoDB: Record "); rec_print_new(ib_stream, (rec_t*)node->data, offsets); ib_logger(ib_stream, "\nInnoDB: on that page." " Page mem address %p, is hashed %lu," " n fields %lu, n bytes %lu\n" "InnoDB: side %lu\n", (void*) page, (ulong) block->is_hashed, (ulong) block->curr_n_fields, (ulong) block->curr_n_bytes, (ulong) block->curr_left_side); if (n_page_dumps < 20) { buf_page_print(page, 0); n_page_dumps++; } } } } for (i = 0; i < cell_count; i += chunk_size) { ulint end_index = ut_min(i + chunk_size - 1, cell_count - 1); /* We release btr_search_latch every once in a while to give other queries a chance to run. */ if (i != 0) { buf_pool_mutex_exit(); rw_lock_x_unlock(&btr_search_latch); os_thread_yield(); rw_lock_x_lock(&btr_search_latch); buf_pool_mutex_enter(); } if (!ha_validate(btr_search_sys->hash_index, i, end_index)) { ok = FALSE; } } buf_pool_mutex_exit(); rw_lock_x_unlock(&btr_search_latch); if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } return(ok); } haildb-2.3.2/btr/btr0btr.c0000644000175000017500000032674411513177357016175 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1994, 2010, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file btr/btr0btr.c The B-tree Created 6/2/1994 Heikki Tuuri *******************************************************/ #include "btr0btr.h" #ifdef UNIV_NONINL #include "btr0btr.ic" #endif #include "fsp0fsp.h" #include "page0page.h" #ifndef UNIV_HOTBACKUP #include "btr0cur.h" #include "btr0sea.h" #include "btr0pcur.h" #include "rem0cmp.h" #include "lock0lock.h" #include "ibuf0ibuf.h" #include "trx0trx.h" /* Latching strategy of the InnoDB B-tree -------------------------------------- A tree latch protects all non-leaf nodes of the tree. Each node of a tree also has a latch of its own. A B-tree operation normally first acquires an S-latch on the tree. It searches down the tree and releases the tree latch when it has the leaf node latch. To save CPU time we do not acquire any latch on non-leaf nodes of the tree during a search, those pages are only bufferfixed. If an operation needs to restructure the tree, it acquires an X-latch on the tree before searching to a leaf node. If it needs, for example, to split a leaf, (1) InnoDB decides the split point in the leaf, (2) allocates a new page, (3) inserts the appropriate node pointer to the first non-leaf level, (4) releases the tree X-latch, (5) and then moves records from the leaf to the new allocated page. Node pointers ------------- Leaf pages of a B-tree contain the index records stored in the tree. On levels n > 0 we store 'node pointers' to pages on level n - 1. For each page there is exactly one node pointer stored: thus the our tree is an ordinary B-tree, not a B-link tree. A node pointer contains a prefix P of an index record. The prefix is long enough so that it determines an index record uniquely. The file page number of the child page is added as the last field. To the child page we can store node pointers or index records which are >= P in the alphabetical order, but < P1 if there is a next node pointer on the level, and P1 is its prefix. If a node pointer with a prefix P points to a non-leaf child, then the leftmost record in the child must have the same prefix P. If it points to a leaf node, the child is not required to contain any record with a prefix equal to P. The leaf case is decided this way to allow arbitrary deletions in a leaf node without touching upper levels of the tree. We have predefined a special minimum record which we define as the smallest record in any alphabetical order. A minimum record is denoted by setting a bit in the record header. A minimum record acts as the prefix of a node pointer which points to a leftmost node on any level of the tree. File page allocation -------------------- In the root node of a B-tree there are two file segment headers. The leaf pages of a tree are allocated from one file segment, to make them consecutive on disk if possible. From the other file segment we allocate pages for the non-leaf levels of the tree. */ #ifdef UNIV_BTR_DEBUG /**************************************************************//** Checks a file segment header within a B-tree root page. @return TRUE if valid */ static ibool btr_root_fseg_validate( /*===================*/ const fseg_header_t* seg_header, /*!< in: segment header */ ulint space) /*!< in: tablespace identifier */ { ulint offset = mach_read_from_2(seg_header + FSEG_HDR_OFFSET); ut_a(mach_read_from_4(seg_header + FSEG_HDR_SPACE) == space); ut_a(offset >= FIL_PAGE_DATA); ut_a(offset <= UNIV_PAGE_SIZE - FIL_PAGE_DATA_END); return(TRUE); } #endif /* UNIV_BTR_DEBUG */ /**************************************************************//** Gets the root node of a tree and x-latches it. @return root page, x-latched */ static buf_block_t* btr_root_block_get( /*===============*/ dict_index_t* dict_index, /*!< in: dict_index tree */ mtr_t* mtr) /*!< in: mtr */ { ulint space; ulint zip_size; ulint root_page_no; buf_block_t* block; space = dict_index_get_space(dict_index); zip_size = dict_table_zip_size(dict_index->table); root_page_no = dict_index_get_page(dict_index); block = btr_block_get(space, zip_size, root_page_no, RW_X_LATCH, mtr); ut_a((ibool)!!page_is_comp(buf_block_get_frame(block)) == dict_table_is_comp(dict_index->table)); #ifdef UNIV_BTR_DEBUG if (!dict_index_is_ibuf(dict_index)) { const page_t* root = buf_block_get_frame(block); ut_a(btr_root_fseg_validate(FIL_PAGE_DATA + PAGE_BTR_SEG_LEAF + root, space)); ut_a(btr_root_fseg_validate(FIL_PAGE_DATA + PAGE_BTR_SEG_TOP + root, space)); } #endif /* UNIV_BTR_DEBUG */ return(block); } /**************************************************************//** Gets the root node of a tree and x-latches it. @return root page, x-latched */ UNIV_INTERN page_t* btr_root_get( /*=========*/ dict_index_t* dict_index, /*!< in: dict_index tree */ mtr_t* mtr) /*!< in: mtr */ { return(buf_block_get_frame(btr_root_block_get(dict_index, mtr))); } /*************************************************************//** Gets pointer to the previous user record in the tree. It is assumed that the caller has appropriate latches on the page and its neighbor. @return previous user record, NULL if there is none */ UNIV_INTERN rec_t* btr_get_prev_user_rec( /*==================*/ rec_t* rec, /*!< in: record on leaf level */ mtr_t* mtr) /*!< in: mtr holding a latch on the page, and if needed, also to the previous page */ { page_t* page; page_t* prev_page; ulint prev_page_no; if (!page_rec_is_infimum(rec)) { rec_t* prev_rec = page_rec_get_prev(rec); if (!page_rec_is_infimum(prev_rec)) { return(prev_rec); } } page = page_align(rec); prev_page_no = btr_page_get_prev(page, mtr); if (prev_page_no != FIL_NULL) { ulint space; ulint zip_size; buf_block_t* prev_block; space = page_get_space_id(page); zip_size = fil_space_get_zip_size(space); prev_block = buf_page_get_with_no_latch(space, zip_size, prev_page_no, mtr); prev_page = buf_block_get_frame(prev_block); /* The caller must already have a latch to the brother */ ut_ad(mtr_memo_contains(mtr, prev_block, MTR_MEMO_PAGE_S_FIX) || mtr_memo_contains(mtr, prev_block, MTR_MEMO_PAGE_X_FIX)); #ifdef UNIV_BTR_DEBUG ut_a(page_is_comp(prev_page) == page_is_comp(page)); ut_a(btr_page_get_next(prev_page, mtr) == page_get_page_no(page)); #endif /* UNIV_BTR_DEBUG */ return(page_rec_get_prev(page_get_supremum_rec(prev_page))); } return(NULL); } /*************************************************************//** Gets pointer to the next user record in the tree. It is assumed that the caller has appropriate latches on the page and its neighbor. @return next user record, NULL if there is none */ UNIV_INTERN rec_t* btr_get_next_user_rec( /*==================*/ rec_t* rec, /*!< in: record on leaf level */ mtr_t* mtr) /*!< in: mtr holding a latch on the page, and if needed, also to the next page */ { page_t* page; page_t* next_page; ulint next_page_no; if (!page_rec_is_supremum(rec)) { rec_t* next_rec = page_rec_get_next(rec); if (!page_rec_is_supremum(next_rec)) { return(next_rec); } } page = page_align(rec); next_page_no = btr_page_get_next(page, mtr); if (next_page_no != FIL_NULL) { ulint space; ulint zip_size; buf_block_t* next_block; space = page_get_space_id(page); zip_size = fil_space_get_zip_size(space); next_block = buf_page_get_with_no_latch(space, zip_size, next_page_no, mtr); next_page = buf_block_get_frame(next_block); /* The caller must already have a latch to the brother */ ut_ad(mtr_memo_contains(mtr, next_block, MTR_MEMO_PAGE_S_FIX) || mtr_memo_contains(mtr, next_block, MTR_MEMO_PAGE_X_FIX)); #ifdef UNIV_BTR_DEBUG ut_a(page_is_comp(next_page) == page_is_comp(page)); ut_a(btr_page_get_prev(next_page, mtr) == page_get_page_no(page)); #endif /* UNIV_BTR_DEBUG */ return(page_rec_get_next(page_get_infimum_rec(next_page))); } return(NULL); } /**************************************************************//** Creates a new index page (not the root, and also not used in page reorganization). @see btr_page_empty(). */ static void btr_page_create( /*============*/ buf_block_t* block, /*!< in/out: page to be created */ page_zip_des_t* page_zip,/*!< in/out: compressed page, or NULL */ dict_index_t* dict_index,/*!< in: dict_index */ ulint level, /*!< in: the B-tree level of the page */ mtr_t* mtr) /*!< in: mtr */ { page_t* page = buf_block_get_frame(block); ut_ad(mtr_memo_contains(mtr, block, MTR_MEMO_PAGE_X_FIX)); if (UNIV_LIKELY_NULL(page_zip)) { page_create_zip(block, dict_index, level, mtr); } else { page_create(block, mtr, dict_table_is_comp(dict_index->table)); /* Set the level of the new index page */ btr_page_set_level(page, NULL, level, mtr); } block->check_index_page_at_flush = TRUE; btr_page_set_index_id(page, page_zip, dict_index->id, mtr); } /**************************************************************//** Allocates a new file page to be used in an ibuf tree. Takes the page from the free list of the tree, which must contain pages! @return new allocated block, x-latched */ static buf_block_t* btr_page_alloc_for_ibuf( /*====================*/ dict_index_t* dict_index, /*!< in: dict_index tree */ mtr_t* mtr) /*!< in: mtr */ { fil_addr_t node_addr; page_t* root; page_t* new_page; buf_block_t* new_block; root = btr_root_get(dict_index, mtr); node_addr = flst_get_first(root + PAGE_HEADER + PAGE_BTR_IBUF_FREE_LIST, mtr); ut_a(node_addr.page != FIL_NULL); new_block = buf_page_get(dict_index_get_space(dict_index), dict_table_zip_size(dict_index->table), node_addr.page, RW_X_LATCH, mtr); new_page = buf_block_get_frame(new_block); buf_block_dbg_add_level(new_block, SYNC_TREE_NODE_NEW); flst_remove(root + PAGE_HEADER + PAGE_BTR_IBUF_FREE_LIST, new_page + PAGE_HEADER + PAGE_BTR_IBUF_FREE_LIST_NODE, mtr); ut_ad(flst_validate(root + PAGE_HEADER + PAGE_BTR_IBUF_FREE_LIST, mtr)); return(new_block); } /**************************************************************//** Allocates a new file page to be used in an index tree. NOTE: we assume that the caller has made the reservation for free extents! @return new allocated block, x-latched; NULL if out of space */ UNIV_INTERN buf_block_t* btr_page_alloc( /*===========*/ dict_index_t* dict_index, /*!< in: dict_index */ ulint hint_page_no, /*!< in: hint of a good page */ byte file_direction, /*!< in: direction where a possible page split is made */ ulint level, /*!< in: level where the page is placed in the tree */ mtr_t* mtr) /*!< in: mtr */ { fseg_header_t* seg_header; page_t* root; buf_block_t* new_block; ulint new_page_no; if (dict_index_is_ibuf(dict_index)) { return(btr_page_alloc_for_ibuf(dict_index, mtr)); } root = btr_root_get(dict_index, mtr); if (level == 0) { seg_header = root + PAGE_HEADER + PAGE_BTR_SEG_LEAF; } else { seg_header = root + PAGE_HEADER + PAGE_BTR_SEG_TOP; } /* Parameter TRUE below states that the caller has made the reservation for free extents, and thus we know that a page can be allocated: */ new_page_no = fseg_alloc_free_page_general(seg_header, hint_page_no, file_direction, TRUE, mtr); if (new_page_no == FIL_NULL) { return(NULL); } new_block = buf_page_get(dict_index_get_space(dict_index), dict_table_zip_size(dict_index->table), new_page_no, RW_X_LATCH, mtr); buf_block_dbg_add_level(new_block, SYNC_TREE_NODE_NEW); return(new_block); } /**************************************************************//** Gets the number of pages in a B-tree. @return number of pages */ UNIV_INTERN ulint btr_get_size( /*=========*/ dict_index_t* dict_index, /*!< in: dict_index */ ulint flag) /*!< in: BTR_N_LEAF_PAGES or BTR_TOTAL_SIZE */ { fseg_header_t* seg_header; page_t* root; ulint n; ulint dummy; mtr_t mtr; mtr_start(&mtr); mtr_s_lock(dict_index_get_lock(dict_index), &mtr); root = btr_root_get(dict_index, &mtr); if (flag == BTR_N_LEAF_PAGES) { seg_header = root + PAGE_HEADER + PAGE_BTR_SEG_LEAF; fseg_n_reserved_pages(seg_header, &n, &mtr); } else if (flag == BTR_TOTAL_SIZE) { seg_header = root + PAGE_HEADER + PAGE_BTR_SEG_TOP; n = fseg_n_reserved_pages(seg_header, &dummy, &mtr); seg_header = root + PAGE_HEADER + PAGE_BTR_SEG_LEAF; n += fseg_n_reserved_pages(seg_header, &dummy, &mtr); } else { ut_error; } mtr_commit(&mtr); return(n); } /**************************************************************//** Frees a page used in an ibuf tree. Puts the page to the free list of the ibuf tree. */ static void btr_page_free_for_ibuf( /*===================*/ dict_index_t* dict_index, /*!< in: dict_index tree */ buf_block_t* block, /*!< in: block to be freed, x-latched */ mtr_t* mtr) /*!< in: mtr */ { page_t* root; ut_ad(mtr_memo_contains(mtr, block, MTR_MEMO_PAGE_X_FIX)); root = btr_root_get(dict_index, mtr); flst_add_first(root + PAGE_HEADER + PAGE_BTR_IBUF_FREE_LIST, buf_block_get_frame(block) + PAGE_HEADER + PAGE_BTR_IBUF_FREE_LIST_NODE, mtr); ut_ad(flst_validate(root + PAGE_HEADER + PAGE_BTR_IBUF_FREE_LIST, mtr)); } /**************************************************************//** Frees a file page used in an index tree. Can be used also to (BLOB) external storage pages, because the page level 0 can be given as an argument. */ UNIV_INTERN void btr_page_free_low( /*==============*/ dict_index_t* dict_index, /*!< in: dict_index tree */ buf_block_t* block, /*!< in: block to be freed, x-latched */ ulint level, /*!< in: page level */ mtr_t* mtr) /*!< in: mtr */ { fseg_header_t* seg_header; page_t* root; ut_ad(mtr_memo_contains(mtr, block, MTR_MEMO_PAGE_X_FIX)); /* The page gets invalid for optimistic searches: increment the frame modify clock */ buf_block_modify_clock_inc(block); if (dict_index_is_ibuf(dict_index)) { btr_page_free_for_ibuf(dict_index, block, mtr); return; } root = btr_root_get(dict_index, mtr); if (level == 0) { seg_header = root + PAGE_HEADER + PAGE_BTR_SEG_LEAF; } else { seg_header = root + PAGE_HEADER + PAGE_BTR_SEG_TOP; } fseg_free_page(seg_header, buf_block_get_space(block), buf_block_get_page_no(block), mtr); } /**************************************************************//** Frees a file page used in an index tree. NOTE: cannot free field external storage pages because the page must contain info on its level. */ UNIV_INTERN void btr_page_free( /*==========*/ dict_index_t* dict_index, /*!< in: dict_index tree */ buf_block_t* block, /*!< in: block to be freed, x-latched */ mtr_t* mtr) /*!< in: mtr */ { ulint level; level = btr_page_get_level(buf_block_get_frame(block), mtr); btr_page_free_low(dict_index, block, level, mtr); } /**************************************************************//** Sets the child node file address in a node pointer. */ UNIV_INLINE void btr_node_ptr_set_child_page_no( /*===========================*/ rec_t* rec, /*!< in: node pointer record */ page_zip_des_t* page_zip,/*!< in/out: compressed page whose uncompressed part will be updated, or NULL */ const ulint* offsets,/*!< in: array returned by rec_get_offsets() */ ulint page_no,/*!< in: child node address */ mtr_t* mtr) /*!< in: mtr */ { byte* field; ulint len; ut_ad(rec_offs_validate(rec, NULL, offsets)); ut_ad(!page_is_leaf(page_align(rec))); ut_ad(!rec_offs_comp(offsets) || rec_get_node_ptr_flag(rec)); /* The child address is in the last field */ field = rec_get_nth_field(rec, offsets, rec_offs_n_fields(offsets) - 1, &len); ut_ad(len == REC_NODE_PTR_SIZE); if (UNIV_LIKELY_NULL(page_zip)) { page_zip_write_node_ptr(page_zip, rec, rec_offs_data_size(offsets), page_no, mtr); } else { mlog_write_ulint(field, page_no, MLOG_4BYTES, mtr); } } /************************************************************//** Returns the child page of a node pointer and x-latches it. @return child page, x-latched */ static buf_block_t* btr_node_ptr_get_child( /*===================*/ const rec_t* node_ptr,/*!< in: node pointer */ dict_index_t* dict_index, /*!< in: dict_index */ const ulint* offsets,/*!< in: array returned by rec_get_offsets() */ mtr_t* mtr) /*!< in: mtr */ { ulint page_no; ulint space; ut_ad(rec_offs_validate(node_ptr, dict_index, offsets)); space = page_get_space_id(page_align(node_ptr)); page_no = btr_node_ptr_get_child_page_no(node_ptr, offsets); return(btr_block_get(space, dict_table_zip_size(dict_index->table), page_no, RW_X_LATCH, mtr)); } /************************************************************//** Returns the upper level node pointer to a page. It is assumed that mtr holds an x-latch on the tree. @return rec_get_offsets() of the node pointer record */ static ulint* btr_page_get_father_node_ptr_func( /*==============================*/ ulint* offsets,/*!< in: work area for the return value */ mem_heap_t* heap, /*!< in: memory heap to use */ btr_cur_t* cursor, /*!< in: cursor pointing to user record, out: cursor on node pointer record, its page x-latched */ const char* file, /*!< in: file name */ ulint line, /*!< in: line where called */ mtr_t* mtr) /*!< in: mtr */ { dtuple_t* tuple; rec_t* user_rec; rec_t* node_ptr; ulint level; ulint page_no; dict_index_t* dict_index; page_no = buf_block_get_page_no(btr_cur_get_block(cursor)); dict_index = btr_cur_get_index(cursor); ut_ad(mtr_memo_contains(mtr, dict_index_get_lock(dict_index), MTR_MEMO_X_LOCK)); ut_ad(dict_index_get_page(dict_index) != page_no); level = btr_page_get_level(btr_cur_get_page(cursor), mtr); user_rec = btr_cur_get_rec(cursor); ut_a(page_rec_is_user_rec(user_rec)); tuple = dict_index_build_node_ptr(dict_index, user_rec, 0, heap, level); btr_cur_search_to_nth_level(dict_index, level + 1, tuple, PAGE_CUR_LE, BTR_CONT_MODIFY_TREE, cursor, 0, file, line, mtr); node_ptr = btr_cur_get_rec(cursor); ut_ad(!page_rec_is_comp(node_ptr) || rec_get_status(node_ptr) == REC_STATUS_NODE_PTR); offsets = rec_get_offsets(node_ptr, dict_index, offsets, ULINT_UNDEFINED, &heap); if (UNIV_UNLIKELY(btr_node_ptr_get_child_page_no(node_ptr, offsets) != page_no)) { rec_t* print_rec; ib_logger(ib_stream, "InnoDB: Dump of the child page:\n"); buf_page_print(page_align(user_rec), 0); ib_logger(ib_stream, "InnoDB: Dump of the parent page:\n"); buf_page_print(page_align(node_ptr), 0); ib_logger(ib_stream, "InnoDB: Corruption of an index tree: table "); ut_print_name(ib_stream, NULL, TRUE, dict_index->table_name); ib_logger(", index ", ib_stream); ut_print_name(ib_stream, NULL, FALSE, dict_index->name); ib_logger(ib_stream, ",\n" "InnoDB: father ptr page no %lu, child page no %lu\n", (ulong) btr_node_ptr_get_child_page_no(node_ptr, offsets), (ulong) page_no); print_rec = page_rec_get_next( page_get_infimum_rec(page_align(user_rec))); offsets = rec_get_offsets(print_rec, dict_index, offsets, ULINT_UNDEFINED, &heap); page_rec_print(print_rec, offsets); offsets = rec_get_offsets(node_ptr, dict_index, offsets, ULINT_UNDEFINED, &heap); page_rec_print(node_ptr, offsets); ib_logger(ib_stream, "InnoDB: You should dump + drop + reimport the table" " to fix the\n" "InnoDB: corruption. If the crash happens at " "the database startup, see\n" "InnoDB: InnoDB website for details about\n" "InnoDB: forcing recovery. " "Then dump + drop + reimport.\n"); ut_error; } return(offsets); } #define btr_page_get_father_node_ptr(of,heap,cur,mtr) \ btr_page_get_father_node_ptr_func(of,heap,cur,__FILE__,__LINE__,mtr) /************************************************************//** Returns the upper level node pointer to a page. It is assumed that mtr holds an x-latch on the tree. @return rec_get_offsets() of the node pointer record */ static ulint* btr_page_get_father_block( /*======================*/ ulint* offsets,/*!< in: work area for the return value */ mem_heap_t* heap, /*!< in: memory heap to use */ dict_index_t* dict_index,/*!< in: b-tree index */ buf_block_t* block, /*!< in: child page in the index */ mtr_t* mtr, /*!< in: mtr */ btr_cur_t* cursor) /*!< out: cursor on node pointer record, its page x-latched */ { rec_t* rec = page_rec_get_next(page_get_infimum_rec(buf_block_get_frame( block))); btr_cur_position(dict_index, rec, block, cursor); return(btr_page_get_father_node_ptr(offsets, heap, cursor, mtr)); } /************************************************************//** Seeks to the upper level node pointer to a page. It is assumed that mtr holds an x-latch on the tree. */ static void btr_page_get_father( /*================*/ dict_index_t* dict_index,/*!< in: b-tree index */ buf_block_t* block, /*!< in: child page in the index */ mtr_t* mtr, /*!< in: mtr */ btr_cur_t* cursor) /*!< out: cursor on node pointer record, its page x-latched */ { mem_heap_t* heap; rec_t* rec = page_rec_get_next(page_get_infimum_rec(buf_block_get_frame( block))); btr_cur_position(dict_index, rec, block, cursor); heap = mem_heap_create(100); btr_page_get_father_node_ptr(NULL, heap, cursor, mtr); mem_heap_free(heap); } /************************************************************//** Creates the root node for a new index tree. @return page number of the created root, FIL_NULL if did not succeed */ UNIV_INTERN ulint btr_create( /*=======*/ ulint type, /*!< in: type of the index */ ulint space, /*!< in: space where created */ ulint zip_size,/*!< in: compressed page size in bytes or 0 for uncompressed pages */ dulint index_id,/*!< in: index id */ dict_index_t* dict_index,/*!< in: dict_index */ mtr_t* mtr) /*!< in: mini-transaction handle */ { ulint page_no; buf_block_t* block; buf_frame_t* frame; page_t* page; page_zip_des_t* page_zip = NULL; /* Create the two new segments (one, in the case of an ibuf tree) for the index tree; the segment headers are put on the allocated root page (for an ibuf tree, not in the root, but on a separate ibuf header page) */ if (type & DICT_IBUF) { /* Allocate first the ibuf header page */ buf_block_t* ibuf_hdr_block = fseg_create( space, 0, IBUF_HEADER + IBUF_TREE_SEG_HEADER, mtr); buf_block_dbg_add_level(ibuf_hdr_block, SYNC_TREE_NODE_NEW); ut_ad(buf_block_get_page_no(ibuf_hdr_block) == IBUF_HEADER_PAGE_NO); /* Allocate then the next page to the segment: it will be the tree root page */ page_no = fseg_alloc_free_page(buf_block_get_frame( ibuf_hdr_block) + IBUF_HEADER + IBUF_TREE_SEG_HEADER, IBUF_TREE_ROOT_PAGE_NO, FSP_UP, mtr); ut_ad(page_no == IBUF_TREE_ROOT_PAGE_NO); block = buf_page_get(space, zip_size, page_no, RW_X_LATCH, mtr); } else { block = fseg_create(space, 0, PAGE_HEADER + PAGE_BTR_SEG_TOP, mtr); } if (block == NULL) { return(FIL_NULL); } page_no = buf_block_get_page_no(block); frame = buf_block_get_frame(block); buf_block_dbg_add_level(block, SYNC_TREE_NODE_NEW); if (type & DICT_IBUF) { /* It is an insert buffer tree: initialize the free list */ ut_ad(page_no == IBUF_TREE_ROOT_PAGE_NO); flst_init(frame + PAGE_HEADER + PAGE_BTR_IBUF_FREE_LIST, mtr); } else { /* It is a non-ibuf tree: create a file segment for leaf pages */ if (!fseg_create(space, page_no, PAGE_HEADER + PAGE_BTR_SEG_LEAF, mtr)) { /* Not enough space for new segment, free root segment before return. */ btr_free_root(space, zip_size, page_no, mtr); return(FIL_NULL); } /* The fseg create acquires a second latch on the page, therefore we must declare it: */ buf_block_dbg_add_level(block, SYNC_TREE_NODE_NEW); } /* Create a new index page on the allocated segment page */ page_zip = buf_block_get_page_zip(block); if (UNIV_LIKELY_NULL(page_zip)) { page = page_create_zip(block, dict_index, 0, mtr); } else { page = page_create(block, mtr, dict_table_is_comp(dict_index->table)); /* Set the level of the new index page */ btr_page_set_level(page, NULL, 0, mtr); } block->check_index_page_at_flush = TRUE; /* Set the index id of the page */ btr_page_set_index_id(page, page_zip, index_id, mtr); /* Set the next node and previous node fields */ btr_page_set_next(page, page_zip, FIL_NULL, mtr); btr_page_set_prev(page, page_zip, FIL_NULL, mtr); /* We reset the free bits for the page to allow creation of several trees in the same mtr, otherwise the latch on a bitmap page would prevent it because of the latching order */ if (!(type & DICT_CLUSTERED)) { ibuf_reset_free_bits(block); } /* In the following assertion we test that two records of maximum allowed size fit on the root page: this fact is needed to ensure correctness of split algorithms */ ut_ad(page_get_max_insert_size(page, 2) > 2 * BTR_PAGE_MAX_REC_SIZE); return(page_no); } /************************************************************//** Frees a B-tree except the root page, which MUST be freed after this by calling btr_free_root. */ UNIV_INTERN void btr_free_but_not_root( /*==================*/ ulint space, /*!< in: space where created */ ulint zip_size, /*!< in: compressed page size in bytes or 0 for uncompressed pages */ ulint root_page_no) /*!< in: root page number */ { ibool finished; page_t* root; mtr_t mtr; leaf_loop: mtr_start(&mtr); root = btr_page_get(space, zip_size, root_page_no, RW_X_LATCH, &mtr); #ifdef UNIV_BTR_DEBUG ut_a(btr_root_fseg_validate(FIL_PAGE_DATA + PAGE_BTR_SEG_LEAF + root, space)); ut_a(btr_root_fseg_validate(FIL_PAGE_DATA + PAGE_BTR_SEG_TOP + root, space)); #endif /* UNIV_BTR_DEBUG */ /* NOTE: page hash indexes are dropped when a page is freed inside fsp0fsp. */ finished = fseg_free_step(root + PAGE_HEADER + PAGE_BTR_SEG_LEAF, &mtr); mtr_commit(&mtr); if (!finished) { goto leaf_loop; } top_loop: mtr_start(&mtr); root = btr_page_get(space, zip_size, root_page_no, RW_X_LATCH, &mtr); #ifdef UNIV_BTR_DEBUG ut_a(btr_root_fseg_validate(FIL_PAGE_DATA + PAGE_BTR_SEG_TOP + root, space)); #endif /* UNIV_BTR_DEBUG */ finished = fseg_free_step_not_header( root + PAGE_HEADER + PAGE_BTR_SEG_TOP, &mtr); mtr_commit(&mtr); if (!finished) { goto top_loop; } } /************************************************************//** Frees the B-tree root page. Other tree MUST already have been freed. */ UNIV_INTERN void btr_free_root( /*==========*/ ulint space, /*!< in: space where created */ ulint zip_size, /*!< in: compressed page size in bytes or 0 for uncompressed pages */ ulint root_page_no, /*!< in: root page number */ mtr_t* mtr) /*!< in: a mini-transaction which has already been started */ { buf_block_t* block; fseg_header_t* header; block = btr_block_get(space, zip_size, root_page_no, RW_X_LATCH, mtr); btr_search_drop_page_hash_index(block); header = buf_block_get_frame(block) + PAGE_HEADER + PAGE_BTR_SEG_TOP; #ifdef UNIV_BTR_DEBUG ut_a(btr_root_fseg_validate(header, space)); #endif /* UNIV_BTR_DEBUG */ while (!fseg_free_step(header, mtr)); } #endif /* !UNIV_HOTBACKUP */ /*************************************************************//** Reorganizes an index page. */ static ibool btr_page_reorganize_low( /*====================*/ ibool recovery,/*!< in: TRUE if called in recovery: locks should not be updated, i.e., there cannot exist locks on the page, and a hash index should not be dropped: it cannot exist */ buf_block_t* block, /*!< in: page to be reorganized */ dict_index_t* dict_index, /*!< in: record descriptor */ mtr_t* mtr) /*!< in: mtr */ { page_t* page = buf_block_get_frame(block); page_zip_des_t* page_zip = buf_block_get_page_zip(block); buf_block_t* temp_block; page_t* temp_page; ulint log_mode; ulint data_size1; ulint data_size2; ulint max_ins_size1; ulint max_ins_size2; ibool success = FALSE; ut_ad(mtr_memo_contains(mtr, block, MTR_MEMO_PAGE_X_FIX)); ut_ad(!!page_is_comp(page) == dict_table_is_comp(dict_index->table)); #ifdef UNIV_ZIP_DEBUG ut_a(!page_zip || page_zip_validate(page_zip, page)); #endif /* UNIV_ZIP_DEBUG */ data_size1 = page_get_data_size(page); max_ins_size1 = page_get_max_insert_size_after_reorganize(page, 1); #ifndef UNIV_HOTBACKUP /* Write the log record */ mlog_open_and_write_index(mtr, page, dict_index, page_is_comp(page) ? MLOG_COMP_PAGE_REORGANIZE : MLOG_PAGE_REORGANIZE, 0); #endif /* !UNIV_HOTBACKUP */ /* Turn logging off */ log_mode = mtr_set_log_mode(mtr, MTR_LOG_NONE); #ifndef UNIV_HOTBACKUP temp_block = buf_block_alloc(0); #else /* !UNIV_HOTBACKUP */ ut_ad(block == back_block1); temp_block = back_block2; #endif /* !UNIV_HOTBACKUP */ temp_page = temp_block->frame; /* Copy the old page to temporary space */ buf_frame_copy(temp_page, page); #ifndef UNIV_HOTBACKUP if (UNIV_LIKELY(!recovery)) { btr_search_drop_page_hash_index(block); } block->check_index_page_at_flush = TRUE; #endif /* !UNIV_HOTBACKUP */ /* Recreate the page: note that global data on page (possible segment headers, next page-field, etc.) is preserved intact */ page_create(block, mtr, dict_table_is_comp(dict_index->table)); /* Copy the records from the temporary space to the recreated page; do not copy the lock bits yet */ page_copy_rec_list_end_no_locks(block, temp_block, page_get_infimum_rec(temp_page), dict_index, mtr); if (dict_index_is_sec_or_ibuf(dict_index) && page_is_leaf(page)) { /* Copy max trx id to recreated page */ trx_id_t max_trx_id = page_get_max_trx_id(temp_page); page_set_max_trx_id(block, NULL, max_trx_id, mtr); /* In crash recovery, dict_index_is_sec_or_ibuf() always returns TRUE, even for clustered indexes. max_trx_id is unused in clustered index pages. */ ut_ad(!ut_dulint_is_zero(max_trx_id) || recovery); } if (UNIV_LIKELY_NULL(page_zip) && UNIV_UNLIKELY (!page_zip_compress(page_zip, page, dict_index, NULL))) { /* Restore the old page and exit. */ #if defined UNIV_DEBUG || defined UNIV_ZIP_DEBUG /* Check that the bytes that we skip are identical. */ ut_a(!memcmp(page, temp_page, PAGE_HEADER)); ut_a(!memcmp(PAGE_HEADER + PAGE_N_RECS + page, PAGE_HEADER + PAGE_N_RECS + temp_page, PAGE_DATA - (PAGE_HEADER + PAGE_N_RECS))); ut_a(!memcmp(UNIV_PAGE_SIZE - FIL_PAGE_DATA_END + page, UNIV_PAGE_SIZE - FIL_PAGE_DATA_END + temp_page, FIL_PAGE_DATA_END)); #endif /* UNIV_DEBUG || UNIV_ZIP_DEBUG */ memcpy(PAGE_HEADER + page, PAGE_HEADER + temp_page, PAGE_N_RECS - PAGE_N_DIR_SLOTS); memcpy(PAGE_DATA + page, PAGE_DATA + temp_page, UNIV_PAGE_SIZE - PAGE_DATA - FIL_PAGE_DATA_END); #if defined UNIV_DEBUG || defined UNIV_ZIP_DEBUG ut_a(!memcmp(page, temp_page, UNIV_PAGE_SIZE)); #endif /* UNIV_DEBUG || UNIV_ZIP_DEBUG */ goto func_exit; } #ifndef UNIV_HOTBACKUP if (UNIV_LIKELY(!recovery)) { /* Update the record lock bitmaps */ lock_move_reorganize_page(block, temp_block); } #endif /* !UNIV_HOTBACKUP */ data_size2 = page_get_data_size(page); max_ins_size2 = page_get_max_insert_size_after_reorganize(page, 1); if (UNIV_UNLIKELY(data_size1 != data_size2) || UNIV_UNLIKELY(max_ins_size1 != max_ins_size2)) { buf_page_print(page, 0); buf_page_print(temp_page, 0); ib_logger(ib_stream, "InnoDB: Error: page old data size %lu" " new data size %lu\n" "InnoDB: Error: page old max ins size %lu" " new max ins size %lu\n" "InnoDB: Submit a detailed bug report, " "check the InnoDB website for details", (unsigned long) data_size1, (unsigned long) data_size2, (unsigned long) max_ins_size1, (unsigned long) max_ins_size2); } else { success = TRUE; } func_exit: #ifdef UNIV_ZIP_DEBUG ut_a(!page_zip || page_zip_validate(page_zip, page)); #endif /* UNIV_ZIP_DEBUG */ #ifndef UNIV_HOTBACKUP buf_block_free(temp_block); #endif /* !UNIV_HOTBACKUP */ /* Restore logging mode */ mtr_set_log_mode(mtr, log_mode); return(success); } #ifndef UNIV_HOTBACKUP /*************************************************************//** Reorganizes an index page. IMPORTANT: if btr_page_reorganize() is invoked on a compressed leaf page of a non-clustered index, the caller must update the insert buffer free bits in the same mini-transaction in such a way that the modification will be redo-logged. @return TRUE on success, FALSE on failure */ UNIV_INTERN ibool btr_page_reorganize( /*================*/ buf_block_t* block, /*!< in: page to be reorganized */ dict_index_t* dict_index, /*!< in: record descriptor */ mtr_t* mtr) /*!< in: mtr */ { return(btr_page_reorganize_low(FALSE, block, dict_index, mtr)); } #endif /* !UNIV_HOTBACKUP */ /***********************************************************//** Parses a redo log record of reorganizing a page. @return end of log record or NULL */ UNIV_INTERN byte* btr_parse_page_reorganize( /*======================*/ byte* ptr, /*!< in: buffer */ byte* end_ptr __attribute__((unused)), /*!< in: buffer end */ dict_index_t* dict_index, /*!< in: record descriptor */ buf_block_t* block, /*!< in: page to be reorganized, or NULL */ mtr_t* mtr) /*!< in: mtr or NULL */ { ut_ad(ptr && end_ptr); /* The record is empty, except for the record initial part */ if (UNIV_LIKELY(block != NULL)) { btr_page_reorganize_low(TRUE, block, dict_index, mtr); } return(ptr); } #ifndef UNIV_HOTBACKUP /*************************************************************//** Empties an index page. @see btr_page_create(). */ static void btr_page_empty( /*===========*/ buf_block_t* block, /*!< in: page to be emptied */ page_zip_des_t* page_zip,/*!< out: compressed page, or NULL */ dict_index_t* dict_index, /*!< in: index of the page */ ulint level, /*!< in: the B-tree level of the page */ mtr_t* mtr) /*!< in: mtr */ { page_t* page = buf_block_get_frame(block); ut_ad(mtr_memo_contains(mtr, block, MTR_MEMO_PAGE_X_FIX)); ut_ad(page_zip == buf_block_get_page_zip(block)); #ifdef UNIV_ZIP_DEBUG ut_a(!page_zip || page_zip_validate(page_zip, page)); #endif /* UNIV_ZIP_DEBUG */ btr_search_drop_page_hash_index(block); /* Recreate the page: note that global data on page (possible segment headers, next page-field, etc.) is preserved intact */ if (UNIV_LIKELY_NULL(page_zip)) { page_create_zip(block, dict_index, level, mtr); } else { page_create(block, mtr, dict_table_is_comp(dict_index->table)); btr_page_set_level(page, NULL, level, mtr); } block->check_index_page_at_flush = TRUE; } /*************************************************************//** Makes tree one level higher by splitting the root, and inserts the tuple. It is assumed that mtr contains an x-latch on the tree. NOTE that the operation of this function must always succeed, we cannot reverse it: therefore enough free disk space must be guaranteed to be available before this function is called. @return inserted record */ UNIV_INTERN rec_t* btr_root_raise_and_insert( /*======================*/ btr_cur_t* cursor, /*!< in: cursor at which to insert: must be on the root page; when the function returns, the cursor is positioned on the predecessor of the inserted record */ const dtuple_t* tuple, /*!< in: tuple to insert */ ulint n_ext, /*!< in: number of externally stored columns */ mtr_t* mtr) /*!< in: mtr */ { dict_index_t* dict_index; page_t* root; page_t* new_page; ulint new_page_no; rec_t* rec; mem_heap_t* heap; dtuple_t* node_ptr; ulint level; rec_t* node_ptr_rec; page_cur_t* page_cursor; page_zip_des_t* root_page_zip = NULL; page_zip_des_t* new_page_zip = NULL; buf_block_t* root_block; buf_block_t* new_block; root = btr_cur_get_page(cursor); root_block = btr_cur_get_block(cursor); root_page_zip = buf_block_get_page_zip(root_block); #ifdef UNIV_ZIP_DEBUG ut_a(!root_page_zip || page_zip_validate(root_page_zip, root)); #endif /* UNIV_ZIP_DEBUG */ dict_index = btr_cur_get_index(cursor); #ifdef UNIV_BTR_DEBUG if (!dict_index_is_ibuf(dict_index)) { ulint space = dict_index_get_space(dict_index); ut_a(btr_root_fseg_validate(FIL_PAGE_DATA + PAGE_BTR_SEG_LEAF + root, space)); ut_a(btr_root_fseg_validate(FIL_PAGE_DATA + PAGE_BTR_SEG_TOP + root, space)); } ut_a(dict_index_get_page(dict_index) == page_get_page_no(root)); #endif /* UNIV_BTR_DEBUG */ ut_ad(mtr_memo_contains(mtr, dict_index_get_lock(dict_index), MTR_MEMO_X_LOCK)); ut_ad(mtr_memo_contains(mtr, root_block, MTR_MEMO_PAGE_X_FIX)); /* Allocate a new page to the tree. Root splitting is done by first moving the root records to the new page, emptying the root, putting a node pointer to the new page, and then splitting the new page. */ level = btr_page_get_level(root, mtr); new_block = btr_page_alloc(dict_index, 0, FSP_NO_DIR, level, mtr); new_page = buf_block_get_frame(new_block); new_page_zip = buf_block_get_page_zip(new_block); ut_a(!new_page_zip == !root_page_zip); ut_a(!new_page_zip || page_zip_get_size(new_page_zip) == page_zip_get_size(root_page_zip)); btr_page_create(new_block, new_page_zip, dict_index, level, mtr); /* Set the next node and previous node fields of new page */ btr_page_set_next(new_page, new_page_zip, FIL_NULL, mtr); btr_page_set_prev(new_page, new_page_zip, FIL_NULL, mtr); /* Copy the records from root to the new page one by one. */ if (0 #ifdef UNIV_ZIP_COPY || new_page_zip #endif /* UNIV_ZIP_COPY */ || UNIV_UNLIKELY (!page_copy_rec_list_end(new_block, root_block, page_get_infimum_rec(root), dict_index, mtr))) { ut_a(new_page_zip); /* Copy the page byte for byte. */ page_zip_copy_recs(new_page_zip, new_page, root_page_zip, root, dict_index, mtr); /* Update the lock table and possible hash index. */ lock_move_rec_list_end(new_block, root_block, page_get_infimum_rec(root)); btr_search_move_or_delete_hash_entries(new_block, root_block, dict_index); } /* If this is a pessimistic insert which is actually done to perform a pessimistic update then we have stored the lock information of the record to be inserted on the infimum of the root page: we cannot discard the lock structs on the root page */ lock_update_root_raise(new_block, root_block); /* Create a memory heap where the node pointer is stored */ heap = mem_heap_create(100); rec = page_rec_get_next(page_get_infimum_rec(new_page)); new_page_no = buf_block_get_page_no(new_block); /* Build the node pointer (= node key and page address) for the child */ node_ptr = dict_index_build_node_ptr(dict_index, rec, new_page_no, heap, level); /* The node pointer must be marked as the predefined minimum record, as there is no lower alphabetical limit to records in the leftmost node of a level: */ dtuple_set_info_bits(node_ptr, dtuple_get_info_bits(node_ptr) | REC_INFO_MIN_REC_FLAG); /* Rebuild the root page to get free space */ btr_page_empty(root_block, root_page_zip, dict_index, level + 1, mtr); /* Set the next node and previous node fields, although they should already have been set. The previous node field must be FIL_NULL if root_page_zip != NULL, because the REC_INFO_MIN_REC_FLAG (of the first user record) will be set if and only if btr_page_get_prev() == FIL_NULL. */ btr_page_set_next(root, root_page_zip, FIL_NULL, mtr); btr_page_set_prev(root, root_page_zip, FIL_NULL, mtr); page_cursor = btr_cur_get_page_cur(cursor); /* Insert node pointer to the root */ page_cur_set_before_first(root_block, page_cursor); node_ptr_rec = page_cur_tuple_insert(page_cursor, node_ptr, dict_index, 0, mtr); /* The root page should only contain the node pointer to new_page at this point. Thus, the data should fit. */ ut_a(node_ptr_rec); /* Free the memory heap */ mem_heap_free(heap); /* We play safe and reset the free bits for the new page */ #if 0 ib_logger(ib_stream, "Root raise new page no %lu\n", new_page_no); #endif if (!dict_index_is_clust(dict_index)) { ibuf_reset_free_bits(new_block); } /* Reposition the cursor to the child node */ page_cur_search(new_block, dict_index, tuple, PAGE_CUR_LE, page_cursor); /* Split the child and insert tuple */ return(btr_page_split_and_insert(cursor, tuple, n_ext, mtr)); } /*************************************************************//** Decides if the page should be split at the convergence point of inserts converging to the left. @return TRUE if split recommended */ UNIV_INTERN ibool btr_page_get_split_rec_to_left( /*===========================*/ btr_cur_t* cursor, /*!< in: cursor at which to insert */ rec_t** split_rec) /*!< out: if split recommended, the first record on upper half page, or NULL if tuple to be inserted should be first */ { page_t* page; rec_t* insert_point; rec_t* infimum; page = btr_cur_get_page(cursor); insert_point = btr_cur_get_rec(cursor); if (page_header_get_ptr(page, PAGE_LAST_INSERT) == page_rec_get_next(insert_point)) { infimum = page_get_infimum_rec(page); /* If the convergence is in the middle of a page, include also the record immediately before the new insert to the upper page. Otherwise, we could repeatedly move from page to page lots of records smaller than the convergence point. */ if (infimum != insert_point && page_rec_get_next(infimum) != insert_point) { *split_rec = insert_point; } else { *split_rec = page_rec_get_next(insert_point); } return(TRUE); } return(FALSE); } /*************************************************************//** Decides if the page should be split at the convergence point of inserts converging to the right. @return TRUE if split recommended */ UNIV_INTERN ibool btr_page_get_split_rec_to_right( /*============================*/ btr_cur_t* cursor, /*!< in: cursor at which to insert */ rec_t** split_rec) /*!< out: if split recommended, the first record on upper half page, or NULL if tuple to be inserted should be first */ { page_t* page; rec_t* insert_point; page = btr_cur_get_page(cursor); insert_point = btr_cur_get_rec(cursor); /* We use eager heuristics: if the new insert would be right after the previous insert on the same page, we assume that there is a pattern of sequential inserts here. */ if (UNIV_LIKELY(page_header_get_ptr(page, PAGE_LAST_INSERT) == insert_point)) { rec_t* next_rec; next_rec = page_rec_get_next(insert_point); if (page_rec_is_supremum(next_rec)) { split_at_new: /* Split at the new record to insert */ *split_rec = NULL; } else { rec_t* next_next_rec = page_rec_get_next(next_rec); if (page_rec_is_supremum(next_next_rec)) { goto split_at_new; } /* If there are >= 2 user records up from the insert point, split all but 1 off. We want to keep one because then sequential inserts can use the adaptive hash index, as they can do the necessary checks of the right search position just by looking at the records on this page. */ *split_rec = next_next_rec; } return(TRUE); } return(FALSE); } /*************************************************************//** Calculates a split record such that the tuple will certainly fit on its half-page when the split is performed. We assume in this function only that the cursor page has at least one user record. @return split record, or NULL if tuple will be the first record on the lower or upper half-page (determined by btr_page_tuple_smaller()) */ static rec_t* btr_page_get_split_rec( /*===================*/ btr_cur_t* cursor, /*!< in: cursor at which insert should be made */ const dtuple_t* tuple, /*!< in: tuple to insert */ ulint n_ext) /*!< in: number of externally stored columns */ { page_t* page; page_zip_des_t* page_zip; ulint insert_size; ulint free_space; ulint total_data; ulint total_n_recs; ulint total_space; ulint incl_data; rec_t* ins_rec; rec_t* rec; rec_t* next_rec; ulint n; mem_heap_t* heap; ulint* offsets; page = btr_cur_get_page(cursor); insert_size = rec_get_converted_size(cursor->index, tuple, n_ext); free_space = page_get_free_space_of_empty(page_is_comp(page)); page_zip = btr_cur_get_page_zip(cursor); if (UNIV_LIKELY_NULL(page_zip)) { /* Estimate the free space of an empty compressed page. */ ulint free_space_zip = page_zip_empty_size( cursor->index->n_fields, page_zip_get_size(page_zip)); if (UNIV_LIKELY(free_space > (ulint) free_space_zip)) { free_space = (ulint) free_space_zip; } } /* free_space is now the free space of a created new page */ total_data = page_get_data_size(page) + insert_size; total_n_recs = page_get_n_recs(page) + 1; ut_ad(total_n_recs >= 2); total_space = total_data + page_dir_calc_reserved_space(total_n_recs); n = 0; incl_data = 0; ins_rec = btr_cur_get_rec(cursor); rec = page_get_infimum_rec(page); heap = NULL; offsets = NULL; /* We start to include records to the left half, and when the space reserved by them exceeds half of total_space, then if the included records fit on the left page, they will be put there if something was left over also for the right page, otherwise the last included record will be the first on the right half page */ do { /* Decide the next record to include */ if (rec == ins_rec) { rec = NULL; /* NULL denotes that tuple is now included */ } else if (rec == NULL) { rec = page_rec_get_next(ins_rec); } else { rec = page_rec_get_next(rec); } if (rec == NULL) { /* Include tuple */ incl_data += insert_size; } else { offsets = rec_get_offsets(rec, cursor->index, offsets, ULINT_UNDEFINED, &heap); incl_data += rec_offs_size(offsets); } n++; } while (incl_data + page_dir_calc_reserved_space(n) < total_space / 2); if (incl_data + page_dir_calc_reserved_space(n) <= free_space) { /* The next record will be the first on the right half page if it is not the supremum record of page */ if (rec == ins_rec) { rec = NULL; goto func_exit; } else if (rec == NULL) { next_rec = page_rec_get_next(ins_rec); } else { next_rec = page_rec_get_next(rec); } ut_ad(next_rec); if (!page_rec_is_supremum(next_rec)) { rec = next_rec; } } func_exit: if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } return(rec); } /*************************************************************//** Returns TRUE if the insert fits on the appropriate half-page with the chosen split_rec. @return TRUE if fits */ static ibool btr_page_insert_fits( /*=================*/ btr_cur_t* cursor, /*!< in: cursor at which insert should be made */ const rec_t* split_rec,/*!< in: suggestion for first record on upper half-page, or NULL if tuple to be inserted should be first */ const ulint* offsets,/*!< in: rec_get_offsets( split_rec, cursor->index) */ const dtuple_t* tuple, /*!< in: tuple to insert */ ulint n_ext, /*!< in: number of externally stored columns */ mem_heap_t* heap) /*!< in: temporary memory heap */ { page_t* page; ulint insert_size; ulint free_space; ulint total_data; ulint total_n_recs; const rec_t* rec; const rec_t* end_rec; ulint* offs; page = btr_cur_get_page(cursor); ut_ad(!split_rec == !offsets); ut_ad(!offsets || !page_is_comp(page) == !rec_offs_comp(offsets)); ut_ad(!offsets || rec_offs_validate(split_rec, cursor->index, offsets)); insert_size = rec_get_converted_size(cursor->index, tuple, n_ext); free_space = page_get_free_space_of_empty(page_is_comp(page)); /* free_space is now the free space of a created new page */ total_data = page_get_data_size(page) + insert_size; total_n_recs = page_get_n_recs(page) + 1; /* We determine which records (from rec to end_rec, not including end_rec) will end up on the other half page from tuple when it is inserted. */ if (split_rec == NULL) { rec = page_rec_get_next(page_get_infimum_rec(page)); end_rec = page_rec_get_next(btr_cur_get_rec(cursor)); } else if (cmp_dtuple_rec( cursor->index->cmp_ctx, tuple, split_rec, offsets) >= 0) { rec = page_rec_get_next(page_get_infimum_rec(page)); end_rec = split_rec; } else { rec = split_rec; end_rec = page_get_supremum_rec(page); } if (total_data + page_dir_calc_reserved_space(total_n_recs) <= free_space) { /* Ok, there will be enough available space on the half page where the tuple is inserted */ return(TRUE); } offs = NULL; while (rec != end_rec) { /* In this loop we calculate the amount of reserved space after rec is removed from page. */ offs = rec_get_offsets(rec, cursor->index, offs, ULINT_UNDEFINED, &heap); total_data -= rec_offs_size(offs); total_n_recs--; if (total_data + page_dir_calc_reserved_space(total_n_recs) <= free_space) { /* Ok, there will be enough available space on the half page where the tuple is inserted */ return(TRUE); } rec = page_rec_get_next_const(rec); } return(FALSE); } /*******************************************************//** Inserts a data tuple to a tree on a non-leaf level. It is assumed that mtr holds an x-latch on the tree. */ UNIV_INTERN void btr_insert_on_non_leaf_level_func( /*==============================*/ dict_index_t* dict_index, /*!< in: index */ ulint level, /*!< in: level, must be > 0 */ dtuple_t* tuple, /*!< in: the record to be inserted */ const char* file, /*!< in: file name */ ulint line, /*!< in: line where called */ mtr_t* mtr) /*!< in: mtr */ { big_rec_t* dummy_big_rec; btr_cur_t cursor; ulint err; rec_t* rec; ut_ad(level > 0); btr_cur_search_to_nth_level(dict_index, level, tuple, PAGE_CUR_LE, BTR_CONT_MODIFY_TREE, &cursor, 0, file, line, mtr); err = btr_cur_pessimistic_insert(BTR_NO_LOCKING_FLAG | BTR_KEEP_SYS_FLAG | BTR_NO_UNDO_LOG_FLAG, &cursor, tuple, &rec, &dummy_big_rec, 0, NULL, mtr); ut_a(err == DB_SUCCESS); } /**************************************************************//** Attaches the halves of an index page on the appropriate level in an index tree. */ static void btr_attach_half_pages( /*==================*/ dict_index_t* dict_index, /*!< in: the index tree */ buf_block_t* block, /*!< in/out: page to be split */ rec_t* split_rec, /*!< in: first record on upper half page */ buf_block_t* new_block, /*!< in/out: the new half page */ ulint direction, /*!< in: FSP_UP or FSP_DOWN */ mtr_t* mtr) /*!< in: mtr */ { ulint space; ulint zip_size; ulint prev_page_no; ulint next_page_no; ulint level; page_t* page = buf_block_get_frame(block); page_t* lower_page; page_t* upper_page; ulint lower_page_no; ulint upper_page_no; page_zip_des_t* lower_page_zip; page_zip_des_t* upper_page_zip; dtuple_t* node_ptr_upper; mem_heap_t* heap; ut_ad(mtr_memo_contains(mtr, block, MTR_MEMO_PAGE_X_FIX)); ut_ad(mtr_memo_contains(mtr, new_block, MTR_MEMO_PAGE_X_FIX)); /* Create a memory heap where the data tuple is stored */ heap = mem_heap_create(1024); /* Based on split direction, decide upper and lower pages */ if (direction == FSP_DOWN) { btr_cur_t cursor; ulint* offsets; lower_page = buf_block_get_frame(new_block); lower_page_no = buf_block_get_page_no(new_block); lower_page_zip = buf_block_get_page_zip(new_block); upper_page = buf_block_get_frame(block); upper_page_no = buf_block_get_page_no(block); upper_page_zip = buf_block_get_page_zip(block); /* Look up the index for the node pointer to page */ offsets = btr_page_get_father_block(NULL, heap, dict_index, block, mtr, &cursor); /* Replace the address of the old child node (= page) with the address of the new lower half */ btr_node_ptr_set_child_page_no( btr_cur_get_rec(&cursor), btr_cur_get_page_zip(&cursor), offsets, lower_page_no, mtr); mem_heap_empty(heap); } else { lower_page = buf_block_get_frame(block); lower_page_no = buf_block_get_page_no(block); lower_page_zip = buf_block_get_page_zip(block); upper_page = buf_block_get_frame(new_block); upper_page_no = buf_block_get_page_no(new_block); upper_page_zip = buf_block_get_page_zip(new_block); } /* Get the level of the split pages */ level = btr_page_get_level(buf_block_get_frame(block), mtr); ut_ad(level == btr_page_get_level(buf_block_get_frame(new_block), mtr)); /* Build the node pointer (= node key and page address) for the upper half */ node_ptr_upper = dict_index_build_node_ptr(dict_index, split_rec, upper_page_no, heap, level); /* Insert it next to the pointer to the lower half. Note that this may generate recursion leading to a split on the higher level. */ btr_insert_on_non_leaf_level(dict_index, level + 1, node_ptr_upper, mtr); /* Free the memory heap */ mem_heap_free(heap); /* Get the previous and next pages of page */ prev_page_no = btr_page_get_prev(page, mtr); next_page_no = btr_page_get_next(page, mtr); space = buf_block_get_space(block); zip_size = buf_block_get_zip_size(block); /* Update page links of the level */ if (prev_page_no != FIL_NULL) { buf_block_t* prev_block = btr_block_get(space, zip_size, prev_page_no, RW_X_LATCH, mtr); #ifdef UNIV_BTR_DEBUG ut_a(page_is_comp(prev_block->frame) == page_is_comp(page)); ut_a(btr_page_get_next(prev_block->frame, mtr) == buf_block_get_page_no(block)); #endif /* UNIV_BTR_DEBUG */ btr_page_set_next(buf_block_get_frame(prev_block), buf_block_get_page_zip(prev_block), lower_page_no, mtr); } if (next_page_no != FIL_NULL) { buf_block_t* next_block = btr_block_get(space, zip_size, next_page_no, RW_X_LATCH, mtr); #ifdef UNIV_BTR_DEBUG ut_a(page_is_comp(next_block->frame) == page_is_comp(page)); ut_a(btr_page_get_prev(next_block->frame, mtr) == page_get_page_no(page)); #endif /* UNIV_BTR_DEBUG */ btr_page_set_prev(buf_block_get_frame(next_block), buf_block_get_page_zip(next_block), upper_page_no, mtr); } btr_page_set_prev(lower_page, lower_page_zip, prev_page_no, mtr); btr_page_set_next(lower_page, lower_page_zip, upper_page_no, mtr); btr_page_set_prev(upper_page, upper_page_zip, lower_page_no, mtr); btr_page_set_next(upper_page, upper_page_zip, next_page_no, mtr); } /*************************************************************//** Determine if a tuple is smaller than any record on the page. @return TRUE if smaller */ static ibool btr_page_tuple_smaller( /*===================*/ btr_cur_t* cursor, /*!< in: b-tree cursor */ const dtuple_t* tuple, /*!< in: tuple to consider */ ulint* offsets,/*!< in/out: temporary storage */ ulint n_uniq, /*!< in: number of unique fields in the index page records */ mem_heap_t** heap) /*!< in/out: heap for offsets */ { buf_block_t* block; const rec_t* first_rec; page_cur_t pcur; /* Read the first user record in the page. */ block = btr_cur_get_block(cursor); page_cur_set_before_first(block, &pcur); page_cur_move_to_next(&pcur); first_rec = page_cur_get_rec(&pcur); offsets = rec_get_offsets( first_rec, cursor->index, offsets, n_uniq, heap); return(cmp_dtuple_rec(cursor->index->cmp_ctx, tuple, first_rec, offsets) < 0); } /*************************************************************//** Splits an index page to halves and inserts the tuple. It is assumed that mtr holds an x-latch to the index tree. NOTE: the tree x-latch is released within this function! NOTE that the operation of this function must always succeed, we cannot reverse it: therefore enough free disk space (2 pages) must be guaranteed to be available before this function is called. @return inserted record */ UNIV_INTERN rec_t* btr_page_split_and_insert( /*======================*/ btr_cur_t* cursor, /*!< in: cursor at which to insert; when the function returns, the cursor is positioned on the predecessor of the inserted record */ const dtuple_t* tuple, /*!< in: tuple to insert */ ulint n_ext, /*!< in: number of externally stored columns */ mtr_t* mtr) /*!< in: mtr */ { buf_block_t* block; page_t* page; page_zip_des_t* page_zip; ulint page_no; byte direction; ulint hint_page_no; buf_block_t* new_block; page_t* new_page; page_zip_des_t* new_page_zip; rec_t* split_rec; buf_block_t* left_block; buf_block_t* right_block; buf_block_t* insert_block; page_t* insert_page; page_cur_t* page_cursor; rec_t* first_rec; byte* buf = 0; /* remove warning */ rec_t* move_limit; ibool insert_will_fit; ibool insert_left; ulint n_iterations = 0; rec_t* rec; mem_heap_t* heap; ulint n_uniq; ulint* offsets; heap = mem_heap_create(1024); n_uniq = dict_index_get_n_unique_in_tree(cursor->index); func_start: mem_heap_empty(heap); offsets = NULL; ut_ad(mtr_memo_contains(mtr, dict_index_get_lock(cursor->index), MTR_MEMO_X_LOCK)); #ifdef UNIV_SYNC_DEBUG ut_ad(rw_lock_own(dict_index_get_lock(cursor->index), RW_LOCK_EX)); #endif /* UNIV_SYNC_DEBUG */ block = btr_cur_get_block(cursor); page = buf_block_get_frame(block); page_zip = buf_block_get_page_zip(block); ut_ad(mtr_memo_contains(mtr, block, MTR_MEMO_PAGE_X_FIX)); ut_ad(page_get_n_recs(page) >= 1); page_no = buf_block_get_page_no(block); /* 1. Decide the split record; split_rec == NULL means that the tuple to be inserted should be the first record on the upper half-page */ insert_left = FALSE; if (n_iterations > 0) { direction = FSP_UP; hint_page_no = page_no + 1; split_rec = btr_page_get_split_rec(cursor, tuple, n_ext); if (UNIV_UNLIKELY(split_rec == NULL)) { insert_left = btr_page_tuple_smaller( cursor, tuple, offsets, n_uniq, &heap); } } else if (btr_page_get_split_rec_to_right(cursor, &split_rec)) { direction = FSP_UP; hint_page_no = page_no + 1; } else if (btr_page_get_split_rec_to_left(cursor, &split_rec)) { direction = FSP_DOWN; hint_page_no = page_no - 1; ut_ad(split_rec); } else { direction = FSP_UP; hint_page_no = page_no + 1; /* If there is only one record in the index page, we can't split the node in the middle by default. We need to determine whether the new record will be inserted to the left or right. */ if (page_get_n_recs(page) > 1) { split_rec = page_get_middle_rec(page); } else if (btr_page_tuple_smaller(cursor, tuple, offsets, n_uniq, &heap)) { split_rec = page_rec_get_next( page_get_infimum_rec(page)); } else { split_rec = NULL; } } /* 2. Allocate a new page to the index */ new_block = btr_page_alloc(cursor->index, hint_page_no, direction, btr_page_get_level(page, mtr), mtr); new_page = buf_block_get_frame(new_block); new_page_zip = buf_block_get_page_zip(new_block); btr_page_create(new_block, new_page_zip, cursor->index, btr_page_get_level(page, mtr), mtr); /* 3. Calculate the first record on the upper half-page, and the first record (move_limit) on original page which ends up on the upper half */ if (split_rec) { first_rec = move_limit = split_rec; offsets = rec_get_offsets(split_rec, cursor->index, offsets, n_uniq, &heap); insert_left = cmp_dtuple_rec( cursor->index->cmp_ctx, tuple, split_rec, offsets) < 0; if (UNIV_UNLIKELY(!insert_left && new_page_zip && n_iterations > 0)) { /* If a compressed page has already been split, avoid further splits by inserting the record to an empty page. */ split_rec = NULL; goto insert_empty; } } else { insert_empty: ut_ad(!split_rec); buf = mem_alloc(rec_get_converted_size(cursor->index, tuple, n_ext)); first_rec = rec_convert_dtuple_to_rec(buf, cursor->index, tuple, n_ext); move_limit = page_rec_get_next(btr_cur_get_rec(cursor)); } /* 4. Do first the modifications in the tree structure */ btr_attach_half_pages(cursor->index, block, first_rec, new_block, direction, mtr); /* If the split is made on the leaf level and the insert will fit on the appropriate half-page, we may release the tree x-latch. We can then move the records after releasing the tree latch, thus reducing the tree latch contention. */ if (split_rec) { insert_will_fit = !new_page_zip && btr_page_insert_fits(cursor, split_rec, offsets, tuple, n_ext, heap); } else { mem_free(buf); insert_will_fit = !new_page_zip && btr_page_insert_fits(cursor, NULL, NULL, tuple, n_ext, heap); } if (insert_will_fit && page_is_leaf(page)) { mtr_memo_release(mtr, dict_index_get_lock(cursor->index), MTR_MEMO_X_LOCK); } /* 5. Move then the records to the new page */ if (direction == FSP_DOWN #ifdef UNIV_BTR_AVOID_COPY && page_rec_is_supremum(move_limit)) { /* Instead of moving all records, make the new page the empty page. */ left_block = block; right_block = new_block; } else if (direction == FSP_DOWN #endif /* UNIV_BTR_AVOID_COPY */ ) { /* ib_logger(ib_stream, "Split left\n"); */ // FIXME: The code below has become very confusing :-( if (0 #ifdef UNIV_ZIP_COPY || page_zip #endif /* UNIV_ZIP_COPY */ || UNIV_UNLIKELY (!page_move_rec_list_start(new_block, block, move_limit, cursor->index, mtr))) { /* For some reason, compressing new_page failed, even though it should contain fewer records than the original page. Copy the page byte for byte and then delete the records from both pages as appropriate. Deleting will always succeed. */ ut_a(new_page_zip); page_zip_copy_recs(new_page_zip, new_page, page_zip, page, cursor->index, mtr); page_delete_rec_list_end(move_limit - page + new_page, new_block, cursor->index, ULINT_UNDEFINED, ULINT_UNDEFINED, mtr); /* Update the lock table and possible hash index. */ lock_move_rec_list_start( new_block, block, move_limit, new_page + PAGE_NEW_INFIMUM); btr_search_move_or_delete_hash_entries( new_block, block, cursor->index); /* Delete the records from the source page. */ page_delete_rec_list_start(move_limit, block, cursor->index, mtr); } left_block = new_block; right_block = block; lock_update_split_left(right_block, left_block); #ifdef UNIV_BTR_AVOID_COPY } else if (!split_rec) { /* Instead of moving all records, make the new page the empty page. */ left_block = new_block; right_block = block; #endif /* UNIV_BTR_AVOID_COPY */ } else { /* ib_logger(ib_stream, "Split right\n"); */ if (0 #ifdef UNIV_ZIP_COPY || page_zip #endif /* UNIV_ZIP_COPY */ || UNIV_UNLIKELY (!page_move_rec_list_end(new_block, block, move_limit, cursor->index, mtr))) { /* For some reason, compressing new_page failed, even though it should contain fewer records than the original page. Copy the page byte for byte and then delete the records from both pages as appropriate. Deleting will always succeed. */ ut_a(new_page_zip); page_zip_copy_recs(new_page_zip, new_page, page_zip, page, cursor->index, mtr); page_delete_rec_list_start(move_limit - page + new_page, new_block, cursor->index, mtr); /* Update the lock table and possible hash index. */ lock_move_rec_list_end(new_block, block, move_limit); btr_search_move_or_delete_hash_entries( new_block, block, cursor->index); /* Delete the records from the source page. */ page_delete_rec_list_end(move_limit, block, cursor->index, ULINT_UNDEFINED, ULINT_UNDEFINED, mtr); } left_block = block; right_block = new_block; lock_update_split_right(right_block, left_block); } #ifdef UNIV_ZIP_DEBUG if (UNIV_LIKELY_NULL(page_zip)) { ut_a(page_zip_validate(page_zip, page)); ut_a(page_zip_validate(new_page_zip, new_page)); } #endif /* UNIV_ZIP_DEBUG */ /* At this point, split_rec, move_limit and first_rec may point to garbage on the old page. */ /* 6. The split and the tree modification is now completed. Decide the page where the tuple should be inserted */ if (insert_left) { insert_block = left_block; } else { insert_block = right_block; } insert_page = buf_block_get_frame(insert_block); /* 7. Reposition the cursor for insert and try insertion */ page_cursor = btr_cur_get_page_cur(cursor); page_cur_search(insert_block, cursor->index, tuple, PAGE_CUR_LE, page_cursor); rec = page_cur_tuple_insert(page_cursor, tuple, cursor->index, n_ext, mtr); #ifdef UNIV_ZIP_DEBUG { page_zip_des_t* insert_page_zip = buf_block_get_page_zip(insert_block); ut_a(!insert_page_zip || page_zip_validate(insert_page_zip, insert_page)); } #endif /* UNIV_ZIP_DEBUG */ if (UNIV_LIKELY(rec != NULL)) { goto func_exit; } /* 8. If insert did not fit, try page reorganization */ if (UNIV_UNLIKELY (!btr_page_reorganize(insert_block, cursor->index, mtr))) { goto insert_failed; } page_cur_search(insert_block, cursor->index, tuple, PAGE_CUR_LE, page_cursor); rec = page_cur_tuple_insert(page_cursor, tuple, cursor->index, n_ext, mtr); if (UNIV_UNLIKELY(rec == NULL)) { /* The insert did not fit on the page: loop back to the start of the function for a new split */ insert_failed: /* We play safe and reset the free bits for new_page */ if (!dict_index_is_clust(cursor->index)) { ibuf_reset_free_bits(new_block); } /* ib_logger(ib_stream, "Split second round %lu\n", page_get_page_no(page)); */ n_iterations++; ut_ad(n_iterations < 2 || buf_block_get_page_zip(insert_block)); ut_ad(!insert_will_fit); goto func_start; } func_exit: /* Insert fit on the page: update the free bits for the left and right pages in the same mtr */ if (!dict_index_is_clust(cursor->index) && page_is_leaf(page)) { ibuf_update_free_bits_for_two_pages_low( buf_block_get_zip_size(left_block), left_block, right_block, mtr); } #if 0 ib_logger(ib_stream, "Split and insert done %lu %lu\n", buf_block_get_page_no(left_block), buf_block_get_page_no(right_block)); #endif ut_ad(page_validate(buf_block_get_frame(left_block), cursor->index)); ut_ad(page_validate(buf_block_get_frame(right_block), cursor->index)); mem_heap_free(heap); return(rec); } /*************************************************************//** Removes a page from the level list of pages. */ static void btr_level_list_remove( /*==================*/ ulint space, /*!< in: space where removed */ ulint zip_size,/*!< in: compressed page size in bytes or 0 for uncompressed pages */ page_t* page, /*!< in: page to remove */ mtr_t* mtr) /*!< in: mtr */ { ulint prev_page_no; ulint next_page_no; ut_ad(page && mtr); ut_ad(mtr_memo_contains_page(mtr, page, MTR_MEMO_PAGE_X_FIX)); ut_ad(space == page_get_space_id(page)); /* Get the previous and next page numbers of page */ prev_page_no = btr_page_get_prev(page, mtr); next_page_no = btr_page_get_next(page, mtr); /* Update page links of the level */ if (prev_page_no != FIL_NULL) { buf_block_t* prev_block = btr_block_get(space, zip_size, prev_page_no, RW_X_LATCH, mtr); page_t* prev_page = buf_block_get_frame(prev_block); #ifdef UNIV_BTR_DEBUG ut_a(page_is_comp(prev_page) == page_is_comp(page)); ut_a(btr_page_get_next(prev_page, mtr) == page_get_page_no(page)); #endif /* UNIV_BTR_DEBUG */ btr_page_set_next(prev_page, buf_block_get_page_zip(prev_block), next_page_no, mtr); } if (next_page_no != FIL_NULL) { buf_block_t* next_block = btr_block_get(space, zip_size, next_page_no, RW_X_LATCH, mtr); page_t* next_page = buf_block_get_frame(next_block); #ifdef UNIV_BTR_DEBUG ut_a(page_is_comp(next_page) == page_is_comp(page)); ut_a(btr_page_get_prev(next_page, mtr) == page_get_page_no(page)); #endif /* UNIV_BTR_DEBUG */ btr_page_set_prev(next_page, buf_block_get_page_zip(next_block), prev_page_no, mtr); } } /****************************************************************//** Writes the redo log record for setting an index record as the predefined minimum record. */ UNIV_INLINE void btr_set_min_rec_mark_log( /*=====================*/ rec_t* rec, /*!< in: record */ byte type, /*!< in: MLOG_COMP_REC_MIN_MARK or MLOG_REC_MIN_MARK */ mtr_t* mtr) /*!< in: mtr */ { mlog_write_initial_log_record(rec, type, mtr); /* Write rec offset as a 2-byte ulint */ mlog_catenate_ulint(mtr, page_offset(rec), MLOG_2BYTES); } #else /* !UNIV_HOTBACKUP */ # define btr_set_min_rec_mark_log(rec,comp,mtr) ((void) 0) #endif /* !UNIV_HOTBACKUP */ /****************************************************************//** Parses the redo log record for setting an index record as the predefined minimum record. @return end of log record or NULL */ UNIV_INTERN byte* btr_parse_set_min_rec_mark( /*=======================*/ byte* ptr, /*!< in: buffer */ byte* end_ptr,/*!< in: buffer end */ ulint comp, /*!< in: nonzero=compact page format */ page_t* page, /*!< in: page or NULL */ mtr_t* mtr) /*!< in: mtr or NULL */ { rec_t* rec; if (end_ptr < ptr + 2) { return(NULL); } if (page) { ut_a(!page_is_comp(page) == !comp); rec = page + mach_read_from_2(ptr); btr_set_min_rec_mark(rec, mtr); } return(ptr + 2); } /****************************************************************//** Sets a record as the predefined minimum record. */ UNIV_INTERN void btr_set_min_rec_mark( /*=================*/ rec_t* rec, /*!< in: record */ mtr_t* mtr) /*!< in: mtr */ { ulint info_bits; if (UNIV_LIKELY(page_rec_is_comp(rec))) { info_bits = rec_get_info_bits(rec, TRUE); rec_set_info_bits_new(rec, info_bits | REC_INFO_MIN_REC_FLAG); btr_set_min_rec_mark_log(rec, MLOG_COMP_REC_MIN_MARK, mtr); } else { info_bits = rec_get_info_bits(rec, FALSE); rec_set_info_bits_old(rec, info_bits | REC_INFO_MIN_REC_FLAG); btr_set_min_rec_mark_log(rec, MLOG_REC_MIN_MARK, mtr); } } #ifndef UNIV_HOTBACKUP /*************************************************************//** Deletes on the upper level the node pointer to a page. */ UNIV_INTERN void btr_node_ptr_delete( /*================*/ dict_index_t* dict_index, /*!< in: index tree */ buf_block_t* block, /*!< in: page whose node pointer is deleted */ mtr_t* mtr) /*!< in: mtr */ { btr_cur_t cursor; ibool compressed; ulint err; ut_ad(mtr_memo_contains(mtr, block, MTR_MEMO_PAGE_X_FIX)); /* Delete node pointer on father page */ btr_page_get_father(dict_index, block, mtr, &cursor); compressed = btr_cur_pessimistic_delete(&err, TRUE, &cursor, RB_NONE, mtr); ut_a(err == DB_SUCCESS); if (!compressed) { btr_cur_compress_if_useful(&cursor, mtr); } } /*************************************************************//** If page is the only on its level, this function moves its records to the father page, thus reducing the tree height. */ static void btr_lift_page_up( /*=============*/ dict_index_t* dict_index, /*!< in: index tree */ buf_block_t* block, /*!< in: page which is the only on its level; must not be empty: use btr_discard_only_page_on_level if the last record from the page should be removed */ mtr_t* mtr) /*!< in: mtr */ { buf_block_t* father_block; page_t* father_page; ulint page_level; page_zip_des_t* father_page_zip; page_t* page = buf_block_get_frame(block); ulint root_page_no; buf_block_t* blocks[BTR_MAX_LEVELS]; ulint n_blocks; /*!< last used index in blocks[] */ ulint i; ut_ad(btr_page_get_prev(page, mtr) == FIL_NULL); ut_ad(btr_page_get_next(page, mtr) == FIL_NULL); ut_ad(mtr_memo_contains(mtr, block, MTR_MEMO_PAGE_X_FIX)); page_level = btr_page_get_level(page, mtr); root_page_no = dict_index_get_page(dict_index); { btr_cur_t cursor; mem_heap_t* heap = mem_heap_create(100); ulint* offsets; buf_block_t* b; offsets = btr_page_get_father_block(NULL, heap, dict_index, block, mtr, &cursor); father_block = btr_cur_get_block(&cursor); father_page_zip = buf_block_get_page_zip(father_block); father_page = buf_block_get_frame(father_block); n_blocks = 0; /* Store all ancestor pages so we can reset their levels later on. We have to do all the searches on the tree now because later on, after we've replaced the first level, the tree is in an inconsistent state and can not be searched. */ for (b = father_block; buf_block_get_page_no(b) != root_page_no; ) { ut_a(n_blocks < BTR_MAX_LEVELS); offsets = btr_page_get_father_block(offsets, heap, dict_index, b, mtr, &cursor); blocks[n_blocks++] = b = btr_cur_get_block(&cursor); } mem_heap_free(heap); } btr_search_drop_page_hash_index(block); /* Make the father empty */ btr_page_empty(father_block, father_page_zip, dict_index, page_level, mtr); /* Copy the records to the father page one by one. */ if (0 #ifdef UNIV_ZIP_COPY || father_page_zip #endif /* UNIV_ZIP_COPY */ || UNIV_UNLIKELY (!page_copy_rec_list_end(father_block, block, page_get_infimum_rec(page), dict_index, mtr))) { const page_zip_des_t* page_zip = buf_block_get_page_zip(block); ut_a(father_page_zip); ut_a(page_zip); /* Copy the page byte for byte. */ page_zip_copy_recs(father_page_zip, father_page, page_zip, page, dict_index, mtr); /* Update the lock table and possible hash index. */ lock_move_rec_list_end(father_block, block, page_get_infimum_rec(page)); btr_search_move_or_delete_hash_entries(father_block, block, dict_index); } lock_update_copy_and_discard(father_block, block); /* Go upward to root page, decrementing levels by one. */ for (i = 0; i < n_blocks; i++, page_level++) { page_t* page = buf_block_get_frame(blocks[i]); page_zip_des_t* page_zip= buf_block_get_page_zip(blocks[i]); ut_ad(btr_page_get_level(page, mtr) == page_level + 1); btr_page_set_level(page, page_zip, page_level, mtr); #ifdef UNIV_ZIP_DEBUG ut_a(!page_zip || page_zip_validate(page_zip, page)); #endif /* UNIV_ZIP_DEBUG */ } /* Free the file page */ btr_page_free(dict_index, block, mtr); /* We play it safe and reset the free bits for the father */ if (!dict_index_is_clust(dict_index)) { ibuf_reset_free_bits(father_block); } ut_ad(page_validate(father_page, dict_index)); ut_ad(btr_check_node_ptr(dict_index, father_block, mtr)); } /*************************************************************//** Tries to merge the page first to the left immediate brother if such a brother exists, and the node pointers to the current page and to the brother reside on the same page. If the left brother does not satisfy these conditions, looks at the right brother. If the page is the only one on that level lifts the records of the page to the father page, thus reducing the tree height. It is assumed that mtr holds an x-latch on the tree and on the page. If cursor is on the leaf level, mtr must also hold x-latches to the brothers, if they exist. @return TRUE on success */ UNIV_INTERN ibool btr_compress( /*=========*/ btr_cur_t* cursor, /*!< in: cursor on the page to merge or lift; the page must not be empty: in record delete use btr_discard_page if the page would become empty */ mtr_t* mtr) /*!< in: mtr */ { dict_index_t* dict_index; ulint space; ulint zip_size; ulint left_page_no; ulint right_page_no; buf_block_t* merge_block; page_t* merge_page; page_zip_des_t* merge_page_zip; ibool is_left; buf_block_t* block; page_t* page; btr_cur_t father_cursor; mem_heap_t* heap; ulint* offsets; ulint data_size; ulint n_recs; ulint max_ins_size; ulint max_ins_size_reorg; ulint level; block = btr_cur_get_block(cursor); page = btr_cur_get_page(cursor); dict_index = btr_cur_get_index(cursor); ut_a((ibool) !!page_is_comp(page) == dict_table_is_comp(dict_index->table)); ut_ad(mtr_memo_contains(mtr, dict_index_get_lock(dict_index), MTR_MEMO_X_LOCK)); ut_ad(mtr_memo_contains(mtr, block, MTR_MEMO_PAGE_X_FIX)); level = btr_page_get_level(page, mtr); space = dict_index_get_space(dict_index); zip_size = dict_table_zip_size(dict_index->table); left_page_no = btr_page_get_prev(page, mtr); right_page_no = btr_page_get_next(page, mtr); #if 0 ib_logger(ib_stream, "Merge left page %lu right %lu \n", left_page_no, right_page_no); #endif heap = mem_heap_create(100); offsets = btr_page_get_father_block(NULL, heap, dict_index, block, mtr, &father_cursor); /* Decide the page to which we try to merge and which will inherit the locks */ is_left = left_page_no != FIL_NULL; if (is_left) { merge_block = btr_block_get(space, zip_size, left_page_no, RW_X_LATCH, mtr); merge_page = buf_block_get_frame(merge_block); #ifdef UNIV_BTR_DEBUG ut_a(btr_page_get_next(merge_page, mtr) == buf_block_get_page_no(block)); #endif /* UNIV_BTR_DEBUG */ } else if (right_page_no != FIL_NULL) { merge_block = btr_block_get(space, zip_size, right_page_no, RW_X_LATCH, mtr); merge_page = buf_block_get_frame(merge_block); #ifdef UNIV_BTR_DEBUG ut_a(btr_page_get_prev(merge_page, mtr) == buf_block_get_page_no(block)); #endif /* UNIV_BTR_DEBUG */ } else { /* The page is the only one on the level, lift the records to the father */ btr_lift_page_up(dict_index, block, mtr); mem_heap_free(heap); return(TRUE); } n_recs = page_get_n_recs(page); data_size = page_get_data_size(page); #ifdef UNIV_BTR_DEBUG ut_a(page_is_comp(merge_page) == page_is_comp(page)); #endif /* UNIV_BTR_DEBUG */ max_ins_size_reorg = page_get_max_insert_size_after_reorganize( merge_page, n_recs); if (data_size > max_ins_size_reorg) { /* No space for merge */ err_exit: /* We play it safe and reset the free bits. */ if (zip_size && page_is_leaf(merge_page) && !dict_index_is_clust(dict_index)) { ibuf_reset_free_bits(merge_block); } mem_heap_free(heap); return(FALSE); } ut_ad(page_validate(merge_page, dict_index)); max_ins_size = page_get_max_insert_size(merge_page, n_recs); if (UNIV_UNLIKELY(data_size > max_ins_size)) { /* We have to reorganize merge_page */ if (UNIV_UNLIKELY(!btr_page_reorganize(merge_block, dict_index, mtr))) { goto err_exit; } max_ins_size = page_get_max_insert_size(merge_page, n_recs); ut_ad(page_validate(merge_page, dict_index)); ut_ad(max_ins_size == max_ins_size_reorg); if (UNIV_UNLIKELY(data_size > max_ins_size)) { /* Add fault tolerance, though this should never happen */ goto err_exit; } } merge_page_zip = buf_block_get_page_zip(merge_block); #ifdef UNIV_ZIP_DEBUG if (UNIV_LIKELY_NULL(merge_page_zip)) { const page_zip_des_t* page_zip = buf_block_get_page_zip(block); ut_a(page_zip); ut_a(page_zip_validate(merge_page_zip, merge_page)); ut_a(page_zip_validate(page_zip, page)); } #endif /* UNIV_ZIP_DEBUG */ /* Move records to the merge page */ if (is_left) { rec_t* orig_pred = page_copy_rec_list_start( merge_block, block, page_get_supremum_rec(page), dict_index, mtr); if (UNIV_UNLIKELY(!orig_pred)) { goto err_exit; } btr_search_drop_page_hash_index(block); /* Remove the page from the level list */ btr_level_list_remove(space, zip_size, page, mtr); btr_node_ptr_delete(dict_index, block, mtr); lock_update_merge_left(merge_block, orig_pred, block); } else { rec_t* orig_succ; #ifdef UNIV_BTR_DEBUG byte fil_page_prev[4]; #endif /* UNIV_BTR_DEBUG */ if (UNIV_LIKELY_NULL(merge_page_zip)) { /* The function page_zip_compress(), which will be invoked by page_copy_rec_list_end() below, requires that FIL_PAGE_PREV be FIL_NULL. Clear the field, but prepare to restore it. */ #ifdef UNIV_BTR_DEBUG memcpy(fil_page_prev, merge_page + FIL_PAGE_PREV, 4); #endif /* UNIV_BTR_DEBUG */ #if FIL_NULL != 0xffffffff # error "FIL_NULL != 0xffffffff" #endif memset(merge_page + FIL_PAGE_PREV, 0xff, 4); } orig_succ = page_copy_rec_list_end(merge_block, block, page_get_infimum_rec(page), cursor->index, mtr); if (UNIV_UNLIKELY(!orig_succ)) { ut_a(merge_page_zip); #ifdef UNIV_BTR_DEBUG /* FIL_PAGE_PREV was restored from merge_page_zip. */ ut_a(!memcmp(fil_page_prev, merge_page + FIL_PAGE_PREV, 4)); #endif /* UNIV_BTR_DEBUG */ goto err_exit; } btr_search_drop_page_hash_index(block); #ifdef UNIV_BTR_DEBUG if (UNIV_LIKELY_NULL(merge_page_zip)) { /* Restore FIL_PAGE_PREV in order to avoid an assertion failure in btr_level_list_remove(), which will set the field again to FIL_NULL. Even though this makes merge_page and merge_page_zip inconsistent for a split second, it is harmless, because the pages are X-latched. */ memcpy(merge_page + FIL_PAGE_PREV, fil_page_prev, 4); } #endif /* UNIV_BTR_DEBUG */ /* Remove the page from the level list */ btr_level_list_remove(space, zip_size, page, mtr); /* Replace the address of the old child node (= page) with the address of the merge page to the right */ btr_node_ptr_set_child_page_no( btr_cur_get_rec(&father_cursor), btr_cur_get_page_zip(&father_cursor), offsets, right_page_no, mtr); btr_node_ptr_delete(dict_index, merge_block, mtr); lock_update_merge_right(merge_block, orig_succ, block); } mem_heap_free(heap); if (!dict_index_is_clust(dict_index) && page_is_leaf(merge_page)) { /* Update the free bits of the B-tree page in the insert buffer bitmap. This has to be done in a separate mini-transaction that is committed before the main mini-transaction. We cannot update the insert buffer bitmap in this mini-transaction, because btr_compress() can be invoked recursively without committing the mini-transaction in between. Since insert buffer bitmap pages have a lower rank than B-tree pages, we must not access other pages in the same mini-transaction after accessing an insert buffer bitmap page. */ /* The free bits in the insert buffer bitmap must never exceed the free space on a page. It is safe to decrement or reset the bits in the bitmap in a mini-transaction that is committed before the mini-transaction that affects the free space. */ /* It is unsafe to increment the bits in a separately committed mini-transaction, because in crash recovery, the free bits could momentarily be set too high. */ if (zip_size) { /* Because the free bits may be incremented and we cannot update the insert buffer bitmap in the same mini-transaction, the only safe thing we can do here is the pessimistic approach: reset the free bits. */ ibuf_reset_free_bits(merge_block); } else { /* On uncompressed pages, the free bits will never increase here. Thus, it is safe to write the bits accurately in a separate mini-transaction. */ ibuf_update_free_bits_if_full(merge_block, UNIV_PAGE_SIZE, ULINT_UNDEFINED); } } ut_ad(page_validate(merge_page, dict_index)); #ifdef UNIV_ZIP_DEBUG ut_a(!merge_page_zip || page_zip_validate(merge_page_zip, merge_page)); #endif /* UNIV_ZIP_DEBUG */ /* Free the file page */ btr_page_free(dict_index, block, mtr); ut_ad(btr_check_node_ptr(dict_index, merge_block, mtr)); return(TRUE); } /*************************************************************//** Discards a page that is the only page on its level. This will empty the whole B-tree, leaving just an empty root page. This function should never be reached, because btr_compress(), which is invoked in delete operations, calls btr_lift_page_up() to flatten the B-tree. */ static void btr_discard_only_page_on_level( /*===========================*/ dict_index_t* dict_index, /*!< in: index tree */ buf_block_t* block, /*!< in: page which is the only on its level */ mtr_t* mtr) /*!< in: mtr */ { ulint page_level = 0; trx_id_t max_trx_id; /* Save the PAGE_MAX_TRX_ID from the leaf page. */ max_trx_id = page_get_max_trx_id(buf_block_get_frame(block)); while (buf_block_get_page_no(block) != dict_index_get_page(dict_index)) { btr_cur_t cursor; buf_block_t* father; const page_t* page = buf_block_get_frame(block); ut_a(page_get_n_recs(page) == 1); ut_a(page_level == btr_page_get_level(page, mtr)); ut_a(btr_page_get_prev(page, mtr) == FIL_NULL); ut_a(btr_page_get_next(page, mtr) == FIL_NULL); ut_ad(mtr_memo_contains(mtr, block, MTR_MEMO_PAGE_X_FIX)); btr_search_drop_page_hash_index(block); btr_page_get_father(dict_index, block, mtr, &cursor); father = btr_cur_get_block(&cursor); lock_update_discard(father, PAGE_HEAP_NO_SUPREMUM, block); /* Free the file page */ btr_page_free(dict_index, block, mtr); block = father; page_level++; } /* block is the root page, which must be empty, except for the node pointer to the (now discarded) block(s). */ #ifdef UNIV_BTR_DEBUG if (!dict_index_is_ibuf(dict_index)) { const page_t* root = buf_block_get_frame(block); const ulint space = dict_index_get_space(dict_index); ut_a(btr_root_fseg_validate(FIL_PAGE_DATA + PAGE_BTR_SEG_LEAF + root, space)); ut_a(btr_root_fseg_validate(FIL_PAGE_DATA + PAGE_BTR_SEG_TOP + root, space)); } #endif /* UNIV_BTR_DEBUG */ btr_page_empty(block, buf_block_get_page_zip(block), dict_index, 0, mtr); if (!dict_index_is_clust(dict_index)) { /* We play it safe and reset the free bits for the root */ ibuf_reset_free_bits(block); if (page_is_leaf(buf_block_get_frame(block))) { ut_a(!ut_dulint_is_zero(max_trx_id)); page_set_max_trx_id(block, buf_block_get_page_zip(block), max_trx_id, mtr); } } } /*************************************************************//** Discards a page from a B-tree. This is used to remove the last record from a B-tree page: the whole page must be removed at the same time. This cannot be used for the root page, which is allowed to be empty. */ UNIV_INTERN void btr_discard_page( /*=============*/ btr_cur_t* cursor, /*!< in: cursor on the page to discard: not on the root page */ mtr_t* mtr) /*!< in: mtr */ { dict_index_t* dict_index; ulint space; ulint zip_size; ulint left_page_no; ulint right_page_no; buf_block_t* merge_block; page_t* merge_page; buf_block_t* block; page_t* page; rec_t* node_ptr; block = btr_cur_get_block(cursor); dict_index = btr_cur_get_index(cursor); ut_ad(dict_index_get_page(dict_index) != buf_block_get_page_no(block)); ut_ad(mtr_memo_contains(mtr, dict_index_get_lock(dict_index), MTR_MEMO_X_LOCK)); ut_ad(mtr_memo_contains(mtr, block, MTR_MEMO_PAGE_X_FIX)); space = dict_index_get_space(dict_index); zip_size = dict_table_zip_size(dict_index->table); /* Decide the page which will inherit the locks */ left_page_no = btr_page_get_prev(buf_block_get_frame(block), mtr); right_page_no = btr_page_get_next(buf_block_get_frame(block), mtr); if (left_page_no != FIL_NULL) { merge_block = btr_block_get(space, zip_size, left_page_no, RW_X_LATCH, mtr); merge_page = buf_block_get_frame(merge_block); #ifdef UNIV_BTR_DEBUG ut_a(btr_page_get_next(merge_page, mtr) == buf_block_get_page_no(block)); #endif /* UNIV_BTR_DEBUG */ } else if (right_page_no != FIL_NULL) { merge_block = btr_block_get(space, zip_size, right_page_no, RW_X_LATCH, mtr); merge_page = buf_block_get_frame(merge_block); #ifdef UNIV_BTR_DEBUG ut_a(btr_page_get_prev(merge_page, mtr) == buf_block_get_page_no(block)); #endif /* UNIV_BTR_DEBUG */ } else { btr_discard_only_page_on_level(dict_index, block, mtr); return; } page = buf_block_get_frame(block); ut_a(page_is_comp(merge_page) == page_is_comp(page)); btr_search_drop_page_hash_index(block); if (left_page_no == FIL_NULL && !page_is_leaf(page)) { /* We have to mark the leftmost node pointer on the right side page as the predefined minimum record */ node_ptr = page_rec_get_next(page_get_infimum_rec(merge_page)); ut_ad(page_rec_is_user_rec(node_ptr)); /* This will make page_zip_validate() fail on merge_page until btr_level_list_remove() completes. This is harmless, because everything will take place within a single mini-transaction and because writing to the redo log is an atomic operation (performed by mtr_commit()). */ btr_set_min_rec_mark(node_ptr, mtr); } btr_node_ptr_delete(dict_index, block, mtr); /* Remove the page from the level list */ btr_level_list_remove(space, zip_size, page, mtr); #ifdef UNIV_ZIP_DEBUG { page_zip_des_t* merge_page_zip = buf_block_get_page_zip(merge_block); ut_a(!merge_page_zip || page_zip_validate(merge_page_zip, merge_page)); } #endif /* UNIV_ZIP_DEBUG */ if (left_page_no != FIL_NULL) { lock_update_discard(merge_block, PAGE_HEAP_NO_SUPREMUM, block); } else { lock_update_discard(merge_block, lock_get_min_heap_no(merge_block), block); } /* Free the file page */ btr_page_free(dict_index, block, mtr); ut_ad(btr_check_node_ptr(dict_index, merge_block, mtr)); } #ifdef UNIV_BTR_PRINT /*************************************************************//** Prints size info of a B-tree. */ UNIV_INTERN void btr_print_size( /*===========*/ dict_index_t* index) /*!< in: index tree */ { page_t* root; fseg_header_t* seg; mtr_t mtr; if (dict_index_is_ibuf(index)) { ib_logger(ib_stream, "Sorry, cannot print info of an ibuf tree:" " use ibuf functions\n"); return; } mtr_start(&mtr); root = btr_root_get(index, &mtr); seg = root + PAGE_HEADER + PAGE_BTR_SEG_TOP; ib_logger(ib_stream, "INFO OF THE NON-LEAF PAGE SEGMENT\n"); fseg_print(seg, &mtr); if (!(index->type & DICT_UNIVERSAL)) { seg = root + PAGE_HEADER + PAGE_BTR_SEG_LEAF; ib_logger(ib_stream, "INFO OF THE LEAF PAGE SEGMENT\n"); fseg_print(seg, &mtr); } mtr_commit(&mtr); } /************************************************************//** Prints recursively index tree pages. */ static void btr_print_recursive( /*================*/ dict_index_t* index, /*!< in: index tree */ buf_block_t* block, /*!< in: index page */ ulint width, /*!< in: print this many entries from start and end */ mem_heap_t** heap, /*!< in/out: heap for rec_get_offsets() */ ulint** offsets,/*!< in/out: buffer for rec_get_offsets() */ mtr_t* mtr) /*!< in: mtr */ { const page_t* page = buf_block_get_frame(block); page_cur_t cursor; ulint n_recs; ulint i = 0; mtr_t mtr2; ut_ad(mtr_memo_contains(mtr, block, MTR_MEMO_PAGE_X_FIX)); ib_logger(ib_stream, "NODE ON LEVEL %lu page number %lu\n", (ulong) btr_page_get_level(page, mtr), (ulong) buf_block_get_page_no(block)); page_print(block, index, width, width); n_recs = page_get_n_recs(page); page_cur_set_before_first(block, &cursor); page_cur_move_to_next(&cursor); while (!page_cur_is_after_last(&cursor)) { if (page_is_leaf(page)) { /* If this is the leaf level, do nothing */ } else if ((i <= width) || (i >= n_recs - width)) { const rec_t* node_ptr; mtr_start(&mtr2); node_ptr = page_cur_get_rec(&cursor); *offsets = rec_get_offsets(node_ptr, index, *offsets, ULINT_UNDEFINED, heap); btr_print_recursive(index, btr_node_ptr_get_child(node_ptr, index, *offsets, &mtr2), width, heap, offsets, &mtr2); mtr_commit(&mtr2); } page_cur_move_to_next(&cursor); i++; } } /**************************************************************//** Prints directories and other info of all nodes in the tree. */ UNIV_INTERN void btr_print_index( /*============*/ dict_index_t* index, /*!< in: index */ ulint width) /*!< in: print this many entries from start and end */ { mtr_t mtr; buf_block_t* root; mem_heap_t* heap = NULL; ulint offsets_[REC_OFFS_NORMAL_SIZE]; ulint* offsets = offsets_; rec_offs_init(offsets_); ib_logger(ib_stream, "--------------------------\n" "INDEX TREE PRINT\n"); mtr_start(&mtr); root = btr_root_block_get(index, &mtr); btr_print_recursive(index, root, width, &heap, &offsets, &mtr); if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } mtr_commit(&mtr); btr_validate_index(index, NULL); } #endif /* UNIV_BTR_PRINT */ #ifdef UNIV_DEBUG /************************************************************//** Checks that the node pointer to a page is appropriate. @return TRUE */ UNIV_INTERN ibool btr_check_node_ptr( /*===============*/ dict_index_t* index, /*!< in: index tree */ buf_block_t* block, /*!< in: index page */ mtr_t* mtr) /*!< in: mtr */ { mem_heap_t* heap; dtuple_t* tuple; ulint* offsets; btr_cur_t cursor; page_t* page = buf_block_get_frame(block); ut_ad(mtr_memo_contains(mtr, block, MTR_MEMO_PAGE_X_FIX)); if (dict_index_get_page(index) == buf_block_get_page_no(block)) { return(TRUE); } heap = mem_heap_create(256); offsets = btr_page_get_father_block(NULL, heap, index, block, mtr, &cursor); if (page_is_leaf(page)) { goto func_exit; } tuple = dict_index_build_node_ptr( index, page_rec_get_next(page_get_infimum_rec(page)), 0, heap, btr_page_get_level(page, mtr)); ut_a(!cmp_dtuple_rec( index->cmp_ctx, tuple, btr_cur_get_rec(&cursor), offsets)); func_exit: mem_heap_free(heap); return(TRUE); } #endif /* UNIV_DEBUG */ /************************************************************//** Display identification information for a record. */ static void btr_index_rec_validate_report( /*==========================*/ const page_t* page, /*!< in: index page */ const rec_t* rec, /*!< in: index record */ const dict_index_t* dict_index) /*!< in: index */ { ib_logger(ib_stream, "InnoDB: Record in "); dict_index_name_print(ib_stream, NULL, dict_index); ib_logger(ib_stream, ", page %lu, at offset %lu\n", page_get_page_no(page), (ulint) page_offset(rec)); } /************************************************************//** Checks the size and number of fields in a record based on the definition of the index. @return TRUE if ok */ UNIV_INTERN ibool btr_index_rec_validate( /*===================*/ const rec_t* rec, /*!< in: index record */ const dict_index_t* dict_index, /*!< in: index */ ibool dump_on_error) /*!< in: TRUE if the function should print hex dump of record and page on error */ { ulint len; ulint n; ulint i; const page_t* page; mem_heap_t* heap = NULL; ulint offsets_[REC_OFFS_NORMAL_SIZE]; ulint* offsets = offsets_; rec_offs_init(offsets_); page = page_align(rec); if (UNIV_UNLIKELY(dict_index->type & DICT_UNIVERSAL)) { /* The insert buffer index tree can contain records from any other index: we cannot check the number of fields or their length */ return(TRUE); } if (UNIV_UNLIKELY((ibool)!!page_is_comp(page) != dict_table_is_comp(dict_index->table))) { btr_index_rec_validate_report(page, rec, dict_index); ib_logger(ib_stream, "InnoDB: compact flag=%lu, should be %lu\n", (ulong) !!page_is_comp(page), (ulong) dict_table_is_comp(dict_index->table)); return(FALSE); } n = dict_index_get_n_fields(dict_index); if (!page_is_comp(page) && UNIV_UNLIKELY(rec_get_n_fields_old(rec) != n)) { btr_index_rec_validate_report(page, rec, dict_index); ib_logger(ib_stream, "InnoDB: has %lu fields, should have %lu\n", (ulong) rec_get_n_fields_old(rec), (ulong) n); if (dump_on_error) { buf_page_print(page, 0); ib_logger(ib_stream, "InnoDB: corrupt record "); rec_print_old(ib_stream, rec); ib_logger(ib_stream, "\n"); } return(FALSE); } offsets = rec_get_offsets(rec, dict_index, offsets, ULINT_UNDEFINED, &heap); for (i = 0; i < n; i++) { ulint fixed_size = dict_col_get_fixed_size( dict_index_get_nth_col(dict_index, i), page_is_comp(page)); rec_get_nth_field_offs(offsets, i, &len); /* Note that if fixed_size != 0, it equals the length of a fixed-size column in the clustered index. A prefix index of the column is of fixed, but different length. When fixed_size == 0, prefix_len is the maximum length of the prefix index column. */ if ((dict_index_get_nth_field(dict_index, i)->prefix_len == 0 && len != UNIV_SQL_NULL && fixed_size && len != fixed_size) || (dict_index_get_nth_field(dict_index, i)->prefix_len > 0 && len != UNIV_SQL_NULL && len > dict_index_get_nth_field(dict_index, i)->prefix_len)) { btr_index_rec_validate_report(page, rec, dict_index); ib_logger(ib_stream, "InnoDB: field %lu len is %lu," " should be %lu\n", (ulong) i, (ulong) len, (ulong) fixed_size); if (dump_on_error) { buf_page_print(page, 0); ib_logger("InnoDB: corrupt record ", ib_stream); rec_print_new(ib_stream, rec, offsets); ib_logger(ib_stream,"\n"); } if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } return(FALSE); } } if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } return(TRUE); } /************************************************************//** Checks the size and number of fields in records based on the definition of the index. @return TRUE if ok */ static ibool btr_index_page_validate( /*====================*/ buf_block_t* block, /*!< in: index page */ dict_index_t* dict_index) /*!< in: index */ { page_cur_t cur; ibool ret = TRUE; page_cur_set_before_first(block, &cur); page_cur_move_to_next(&cur); for (;;) { if (page_cur_is_after_last(&cur)) { break; } if (!btr_index_rec_validate(cur.rec, dict_index, TRUE)) { return(FALSE); } page_cur_move_to_next(&cur); } return(ret); } /************************************************************//** Report an error on one page of an index tree. */ static void btr_validate_report1( /*=================*/ dict_index_t* dict_index, /*!< in: index */ ulint level, /*!< in: B-tree level */ const buf_block_t* block) /*!< in: index page */ { ib_logger(ib_stream, "InnoDB: Error in page %lu of ", buf_block_get_page_no(block)); dict_index_name_print(ib_stream, NULL, dict_index); if (level) { ib_logger(ib_stream, ", index tree level %lu", level); } ib_logger(ib_stream, "\n"); } /************************************************************//** Report an error on two pages of an index tree. */ static void btr_validate_report2( /*=================*/ const dict_index_t* dict_index, /*!< in: index */ ulint level, /*!< in: B-tree level */ const buf_block_t* block1, /*!< in: first index page */ const buf_block_t* block2) /*!< in: second index page */ { ib_logger(ib_stream, "InnoDB: Error in pages %lu and %lu of ", buf_block_get_page_no(block1), buf_block_get_page_no(block2)); dict_index_name_print(ib_stream, NULL, dict_index); if (level) { ib_logger(ib_stream, ", index tree level %lu", level); } ib_logger(ib_stream,"\n"); } /************************************************************//** Validates index tree level. @return TRUE if ok */ static ibool btr_validate_level( /*===============*/ dict_index_t* dict_index, /*!< in: index tree */ trx_t* trx, /*!< in: transaction or NULL */ ulint level) /*!< in: level number */ { ulint space; ulint zip_size; buf_block_t* block; page_t* page; buf_block_t* right_block = 0; /* remove warning */ page_t* right_page = 0; /* remove warning */ page_t* father_page; btr_cur_t node_cur; btr_cur_t right_node_cur; rec_t* rec; ulint right_page_no; ulint left_page_no; page_cur_t cursor; dtuple_t* node_ptr_tuple; ibool ret = TRUE; mtr_t mtr; mem_heap_t* heap = mem_heap_create(256); ulint* offsets = NULL; ulint* offsets2= NULL; #ifdef UNIV_ZIP_DEBUG page_zip_des_t* page_zip; #endif /* UNIV_ZIP_DEBUG */ mtr_start(&mtr); mtr_x_lock(dict_index_get_lock(dict_index), &mtr); block = btr_root_block_get(dict_index, &mtr); page = buf_block_get_frame(block); space = dict_index_get_space(dict_index); zip_size = dict_table_zip_size(dict_index->table); while (level != btr_page_get_level(page, &mtr)) { const rec_t* node_ptr; ut_a(space == buf_block_get_space(block)); ut_a(space == page_get_space_id(page)); #ifdef UNIV_ZIP_DEBUG page_zip = buf_block_get_page_zip(block); ut_a(!page_zip || page_zip_validate(page_zip, page)); #endif /* UNIV_ZIP_DEBUG */ ut_a(!page_is_leaf(page)); page_cur_set_before_first(block, &cursor); page_cur_move_to_next(&cursor); node_ptr = page_cur_get_rec(&cursor); offsets = rec_get_offsets(node_ptr, dict_index, offsets, ULINT_UNDEFINED, &heap); block = btr_node_ptr_get_child(node_ptr, dict_index, offsets, &mtr); page = buf_block_get_frame(block); } /* Now we are on the desired level. Loop through the pages on that level. */ loop: if (trx_is_interrupted(trx)) { mtr_commit(&mtr); mem_heap_free(heap); return(ret); } mem_heap_empty(heap); offsets = offsets2 = NULL; mtr_x_lock(dict_index_get_lock(dict_index), &mtr); #ifdef UNIV_ZIP_DEBUG page_zip = buf_block_get_page_zip(block); ut_a(!page_zip || page_zip_validate(page_zip, page)); #endif /* UNIV_ZIP_DEBUG */ /* Check ordering etc. of records */ if (!page_validate(page, dict_index)) { btr_validate_report1(dict_index, level, block); ret = FALSE; } else if (level == 0) { /* We are on level 0. Check that the records have the right number of fields, and field lengths are right. */ if (!btr_index_page_validate(block, dict_index)) { ret = FALSE; } } ut_a(btr_page_get_level(page, &mtr) == level); right_page_no = btr_page_get_next(page, &mtr); left_page_no = btr_page_get_prev(page, &mtr); ut_a(page_get_n_recs(page) > 0 || (level == 0 && page_get_page_no(page) == dict_index_get_page(dict_index))); if (right_page_no != FIL_NULL) { const rec_t* right_rec; right_block = btr_block_get(space, zip_size, right_page_no, RW_X_LATCH, &mtr); right_page = buf_block_get_frame(right_block); if (UNIV_UNLIKELY(btr_page_get_prev(right_page, &mtr) != page_get_page_no(page))) { btr_validate_report2(dict_index, level, block, right_block); ib_logger("InnoDB: broken FIL_PAGE_NEXT" " or FIL_PAGE_PREV links\n", ib_stream); buf_page_print(page, 0); buf_page_print(right_page, 0); ret = FALSE; } if (UNIV_UNLIKELY(page_is_comp(right_page) != page_is_comp(page))) { btr_validate_report2(dict_index, level, block, right_block); ib_logger("InnoDB: 'compact' flag mismatch\n", ib_stream); buf_page_print(page, 0); buf_page_print(right_page, 0); ret = FALSE; goto node_ptr_fails; } rec = page_rec_get_prev(page_get_supremum_rec(page)); right_rec = page_rec_get_next(page_get_infimum_rec( right_page)); offsets = rec_get_offsets(rec, dict_index, offsets, ULINT_UNDEFINED, &heap); offsets2 = rec_get_offsets(right_rec, dict_index, offsets2, ULINT_UNDEFINED, &heap); if (UNIV_UNLIKELY(cmp_rec_rec(rec, right_rec, offsets, offsets2, dict_index) >= 0)) { btr_validate_report2(dict_index, level, block, right_block); ib_logger("InnoDB: records in wrong order" " on adjacent pages\n", ib_stream); buf_page_print(page, 0); buf_page_print(right_page, 0); ib_logger("InnoDB: record ", ib_stream); rec = page_rec_get_prev(page_get_supremum_rec(page)); rec_print(ib_stream, rec, dict_index); ib_logger(ib_stream,"\n"); ib_logger(ib_stream, "InnoDB: record "); rec = page_rec_get_next( page_get_infimum_rec(right_page)); rec_print(ib_stream, rec, dict_index); ib_logger(ib_stream, "\n"); ret = FALSE; } } if (level > 0 && left_page_no == FIL_NULL) { ut_a(REC_INFO_MIN_REC_FLAG & rec_get_info_bits( page_rec_get_next(page_get_infimum_rec(page)), page_is_comp(page))); } if (buf_block_get_page_no(block) != dict_index_get_page(dict_index)) { /* Check father node pointers */ rec_t* node_ptr; offsets = btr_page_get_father_block(offsets, heap, dict_index, block, &mtr, &node_cur); father_page = btr_cur_get_page(&node_cur); node_ptr = btr_cur_get_rec(&node_cur); btr_cur_position(dict_index, page_rec_get_prev(page_get_supremum_rec(page)), block, &node_cur); offsets = btr_page_get_father_node_ptr(offsets, heap, &node_cur, &mtr); if (UNIV_UNLIKELY(node_ptr != btr_cur_get_rec(&node_cur)) || UNIV_UNLIKELY(btr_node_ptr_get_child_page_no(node_ptr, offsets) != buf_block_get_page_no(block))) { btr_validate_report1(dict_index, level, block); ib_logger(ib_stream, "InnoDB: node pointer to the page is wrong\n"); buf_page_print(father_page, 0); buf_page_print(page, 0); ib_logger(ib_stream, "InnoDB: node ptr "); rec_print(ib_stream, node_ptr, dict_index); rec = btr_cur_get_rec(&node_cur); ib_logger(ib_stream, "\n" "InnoDB: node ptr child page n:o %lu\n", (ulong) btr_node_ptr_get_child_page_no( rec, offsets)); ib_logger(ib_stream, "InnoDB: record on page "); rec_print_new(ib_stream, rec, offsets); ib_logger(ib_stream, "\n"); ret = FALSE; goto node_ptr_fails; } if (!page_is_leaf(page)) { node_ptr_tuple = dict_index_build_node_ptr( dict_index, page_rec_get_next(page_get_infimum_rec(page)), 0, heap, btr_page_get_level(page, &mtr)); if (cmp_dtuple_rec(dict_index->cmp_ctx, node_ptr_tuple, node_ptr, offsets)) { const rec_t* first_rec = page_rec_get_next( page_get_infimum_rec(page)); btr_validate_report1(dict_index, level, block); buf_page_print(father_page, 0); buf_page_print(page, 0); ib_logger(ib_stream, "InnoDB: Error: node ptrs differ" " on levels > 0\n" "InnoDB: node ptr "); rec_print_new(ib_stream, node_ptr, offsets); ib_logger(ib_stream, "InnoDB: first rec "); rec_print(ib_stream, first_rec, dict_index); ib_logger(ib_stream, "\n"); ret = FALSE; goto node_ptr_fails; } } if (left_page_no == FIL_NULL) { ut_a(node_ptr == page_rec_get_next( page_get_infimum_rec(father_page))); ut_a(btr_page_get_prev(father_page, &mtr) == FIL_NULL); } if (right_page_no == FIL_NULL) { ut_a(node_ptr == page_rec_get_prev( page_get_supremum_rec(father_page))); ut_a(btr_page_get_next(father_page, &mtr) == FIL_NULL); } else { const rec_t* right_node_ptr = page_rec_get_next(node_ptr); offsets = btr_page_get_father_block( offsets, heap, dict_index, right_block, &mtr, &right_node_cur); if (right_node_ptr != page_get_supremum_rec(father_page)) { if (btr_cur_get_rec(&right_node_cur) != right_node_ptr) { ret = FALSE; ib_logger(ib_stream, "InnoDB: node pointer to" " the right page is wrong\n"); btr_validate_report1(dict_index, level, block); buf_page_print(father_page, 0); buf_page_print(page, 0); buf_page_print(right_page, 0); } } else { page_t* right_father_page = btr_cur_get_page(&right_node_cur); if (btr_cur_get_rec(&right_node_cur) != page_rec_get_next( page_get_infimum_rec( right_father_page))) { ret = FALSE; ib_logger(ib_stream, "InnoDB: node pointer 2 to" " the right page is wrong\n"); btr_validate_report1(dict_index, level, block); buf_page_print(father_page, 0); buf_page_print(right_father_page, 0); buf_page_print(page, 0); buf_page_print(right_page, 0); } if (page_get_page_no(right_father_page) != btr_page_get_next(father_page, &mtr)) { ret = FALSE; ib_logger(ib_stream, "InnoDB: node pointer 3 to" " the right page is wrong\n"); btr_validate_report1(dict_index, level, block); buf_page_print(father_page, 0); buf_page_print(right_father_page, 0); buf_page_print(page, 0); buf_page_print(right_page, 0); } } } } node_ptr_fails: /* Commit the mini-transaction to release the latch on 'page'. Re-acquire the latch on right_page, which will become 'page' on the next loop. The page has already been checked. */ mtr_commit(&mtr); if (right_page_no != FIL_NULL) { mtr_start(&mtr); block = btr_block_get(space, zip_size, right_page_no, RW_X_LATCH, &mtr); page = buf_block_get_frame(block); goto loop; } mem_heap_free(heap); return(ret); } /**************************************************************//** Checks the consistency of an index tree. @return TRUE if ok */ UNIV_INTERN ibool btr_validate_index( /*===============*/ dict_index_t* dict_index, /*!< in: index */ trx_t* trx) /*!< in: transaction or NULL */ { mtr_t mtr; page_t* root; ulint i; ulint n; mtr_start(&mtr); mtr_x_lock(dict_index_get_lock(dict_index), &mtr); root = btr_root_get(dict_index, &mtr); n = btr_page_get_level(root, &mtr); for (i = 0; i <= n && !trx_is_interrupted(trx); i++) { if (!btr_validate_level(dict_index, trx, n - i)) { mtr_commit(&mtr); return(FALSE); } } mtr_commit(&mtr); return(TRUE); } #endif /* !UNIV_HOTBACKUP */ haildb-2.3.2/pars/0000755000175000017500000000000011513177437014607 5ustar00pcrewspcrews00000000000000haildb-2.3.2/pars/pars0lex.l0000644000175000017500000002561211513177357016531 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1997, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /****************************************************** SQL parser lexical analyzer: input file for the GNU Flex lexer generator The InnoDB parser is frozen because MySQL takes care of SQL parsing. Therefore we normally keep the InnoDB parser C files as they are, and do not automatically generate them from pars0grm.y and pars0lex.l. How to make the InnoDB parser and lexer C files: 1. Run ./make_flex.sh to generate lexer files. 2. Run ./make_bison.sh to generate parser files. These instructions seem to work at least with bison-1.875d and flex-2.5.31 on Linux. Created 12/14/1997 Heikki Tuuri *******************************************************/ %option nostdinit %option 8bit %option warn %option pointer %option never-interactive %option nodefault %option noinput %option nounput %option noyywrap %option noyy_scan_buffer %option noyy_scan_bytes %option noyy_scan_string %option nounistd %option extra-type="que_node_t*" %{ #define YYSTYPE que_node_t* #include "univ.i" #include "pars0pars.h" #include "pars0grm.h" #include "pars0sym.h" #include "mem0mem.h" #include "os0proc.h" #define malloc(A) ut_malloc(A) #define free(A) ut_free(A) #define realloc(P, A) ut_realloc(P, A) #define exit(A) ut_error #define YY_INPUT(buf, result, max_size) pars_get_lex_chars(buf, &result, max_size) /* String buffer for removing quotes */ static ulint stringbuf_len_alloc = 0; /* Allocated length */ static ulint stringbuf_len = 0; /* Current length */ static char* stringbuf; /* Start of buffer */ /** Appends a string to the buffer. */ static void string_append( /*==========*/ const char* str, /*!< in: string to be appended */ ulint len) /*!< in: length of the string */ { if (stringbuf == NULL) { stringbuf = ut_malloc(1); stringbuf_len_alloc = 1; } if (stringbuf_len + len > stringbuf_len_alloc) { while (stringbuf_len + len > stringbuf_len_alloc) { stringbuf_len_alloc <<= 1; } stringbuf = ut_realloc(stringbuf, stringbuf_len_alloc); } memcpy(stringbuf + stringbuf_len, str, len); stringbuf_len += len; } /************************************************************************* Reset the lexing variables. */ UNIV_INTERN void pars_lexer_var_init(void) /*=====================*/ { stringbuf_len = 0; stringbuf_len_alloc = 0; if (stringbuf != NULL) { ut_free(stringbuf); stringbuf = NULL; } } %} DIGIT [0-9] ID [a-z_A-Z][a-z_A-Z0-9]* BOUND_LIT \:[a-z_A-Z0-9]+ BOUND_ID \$[a-z_A-Z0-9]+ %x comment %x quoted %x id %% {DIGIT}+ { yylval = sym_tab_add_int_lit(pars_sym_tab_global, atoi(yytext)); return(PARS_INT_LIT); } {DIGIT}+"."{DIGIT}* { ut_error; /* not implemented */ return(PARS_FLOAT_LIT); } {BOUND_LIT} { ulint type; yylval = sym_tab_add_bound_lit(pars_sym_tab_global, yytext + 1, &type); return((int) type); } {BOUND_ID} { yylval = sym_tab_add_bound_id(pars_sym_tab_global, yytext + 1); return(PARS_ID_TOKEN); } "'" { /* Quoted character string literals are handled in an explicit start state 'quoted'. This state is entered and the buffer for the scanned string is emptied upon encountering a starting quote. In the state 'quoted', only two actions are possible (defined below). */ BEGIN(quoted); stringbuf_len = 0; } [^\']+ { /* Got a sequence of characters other than "'": append to string buffer */ string_append(yytext, yyleng); } "'"+ { /* Got a sequence of "'" characters: append half of them to string buffer, as "''" represents a single "'". We apply truncating division, so that "'''" will result in "'". */ string_append(yytext, yyleng / 2); /* If we got an odd number of quotes, then the last quote we got is the terminating quote. At the end of the string, we return to the initial start state and report the scanned string literal. */ if (yyleng % 2) { BEGIN(INITIAL); yylval = sym_tab_add_str_lit( pars_sym_tab_global, (byte*) stringbuf, stringbuf_len); return(PARS_STR_LIT); } } \" { /* Quoted identifiers are handled in an explicit start state 'id'. This state is entered and the buffer for the scanned string is emptied upon encountering a starting quote. In the state 'id', only two actions are possible (defined below). */ BEGIN(id); stringbuf_len = 0; } [^\"]+ { /* Got a sequence of characters other than '"': append to string buffer */ string_append(yytext, yyleng); } \"+ { /* Got a sequence of '"' characters: append half of them to string buffer, as '""' represents a single '"'. We apply truncating division, so that '"""' will result in '"'. */ string_append(yytext, yyleng / 2); /* If we got an odd number of quotes, then the last quote we got is the terminating quote. At the end of the string, we return to the initial start state and report the scanned identifier. */ if (yyleng % 2) { BEGIN(INITIAL); yylval = sym_tab_add_id( pars_sym_tab_global, (byte*) stringbuf, stringbuf_len); return(PARS_ID_TOKEN); } } "NULL" { yylval = sym_tab_add_null_lit(pars_sym_tab_global); return(PARS_NULL_LIT); } "SQL" { /* Implicit cursor name */ yylval = sym_tab_add_str_lit(pars_sym_tab_global, (byte*) yytext, yyleng); return(PARS_SQL_TOKEN); } "AND" { return(PARS_AND_TOKEN); } "OR" { return(PARS_OR_TOKEN); } "NOT" { return(PARS_NOT_TOKEN); } "PROCEDURE" { return(PARS_PROCEDURE_TOKEN); } "IN" { return(PARS_IN_TOKEN); } "OUT" { return(PARS_OUT_TOKEN); } "BINARY" { return(PARS_BINARY_TOKEN); } "BLOB" { return(PARS_BLOB_TOKEN); } "INT" { return(PARS_INT_TOKEN); } "INTEGER" { return(PARS_INT_TOKEN); } "FLOAT" { return(PARS_FLOAT_TOKEN); } "CHAR" { return(PARS_CHAR_TOKEN); } "IS" { return(PARS_IS_TOKEN); } "BEGIN" { return(PARS_BEGIN_TOKEN); } "END" { return(PARS_END_TOKEN); } "IF" { return(PARS_IF_TOKEN); } "THEN" { return(PARS_THEN_TOKEN); } "ELSE" { return(PARS_ELSE_TOKEN); } "ELSIF" { return(PARS_ELSIF_TOKEN); } "LOOP" { return(PARS_LOOP_TOKEN); } "WHILE" { return(PARS_WHILE_TOKEN); } "RETURN" { return(PARS_RETURN_TOKEN); } "SELECT" { return(PARS_SELECT_TOKEN); } "SUM" { return(PARS_SUM_TOKEN); } "COUNT" { return(PARS_COUNT_TOKEN); } "DISTINCT" { return(PARS_DISTINCT_TOKEN); } "FROM" { return(PARS_FROM_TOKEN); } "WHERE" { return(PARS_WHERE_TOKEN); } "FOR" { return(PARS_FOR_TOKEN); } "READ" { return(PARS_READ_TOKEN); } "ORDER" { return(PARS_ORDER_TOKEN); } "BY" { return(PARS_BY_TOKEN); } "ASC" { return(PARS_ASC_TOKEN); } "DESC" { return(PARS_DESC_TOKEN); } "INSERT" { return(PARS_INSERT_TOKEN); } "INTO" { return(PARS_INTO_TOKEN); } "VALUES" { return(PARS_VALUES_TOKEN); } "UPDATE" { return(PARS_UPDATE_TOKEN); } "SET" { return(PARS_SET_TOKEN); } "DELETE" { return(PARS_DELETE_TOKEN); } "CURRENT" { return(PARS_CURRENT_TOKEN); } "OF" { return(PARS_OF_TOKEN); } "CREATE" { return(PARS_CREATE_TOKEN); } "TABLE" { return(PARS_TABLE_TOKEN); } "INDEX" { return(PARS_INDEX_TOKEN); } "UNIQUE" { return(PARS_UNIQUE_TOKEN); } "CLUSTERED" { return(PARS_CLUSTERED_TOKEN); } "DOES_NOT_FIT_IN_MEMORY" { return(PARS_DOES_NOT_FIT_IN_MEM_TOKEN); } "ON" { return(PARS_ON_TOKEN); } "DECLARE" { return(PARS_DECLARE_TOKEN); } "CURSOR" { return(PARS_CURSOR_TOKEN); } "OPEN" { return(PARS_OPEN_TOKEN); } "FETCH" { return(PARS_FETCH_TOKEN); } "CLOSE" { return(PARS_CLOSE_TOKEN); } "NOTFOUND" { return(PARS_NOTFOUND_TOKEN); } "TO_CHAR" { return(PARS_TO_CHAR_TOKEN); } "TO_NUMBER" { return(PARS_TO_NUMBER_TOKEN); } "TO_BINARY" { return(PARS_TO_BINARY_TOKEN); } "BINARY_TO_NUMBER" { return(PARS_BINARY_TO_NUMBER_TOKEN); } "SUBSTR" { return(PARS_SUBSTR_TOKEN); } "REPLSTR" { return(PARS_REPLSTR_TOKEN); } "CONCAT" { return(PARS_CONCAT_TOKEN); } "INSTR" { return(PARS_INSTR_TOKEN); } "LENGTH" { return(PARS_LENGTH_TOKEN); } "SYSDATE" { return(PARS_SYSDATE_TOKEN); } "PRINTF" { return(PARS_PRINTF_TOKEN); } "ASSERT" { return(PARS_ASSERT_TOKEN); } "RND" { return(PARS_RND_TOKEN); } "RND_STR" { return(PARS_RND_STR_TOKEN); } "ROW_PRINTF" { return(PARS_ROW_PRINTF_TOKEN); } "COMMIT" { return(PARS_COMMIT_TOKEN); } "ROLLBACK" { return(PARS_ROLLBACK_TOKEN); } "WORK" { return(PARS_WORK_TOKEN); } "UNSIGNED" { return(PARS_UNSIGNED_TOKEN); } "EXIT" { return(PARS_EXIT_TOKEN); } "FUNCTION" { return(PARS_FUNCTION_TOKEN); } "LOCK" { return(PARS_LOCK_TOKEN); } "SHARE" { return(PARS_SHARE_TOKEN); } "MODE" { return(PARS_MODE_TOKEN); } {ID} { yylval = sym_tab_add_id(pars_sym_tab_global, (byte*)yytext, ut_strlen(yytext)); return(PARS_ID_TOKEN); } ".." { return(PARS_DDOT_TOKEN); } ":=" { return(PARS_ASSIGN_TOKEN); } "<=" { return(PARS_LE_TOKEN); } ">=" { return(PARS_GE_TOKEN); } "<>" { return(PARS_NE_TOKEN); } "(" { return((int)(*yytext)); } "=" { return((int)(*yytext)); } ">" { return((int)(*yytext)); } "<" { return((int)(*yytext)); } "," { return((int)(*yytext)); } ";" { return((int)(*yytext)); } ")" { return((int)(*yytext)); } "+" { return((int)(*yytext)); } "-" { return((int)(*yytext)); } "*" { return((int)(*yytext)); } "/" { return((int)(*yytext)); } "%" { return((int)(*yytext)); } "{" { return((int)(*yytext)); } "}" { return((int)(*yytext)); } "?" { return((int)(*yytext)); } "/*" BEGIN(comment); /* eat up comment */ [^*]* "*"+[^*/]* "*"+"/" BEGIN(INITIAL); [ \t\n]+ /* eat up whitespace */ . { fprintf(stderr,"Unrecognized character: %02x\n", *yytext); ut_error; return(0); } %% /* This definition is added here to get rid of a warning. */ void yyset_extra(void* p) { } /********************************************************************** Release any resources used by the lexer. */ UNIV_INTERN void pars_lexer_close(void) /*==================*/ { yylex_destroy(); if (stringbuf != NULL) { ut_free(stringbuf); stringbuf = NULL; } stringbuf_len_alloc = stringbuf_len = 0; } haildb-2.3.2/pars/pars0pars.c0000644000175000017500000016070311513177357016676 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file pars/pars0pars.c SQL parser Created 11/19/1996 Heikki Tuuri *******************************************************/ /* Historical note: Innobase executed its first SQL string (CREATE TABLE) on 1/27/1998 */ #include "pars0pars.h" #ifdef UNIV_NONINL #include "pars0pars.ic" #endif #include "row0sel.h" #include "row0ins.h" #include "row0upd.h" #include "dict0dict.h" #include "dict0mem.h" #include "dict0crea.h" #include "que0que.h" #include "pars0grm.h" #include "pars0opt.h" #include "data0data.h" #include "data0type.h" #include "trx0trx.h" #include "trx0roll.h" #include "lock0lock.h" #include "eval0eval.h" #ifdef UNIV_SQL_DEBUG /** If the following is set TRUE, the lexer will print the SQL string as it tokenizes it */ UNIV_INTERN ibool pars_print_lexed = FALSE; #endif /* UNIV_SQL_DEBUG */ /* Global variable used while parsing a single procedure or query : the code is NOT re-entrant */ UNIV_INTERN sym_tab_t* pars_sym_tab_global; /* Global variables used to denote certain reserved words, used in constructing the parsing tree */ UNIV_INTERN pars_res_word_t pars_to_char_token = {PARS_TO_CHAR_TOKEN}; UNIV_INTERN pars_res_word_t pars_to_number_token = {PARS_TO_NUMBER_TOKEN}; UNIV_INTERN pars_res_word_t pars_to_binary_token = {PARS_TO_BINARY_TOKEN}; UNIV_INTERN pars_res_word_t pars_binary_to_number_token = {PARS_BINARY_TO_NUMBER_TOKEN}; UNIV_INTERN pars_res_word_t pars_substr_token = {PARS_SUBSTR_TOKEN}; UNIV_INTERN pars_res_word_t pars_replstr_token = {PARS_REPLSTR_TOKEN}; UNIV_INTERN pars_res_word_t pars_concat_token = {PARS_CONCAT_TOKEN}; UNIV_INTERN pars_res_word_t pars_instr_token = {PARS_INSTR_TOKEN}; UNIV_INTERN pars_res_word_t pars_length_token = {PARS_LENGTH_TOKEN}; UNIV_INTERN pars_res_word_t pars_sysdate_token = {PARS_SYSDATE_TOKEN}; UNIV_INTERN pars_res_word_t pars_printf_token = {PARS_PRINTF_TOKEN}; UNIV_INTERN pars_res_word_t pars_assert_token = {PARS_ASSERT_TOKEN}; UNIV_INTERN pars_res_word_t pars_rnd_token = {PARS_RND_TOKEN}; UNIV_INTERN pars_res_word_t pars_rnd_str_token = {PARS_RND_STR_TOKEN}; UNIV_INTERN pars_res_word_t pars_count_token = {PARS_COUNT_TOKEN}; UNIV_INTERN pars_res_word_t pars_sum_token = {PARS_SUM_TOKEN}; UNIV_INTERN pars_res_word_t pars_distinct_token = {PARS_DISTINCT_TOKEN}; UNIV_INTERN pars_res_word_t pars_binary_token = {PARS_BINARY_TOKEN}; UNIV_INTERN pars_res_word_t pars_blob_token = {PARS_BLOB_TOKEN}; UNIV_INTERN pars_res_word_t pars_int_token = {PARS_INT_TOKEN}; UNIV_INTERN pars_res_word_t pars_char_token = {PARS_CHAR_TOKEN}; UNIV_INTERN pars_res_word_t pars_float_token = {PARS_FLOAT_TOKEN}; UNIV_INTERN pars_res_word_t pars_update_token = {PARS_UPDATE_TOKEN}; UNIV_INTERN pars_res_word_t pars_asc_token = {PARS_ASC_TOKEN}; UNIV_INTERN pars_res_word_t pars_desc_token = {PARS_DESC_TOKEN}; UNIV_INTERN pars_res_word_t pars_open_token = {PARS_OPEN_TOKEN}; UNIV_INTERN pars_res_word_t pars_close_token = {PARS_CLOSE_TOKEN}; UNIV_INTERN pars_res_word_t pars_share_token = {PARS_SHARE_TOKEN}; UNIV_INTERN pars_res_word_t pars_unique_token = {PARS_UNIQUE_TOKEN}; UNIV_INTERN pars_res_word_t pars_clustered_token = {PARS_CLUSTERED_TOKEN}; /** Global variable used to denote the '*' in SELECT * FROM.. */ #define PARS_STAR_DENOTER 12345678 UNIV_INTERN ulint pars_star_denoter = PARS_STAR_DENOTER; /***********************************************************************//** Reset and check parser variables. */ UNIV_INTERN void pars_var_init(void) /*===============*/ { #ifdef UNIV_SQL_DEBUG pars_print_lexed = FALSE; #endif /* UNIV_SQL_DEBUG */ pars_lexer_var_init(); pars_sym_tab_global = NULL; /* These should really be const, so we simply check that they are set to the correct value. */ ut_a(pars_to_char_token.code == PARS_TO_CHAR_TOKEN); ut_a(pars_to_number_token.code == PARS_TO_NUMBER_TOKEN); ut_a(pars_to_binary_token.code == PARS_TO_BINARY_TOKEN); ut_a(pars_binary_to_number_token.code == PARS_BINARY_TO_NUMBER_TOKEN); ut_a(pars_substr_token.code == PARS_SUBSTR_TOKEN); ut_a(pars_replstr_token.code == PARS_REPLSTR_TOKEN); ut_a(pars_concat_token.code == PARS_CONCAT_TOKEN); ut_a(pars_instr_token.code == PARS_INSTR_TOKEN); ut_a(pars_length_token.code == PARS_LENGTH_TOKEN); ut_a(pars_sysdate_token.code == PARS_SYSDATE_TOKEN); ut_a(pars_printf_token.code == PARS_PRINTF_TOKEN); ut_a(pars_assert_token.code == PARS_ASSERT_TOKEN); ut_a(pars_rnd_token.code == PARS_RND_TOKEN); ut_a(pars_rnd_str_token.code == PARS_RND_STR_TOKEN); ut_a(pars_count_token.code == PARS_COUNT_TOKEN); ut_a(pars_sum_token.code == PARS_SUM_TOKEN); ut_a(pars_distinct_token.code == PARS_DISTINCT_TOKEN); ut_a(pars_binary_token.code == PARS_BINARY_TOKEN); ut_a(pars_blob_token.code == PARS_BLOB_TOKEN); ut_a(pars_int_token.code == PARS_INT_TOKEN); ut_a(pars_char_token.code == PARS_CHAR_TOKEN); ut_a(pars_float_token.code == PARS_FLOAT_TOKEN); ut_a(pars_update_token.code == PARS_UPDATE_TOKEN); ut_a(pars_asc_token.code == PARS_ASC_TOKEN); ut_a(pars_desc_token.code == PARS_DESC_TOKEN); ut_a(pars_open_token.code == PARS_OPEN_TOKEN); ut_a(pars_close_token.code == PARS_CLOSE_TOKEN); ut_a(pars_share_token.code == PARS_SHARE_TOKEN); ut_a(pars_unique_token.code == PARS_UNIQUE_TOKEN); ut_a(pars_clustered_token.code == PARS_CLUSTERED_TOKEN); pars_star_denoter = PARS_STAR_DENOTER; } /*********************************************************************//** Determines the class of a function code. @return function class: PARS_FUNC_ARITH, ... */ static ulint pars_func_get_class( /*================*/ int func) /*!< in: function code: '=', PARS_GE_TOKEN, ... */ { switch (func) { case '+': case '-': case '*': case '/': return(PARS_FUNC_ARITH); case '=': case '<': case '>': case PARS_GE_TOKEN: case PARS_LE_TOKEN: case PARS_NE_TOKEN: return(PARS_FUNC_CMP); case PARS_AND_TOKEN: case PARS_OR_TOKEN: case PARS_NOT_TOKEN: return(PARS_FUNC_LOGICAL); case PARS_COUNT_TOKEN: case PARS_SUM_TOKEN: return(PARS_FUNC_AGGREGATE); case PARS_TO_CHAR_TOKEN: case PARS_TO_NUMBER_TOKEN: case PARS_TO_BINARY_TOKEN: case PARS_BINARY_TO_NUMBER_TOKEN: case PARS_SUBSTR_TOKEN: case PARS_CONCAT_TOKEN: case PARS_LENGTH_TOKEN: case PARS_INSTR_TOKEN: case PARS_SYSDATE_TOKEN: case PARS_NOTFOUND_TOKEN: case PARS_PRINTF_TOKEN: case PARS_ASSERT_TOKEN: case PARS_RND_TOKEN: case PARS_RND_STR_TOKEN: case PARS_REPLSTR_TOKEN: return(PARS_FUNC_PREDEFINED); default: return(PARS_FUNC_OTHER); } } /*********************************************************************//** Parses an operator or predefined function expression. @return own: function node in a query tree */ static func_node_t* pars_func_low( /*==========*/ int func, /*!< in: function token code */ que_node_t* arg) /*!< in: first argument in the argument list */ { func_node_t* node; node = mem_heap_alloc(pars_sym_tab_global->heap, sizeof(func_node_t)); node->common.type = QUE_NODE_FUNC; dfield_set_data(&(node->common.val), NULL, 0); node->common.val_buf_size = 0; node->func = func; node->class = pars_func_get_class(func); node->args = arg; UT_LIST_ADD_LAST(func_node_list, pars_sym_tab_global->func_node_list, node); return(node); } /*********************************************************************//** Parses a function expression. @return own: function node in a query tree */ UNIV_INTERN func_node_t* pars_func( /*======*/ que_node_t* res_word,/*!< in: function name reserved word */ que_node_t* arg) /*!< in: first argument in the argument list */ { return(pars_func_low(((pars_res_word_t*)res_word)->code, arg)); } /*********************************************************************//** Parses an operator expression. @return own: function node in a query tree */ UNIV_INTERN func_node_t* pars_op( /*====*/ int func, /*!< in: operator token code */ que_node_t* arg1, /*!< in: first argument */ que_node_t* arg2) /*!< in: second argument or NULL for an unary operator */ { que_node_list_add_last(NULL, arg1); if (arg2) { que_node_list_add_last(arg1, arg2); } return(pars_func_low(func, arg1)); } /*********************************************************************//** Parses an ORDER BY clause. Order by a single column only is supported. @return own: order-by node in a query tree */ UNIV_INTERN order_node_t* pars_order_by( /*==========*/ sym_node_t* column, /*!< in: column name */ pars_res_word_t* asc) /*!< in: &pars_asc_token or pars_desc_token */ { order_node_t* node; node = mem_heap_alloc(pars_sym_tab_global->heap, sizeof(order_node_t)); node->common.type = QUE_NODE_ORDER; node->column = column; if (asc == &pars_asc_token) { node->asc = TRUE; } else { ut_a(asc == &pars_desc_token); node->asc = FALSE; } return(node); } /*********************************************************************//** Determine if a data type is a built-in string data type of the InnoDB SQL parser. @return TRUE if string data type */ static ibool pars_is_string_type( /*================*/ ulint mtype) /*!< in: main data type */ { switch (mtype) { case DATA_VARCHAR: case DATA_CHAR: case DATA_FIXBINARY: case DATA_BINARY: return(TRUE); } return(FALSE); } /*********************************************************************//** Resolves the data type of a function in an expression. The argument data types must already be resolved. */ static void pars_resolve_func_data_type( /*========================*/ func_node_t* node) /*!< in: function node */ { que_node_t* arg; ut_a(que_node_get_type(node) == QUE_NODE_FUNC); arg = node->args; switch (node->func) { case PARS_SUM_TOKEN: case '+': case '-': case '*': case '/': /* Inherit the data type from the first argument (which must not be the SQL null literal whose type is DATA_ERROR) */ dtype_copy(que_node_get_data_type(node), que_node_get_data_type(arg)); ut_a(dtype_get_mtype(que_node_get_data_type(node)) == DATA_INT); break; case PARS_COUNT_TOKEN: ut_a(arg); dtype_set(que_node_get_data_type(node), DATA_INT, 0, 4); break; case PARS_TO_CHAR_TOKEN: case PARS_RND_STR_TOKEN: ut_a(dtype_get_mtype(que_node_get_data_type(arg)) == DATA_INT); dtype_set(que_node_get_data_type(node), DATA_VARCHAR, DATA_ENGLISH, 0); break; case PARS_TO_BINARY_TOKEN: if (dtype_get_mtype(que_node_get_data_type(arg)) == DATA_INT) { dtype_set(que_node_get_data_type(node), DATA_VARCHAR, DATA_ENGLISH, 0); } else { dtype_set(que_node_get_data_type(node), DATA_BINARY, 0, 0); } break; case PARS_TO_NUMBER_TOKEN: case PARS_BINARY_TO_NUMBER_TOKEN: case PARS_LENGTH_TOKEN: case PARS_INSTR_TOKEN: ut_a(pars_is_string_type(que_node_get_data_type(arg)->mtype)); dtype_set(que_node_get_data_type(node), DATA_INT, 0, 4); break; case PARS_SYSDATE_TOKEN: ut_a(arg == NULL); dtype_set(que_node_get_data_type(node), DATA_INT, 0, 4); break; case PARS_SUBSTR_TOKEN: case PARS_CONCAT_TOKEN: ut_a(pars_is_string_type(que_node_get_data_type(arg)->mtype)); dtype_set(que_node_get_data_type(node), DATA_VARCHAR, DATA_ENGLISH, 0); break; case '>': case '<': case '=': case PARS_GE_TOKEN: case PARS_LE_TOKEN: case PARS_NE_TOKEN: case PARS_AND_TOKEN: case PARS_OR_TOKEN: case PARS_NOT_TOKEN: case PARS_NOTFOUND_TOKEN: /* We currently have no iboolean type: use integer type */ dtype_set(que_node_get_data_type(node), DATA_INT, 0, 4); break; case PARS_RND_TOKEN: ut_a(dtype_get_mtype(que_node_get_data_type(arg)) == DATA_INT); dtype_set(que_node_get_data_type(node), DATA_INT, 0, 4); break; default: ut_error; } } /*********************************************************************//** Resolves the meaning of variables in an expression and the data types of functions. It is an error if some identifier cannot be resolved here. */ static void pars_resolve_exp_variables_and_types( /*=================================*/ sel_node_t* select_node, /*!< in: select node or NULL; if this is not NULL then the variable sym nodes are added to the copy_variables list of select_node */ que_node_t* exp_node) /*!< in: expression */ { func_node_t* func_node; que_node_t* arg; sym_node_t* sym_node; sym_node_t* node; ut_a(exp_node); if (que_node_get_type(exp_node) == QUE_NODE_FUNC) { func_node = exp_node; arg = func_node->args; while (arg) { pars_resolve_exp_variables_and_types(select_node, arg); arg = que_node_get_next(arg); } pars_resolve_func_data_type(func_node); return; } ut_a(que_node_get_type(exp_node) == QUE_NODE_SYMBOL); sym_node = exp_node; if (sym_node->resolved) { return; } /* Not resolved yet: look in the symbol table for a variable or a cursor or a function with the same name */ node = UT_LIST_GET_FIRST(pars_sym_tab_global->sym_list); while (node) { if (node->resolved && ((node->token_type == SYM_VAR) || (node->token_type == SYM_CURSOR) || (node->token_type == SYM_FUNCTION)) && node->name && (sym_node->name_len == node->name_len) && (ut_memcmp(sym_node->name, node->name, node->name_len) == 0)) { /* Found a variable or a cursor declared with the same name */ break; } node = UT_LIST_GET_NEXT(sym_list, node); } if (!node) { ib_logger(ib_stream, "PARSER ERROR: Unresolved identifier %s\n", sym_node->name); } ut_a(node); sym_node->resolved = TRUE; sym_node->token_type = SYM_IMPLICIT_VAR; sym_node->alias = node; sym_node->indirection = node; if (select_node) { UT_LIST_ADD_LAST(col_var_list, select_node->copy_variables, sym_node); } dfield_set_type(que_node_get_val(sym_node), que_node_get_data_type(node)); } /*********************************************************************//** Resolves the meaning of variables in an expression list. It is an error if some identifier cannot be resolved here. Resolves also the data types of functions. */ static void pars_resolve_exp_list_variables_and_types( /*======================================*/ sel_node_t* select_node, /*!< in: select node or NULL */ que_node_t* exp_node) /*!< in: expression list first node, or NULL */ { while (exp_node) { pars_resolve_exp_variables_and_types(select_node, exp_node); exp_node = que_node_get_next(exp_node); } } /*********************************************************************//** Resolves the columns in an expression. */ static void pars_resolve_exp_columns( /*=====================*/ sym_node_t* table_node, /*!< in: first node in a table list */ que_node_t* exp_node) /*!< in: expression */ { func_node_t* func_node; que_node_t* arg; sym_node_t* sym_node; dict_table_t* table; sym_node_t* t_node; ulint n_cols; ulint i; ut_a(exp_node); if (que_node_get_type(exp_node) == QUE_NODE_FUNC) { func_node = exp_node; arg = func_node->args; while (arg) { pars_resolve_exp_columns(table_node, arg); arg = que_node_get_next(arg); } return; } ut_a(que_node_get_type(exp_node) == QUE_NODE_SYMBOL); sym_node = exp_node; if (sym_node->resolved) { return; } /* Not resolved yet: look in the table list for a column with the same name */ t_node = table_node; while (t_node) { table = t_node->table; n_cols = dict_table_get_n_cols(table); for (i = 0; i < n_cols; i++) { const dict_col_t* col = dict_table_get_nth_col(table, i); const char* col_name = dict_table_get_col_name(table, i); if ((sym_node->name_len == ut_strlen(col_name)) && (0 == ut_memcmp(sym_node->name, col_name, sym_node->name_len))) { /* Found */ sym_node->resolved = TRUE; sym_node->token_type = SYM_COLUMN; sym_node->table = table; sym_node->col_no = i; sym_node->prefetch_buf = NULL; dict_col_copy_type( col, dfield_get_type(&sym_node ->common.val)); return; } } t_node = que_node_get_next(t_node); } } /*********************************************************************//** Resolves the meaning of columns in an expression list. */ static void pars_resolve_exp_list_columns( /*==========================*/ sym_node_t* table_node, /*!< in: first node in a table list */ que_node_t* exp_node) /*!< in: expression list first node, or NULL */ { while (exp_node) { pars_resolve_exp_columns(table_node, exp_node); exp_node = que_node_get_next(exp_node); } } /*********************************************************************//** Retrieves the table definition for a table name id. */ static void pars_retrieve_table_def( /*====================*/ sym_node_t* sym_node) /*!< in: table node */ { const char* table_name; ut_a(sym_node); ut_a(que_node_get_type(sym_node) == QUE_NODE_SYMBOL); sym_node->resolved = TRUE; sym_node->token_type = SYM_TABLE; table_name = (const char*) sym_node->name; sym_node->table = dict_table_get_low(table_name); ut_a(sym_node->table); } /*********************************************************************//** Retrieves the table definitions for a list of table name ids. @return number of tables */ static ulint pars_retrieve_table_list_defs( /*==========================*/ sym_node_t* sym_node) /*!< in: first table node in list */ { ulint count = 0; if (sym_node == NULL) { return(count); } while (sym_node) { pars_retrieve_table_def(sym_node); count++; sym_node = que_node_get_next(sym_node); } return(count); } /*********************************************************************//** Adds all columns to the select list if the query is SELECT * FROM ... */ static void pars_select_all_columns( /*====================*/ sel_node_t* select_node) /*!< in: select node already containing the table list */ { sym_node_t* col_node; sym_node_t* table_node; dict_table_t* table; ulint i; select_node->select_list = NULL; table_node = select_node->table_list; while (table_node) { table = table_node->table; for (i = 0; i < dict_table_get_n_user_cols(table); i++) { const char* col_name = dict_table_get_col_name( table, i); col_node = sym_tab_add_id(pars_sym_tab_global, (byte*)col_name, ut_strlen(col_name)); select_node->select_list = que_node_list_add_last( select_node->select_list, col_node); } table_node = que_node_get_next(table_node); } } /*********************************************************************//** Parses a select list; creates a query graph node for the whole SELECT statement. @return own: select node in a query tree */ UNIV_INTERN sel_node_t* pars_select_list( /*=============*/ que_node_t* select_list, /*!< in: select list */ sym_node_t* into_list) /*!< in: variables list or NULL */ { sel_node_t* node; node = sel_node_create(pars_sym_tab_global->heap); node->select_list = select_list; node->into_list = into_list; pars_resolve_exp_list_variables_and_types(NULL, into_list); return(node); } /*********************************************************************//** Checks if the query is an aggregate query, in which case the selct list must contain only aggregate function items. */ static void pars_check_aggregate( /*=================*/ sel_node_t* select_node) /*!< in: select node already containing the select list */ { que_node_t* exp_node; func_node_t* func_node; ulint n_nodes = 0; ulint n_aggregate_nodes = 0; exp_node = select_node->select_list; while (exp_node) { n_nodes++; if (que_node_get_type(exp_node) == QUE_NODE_FUNC) { func_node = exp_node; if (func_node->class == PARS_FUNC_AGGREGATE) { n_aggregate_nodes++; } } exp_node = que_node_get_next(exp_node); } if (n_aggregate_nodes > 0) { ut_a(n_nodes == n_aggregate_nodes); select_node->is_aggregate = TRUE; } else { select_node->is_aggregate = FALSE; } } /*********************************************************************//** Parses a select statement. @return own: select node in a query tree */ UNIV_INTERN sel_node_t* pars_select_statement( /*==================*/ sel_node_t* select_node, /*!< in: select node already containing the select list */ sym_node_t* table_list, /*!< in: table list */ que_node_t* search_cond, /*!< in: search condition or NULL */ pars_res_word_t* for_update, /*!< in: NULL or &pars_update_token */ pars_res_word_t* lock_shared, /*!< in: NULL or &pars_share_token */ order_node_t* order_by) /*!< in: NULL or an order-by node */ { select_node->state = SEL_NODE_OPEN; select_node->table_list = table_list; select_node->n_tables = pars_retrieve_table_list_defs(table_list); if (select_node->select_list == &pars_star_denoter) { /* SELECT * FROM ... */ pars_select_all_columns(select_node); } if (select_node->into_list) { ut_a(que_node_list_get_len(select_node->into_list) == que_node_list_get_len(select_node->select_list)); } UT_LIST_INIT(select_node->copy_variables); pars_resolve_exp_list_columns(table_list, select_node->select_list); pars_resolve_exp_list_variables_and_types(select_node, select_node->select_list); pars_check_aggregate(select_node); select_node->search_cond = search_cond; if (search_cond) { pars_resolve_exp_columns(table_list, search_cond); pars_resolve_exp_variables_and_types(select_node, search_cond); } if (for_update) { ut_a(!lock_shared); select_node->set_x_locks = TRUE; select_node->row_lock_mode = LOCK_X; select_node->consistent_read = FALSE; select_node->read_view = NULL; } else if (lock_shared){ select_node->set_x_locks = FALSE; select_node->row_lock_mode = LOCK_S; select_node->consistent_read = FALSE; select_node->read_view = NULL; } else { select_node->set_x_locks = FALSE; select_node->row_lock_mode = LOCK_S; select_node->consistent_read = TRUE; } select_node->order_by = order_by; if (order_by) { pars_resolve_exp_columns(table_list, order_by->column); } /* The final value of the following fields depend on the environment where the select statement appears: */ select_node->can_get_updated = FALSE; select_node->explicit_cursor = NULL; opt_search_plan(select_node); return(select_node); } /*********************************************************************//** Parses a cursor declaration. @return sym_node */ UNIV_INTERN que_node_t* pars_cursor_declaration( /*====================*/ sym_node_t* sym_node, /*!< in: cursor id node in the symbol table */ sel_node_t* select_node) /*!< in: select node */ { sym_node->resolved = TRUE; sym_node->token_type = SYM_CURSOR; sym_node->cursor_def = select_node; select_node->state = SEL_NODE_CLOSED; select_node->explicit_cursor = sym_node; return(sym_node); } /*********************************************************************//** Parses a function declaration. @return sym_node */ UNIV_INTERN que_node_t* pars_function_declaration( /*======================*/ sym_node_t* sym_node) /*!< in: function id node in the symbol table */ { sym_node->resolved = TRUE; sym_node->token_type = SYM_FUNCTION; /* Check that the function exists. */ ut_a(pars_info_get_user_func(pars_sym_tab_global->info, sym_node->name)); return(sym_node); } /*********************************************************************//** Parses a delete or update statement start. @return own: update node in a query tree */ UNIV_INTERN upd_node_t* pars_update_statement_start( /*========================*/ ibool is_delete, /*!< in: TRUE if delete */ sym_node_t* table_sym, /*!< in: table name node */ col_assign_node_t* col_assign_list)/*!< in: column assignment list, NULL if delete */ { upd_node_t* node; node = upd_node_create(pars_sym_tab_global->heap); node->is_delete = is_delete; node->table_sym = table_sym; node->col_assign_list = col_assign_list; return(node); } /*********************************************************************//** Parses a column assignment in an update. @return column assignment node */ UNIV_INTERN col_assign_node_t* pars_column_assignment( /*===================*/ sym_node_t* column, /*!< in: column to assign */ que_node_t* exp) /*!< in: value to assign */ { col_assign_node_t* node; node = mem_heap_alloc(pars_sym_tab_global->heap, sizeof(col_assign_node_t)); node->common.type = QUE_NODE_COL_ASSIGNMENT; node->col = column; node->val = exp; return(node); } /*********************************************************************//** Processes an update node assignment list. */ static void pars_process_assign_list( /*=====================*/ upd_node_t* node) /*!< in: update node */ { col_assign_node_t* col_assign_list; sym_node_t* table_sym; col_assign_node_t* assign_node; upd_field_t* upd_field; dict_index_t* clust_index; sym_node_t* col_sym; ulint changes_ord_field; ulint changes_field_size; ulint n_assigns; ulint i; table_sym = node->table_sym; col_assign_list = node->col_assign_list; clust_index = dict_table_get_first_index(node->table); assign_node = col_assign_list; n_assigns = 0; while (assign_node) { pars_resolve_exp_columns(table_sym, assign_node->col); pars_resolve_exp_columns(table_sym, assign_node->val); pars_resolve_exp_variables_and_types(NULL, assign_node->val); #if 0 ut_a(dtype_get_mtype( dfield_get_type(que_node_get_val( assign_node->col))) == dtype_get_mtype( dfield_get_type(que_node_get_val( assign_node->val)))); #endif /* Add to the update node all the columns found in assignment values as columns to copy: therefore, TRUE */ opt_find_all_cols(TRUE, clust_index, &(node->columns), NULL, assign_node->val); n_assigns++; assign_node = que_node_get_next(assign_node); } node->update = upd_create(n_assigns, pars_sym_tab_global->heap); assign_node = col_assign_list; changes_field_size = UPD_NODE_NO_SIZE_CHANGE; for (i = 0; i < n_assigns; i++) { upd_field = upd_get_nth_field(node->update, i); col_sym = assign_node->col; upd_field_set_field_no(upd_field, dict_index_get_nth_col_pos( clust_index, col_sym->col_no), clust_index, NULL); upd_field->exp = assign_node->val; if (!dict_col_get_fixed_size( dict_index_get_nth_col(clust_index, upd_field->field_no), dict_table_is_comp(node->table))) { changes_field_size = 0; } assign_node = que_node_get_next(assign_node); } /* Find out if the update can modify an ordering field in any index */ changes_ord_field = UPD_NODE_NO_ORD_CHANGE; if (row_upd_changes_some_index_ord_field_binary(node->table, node->update)) { changes_ord_field = 0; } node->cmpl_info = changes_ord_field | changes_field_size; } /*********************************************************************//** Parses an update or delete statement. @return own: update node in a query tree */ UNIV_INTERN upd_node_t* pars_update_statement( /*==================*/ upd_node_t* node, /*!< in: update node */ sym_node_t* cursor_sym, /*!< in: pointer to a cursor entry in the symbol table or NULL */ que_node_t* search_cond) /*!< in: search condition or NULL */ { sym_node_t* table_sym; sel_node_t* sel_node; plan_t* plan; table_sym = node->table_sym; pars_retrieve_table_def(table_sym); node->table = table_sym->table; UT_LIST_INIT(node->columns); /* Make the single table node into a list of table nodes of length 1 */ que_node_list_add_last(NULL, table_sym); if (cursor_sym) { pars_resolve_exp_variables_and_types(NULL, cursor_sym); sel_node = cursor_sym->alias->cursor_def; node->searched_update = FALSE; } else { sel_node = pars_select_list(NULL, NULL); pars_select_statement(sel_node, table_sym, search_cond, NULL, &pars_share_token, NULL); node->searched_update = TRUE; sel_node->common.parent = node; } node->select = sel_node; ut_a(!node->is_delete || (node->col_assign_list == NULL)); ut_a(node->is_delete || (node->col_assign_list != NULL)); if (node->is_delete) { node->cmpl_info = 0; } else { pars_process_assign_list(node); } if (node->searched_update) { node->has_clust_rec_x_lock = TRUE; sel_node->set_x_locks = TRUE; sel_node->row_lock_mode = LOCK_X; } else { node->has_clust_rec_x_lock = sel_node->set_x_locks; } ut_a(sel_node->n_tables == 1); ut_a(sel_node->consistent_read == FALSE); ut_a(sel_node->order_by == NULL); ut_a(sel_node->is_aggregate == FALSE); sel_node->can_get_updated = TRUE; node->state = UPD_NODE_UPDATE_CLUSTERED; plan = sel_node_get_nth_plan(sel_node, 0); plan->no_prefetch = TRUE; if (!dict_index_is_clust(plan->index)) { plan->must_get_clust = TRUE; node->pcur = &(plan->clust_pcur); } else { node->pcur = &(plan->pcur); } return(node); } /*********************************************************************//** Parses an insert statement. @return own: update node in a query tree */ UNIV_INTERN ins_node_t* pars_insert_statement( /*==================*/ sym_node_t* table_sym, /*!< in: table name node */ que_node_t* values_list, /*!< in: value expression list or NULL */ sel_node_t* select) /*!< in: select condition or NULL */ { ins_node_t* node; dtuple_t* row; ulint ins_type; ut_a(values_list || select); ut_a(!values_list || !select); if (values_list) { ins_type = INS_VALUES; } else { ins_type = INS_SEARCHED; } pars_retrieve_table_def(table_sym); node = row_ins_node_create(ins_type, table_sym->table, pars_sym_tab_global->heap); row = dtuple_create(pars_sym_tab_global->heap, dict_table_get_n_cols(node->table)); dict_table_copy_types(row, table_sym->table); row_ins_node_set_new_row(node, row); node->select = select; if (select) { select->common.parent = node; ut_a(que_node_list_get_len(select->select_list) == dict_table_get_n_user_cols(table_sym->table)); } node->values_list = values_list; if (node->values_list) { pars_resolve_exp_list_variables_and_types(NULL, values_list); ut_a(que_node_list_get_len(values_list) == dict_table_get_n_user_cols(table_sym->table)); } return(node); } /*********************************************************************//** Set the type of a dfield. */ static void pars_set_dfield_type( /*=================*/ dfield_t* dfield, /*!< in: dfield */ pars_res_word_t* type, /*!< in: pointer to a type token */ ulint len, /*!< in: length, or 0 */ ibool is_unsigned, /*!< in: if TRUE, column is UNSIGNED. */ ibool is_not_null) /*!< in: if TRUE, column is NOT NULL. */ { ulint flags = 0; if (is_not_null) { flags |= DATA_NOT_NULL; } if (is_unsigned) { flags |= DATA_UNSIGNED; } if (type == &pars_int_token) { ut_a(len == 0); dtype_set(dfield_get_type(dfield), DATA_INT, flags, 4); } else if (type == &pars_char_token) { ut_a(len == 0); dtype_set(dfield_get_type(dfield), DATA_VARCHAR, DATA_ENGLISH | flags, 0); } else if (type == &pars_binary_token) { ut_a(len != 0); dtype_set(dfield_get_type(dfield), DATA_FIXBINARY, DATA_BINARY_TYPE | flags, len); } else if (type == &pars_blob_token) { ut_a(len == 0); dtype_set(dfield_get_type(dfield), DATA_BLOB, DATA_BINARY_TYPE | flags, 0); } else { ut_error; } } /*********************************************************************//** Parses a variable declaration. @return own: symbol table node of type SYM_VAR */ UNIV_INTERN sym_node_t* pars_variable_declaration( /*======================*/ sym_node_t* node, /*!< in: symbol table node allocated for the id of the variable */ pars_res_word_t* type) /*!< in: pointer to a type token */ { node->resolved = TRUE; node->token_type = SYM_VAR; node->param_type = PARS_NOT_PARAM; pars_set_dfield_type(que_node_get_val(node), type, 0, FALSE, FALSE); return(node); } /*********************************************************************//** Parses a procedure parameter declaration. @return own: symbol table node of type SYM_VAR */ UNIV_INTERN sym_node_t* pars_parameter_declaration( /*=======================*/ sym_node_t* node, /*!< in: symbol table node allocated for the id of the parameter */ ulint param_type, /*!< in: PARS_INPUT or PARS_OUTPUT */ pars_res_word_t* type) /*!< in: pointer to a type token */ { ut_a((param_type == PARS_INPUT) || (param_type == PARS_OUTPUT)); pars_variable_declaration(node, type); node->param_type = param_type; return(node); } /*********************************************************************//** Sets the parent field in a query node list. */ static void pars_set_parent_in_list( /*====================*/ que_node_t* node_list, /*!< in: first node in a list */ que_node_t* parent) /*!< in: parent value to set in all nodes of the list */ { que_common_t* common; common = node_list; while (common) { common->parent = parent; common = que_node_get_next(common); } } /*********************************************************************//** Parses an elsif element. @return elsif node */ UNIV_INTERN elsif_node_t* pars_elsif_element( /*===============*/ que_node_t* cond, /*!< in: if-condition */ que_node_t* stat_list) /*!< in: statement list */ { elsif_node_t* node; node = mem_heap_alloc(pars_sym_tab_global->heap, sizeof(elsif_node_t)); node->common.type = QUE_NODE_ELSIF; node->cond = cond; pars_resolve_exp_variables_and_types(NULL, cond); node->stat_list = stat_list; return(node); } /*********************************************************************//** Parses an if-statement. @return if-statement node */ UNIV_INTERN if_node_t* pars_if_statement( /*==============*/ que_node_t* cond, /*!< in: if-condition */ que_node_t* stat_list, /*!< in: statement list */ que_node_t* else_part) /*!< in: else-part statement list or elsif element list */ { if_node_t* node; elsif_node_t* elsif_node; node = mem_heap_alloc(pars_sym_tab_global->heap, sizeof(if_node_t)); node->common.type = QUE_NODE_IF; node->cond = cond; pars_resolve_exp_variables_and_types(NULL, cond); node->stat_list = stat_list; if (else_part && (que_node_get_type(else_part) == QUE_NODE_ELSIF)) { /* There is a list of elsif conditions */ node->else_part = NULL; node->elsif_list = else_part; elsif_node = else_part; while (elsif_node) { pars_set_parent_in_list(elsif_node->stat_list, node); elsif_node = que_node_get_next(elsif_node); } } else { node->else_part = else_part; node->elsif_list = NULL; pars_set_parent_in_list(else_part, node); } pars_set_parent_in_list(stat_list, node); return(node); } /*********************************************************************//** Parses a while-statement. @return while-statement node */ UNIV_INTERN while_node_t* pars_while_statement( /*=================*/ que_node_t* cond, /*!< in: while-condition */ que_node_t* stat_list) /*!< in: statement list */ { while_node_t* node; node = mem_heap_alloc(pars_sym_tab_global->heap, sizeof(while_node_t)); node->common.type = QUE_NODE_WHILE; node->cond = cond; pars_resolve_exp_variables_and_types(NULL, cond); node->stat_list = stat_list; pars_set_parent_in_list(stat_list, node); return(node); } /*********************************************************************//** Parses a for-loop-statement. @return for-statement node */ UNIV_INTERN for_node_t* pars_for_statement( /*===============*/ sym_node_t* loop_var, /*!< in: loop variable */ que_node_t* loop_start_limit,/*!< in: loop start expression */ que_node_t* loop_end_limit, /*!< in: loop end expression */ que_node_t* stat_list) /*!< in: statement list */ { for_node_t* node; node = mem_heap_alloc(pars_sym_tab_global->heap, sizeof(for_node_t)); node->common.type = QUE_NODE_FOR; pars_resolve_exp_variables_and_types(NULL, loop_var); pars_resolve_exp_variables_and_types(NULL, loop_start_limit); pars_resolve_exp_variables_and_types(NULL, loop_end_limit); node->loop_var = loop_var->indirection; ut_a(loop_var->indirection); node->loop_start_limit = loop_start_limit; node->loop_end_limit = loop_end_limit; node->stat_list = stat_list; pars_set_parent_in_list(stat_list, node); return(node); } /*********************************************************************//** Parses an exit statement. @return exit statement node */ UNIV_INTERN exit_node_t* pars_exit_statement(void) /*=====================*/ { exit_node_t* node; node = mem_heap_alloc(pars_sym_tab_global->heap, sizeof(exit_node_t)); node->common.type = QUE_NODE_EXIT; return(node); } /*********************************************************************//** Parses a return-statement. @return return-statement node */ UNIV_INTERN return_node_t* pars_return_statement(void) /*=======================*/ { return_node_t* node; node = mem_heap_alloc(pars_sym_tab_global->heap, sizeof(return_node_t)); node->common.type = QUE_NODE_RETURN; return(node); } /*********************************************************************//** Parses an assignment statement. @return assignment statement node */ UNIV_INTERN assign_node_t* pars_assignment_statement( /*======================*/ sym_node_t* var, /*!< in: variable to assign */ que_node_t* val) /*!< in: value to assign */ { assign_node_t* node; node = mem_heap_alloc(pars_sym_tab_global->heap, sizeof(assign_node_t)); node->common.type = QUE_NODE_ASSIGNMENT; node->var = var; node->val = val; pars_resolve_exp_variables_and_types(NULL, var); pars_resolve_exp_variables_and_types(NULL, val); ut_a(dtype_get_mtype(dfield_get_type(que_node_get_val(var))) == dtype_get_mtype(dfield_get_type(que_node_get_val(val)))); return(node); } /*********************************************************************//** Parses a procedure call. @return function node */ UNIV_INTERN func_node_t* pars_procedure_call( /*================*/ que_node_t* res_word,/*!< in: procedure name reserved word */ que_node_t* args) /*!< in: argument list */ { func_node_t* node; node = pars_func(res_word, args); pars_resolve_exp_list_variables_and_types(NULL, args); return(node); } /*********************************************************************//** Parses a fetch statement. into_list or user_func (but not both) must be non-NULL. @return fetch statement node */ UNIV_INTERN fetch_node_t* pars_fetch_statement( /*=================*/ sym_node_t* cursor, /*!< in: cursor node */ sym_node_t* into_list, /*!< in: variables to set, or NULL */ sym_node_t* user_func) /*!< in: user function name, or NULL */ { sym_node_t* cursor_decl; fetch_node_t* node; /* Logical XOR. */ ut_a(!into_list != !user_func); node = mem_heap_alloc(pars_sym_tab_global->heap, sizeof(fetch_node_t)); node->common.type = QUE_NODE_FETCH; pars_resolve_exp_variables_and_types(NULL, cursor); if (into_list) { pars_resolve_exp_list_variables_and_types(NULL, into_list); node->into_list = into_list; node->func = NULL; } else { pars_resolve_exp_variables_and_types(NULL, user_func); node->func = pars_info_get_user_func(pars_sym_tab_global->info, user_func->name); ut_a(node->func); node->into_list = NULL; } cursor_decl = cursor->alias; ut_a(cursor_decl->token_type == SYM_CURSOR); node->cursor_def = cursor_decl->cursor_def; if (into_list) { ut_a(que_node_list_get_len(into_list) == que_node_list_get_len(node->cursor_def->select_list)); } return(node); } /*********************************************************************//** Parses an open or close cursor statement. @return fetch statement node */ UNIV_INTERN open_node_t* pars_open_statement( /*================*/ ulint type, /*!< in: ROW_SEL_OPEN_CURSOR or ROW_SEL_CLOSE_CURSOR */ sym_node_t* cursor) /*!< in: cursor node */ { sym_node_t* cursor_decl; open_node_t* node; node = mem_heap_alloc(pars_sym_tab_global->heap, sizeof(open_node_t)); node->common.type = QUE_NODE_OPEN; pars_resolve_exp_variables_and_types(NULL, cursor); cursor_decl = cursor->alias; ut_a(cursor_decl->token_type == SYM_CURSOR); node->op_type = type; node->cursor_def = cursor_decl->cursor_def; return(node); } /*********************************************************************//** Parses a row_printf-statement. @return row_printf-statement node */ UNIV_INTERN row_printf_node_t* pars_row_printf_statement( /*======================*/ sel_node_t* sel_node) /*!< in: select node */ { row_printf_node_t* node; node = mem_heap_alloc(pars_sym_tab_global->heap, sizeof(row_printf_node_t)); node->common.type = QUE_NODE_ROW_PRINTF; node->sel_node = sel_node; sel_node->common.parent = node; return(node); } /*********************************************************************//** Parses a commit statement. @return own: commit node struct */ UNIV_INTERN commit_node_t* pars_commit_statement(void) /*=======================*/ { return(commit_node_create(pars_sym_tab_global->heap)); } /*********************************************************************//** Parses a rollback statement. @return own: rollback node struct */ UNIV_INTERN roll_node_t* pars_rollback_statement(void) /*=========================*/ { return(roll_node_create(pars_sym_tab_global->heap)); } /*********************************************************************//** Parses a column definition at a table creation. @return column sym table node */ UNIV_INTERN sym_node_t* pars_column_def( /*============*/ sym_node_t* sym_node, /*!< in: column node in the symbol table */ pars_res_word_t* type, /*!< in: data type */ sym_node_t* len, /*!< in: length of column, or NULL */ void* is_unsigned, /*!< in: if not NULL, column is of type UNSIGNED. */ void* is_not_null) /*!< in: if not NULL, column is of type NOT NULL. */ { ulint len2; if (len) { len2 = eval_node_get_int_val(len); } else { len2 = 0; } pars_set_dfield_type(que_node_get_val(sym_node), type, len2, is_unsigned != NULL, is_not_null != NULL); return(sym_node); } /*********************************************************************//** Parses a table creation operation. @return table create subgraph */ UNIV_INTERN tab_node_t* pars_create_table( /*==============*/ sym_node_t* table_sym, /*!< in: table name node in the symbol table */ sym_node_t* column_defs, /*!< in: list of column names */ void* not_fit_in_memory __attribute__((unused))) /*!< in: a non-NULL pointer means that this is a table which in simulations should be simulated as not fitting in memory; thread is put to sleep to simulate disk accesses; NOTE that this flag is not stored to the data dictionary on disk, and the database will forget about non-NULL value if it has to reload the table definition from disk */ { dict_table_t* table; sym_node_t* column; tab_node_t* node; const dtype_t* dtype; ulint n_cols; n_cols = que_node_list_get_len(column_defs); /* As the InnoDB SQL parser is for internal use only, for creating some system tables, this function will only create tables in the old (not compact) record format. */ table = dict_mem_table_create(table_sym->name, 0, n_cols, 0); #ifdef UNIV_DEBUG if (not_fit_in_memory != NULL) { table->does_not_fit_in_memory = TRUE; } #endif /* UNIV_DEBUG */ column = column_defs; while (column) { dtype = dfield_get_type(que_node_get_val(column)); dict_mem_table_add_col(table, table->heap, column->name, dtype->mtype, dtype->prtype, dtype->len); column->resolved = TRUE; column->token_type = SYM_COLUMN; column = que_node_get_next(column); } node = tab_create_graph_create(table, pars_sym_tab_global->heap, TRUE); table_sym->resolved = TRUE; table_sym->token_type = SYM_TABLE; return(node); } /*********************************************************************//** Parses an index creation operation. @return index create subgraph */ UNIV_INTERN ind_node_t* pars_create_index( /*==============*/ pars_res_word_t* unique_def, /*!< in: not NULL if a unique index */ pars_res_word_t* clustered_def, /*!< in: not NULL if a clustered index */ sym_node_t* index_sym, /*!< in: index name node in the symbol table */ sym_node_t* table_sym, /*!< in: table name node in the symbol table */ sym_node_t* column_list) /*!< in: list of column names */ { dict_index_t* index; sym_node_t* column; ind_node_t* node; ulint n_fields; ulint ind_type; n_fields = que_node_list_get_len(column_list); ind_type = 0; if (unique_def) { ind_type = ind_type | DICT_UNIQUE; } if (clustered_def) { ind_type = ind_type | DICT_CLUSTERED; } index = dict_mem_index_create(table_sym->name, index_sym->name, 0, ind_type, n_fields); column = column_list; while (column) { dict_mem_index_add_field(index, column->name, 0); column->resolved = TRUE; column->token_type = SYM_COLUMN; column = que_node_get_next(column); } node = ind_create_graph_create(index, pars_sym_tab_global->heap, TRUE); table_sym->resolved = TRUE; table_sym->token_type = SYM_TABLE; index_sym->resolved = TRUE; index_sym->token_type = SYM_TABLE; return(node); } /*********************************************************************//** Parses a procedure definition. @return query fork node */ UNIV_INTERN que_fork_t* pars_procedure_definition( /*======================*/ sym_node_t* sym_node, /*!< in: procedure id node in the symbol table */ sym_node_t* param_list, /*!< in: parameter declaration list */ que_node_t* stat_list) /*!< in: statement list */ { proc_node_t* node; que_fork_t* fork; que_thr_t* thr; mem_heap_t* heap; heap = pars_sym_tab_global->heap; fork = que_fork_create(NULL, NULL, QUE_FORK_PROCEDURE, heap); fork->trx = NULL; thr = que_thr_create(fork, heap); node = mem_heap_alloc(heap, sizeof(proc_node_t)); node->common.type = QUE_NODE_PROC; node->common.parent = thr; sym_node->token_type = SYM_PROCEDURE_NAME; sym_node->resolved = TRUE; node->proc_id = sym_node; node->param_list = param_list; node->stat_list = stat_list; pars_set_parent_in_list(stat_list, node); node->sym_tab = pars_sym_tab_global; thr->child = node; pars_sym_tab_global->query_graph = fork; return(fork); } /*************************************************************//** Parses a stored procedure call, when this is not within another stored procedure, that is, the client issues a procedure call directly. In InnoDB, stored InnoDB procedures are invoked via the parsed procedure tree, not via InnoDB SQL, so this function is not used. @return query graph */ UNIV_INTERN que_fork_t* pars_stored_procedure_call( /*=======================*/ sym_node_t* sym_node __attribute__((unused))) /*!< in: stored procedure name */ { ut_error; return(NULL); } /*************************************************************//** Retrieves characters to the lexical analyzer. */ UNIV_INTERN void pars_get_lex_chars( /*===============*/ char* buf, /*!< in/out: buffer where to copy */ int* result, /*!< out: number of characters copied or EOF */ int max_size) /*!< in: maximum number of characters which fit in the buffer */ { int len; len = pars_sym_tab_global->string_len - pars_sym_tab_global->next_char_pos; if (len == 0) { #ifdef YYDEBUG /* ib_logger(ib_stream, "SQL string ends\n"); */ #endif *result = 0; return; } if (len > max_size) { len = max_size; } #ifdef UNIV_SQL_DEBUG if (pars_print_lexed) { if (len >= 5) { len = 5; } fwrite(pars_sym_tab_global->sql_string + pars_sym_tab_global->next_char_pos, 1, len, ib_stream); } #endif /* UNIV_SQL_DEBUG */ ut_memcpy(buf, pars_sym_tab_global->sql_string + pars_sym_tab_global->next_char_pos, len); *result = len; pars_sym_tab_global->next_char_pos += len; } /*************************************************************//** Called by yyparse on error. */ UNIV_INTERN void yyerror( /*====*/ const char* s __attribute__((unused))) /*!< in: error message string */ { ut_ad(s); ib_logger(ib_stream, "PARSER ERROR: Syntax error in SQL string\n"); ut_error; } /*************************************************************//** Parses an SQL string returning the query graph. @return own: the query graph */ UNIV_INTERN que_t* pars_sql( /*=====*/ pars_info_t* info, /*!< in: extra information, or NULL */ const char* str) /*!< in: SQL string */ { sym_node_t* sym_node; mem_heap_t* heap; que_t* graph; ut_ad(str); heap = mem_heap_create(256); /* Currently, the parser is not reentrant: */ ut_ad(mutex_own(&(dict_sys->mutex))); pars_sym_tab_global = sym_tab_create(heap); pars_sym_tab_global->string_len = strlen(str); pars_sym_tab_global->sql_string = mem_heap_dup( heap, str, pars_sym_tab_global->string_len + 1); pars_sym_tab_global->next_char_pos = 0; pars_sym_tab_global->info = info; yyparse(); sym_node = UT_LIST_GET_FIRST(pars_sym_tab_global->sym_list); while (sym_node) { ut_a(sym_node->resolved); sym_node = UT_LIST_GET_NEXT(sym_list, sym_node); } graph = pars_sym_tab_global->query_graph; graph->sym_tab = pars_sym_tab_global; graph->info = info; /* ib_logger(ib_stream, "SQL graph size %lu\n", mem_heap_get_size(heap)); */ return(graph); } /******************************************************************//** Completes a query graph by adding query thread and fork nodes above it and prepares the graph for running. The fork created is of type QUE_FORK_USER_INTERFACE. @return query thread node to run */ UNIV_INTERN que_thr_t* pars_complete_graph_for_exec( /*=========================*/ que_node_t* node, /*!< in: root node for an incomplete query graph */ trx_t* trx, /*!< in: transaction handle */ mem_heap_t* heap) /*!< in: memory heap from which allocated */ { que_fork_t* fork; que_thr_t* thr; fork = que_fork_create(NULL, NULL, QUE_FORK_USER_INTERFACE, heap); fork->trx = trx; thr = que_thr_create(fork, heap); thr->child = node; que_node_set_parent(node, thr); trx->graph = NULL; return(thr); } /****************************************************************//** Create parser info struct. @return own: info struct */ UNIV_INTERN pars_info_t* pars_info_create(void) /*==================*/ { pars_info_t* info; mem_heap_t* heap; heap = mem_heap_create(512); info = mem_heap_alloc(heap, sizeof(*info)); info->heap = heap; info->funcs = NULL; info->bound_lits = NULL; info->bound_ids = NULL; info->graph_owns_us = TRUE; return(info); } /****************************************************************//** Free info struct and everything it contains. */ UNIV_INTERN void pars_info_free( /*===========*/ pars_info_t* info) /*!< in, own: info struct */ { mem_heap_free(info->heap); } /****************************************************************//** Add bound literal. */ UNIV_INTERN void pars_info_add_literal( /*==================*/ pars_info_t* info, /*!< in: info struct */ const char* name, /*!< in: name */ const void* address, /*!< in: address */ ulint length, /*!< in: length of data */ ulint type, /*!< in: type, e.g. DATA_FIXBINARY */ ulint prtype) /*!< in: precise type, e.g. DATA_UNSIGNED */ { pars_bound_lit_t* pbl; ut_ad(!pars_info_get_bound_lit(info, name)); pbl = mem_heap_alloc(info->heap, sizeof(*pbl)); pbl->name = name; pbl->address = address; pbl->length = length; pbl->type = type; pbl->prtype = prtype; if (!info->bound_lits) { info->bound_lits = ib_vector_create(info->heap, 8); } ib_vector_push(info->bound_lits, pbl); } /****************************************************************//** Equivalent to pars_info_add_literal(info, name, str, strlen(str), DATA_VARCHAR, DATA_ENGLISH). */ UNIV_INTERN void pars_info_add_str_literal( /*======================*/ pars_info_t* info, /*!< in: info struct */ const char* name, /*!< in: name */ const char* str) /*!< in: string */ { pars_info_add_literal(info, name, str, strlen(str), DATA_VARCHAR, DATA_ENGLISH); } /****************************************************************//** Equivalent to: char buf[4]; mach_write_to_4(buf, val); pars_info_add_literal(info, name, buf, 4, DATA_INT, 0); except that the buffer is dynamically allocated from the info struct's heap. */ UNIV_INTERN void pars_info_add_int4_literal( /*=======================*/ pars_info_t* info, /*!< in: info struct */ const char* name, /*!< in: name */ lint val) /*!< in: value */ { byte* buf = mem_heap_alloc(info->heap, 4); mach_write_to_4(buf, val); pars_info_add_literal(info, name, buf, 4, DATA_INT, 0); } /****************************************************************//** Equivalent to: char buf[8]; mach_write_ull(buf, val); pars_info_add_literal(info, name, buf, 8, DATA_INT, 0); except that the buffer is dynamically allocated from the info struct's heap. */ UNIV_INTERN void pars_info_add_int8_literal( /*=======================*/ pars_info_t* info, /*!< in: info struct */ const char* name, /*!< in: name */ ib_uint64_t val) /*!< in: value */ { byte* buf = mem_heap_alloc(info->heap, sizeof(val)); mach_write_ull(buf, val); pars_info_add_literal(info, name, buf, sizeof(val), DATA_INT, 0); } /******************************************************************** Equivalent to: char buf[8]; mach_write_to_8(buf, val); pars_info_add_literal(info, name, buf, 8, DATA_FIXBINARY, 0); except that the buffer is dynamically allocated from the info struct's heap. */ UNIV_INTERN void pars_info_add_dulint_literal( /*=========================*/ pars_info_t* info, /*!< in: info struct */ const char* name, /*!< in: name */ dulint val) /*!< in: value */ { byte* buf = mem_heap_alloc(info->heap, 8); mach_write_to_8(buf, val); pars_info_add_literal(info, name, buf, 8, DATA_FIXBINARY, 0); } /****************************************************************//** Add user function. */ UNIV_INTERN void pars_info_add_function( /*===================*/ pars_info_t* info, /*!< in: info struct */ const char* name, /*!< in: function name */ pars_user_func_cb_t func, /*!< in: function address */ void* arg) /*!< in: user-supplied argument */ { pars_user_func_t* puf; ut_ad(!pars_info_get_user_func(info, name)); puf = mem_heap_alloc(info->heap, sizeof(*puf)); puf->name = name; puf->func = func; puf->arg = arg; if (!info->funcs) { info->funcs = ib_vector_create(info->heap, 8); } ib_vector_push(info->funcs, puf); } /****************************************************************//** Add bound id. */ UNIV_INTERN void pars_info_add_id( /*=============*/ pars_info_t* info, /*!< in: info struct */ const char* name, /*!< in: name */ const char* id) /*!< in: id */ { pars_bound_id_t* bid; ut_ad(!pars_info_get_bound_id(info, name)); bid = mem_heap_alloc(info->heap, sizeof(*bid)); bid->name = name; bid->id = id; if (!info->bound_ids) { info->bound_ids = ib_vector_create(info->heap, 8); } ib_vector_push(info->bound_ids, bid); } /****************************************************************//** Get user function with the given name. @return user func, or NULL if not found */ UNIV_INTERN pars_user_func_t* pars_info_get_user_func( /*====================*/ pars_info_t* info, /*!< in: info struct */ const char* name) /*!< in: function name to find*/ { ulint i; ib_vector_t* vec; if (!info || !info->funcs) { return(NULL); } vec = info->funcs; for (i = 0; i < ib_vector_size(vec); i++) { pars_user_func_t* puf = ib_vector_get(vec, i); if (strcmp(puf->name, name) == 0) { return(puf); } } return(NULL); } /****************************************************************//** Get bound literal with the given name. @return bound literal, or NULL if not found */ UNIV_INTERN pars_bound_lit_t* pars_info_get_bound_lit( /*====================*/ pars_info_t* info, /*!< in: info struct */ const char* name) /*!< in: bound literal name to find */ { ulint i; ib_vector_t* vec; if (!info || !info->bound_lits) { return(NULL); } vec = info->bound_lits; for (i = 0; i < ib_vector_size(vec); i++) { pars_bound_lit_t* pbl = ib_vector_get(vec, i); if (strcmp(pbl->name, name) == 0) { return(pbl); } } return(NULL); } /****************************************************************//** Get bound id with the given name. @return bound id, or NULL if not found */ UNIV_INTERN pars_bound_id_t* pars_info_get_bound_id( /*===================*/ pars_info_t* info, /*!< in: info struct */ const char* name) /*!< in: bound id name to find */ { ulint i; ib_vector_t* vec; if (!info || !info->bound_ids) { return(NULL); } vec = info->bound_ids; for (i = 0; i < ib_vector_size(vec); i++) { pars_bound_id_t* bid = ib_vector_get(vec, i); if (strcmp(bid->name, name) == 0) { return(bid); } } return(NULL); } haildb-2.3.2/pars/pars0grm.c0000644000175000017500000027733411513177357016527 0ustar00pcrewspcrews00000000000000 /* A Bison parser, made by GNU Bison 2.4.1. */ /* Skeleton implementation for Bison's Yacc-like parsers in C Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* As a special exception, you may create a larger work that contains part or all of the Bison parser skeleton and distribute that work under terms of your choice, so long as that work isn't itself a parser generator using the skeleton or a modified version thereof as a parser skeleton. Alternatively, if you modify or redistribute the parser skeleton itself, you may (at your option) remove this special exception, which will cause the skeleton and the resulting Bison output files to be licensed under the GNU General Public License without this special exception. This special exception was added by the Free Software Foundation in version 2.2 of Bison. */ /* C LALR(1) parser skeleton written by Richard Stallman, by simplifying the original so-called "semantic" parser. */ /* All symbols defined below should begin with yy or YY, to avoid infringing on user name space. This should be done even for local variables, as they might otherwise be expanded by user macros. There are some unavoidable exceptions within include files to define necessary library symbols; they are noted "INFRINGES ON USER NAME SPACE" below. */ /* Identify Bison output. */ #define YYBISON 1 /* Bison version. */ #define YYBISON_VERSION "2.4.1" /* Skeleton name. */ #define YYSKELETON_NAME "yacc.c" /* Pure parsers. */ #define YYPURE 0 /* Push parsers. */ #define YYPUSH 0 /* Pull parsers. */ #define YYPULL 1 /* Using locations. */ #define YYLSP_NEEDED 0 /* Copy the first part of user declarations. */ /* Line 189 of yacc.c */ #line 28 "pars0grm.y" /* The value of the semantic attribute is a pointer to a query tree node que_node_t */ #include "univ.i" #include /* Can't be before univ.i */ #include "pars0pars.h" #include "mem0mem.h" #include "que0types.h" #include "que0que.h" #include "row0sel.h" #define YYSTYPE que_node_t* /* #define __STDC__ */ int yylex(void); /* Line 189 of yacc.c */ #line 94 "pars/pars0grm.c" /* Enabling traces. */ #ifndef YYDEBUG # define YYDEBUG 0 #endif /* Enabling verbose error messages. */ #ifdef YYERROR_VERBOSE # undef YYERROR_VERBOSE # define YYERROR_VERBOSE 1 #else # define YYERROR_VERBOSE 0 #endif /* Enabling the token table. */ #ifndef YYTOKEN_TABLE # define YYTOKEN_TABLE 0 #endif /* Tokens. */ #ifndef YYTOKENTYPE # define YYTOKENTYPE /* Put the tokens into the symbol table, so that GDB and other debuggers know about them. */ enum yytokentype { PARS_INT_LIT = 258, PARS_FLOAT_LIT = 259, PARS_STR_LIT = 260, PARS_FIXBINARY_LIT = 261, PARS_BLOB_LIT = 262, PARS_NULL_LIT = 263, PARS_ID_TOKEN = 264, PARS_AND_TOKEN = 265, PARS_OR_TOKEN = 266, PARS_NOT_TOKEN = 267, PARS_GE_TOKEN = 268, PARS_LE_TOKEN = 269, PARS_NE_TOKEN = 270, PARS_PROCEDURE_TOKEN = 271, PARS_IN_TOKEN = 272, PARS_OUT_TOKEN = 273, PARS_BINARY_TOKEN = 274, PARS_BLOB_TOKEN = 275, PARS_INT_TOKEN = 276, PARS_INTEGER_TOKEN = 277, PARS_FLOAT_TOKEN = 278, PARS_CHAR_TOKEN = 279, PARS_IS_TOKEN = 280, PARS_BEGIN_TOKEN = 281, PARS_END_TOKEN = 282, PARS_IF_TOKEN = 283, PARS_THEN_TOKEN = 284, PARS_ELSE_TOKEN = 285, PARS_ELSIF_TOKEN = 286, PARS_LOOP_TOKEN = 287, PARS_WHILE_TOKEN = 288, PARS_RETURN_TOKEN = 289, PARS_SELECT_TOKEN = 290, PARS_SUM_TOKEN = 291, PARS_COUNT_TOKEN = 292, PARS_DISTINCT_TOKEN = 293, PARS_FROM_TOKEN = 294, PARS_WHERE_TOKEN = 295, PARS_FOR_TOKEN = 296, PARS_DDOT_TOKEN = 297, PARS_READ_TOKEN = 298, PARS_ORDER_TOKEN = 299, PARS_BY_TOKEN = 300, PARS_ASC_TOKEN = 301, PARS_DESC_TOKEN = 302, PARS_INSERT_TOKEN = 303, PARS_INTO_TOKEN = 304, PARS_VALUES_TOKEN = 305, PARS_UPDATE_TOKEN = 306, PARS_SET_TOKEN = 307, PARS_DELETE_TOKEN = 308, PARS_CURRENT_TOKEN = 309, PARS_OF_TOKEN = 310, PARS_CREATE_TOKEN = 311, PARS_TABLE_TOKEN = 312, PARS_INDEX_TOKEN = 313, PARS_UNIQUE_TOKEN = 314, PARS_CLUSTERED_TOKEN = 315, PARS_DOES_NOT_FIT_IN_MEM_TOKEN = 316, PARS_ON_TOKEN = 317, PARS_ASSIGN_TOKEN = 318, PARS_DECLARE_TOKEN = 319, PARS_CURSOR_TOKEN = 320, PARS_SQL_TOKEN = 321, PARS_OPEN_TOKEN = 322, PARS_FETCH_TOKEN = 323, PARS_CLOSE_TOKEN = 324, PARS_NOTFOUND_TOKEN = 325, PARS_TO_CHAR_TOKEN = 326, PARS_TO_NUMBER_TOKEN = 327, PARS_TO_BINARY_TOKEN = 328, PARS_BINARY_TO_NUMBER_TOKEN = 329, PARS_SUBSTR_TOKEN = 330, PARS_REPLSTR_TOKEN = 331, PARS_CONCAT_TOKEN = 332, PARS_INSTR_TOKEN = 333, PARS_LENGTH_TOKEN = 334, PARS_SYSDATE_TOKEN = 335, PARS_PRINTF_TOKEN = 336, PARS_ASSERT_TOKEN = 337, PARS_RND_TOKEN = 338, PARS_RND_STR_TOKEN = 339, PARS_ROW_PRINTF_TOKEN = 340, PARS_COMMIT_TOKEN = 341, PARS_ROLLBACK_TOKEN = 342, PARS_WORK_TOKEN = 343, PARS_UNSIGNED_TOKEN = 344, PARS_EXIT_TOKEN = 345, PARS_FUNCTION_TOKEN = 346, PARS_LOCK_TOKEN = 347, PARS_SHARE_TOKEN = 348, PARS_MODE_TOKEN = 349, NEG = 350 }; #endif /* Tokens. */ #define PARS_INT_LIT 258 #define PARS_FLOAT_LIT 259 #define PARS_STR_LIT 260 #define PARS_FIXBINARY_LIT 261 #define PARS_BLOB_LIT 262 #define PARS_NULL_LIT 263 #define PARS_ID_TOKEN 264 #define PARS_AND_TOKEN 265 #define PARS_OR_TOKEN 266 #define PARS_NOT_TOKEN 267 #define PARS_GE_TOKEN 268 #define PARS_LE_TOKEN 269 #define PARS_NE_TOKEN 270 #define PARS_PROCEDURE_TOKEN 271 #define PARS_IN_TOKEN 272 #define PARS_OUT_TOKEN 273 #define PARS_BINARY_TOKEN 274 #define PARS_BLOB_TOKEN 275 #define PARS_INT_TOKEN 276 #define PARS_INTEGER_TOKEN 277 #define PARS_FLOAT_TOKEN 278 #define PARS_CHAR_TOKEN 279 #define PARS_IS_TOKEN 280 #define PARS_BEGIN_TOKEN 281 #define PARS_END_TOKEN 282 #define PARS_IF_TOKEN 283 #define PARS_THEN_TOKEN 284 #define PARS_ELSE_TOKEN 285 #define PARS_ELSIF_TOKEN 286 #define PARS_LOOP_TOKEN 287 #define PARS_WHILE_TOKEN 288 #define PARS_RETURN_TOKEN 289 #define PARS_SELECT_TOKEN 290 #define PARS_SUM_TOKEN 291 #define PARS_COUNT_TOKEN 292 #define PARS_DISTINCT_TOKEN 293 #define PARS_FROM_TOKEN 294 #define PARS_WHERE_TOKEN 295 #define PARS_FOR_TOKEN 296 #define PARS_DDOT_TOKEN 297 #define PARS_READ_TOKEN 298 #define PARS_ORDER_TOKEN 299 #define PARS_BY_TOKEN 300 #define PARS_ASC_TOKEN 301 #define PARS_DESC_TOKEN 302 #define PARS_INSERT_TOKEN 303 #define PARS_INTO_TOKEN 304 #define PARS_VALUES_TOKEN 305 #define PARS_UPDATE_TOKEN 306 #define PARS_SET_TOKEN 307 #define PARS_DELETE_TOKEN 308 #define PARS_CURRENT_TOKEN 309 #define PARS_OF_TOKEN 310 #define PARS_CREATE_TOKEN 311 #define PARS_TABLE_TOKEN 312 #define PARS_INDEX_TOKEN 313 #define PARS_UNIQUE_TOKEN 314 #define PARS_CLUSTERED_TOKEN 315 #define PARS_DOES_NOT_FIT_IN_MEM_TOKEN 316 #define PARS_ON_TOKEN 317 #define PARS_ASSIGN_TOKEN 318 #define PARS_DECLARE_TOKEN 319 #define PARS_CURSOR_TOKEN 320 #define PARS_SQL_TOKEN 321 #define PARS_OPEN_TOKEN 322 #define PARS_FETCH_TOKEN 323 #define PARS_CLOSE_TOKEN 324 #define PARS_NOTFOUND_TOKEN 325 #define PARS_TO_CHAR_TOKEN 326 #define PARS_TO_NUMBER_TOKEN 327 #define PARS_TO_BINARY_TOKEN 328 #define PARS_BINARY_TO_NUMBER_TOKEN 329 #define PARS_SUBSTR_TOKEN 330 #define PARS_REPLSTR_TOKEN 331 #define PARS_CONCAT_TOKEN 332 #define PARS_INSTR_TOKEN 333 #define PARS_LENGTH_TOKEN 334 #define PARS_SYSDATE_TOKEN 335 #define PARS_PRINTF_TOKEN 336 #define PARS_ASSERT_TOKEN 337 #define PARS_RND_TOKEN 338 #define PARS_RND_STR_TOKEN 339 #define PARS_ROW_PRINTF_TOKEN 340 #define PARS_COMMIT_TOKEN 341 #define PARS_ROLLBACK_TOKEN 342 #define PARS_WORK_TOKEN 343 #define PARS_UNSIGNED_TOKEN 344 #define PARS_EXIT_TOKEN 345 #define PARS_FUNCTION_TOKEN 346 #define PARS_LOCK_TOKEN 347 #define PARS_SHARE_TOKEN 348 #define PARS_MODE_TOKEN 349 #define NEG 350 #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED typedef int YYSTYPE; # define YYSTYPE_IS_TRIVIAL 1 # define yystype YYSTYPE /* obsolescent; will be withdrawn */ # define YYSTYPE_IS_DECLARED 1 #endif /* Copy the second part of user declarations. */ /* Line 264 of yacc.c */ #line 326 "pars/pars0grm.c" #ifdef short # undef short #endif #ifdef YYTYPE_UINT8 typedef YYTYPE_UINT8 yytype_uint8; #else typedef unsigned char yytype_uint8; #endif #ifdef YYTYPE_INT8 typedef YYTYPE_INT8 yytype_int8; #elif (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) typedef signed char yytype_int8; #else typedef short int yytype_int8; #endif #ifdef YYTYPE_UINT16 typedef YYTYPE_UINT16 yytype_uint16; #else typedef unsigned short int yytype_uint16; #endif #ifdef YYTYPE_INT16 typedef YYTYPE_INT16 yytype_int16; #else typedef short int yytype_int16; #endif #ifndef YYSIZE_T # ifdef __SIZE_TYPE__ # define YYSIZE_T __SIZE_TYPE__ # elif defined size_t # define YYSIZE_T size_t # elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) # include /* INFRINGES ON USER NAME SPACE */ # define YYSIZE_T size_t # else # define YYSIZE_T unsigned int # endif #endif #define YYSIZE_MAXIMUM ((YYSIZE_T) -1) #ifndef YY_ # if YYENABLE_NLS # if ENABLE_NLS # include /* INFRINGES ON USER NAME SPACE */ # define YY_(msgid) dgettext ("bison-runtime", msgid) # endif # endif # ifndef YY_ # define YY_(msgid) msgid # endif #endif /* Suppress unused-variable warnings by "using" E. */ #if ! defined lint || defined __GNUC__ # define YYUSE(e) ((void) (e)) #else # define YYUSE(e) /* empty */ #endif /* Identity function, used to suppress warnings about constant conditions. */ #ifndef lint # define YYID(n) (n) #else #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static int YYID (int yyi) #else static int YYID (yyi) int yyi; #endif { return yyi; } #endif #if ! defined yyoverflow || YYERROR_VERBOSE /* The parser invokes alloca or malloc; define the necessary symbols. */ # ifdef YYSTACK_USE_ALLOCA # if YYSTACK_USE_ALLOCA # ifdef __GNUC__ # define YYSTACK_ALLOC __builtin_alloca # elif defined __BUILTIN_VA_ARG_INCR # include /* INFRINGES ON USER NAME SPACE */ # elif defined _AIX # define YYSTACK_ALLOC __alloca # elif defined _MSC_VER # include /* INFRINGES ON USER NAME SPACE */ # define alloca _alloca # else # define YYSTACK_ALLOC alloca # if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) # include /* INFRINGES ON USER NAME SPACE */ # ifndef _STDLIB_H # define _STDLIB_H 1 # endif # endif # endif # endif # endif # ifdef YYSTACK_ALLOC /* Pacify GCC's `empty if-body' warning. */ # define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0)) # ifndef YYSTACK_ALLOC_MAXIMUM /* The OS might guarantee only one guard page at the bottom of the stack, and a page size can be as small as 4096 bytes. So we cannot safely invoke alloca (N) if N exceeds 4096. Use a slightly smaller number to allow for a few compiler-allocated temporary stack slots. */ # define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ # endif # else # define YYSTACK_ALLOC YYMALLOC # define YYSTACK_FREE YYFREE # ifndef YYSTACK_ALLOC_MAXIMUM # define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM # endif # if (defined __cplusplus && ! defined _STDLIB_H \ && ! ((defined YYMALLOC || defined malloc) \ && (defined YYFREE || defined free))) # include /* INFRINGES ON USER NAME SPACE */ # ifndef _STDLIB_H # define _STDLIB_H 1 # endif # endif # ifndef YYMALLOC # define YYMALLOC malloc # if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ # endif # endif # ifndef YYFREE # define YYFREE free # if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) void free (void *); /* INFRINGES ON USER NAME SPACE */ # endif # endif # endif #endif /* ! defined yyoverflow || YYERROR_VERBOSE */ #if (! defined yyoverflow \ && (! defined __cplusplus \ || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) /* A type that is properly aligned for any stack member. */ union yyalloc { yytype_int16 yyss_alloc; YYSTYPE yyvs_alloc; }; /* The size of the maximum gap between one aligned stack and the next. */ # define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) /* The size of an array large to enough to hold all stacks, each with N elements. */ # define YYSTACK_BYTES(N) \ ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \ + YYSTACK_GAP_MAXIMUM) /* Copy COUNT objects from FROM to TO. The source and destination do not overlap. */ # ifndef YYCOPY # if defined __GNUC__ && 1 < __GNUC__ # define YYCOPY(To, From, Count) \ __builtin_memcpy (To, From, (Count) * sizeof (*(From))) # else # define YYCOPY(To, From, Count) \ do \ { \ YYSIZE_T yyi; \ for (yyi = 0; yyi < (Count); yyi++) \ (To)[yyi] = (From)[yyi]; \ } \ while (YYID (0)) # endif # endif /* Relocate STACK from its old location to the new one. The local variables YYSIZE and YYSTACKSIZE give the old and new number of elements in the stack, and YYPTR gives the new location of the stack. Advance YYPTR to a properly aligned location for the next stack. */ # define YYSTACK_RELOCATE(Stack_alloc, Stack) \ do \ { \ YYSIZE_T yynewbytes; \ YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ Stack = &yyptr->Stack_alloc; \ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ yyptr += yynewbytes / sizeof (*yyptr); \ } \ while (YYID (0)) #endif /* YYFINAL -- State number of the termination state. */ #define YYFINAL 5 /* YYLAST -- Last index in YYTABLE. */ #define YYLAST 752 /* YYNTOKENS -- Number of terminals. */ #define YYNTOKENS 111 /* YYNNTS -- Number of nonterminals. */ #define YYNNTS 70 /* YYNRULES -- Number of rules. */ #define YYNRULES 175 /* YYNRULES -- Number of states. */ #define YYNSTATES 339 /* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ #define YYUNDEFTOK 2 #define YYMAXUTOK 350 #define YYTRANSLATE(YYX) \ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) /* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ static const yytype_uint8 yytranslate[] = { 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 103, 2, 2, 105, 106, 100, 99, 108, 98, 2, 101, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 104, 96, 95, 97, 107, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 109, 2, 110, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 102 }; #if YYDEBUG /* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in YYRHS. */ static const yytype_uint16 yyprhs[] = { 0, 0, 3, 6, 8, 11, 14, 17, 20, 23, 26, 29, 32, 35, 38, 41, 44, 47, 50, 53, 56, 59, 62, 65, 68, 71, 73, 76, 78, 83, 85, 87, 89, 91, 93, 95, 97, 101, 105, 109, 113, 116, 120, 124, 128, 132, 136, 140, 144, 148, 152, 155, 159, 163, 165, 167, 169, 171, 173, 175, 177, 179, 181, 183, 185, 186, 188, 192, 199, 204, 206, 208, 210, 214, 216, 220, 221, 223, 227, 228, 230, 234, 236, 241, 247, 252, 253, 255, 259, 261, 265, 267, 268, 271, 272, 275, 276, 281, 282, 284, 286, 287, 292, 301, 305, 311, 314, 318, 320, 324, 329, 334, 337, 340, 344, 347, 350, 353, 357, 362, 364, 367, 368, 371, 373, 381, 388, 399, 401, 403, 406, 409, 414, 419, 425, 427, 431, 432, 436, 437, 439, 440, 443, 444, 446, 454, 456, 460, 461, 463, 464, 466, 477, 480, 483, 485, 487, 489, 491, 493, 497, 501, 502, 504, 508, 512, 513, 515, 518, 525, 530, 532, 534, 535, 537, 540 }; /* YYRHS -- A `-1'-separated list of the rules' RHS. */ static const yytype_int16 yyrhs[] = { 112, 0, -1, 180, 104, -1, 118, -1, 119, 104, -1, 151, 104, -1, 152, 104, -1, 153, 104, -1, 150, 104, -1, 154, 104, -1, 146, 104, -1, 133, 104, -1, 135, 104, -1, 145, 104, -1, 143, 104, -1, 144, 104, -1, 140, 104, -1, 141, 104, -1, 155, 104, -1, 157, 104, -1, 156, 104, -1, 169, 104, -1, 170, 104, -1, 164, 104, -1, 168, 104, -1, 113, -1, 114, 113, -1, 9, -1, 116, 105, 124, 106, -1, 3, -1, 4, -1, 5, -1, 6, -1, 7, -1, 8, -1, 66, -1, 115, 99, 115, -1, 115, 98, 115, -1, 115, 100, 115, -1, 115, 101, 115, -1, 98, 115, -1, 105, 115, 106, -1, 115, 95, 115, -1, 115, 96, 115, -1, 115, 97, 115, -1, 115, 13, 115, -1, 115, 14, 115, -1, 115, 15, 115, -1, 115, 10, 115, -1, 115, 11, 115, -1, 12, 115, -1, 9, 103, 70, -1, 66, 103, 70, -1, 71, -1, 72, -1, 73, -1, 74, -1, 75, -1, 77, -1, 78, -1, 79, -1, 80, -1, 83, -1, 84, -1, -1, 107, -1, 117, 108, 107, -1, 109, 9, 105, 117, 106, 110, -1, 120, 105, 124, 106, -1, 76, -1, 81, -1, 82, -1, 9, 105, 106, -1, 9, -1, 122, 108, 9, -1, -1, 9, -1, 123, 108, 9, -1, -1, 115, -1, 124, 108, 115, -1, 115, -1, 37, 105, 100, 106, -1, 37, 105, 38, 9, 106, -1, 36, 105, 115, 106, -1, -1, 125, -1, 126, 108, 125, -1, 100, -1, 126, 49, 123, -1, 126, -1, -1, 40, 115, -1, -1, 41, 51, -1, -1, 92, 17, 93, 94, -1, -1, 46, -1, 47, -1, -1, 44, 45, 9, 131, -1, 35, 127, 39, 122, 128, 129, 130, 132, -1, 48, 49, 9, -1, 134, 50, 105, 124, 106, -1, 134, 133, -1, 9, 95, 115, -1, 136, -1, 137, 108, 136, -1, 40, 54, 55, 9, -1, 51, 9, 52, 137, -1, 139, 128, -1, 139, 138, -1, 53, 39, 9, -1, 142, 128, -1, 142, 138, -1, 85, 133, -1, 9, 63, 115, -1, 31, 115, 29, 114, -1, 147, -1, 148, 147, -1, -1, 30, 114, -1, 148, -1, 28, 115, 29, 114, 149, 27, 28, -1, 33, 115, 32, 114, 27, 32, -1, 41, 9, 17, 115, 42, 115, 32, 114, 27, 32, -1, 90, -1, 34, -1, 67, 9, -1, 69, 9, -1, 68, 9, 49, 123, -1, 68, 9, 49, 121, -1, 9, 171, 160, 161, 162, -1, 158, -1, 159, 108, 158, -1, -1, 105, 3, 106, -1, -1, 89, -1, -1, 12, 8, -1, -1, 61, -1, 56, 57, 9, 105, 159, 106, 163, -1, 9, -1, 165, 108, 9, -1, -1, 59, -1, -1, 60, -1, 56, 166, 167, 58, 9, 62, 9, 105, 165, 106, -1, 86, 88, -1, 87, 88, -1, 21, -1, 22, -1, 24, -1, 19, -1, 20, -1, 9, 17, 171, -1, 9, 18, 171, -1, -1, 172, -1, 173, 108, 172, -1, 9, 171, 104, -1, -1, 174, -1, 175, 174, -1, 64, 65, 9, 25, 133, 104, -1, 64, 91, 9, 104, -1, 176, -1, 177, -1, -1, 178, -1, 179, 178, -1, 16, 9, 105, 173, 106, 25, 175, 179, 26, 114, 27, -1 }; /* YYRLINE[YYN] -- source line where rule number YYN was defined. */ static const yytype_uint16 yyrline[] = { 0, 153, 153, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 181, 182, 187, 188, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 214, 219, 220, 221, 222, 224, 225, 226, 227, 228, 229, 230, 233, 235, 236, 240, 245, 250, 251, 252, 256, 260, 261, 266, 267, 268, 273, 274, 275, 279, 280, 285, 291, 298, 299, 300, 305, 307, 309, 313, 314, 318, 319, 324, 325, 330, 331, 332, 336, 337, 342, 352, 357, 359, 364, 368, 369, 374, 380, 387, 392, 397, 403, 408, 413, 418, 423, 429, 430, 435, 436, 438, 442, 449, 455, 463, 467, 471, 477, 483, 485, 490, 495, 496, 501, 502, 507, 508, 514, 515, 521, 522, 528, 534, 535, 540, 541, 545, 546, 550, 558, 563, 568, 569, 570, 571, 572, 576, 579, 585, 586, 587, 592, 596, 598, 599, 603, 609, 614, 615, 618, 620, 621, 625 }; #endif #if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE /* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. First, the terminals, then, starting at YYNTOKENS, nonterminals. */ static const char *const yytname[] = { "$end", "error", "$undefined", "PARS_INT_LIT", "PARS_FLOAT_LIT", "PARS_STR_LIT", "PARS_FIXBINARY_LIT", "PARS_BLOB_LIT", "PARS_NULL_LIT", "PARS_ID_TOKEN", "PARS_AND_TOKEN", "PARS_OR_TOKEN", "PARS_NOT_TOKEN", "PARS_GE_TOKEN", "PARS_LE_TOKEN", "PARS_NE_TOKEN", "PARS_PROCEDURE_TOKEN", "PARS_IN_TOKEN", "PARS_OUT_TOKEN", "PARS_BINARY_TOKEN", "PARS_BLOB_TOKEN", "PARS_INT_TOKEN", "PARS_INTEGER_TOKEN", "PARS_FLOAT_TOKEN", "PARS_CHAR_TOKEN", "PARS_IS_TOKEN", "PARS_BEGIN_TOKEN", "PARS_END_TOKEN", "PARS_IF_TOKEN", "PARS_THEN_TOKEN", "PARS_ELSE_TOKEN", "PARS_ELSIF_TOKEN", "PARS_LOOP_TOKEN", "PARS_WHILE_TOKEN", "PARS_RETURN_TOKEN", "PARS_SELECT_TOKEN", "PARS_SUM_TOKEN", "PARS_COUNT_TOKEN", "PARS_DISTINCT_TOKEN", "PARS_FROM_TOKEN", "PARS_WHERE_TOKEN", "PARS_FOR_TOKEN", "PARS_DDOT_TOKEN", "PARS_READ_TOKEN", "PARS_ORDER_TOKEN", "PARS_BY_TOKEN", "PARS_ASC_TOKEN", "PARS_DESC_TOKEN", "PARS_INSERT_TOKEN", "PARS_INTO_TOKEN", "PARS_VALUES_TOKEN", "PARS_UPDATE_TOKEN", "PARS_SET_TOKEN", "PARS_DELETE_TOKEN", "PARS_CURRENT_TOKEN", "PARS_OF_TOKEN", "PARS_CREATE_TOKEN", "PARS_TABLE_TOKEN", "PARS_INDEX_TOKEN", "PARS_UNIQUE_TOKEN", "PARS_CLUSTERED_TOKEN", "PARS_DOES_NOT_FIT_IN_MEM_TOKEN", "PARS_ON_TOKEN", "PARS_ASSIGN_TOKEN", "PARS_DECLARE_TOKEN", "PARS_CURSOR_TOKEN", "PARS_SQL_TOKEN", "PARS_OPEN_TOKEN", "PARS_FETCH_TOKEN", "PARS_CLOSE_TOKEN", "PARS_NOTFOUND_TOKEN", "PARS_TO_CHAR_TOKEN", "PARS_TO_NUMBER_TOKEN", "PARS_TO_BINARY_TOKEN", "PARS_BINARY_TO_NUMBER_TOKEN", "PARS_SUBSTR_TOKEN", "PARS_REPLSTR_TOKEN", "PARS_CONCAT_TOKEN", "PARS_INSTR_TOKEN", "PARS_LENGTH_TOKEN", "PARS_SYSDATE_TOKEN", "PARS_PRINTF_TOKEN", "PARS_ASSERT_TOKEN", "PARS_RND_TOKEN", "PARS_RND_STR_TOKEN", "PARS_ROW_PRINTF_TOKEN", "PARS_COMMIT_TOKEN", "PARS_ROLLBACK_TOKEN", "PARS_WORK_TOKEN", "PARS_UNSIGNED_TOKEN", "PARS_EXIT_TOKEN", "PARS_FUNCTION_TOKEN", "PARS_LOCK_TOKEN", "PARS_SHARE_TOKEN", "PARS_MODE_TOKEN", "'='", "'<'", "'>'", "'-'", "'+'", "'*'", "'/'", "NEG", "'%'", "';'", "'('", "')'", "'?'", "','", "'{'", "'}'", "$accept", "top_statement", "statement", "statement_list", "exp", "function_name", "question_mark_list", "stored_procedure_call", "predefined_procedure_call", "predefined_procedure_name", "user_function_call", "table_list", "variable_list", "exp_list", "select_item", "select_item_list", "select_list", "search_condition", "for_update_clause", "lock_shared_clause", "order_direction", "order_by_clause", "select_statement", "insert_statement_start", "insert_statement", "column_assignment", "column_assignment_list", "cursor_positioned", "update_statement_start", "update_statement_searched", "update_statement_positioned", "delete_statement_start", "delete_statement_searched", "delete_statement_positioned", "row_printf_statement", "assignment_statement", "elsif_element", "elsif_list", "else_part", "if_statement", "while_statement", "for_statement", "exit_statement", "return_statement", "open_cursor_statement", "close_cursor_statement", "fetch_statement", "column_def", "column_def_list", "opt_column_len", "opt_unsigned", "opt_not_null", "not_fit_in_memory", "create_table", "column_list", "unique_def", "clustered_def", "create_index", "commit_statement", "rollback_statement", "type_name", "parameter_declaration", "parameter_declaration_list", "variable_declaration", "variable_declaration_list", "cursor_declaration", "function_declaration", "declaration", "declaration_list", "procedure_definition", 0 }; #endif # ifdef YYPRINT /* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to token YYLEX-NUM. */ static const yytype_uint16 yytoknum[] = { 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 61, 60, 62, 45, 43, 42, 47, 350, 37, 59, 40, 41, 63, 44, 123, 125 }; # endif /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ static const yytype_uint8 yyr1[] = { 0, 111, 112, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 114, 114, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 115, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 117, 117, 117, 118, 119, 120, 120, 120, 121, 122, 122, 123, 123, 123, 124, 124, 124, 125, 125, 125, 125, 126, 126, 126, 127, 127, 127, 128, 128, 129, 129, 130, 130, 131, 131, 131, 132, 132, 133, 134, 135, 135, 136, 137, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 148, 149, 149, 149, 150, 151, 152, 153, 154, 155, 156, 157, 157, 158, 159, 159, 160, 160, 161, 161, 162, 162, 163, 163, 164, 165, 165, 166, 166, 167, 167, 168, 169, 170, 171, 171, 171, 171, 171, 172, 172, 173, 173, 173, 174, 175, 175, 175, 176, 177, 178, 178, 179, 179, 179, 180 }; /* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ static const yytype_uint8 yyr2[] = { 0, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 1, 4, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 3, 6, 4, 1, 1, 1, 3, 1, 3, 0, 1, 3, 0, 1, 3, 1, 4, 5, 4, 0, 1, 3, 1, 3, 1, 0, 2, 0, 2, 0, 4, 0, 1, 1, 0, 4, 8, 3, 5, 2, 3, 1, 3, 4, 4, 2, 2, 3, 2, 2, 2, 3, 4, 1, 2, 0, 2, 1, 7, 6, 10, 1, 1, 2, 2, 4, 4, 5, 1, 3, 0, 3, 0, 1, 0, 2, 0, 1, 7, 1, 3, 0, 1, 0, 1, 10, 2, 2, 1, 1, 1, 1, 1, 3, 3, 0, 1, 3, 3, 0, 1, 2, 6, 4, 1, 1, 0, 1, 2, 11 }; /* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state STATE-NUM when YYTABLE doesn't specify something else to do. Zero means the default is an error. */ static const yytype_uint8 yydefact[] = { 0, 0, 0, 0, 0, 1, 2, 161, 0, 162, 0, 0, 0, 0, 0, 157, 158, 154, 155, 156, 159, 160, 165, 163, 0, 166, 172, 0, 0, 167, 170, 171, 173, 0, 164, 0, 0, 0, 174, 0, 0, 0, 0, 0, 128, 85, 0, 0, 0, 0, 147, 0, 0, 0, 69, 70, 71, 0, 0, 0, 127, 0, 25, 0, 3, 0, 0, 0, 0, 0, 91, 0, 0, 91, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 169, 0, 29, 30, 31, 32, 33, 34, 27, 0, 35, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 0, 0, 0, 0, 0, 0, 0, 88, 81, 86, 90, 0, 0, 0, 0, 0, 0, 148, 149, 129, 0, 130, 116, 152, 153, 0, 175, 26, 4, 78, 11, 0, 105, 12, 0, 111, 112, 16, 17, 114, 115, 14, 15, 13, 10, 8, 5, 6, 7, 9, 18, 20, 19, 23, 24, 21, 22, 0, 117, 0, 50, 0, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 78, 0, 0, 0, 75, 0, 0, 0, 103, 0, 113, 0, 150, 0, 75, 64, 79, 0, 78, 0, 92, 168, 51, 52, 41, 48, 49, 45, 46, 47, 121, 42, 43, 44, 37, 36, 38, 39, 0, 0, 0, 0, 0, 76, 89, 87, 73, 91, 0, 0, 107, 110, 0, 0, 76, 132, 131, 65, 0, 68, 0, 0, 0, 0, 0, 119, 123, 0, 28, 0, 84, 0, 82, 0, 0, 0, 93, 0, 0, 0, 0, 134, 0, 0, 0, 0, 0, 80, 104, 109, 122, 0, 120, 0, 125, 83, 77, 74, 0, 95, 0, 106, 108, 136, 142, 0, 0, 72, 67, 66, 0, 124, 94, 0, 100, 0, 0, 138, 143, 144, 135, 0, 118, 0, 0, 102, 0, 0, 139, 140, 0, 0, 0, 0, 137, 0, 133, 145, 0, 96, 97, 126, 141, 151, 0, 98, 99, 101, 146 }; /* YYDEFGOTO[NTERM-NUM]. */ static const yytype_int16 yydefgoto[] = { -1, 2, 62, 63, 206, 116, 248, 64, 65, 66, 245, 237, 234, 207, 122, 123, 124, 148, 289, 304, 337, 315, 67, 68, 69, 240, 241, 149, 70, 71, 72, 73, 74, 75, 76, 77, 255, 256, 257, 78, 79, 80, 81, 82, 83, 84, 85, 271, 272, 307, 319, 326, 309, 86, 328, 131, 203, 87, 88, 89, 20, 9, 10, 25, 26, 30, 31, 32, 33, 3 }; /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing STATE-NUM. */ #define YYPACT_NINF -177 static const yytype_int16 yypact[] = { 28, 38, 54, -46, -29, -177, -177, 56, 50, -177, -75, 8, 8, 46, 56, -177, -177, -177, -177, -177, -177, -177, 63, -177, 8, -177, 2, -26, -51, -177, -177, -177, -177, -13, -177, 71, 72, 587, -177, 57, -21, 26, 272, 272, -177, 13, 91, 55, 96, 67, -22, 99, 100, 103, -177, -177, -177, 75, 29, 35, -177, 116, -177, 396, -177, 22, 23, 27, -9, 30, 87, 31, 32, 87, 47, 49, 52, 58, 59, 60, 61, 62, 65, 66, 74, 77, 78, 86, 89, 102, 75, -177, 272, -177, -177, -177, -177, -177, -177, 39, 272, 51, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, 272, 272, 361, 25, 489, 45, 90, -177, 651, -177, -39, 93, 142, 124, 108, 152, 170, -177, 131, -177, 143, -177, -177, -177, -177, 98, -177, -177, -177, 272, -177, 110, -177, -177, 256, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, 112, 651, 137, 101, 147, 204, 88, 272, 272, 272, 272, 272, 587, 272, 272, 272, 272, 272, 272, 272, 272, 587, 272, -30, 211, 168, 212, 272, -177, 213, -177, 118, -177, 167, 217, 122, 651, -63, 272, 175, 651, -177, -177, -177, -177, 101, 101, 21, 21, 651, 332, 21, 21, 21, -6, -6, 204, 204, -60, 460, 198, 222, 126, -177, 125, -177, -177, -33, 584, 140, -177, 128, 228, 229, 139, -177, 125, -177, -53, -177, 272, -49, 240, 587, 272, -177, 224, 226, -177, 225, -177, 150, -177, 258, 272, 260, 230, 272, 272, 213, 8, -177, -45, 208, 166, 164, 176, 651, -177, -177, 587, 631, -177, 254, -177, -177, -177, -177, 234, 194, 638, 651, -177, 182, 227, 228, 280, -177, -177, -177, 587, -177, -177, 273, 247, 587, 289, 214, -177, -177, -177, 195, 587, 209, 261, -177, 524, 199, -177, 295, 292, 215, 299, 279, -177, 304, -177, -177, -44, -177, -8, -177, -177, -177, 305, -177, -177, -177, -177 }; /* YYPGOTO[NTERM-NUM]. */ static const yytype_int16 yypgoto[] = { -177, -177, -62, -176, -40, -177, -177, -177, -177, -177, -177, -177, 109, -166, 120, -177, -177, -69, -177, -177, -177, -177, -34, -177, -177, 48, -177, 243, -177, -177, -177, -177, -177, -177, -177, -177, 64, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, 24, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -177, -12, 307, -177, 297, -177, -177, -177, 285, -177, -177 }; /* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If positive, shift that token. If negative, reduce the rule which number is the opposite. If zero, do what YYDEFACT says. If YYTABLE_NINF, syntax error. */ #define YYTABLE_NINF -1 static const yytype_uint16 yytable[] = { 21, 140, 115, 117, 152, 121, 220, 264, 231, 181, 194, 24, 27, 37, 35, 229, 93, 94, 95, 96, 97, 98, 99, 135, 228, 100, 45, 15, 16, 17, 18, 13, 19, 14, 145, 129, 181, 130, 335, 336, 36, 144, 251, 249, 1, 250, 258, 4, 250, 118, 119, 28, 171, 275, 5, 276, 170, 278, 6, 250, 173, 294, 333, 295, 334, 8, 28, 11, 12, 195, 232, 22, 24, 175, 176, 265, 7, 280, 34, 101, 39, 40, 90, 91, 102, 103, 104, 105, 106, 92, 107, 108, 109, 110, 188, 189, 111, 112, 177, 178, 125, 179, 180, 181, 126, 127, 128, 210, 132, 133, 45, 113, 134, 120, 179, 180, 181, 136, 114, 186, 187, 188, 189, 137, 312, 138, 141, 147, 142, 316, 190, 143, 196, 198, 146, 150, 151, 215, 216, 217, 218, 219, 172, 221, 222, 223, 224, 225, 226, 227, 192, 154, 230, 155, 174, 121, 156, 238, 140, 197, 199, 200, 157, 158, 159, 160, 161, 140, 266, 162, 163, 93, 94, 95, 96, 97, 98, 99, 164, 201, 100, 165, 166, 183, 184, 185, 186, 187, 188, 189, 167, 202, 204, 168, 214, 193, 183, 184, 185, 186, 187, 188, 189, 205, 118, 119, 169, 212, 177, 178, 277, 179, 180, 181, 281, 208, 211, 213, 140, 181, 233, 236, 239, 242, 210, 243, 244, 290, 291, 247, 252, 261, 262, 263, 101, 268, 269, 270, 273, 102, 103, 104, 105, 106, 274, 107, 108, 109, 110, 279, 140, 111, 112, 283, 140, 254, 285, 284, 293, 93, 94, 95, 96, 97, 98, 99, 113, 286, 100, 287, 296, 288, 297, 114, 298, 93, 94, 95, 96, 97, 98, 99, 301, 299, 100, 302, 303, 306, 308, 311, 313, 314, 317, 183, 184, 185, 186, 187, 188, 189, 320, 327, 321, 318, 260, 324, 322, 325, 330, 329, 209, 331, 332, 246, 338, 235, 153, 292, 38, 310, 282, 23, 101, 29, 0, 0, 0, 102, 103, 104, 105, 106, 0, 107, 108, 109, 110, 0, 101, 111, 112, 41, 0, 102, 103, 104, 105, 106, 0, 107, 108, 109, 110, 0, 113, 111, 112, 0, 0, 0, 42, 114, 253, 254, 0, 43, 44, 45, 0, 0, 113, 177, 178, 46, 179, 180, 181, 114, 0, 0, 47, 0, 0, 48, 0, 49, 0, 0, 50, 0, 182, 0, 0, 0, 0, 0, 0, 0, 0, 51, 52, 53, 0, 0, 0, 41, 0, 0, 54, 0, 0, 0, 0, 55, 56, 0, 0, 57, 58, 59, 0, 0, 60, 139, 42, 0, 0, 0, 0, 43, 44, 45, 0, 0, 0, 0, 0, 46, 0, 0, 0, 61, 0, 0, 47, 0, 0, 48, 0, 49, 0, 0, 50, 0, 0, 0, 183, 184, 185, 186, 187, 188, 189, 51, 52, 53, 0, 0, 0, 41, 0, 0, 54, 0, 0, 0, 0, 55, 56, 0, 0, 57, 58, 59, 0, 0, 60, 259, 42, 0, 0, 0, 0, 43, 44, 45, 0, 0, 0, 177, 178, 46, 179, 180, 181, 61, 0, 0, 47, 0, 0, 48, 0, 49, 0, 0, 50, 0, 0, 0, 0, 191, 0, 0, 0, 0, 0, 51, 52, 53, 0, 0, 0, 41, 0, 0, 54, 0, 0, 0, 0, 55, 56, 0, 0, 57, 58, 59, 0, 0, 60, 323, 42, 0, 0, 0, 0, 43, 44, 45, 0, 0, 0, 0, 0, 46, 0, 0, 0, 61, 0, 0, 47, 0, 0, 48, 0, 49, 0, 0, 50, 0, 0, 0, 183, 184, 185, 186, 187, 188, 189, 51, 52, 53, 177, 178, 41, 179, 180, 181, 54, 0, 0, 0, 0, 55, 56, 0, 0, 57, 58, 59, 0, 0, 60, 42, 0, 0, 0, 0, 43, 44, 45, 0, 0, 0, 267, 0, 46, 0, 0, 0, 0, 61, 0, 47, 0, 0, 48, 0, 49, 177, 178, 50, 179, 180, 181, 0, 177, 178, 0, 179, 180, 181, 51, 52, 53, 0, 0, 0, 300, 177, 178, 54, 179, 180, 181, 0, 55, 56, 305, 0, 57, 58, 59, 0, 0, 60, 0, 183, 184, 185, 186, 187, 188, 189, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 61, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 183, 184, 185, 186, 187, 188, 189, 183, 184, 185, 186, 187, 188, 189, 0, 0, 0, 0, 0, 0, 183, 184, 185, 186, 187, 188, 189 }; static const yytype_int16 yycheck[] = { 12, 63, 42, 43, 73, 45, 182, 40, 38, 15, 49, 9, 24, 26, 65, 191, 3, 4, 5, 6, 7, 8, 9, 57, 190, 12, 35, 19, 20, 21, 22, 106, 24, 108, 68, 57, 15, 59, 46, 47, 91, 50, 208, 106, 16, 108, 106, 9, 108, 36, 37, 64, 92, 106, 0, 108, 90, 106, 104, 108, 100, 106, 106, 108, 108, 9, 64, 17, 18, 108, 100, 25, 9, 113, 114, 108, 105, 253, 104, 66, 9, 9, 25, 104, 71, 72, 73, 74, 75, 63, 77, 78, 79, 80, 100, 101, 83, 84, 10, 11, 9, 13, 14, 15, 49, 9, 39, 147, 9, 9, 35, 98, 9, 100, 13, 14, 15, 88, 105, 98, 99, 100, 101, 88, 300, 9, 104, 40, 105, 305, 105, 104, 39, 9, 104, 104, 104, 177, 178, 179, 180, 181, 103, 183, 184, 185, 186, 187, 188, 189, 105, 104, 192, 104, 103, 195, 104, 197, 220, 17, 52, 9, 104, 104, 104, 104, 104, 229, 237, 104, 104, 3, 4, 5, 6, 7, 8, 9, 104, 9, 12, 104, 104, 95, 96, 97, 98, 99, 100, 101, 104, 60, 49, 104, 106, 105, 95, 96, 97, 98, 99, 100, 101, 105, 36, 37, 104, 70, 10, 11, 250, 13, 14, 15, 254, 105, 104, 70, 280, 15, 9, 9, 9, 105, 264, 58, 9, 267, 268, 107, 55, 9, 106, 108, 66, 95, 108, 9, 9, 71, 72, 73, 74, 75, 105, 77, 78, 79, 80, 9, 312, 83, 84, 27, 316, 31, 106, 32, 270, 3, 4, 5, 6, 7, 8, 9, 98, 9, 12, 9, 62, 41, 106, 105, 110, 3, 4, 5, 6, 7, 8, 9, 28, 107, 12, 51, 92, 105, 61, 9, 17, 44, 3, 95, 96, 97, 98, 99, 100, 101, 105, 9, 93, 89, 106, 106, 45, 12, 9, 94, 54, 32, 8, 204, 9, 195, 73, 269, 33, 295, 256, 14, 66, 26, -1, -1, -1, 71, 72, 73, 74, 75, -1, 77, 78, 79, 80, -1, 66, 83, 84, 9, -1, 71, 72, 73, 74, 75, -1, 77, 78, 79, 80, -1, 98, 83, 84, -1, -1, -1, 28, 105, 30, 31, -1, 33, 34, 35, -1, -1, 98, 10, 11, 41, 13, 14, 15, 105, -1, -1, 48, -1, -1, 51, -1, 53, -1, -1, 56, -1, 29, -1, -1, -1, -1, -1, -1, -1, -1, 67, 68, 69, -1, -1, -1, 9, -1, -1, 76, -1, -1, -1, -1, 81, 82, -1, -1, 85, 86, 87, -1, -1, 90, 27, 28, -1, -1, -1, -1, 33, 34, 35, -1, -1, -1, -1, -1, 41, -1, -1, -1, 109, -1, -1, 48, -1, -1, 51, -1, 53, -1, -1, 56, -1, -1, -1, 95, 96, 97, 98, 99, 100, 101, 67, 68, 69, -1, -1, -1, 9, -1, -1, 76, -1, -1, -1, -1, 81, 82, -1, -1, 85, 86, 87, -1, -1, 90, 27, 28, -1, -1, -1, -1, 33, 34, 35, -1, -1, -1, 10, 11, 41, 13, 14, 15, 109, -1, -1, 48, -1, -1, 51, -1, 53, -1, -1, 56, -1, -1, -1, -1, 32, -1, -1, -1, -1, -1, 67, 68, 69, -1, -1, -1, 9, -1, -1, 76, -1, -1, -1, -1, 81, 82, -1, -1, 85, 86, 87, -1, -1, 90, 27, 28, -1, -1, -1, -1, 33, 34, 35, -1, -1, -1, -1, -1, 41, -1, -1, -1, 109, -1, -1, 48, -1, -1, 51, -1, 53, -1, -1, 56, -1, -1, -1, 95, 96, 97, 98, 99, 100, 101, 67, 68, 69, 10, 11, 9, 13, 14, 15, 76, -1, -1, -1, -1, 81, 82, -1, -1, 85, 86, 87, -1, -1, 90, 28, -1, -1, -1, -1, 33, 34, 35, -1, -1, -1, 42, -1, 41, -1, -1, -1, -1, 109, -1, 48, -1, -1, 51, -1, 53, 10, 11, 56, 13, 14, 15, -1, 10, 11, -1, 13, 14, 15, 67, 68, 69, -1, -1, -1, 29, 10, 11, 76, 13, 14, 15, -1, 81, 82, 32, -1, 85, 86, 87, -1, -1, 90, -1, 95, 96, 97, 98, 99, 100, 101, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 109, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 95, 96, 97, 98, 99, 100, 101, 95, 96, 97, 98, 99, 100, 101, -1, -1, -1, -1, -1, -1, 95, 96, 97, 98, 99, 100, 101 }; /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing symbol of state STATE-NUM. */ static const yytype_uint8 yystos[] = { 0, 16, 112, 180, 9, 0, 104, 105, 9, 172, 173, 17, 18, 106, 108, 19, 20, 21, 22, 24, 171, 171, 25, 172, 9, 174, 175, 171, 64, 174, 176, 177, 178, 179, 104, 65, 91, 26, 178, 9, 9, 9, 28, 33, 34, 35, 41, 48, 51, 53, 56, 67, 68, 69, 76, 81, 82, 85, 86, 87, 90, 109, 113, 114, 118, 119, 120, 133, 134, 135, 139, 140, 141, 142, 143, 144, 145, 146, 150, 151, 152, 153, 154, 155, 156, 157, 164, 168, 169, 170, 25, 104, 63, 3, 4, 5, 6, 7, 8, 9, 12, 66, 71, 72, 73, 74, 75, 77, 78, 79, 80, 83, 84, 98, 105, 115, 116, 115, 36, 37, 100, 115, 125, 126, 127, 9, 49, 9, 39, 57, 59, 166, 9, 9, 9, 133, 88, 88, 9, 27, 113, 104, 105, 104, 50, 133, 104, 40, 128, 138, 104, 104, 128, 138, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 133, 115, 103, 115, 103, 115, 115, 10, 11, 13, 14, 15, 29, 95, 96, 97, 98, 99, 100, 101, 105, 32, 105, 105, 49, 108, 39, 17, 9, 52, 9, 9, 60, 167, 49, 105, 115, 124, 105, 54, 115, 104, 70, 70, 106, 115, 115, 115, 115, 115, 114, 115, 115, 115, 115, 115, 115, 115, 124, 114, 115, 38, 100, 9, 123, 125, 9, 122, 115, 9, 136, 137, 105, 58, 9, 121, 123, 107, 117, 106, 108, 124, 55, 30, 31, 147, 148, 149, 106, 27, 106, 9, 106, 108, 40, 108, 128, 42, 95, 108, 9, 158, 159, 9, 105, 106, 108, 115, 106, 9, 114, 115, 147, 27, 32, 106, 9, 9, 41, 129, 115, 115, 136, 171, 106, 108, 62, 106, 110, 107, 29, 28, 51, 92, 130, 32, 105, 160, 61, 163, 158, 9, 114, 17, 44, 132, 114, 3, 89, 161, 105, 93, 45, 27, 106, 12, 162, 9, 165, 94, 9, 32, 8, 106, 108, 46, 47, 131, 9 }; #define yyerrok (yyerrstatus = 0) #define yyclearin (yychar = YYEMPTY) #define YYEMPTY (-2) #define YYEOF 0 #define YYACCEPT goto yyacceptlab #define YYABORT goto yyabortlab #define YYERROR goto yyerrorlab /* Like YYERROR except do call yyerror. This remains here temporarily to ease the transition to the new meaning of YYERROR, for GCC. Once GCC version 2 has supplanted version 1, this can go. */ #define YYFAIL goto yyerrlab #define YYRECOVERING() (!!yyerrstatus) #define YYBACKUP(Token, Value) \ do \ if (yychar == YYEMPTY && yylen == 1) \ { \ yychar = (Token); \ yylval = (Value); \ yytoken = YYTRANSLATE (yychar); \ YYPOPSTACK (1); \ goto yybackup; \ } \ else \ { \ yyerror (YY_("syntax error: cannot back up")); \ YYERROR; \ } \ while (YYID (0)) #define YYTERROR 1 #define YYERRCODE 256 /* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. If N is 0, then set CURRENT to the empty location which ends the previous symbol: RHS[0] (always defined). */ #define YYRHSLOC(Rhs, K) ((Rhs)[K]) #ifndef YYLLOC_DEFAULT # define YYLLOC_DEFAULT(Current, Rhs, N) \ do \ if (YYID (N)) \ { \ (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ } \ else \ { \ (Current).first_line = (Current).last_line = \ YYRHSLOC (Rhs, 0).last_line; \ (Current).first_column = (Current).last_column = \ YYRHSLOC (Rhs, 0).last_column; \ } \ while (YYID (0)) #endif /* YY_LOCATION_PRINT -- Print the location on the stream. This macro was not mandated originally: define only if we know we won't break user code: when these are the locations we know. */ #ifndef YY_LOCATION_PRINT # if YYLTYPE_IS_TRIVIAL # define YY_LOCATION_PRINT(File, Loc) \ fprintf (File, "%d.%d-%d.%d", \ (Loc).first_line, (Loc).first_column, \ (Loc).last_line, (Loc).last_column) # else # define YY_LOCATION_PRINT(File, Loc) ((void) 0) # endif #endif /* YYLEX -- calling `yylex' with the right arguments. */ #ifdef YYLEX_PARAM # define YYLEX yylex (YYLEX_PARAM) #else # define YYLEX yylex () #endif /* Enable debugging if requested. */ #if YYDEBUG # ifndef YYFPRINTF # include /* INFRINGES ON USER NAME SPACE */ # define YYFPRINTF fprintf # endif # define YYDPRINTF(Args) \ do { \ if (yydebug) \ YYFPRINTF Args; \ } while (YYID (0)) # define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ do { \ if (yydebug) \ { \ YYFPRINTF (stderr, "%s ", Title); \ yy_symbol_print (stderr, \ Type, Value); \ YYFPRINTF (stderr, "\n"); \ } \ } while (YYID (0)) /*--------------------------------. | Print this symbol on YYOUTPUT. | `--------------------------------*/ /*ARGSUSED*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) #else static void yy_symbol_value_print (yyoutput, yytype, yyvaluep) FILE *yyoutput; int yytype; YYSTYPE const * const yyvaluep; #endif { if (!yyvaluep) return; # ifdef YYPRINT if (yytype < YYNTOKENS) YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); # else YYUSE (yyoutput); # endif switch (yytype) { default: break; } } /*--------------------------------. | Print this symbol on YYOUTPUT. | `--------------------------------*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) #else static void yy_symbol_print (yyoutput, yytype, yyvaluep) FILE *yyoutput; int yytype; YYSTYPE const * const yyvaluep; #endif { if (yytype < YYNTOKENS) YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); else YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); yy_symbol_value_print (yyoutput, yytype, yyvaluep); YYFPRINTF (yyoutput, ")"); } /*------------------------------------------------------------------. | yy_stack_print -- Print the state stack from its BOTTOM up to its | | TOP (included). | `------------------------------------------------------------------*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop) #else static void yy_stack_print (yybottom, yytop) yytype_int16 *yybottom; yytype_int16 *yytop; #endif { YYFPRINTF (stderr, "Stack now"); for (; yybottom <= yytop; yybottom++) { int yybot = *yybottom; YYFPRINTF (stderr, " %d", yybot); } YYFPRINTF (stderr, "\n"); } # define YY_STACK_PRINT(Bottom, Top) \ do { \ if (yydebug) \ yy_stack_print ((Bottom), (Top)); \ } while (YYID (0)) /*------------------------------------------------. | Report that the YYRULE is going to be reduced. | `------------------------------------------------*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yy_reduce_print (YYSTYPE *yyvsp, int yyrule) #else static void yy_reduce_print (yyvsp, yyrule) YYSTYPE *yyvsp; int yyrule; #endif { int yynrhs = yyr2[yyrule]; int yyi; unsigned long int yylno = yyrline[yyrule]; YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", yyrule - 1, yylno); /* The symbols being reduced. */ for (yyi = 0; yyi < yynrhs; yyi++) { YYFPRINTF (stderr, " $%d = ", yyi + 1); yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi], &(yyvsp[(yyi + 1) - (yynrhs)]) ); YYFPRINTF (stderr, "\n"); } } # define YY_REDUCE_PRINT(Rule) \ do { \ if (yydebug) \ yy_reduce_print (yyvsp, Rule); \ } while (YYID (0)) /* Nonzero means print parse trace. It is left uninitialized so that multiple parsers can coexist. */ int yydebug; #else /* !YYDEBUG */ # define YYDPRINTF(Args) # define YY_SYMBOL_PRINT(Title, Type, Value, Location) # define YY_STACK_PRINT(Bottom, Top) # define YY_REDUCE_PRINT(Rule) #endif /* !YYDEBUG */ /* YYINITDEPTH -- initial size of the parser's stacks. */ #ifndef YYINITDEPTH # define YYINITDEPTH 200 #endif /* YYMAXDEPTH -- maximum size the stacks can grow to (effective only if the built-in stack extension method is used). Do not make this value too large; the results are undefined if YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) evaluated with infinite-precision integer arithmetic. */ #ifndef YYMAXDEPTH # define YYMAXDEPTH 10000 #endif #if YYERROR_VERBOSE # ifndef yystrlen # if defined __GLIBC__ && defined _STRING_H # define yystrlen strlen # else /* Return the length of YYSTR. */ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static YYSIZE_T yystrlen (const char *yystr) #else static YYSIZE_T yystrlen (yystr) const char *yystr; #endif { YYSIZE_T yylen; for (yylen = 0; yystr[yylen]; yylen++) continue; return yylen; } # endif # endif # ifndef yystpcpy # if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE # define yystpcpy stpcpy # else /* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in YYDEST. */ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static char * yystpcpy (char *yydest, const char *yysrc) #else static char * yystpcpy (yydest, yysrc) char *yydest; const char *yysrc; #endif { char *yyd = yydest; const char *yys = yysrc; while ((*yyd++ = *yys++) != '\0') continue; return yyd - 1; } # endif # endif # ifndef yytnamerr /* Copy to YYRES the contents of YYSTR after stripping away unnecessary quotes and backslashes, so that it's suitable for yyerror. The heuristic is that double-quoting is unnecessary unless the string contains an apostrophe, a comma, or backslash (other than backslash-backslash). YYSTR is taken from yytname. If YYRES is null, do not copy; instead, return the length of what the result would have been. */ static YYSIZE_T yytnamerr (char *yyres, const char *yystr) { if (*yystr == '"') { YYSIZE_T yyn = 0; char const *yyp = yystr; for (;;) switch (*++yyp) { case '\'': case ',': goto do_not_strip_quotes; case '\\': if (*++yyp != '\\') goto do_not_strip_quotes; /* Fall through. */ default: if (yyres) yyres[yyn] = *yyp; yyn++; break; case '"': if (yyres) yyres[yyn] = '\0'; return yyn; } do_not_strip_quotes: ; } if (! yyres) return yystrlen (yystr); return yystpcpy (yyres, yystr) - yyres; } # endif /* Copy into YYRESULT an error message about the unexpected token YYCHAR while in state YYSTATE. Return the number of bytes copied, including the terminating null byte. If YYRESULT is null, do not copy anything; just return the number of bytes that would be copied. As a special case, return 0 if an ordinary "syntax error" message will do. Return YYSIZE_MAXIMUM if overflow occurs during size calculation. */ static YYSIZE_T yysyntax_error (char *yyresult, int yystate, int yychar) { int yyn = yypact[yystate]; if (! (YYPACT_NINF < yyn && yyn <= YYLAST)) return 0; else { int yytype = YYTRANSLATE (yychar); YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]); YYSIZE_T yysize = yysize0; YYSIZE_T yysize1; int yysize_overflow = 0; enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; int yyx; # if 0 /* This is so xgettext sees the translatable formats that are constructed on the fly. */ YY_("syntax error, unexpected %s"); YY_("syntax error, unexpected %s, expecting %s"); YY_("syntax error, unexpected %s, expecting %s or %s"); YY_("syntax error, unexpected %s, expecting %s or %s or %s"); YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"); # endif char *yyfmt; char const *yyf; static char const yyunexpected[] = "syntax error, unexpected %s"; static char const yyexpecting[] = ", expecting %s"; static char const yyor[] = " or %s"; char yyformat[sizeof yyunexpected + sizeof yyexpecting - 1 + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2) * (sizeof yyor - 1))]; char const *yyprefix = yyexpecting; /* Start YYX at -YYN if negative to avoid negative indexes in YYCHECK. */ int yyxbegin = yyn < 0 ? -yyn : 0; /* Stay within bounds of both yycheck and yytname. */ int yychecklim = YYLAST - yyn + 1; int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; int yycount = 1; yyarg[0] = yytname[yytype]; yyfmt = yystpcpy (yyformat, yyunexpected); for (yyx = yyxbegin; yyx < yyxend; ++yyx) if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) { if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) { yycount = 1; yysize = yysize0; yyformat[sizeof yyunexpected - 1] = '\0'; break; } yyarg[yycount++] = yytname[yyx]; yysize1 = yysize + yytnamerr (0, yytname[yyx]); yysize_overflow |= (yysize1 < yysize); yysize = yysize1; yyfmt = yystpcpy (yyfmt, yyprefix); yyprefix = yyor; } yyf = YY_(yyformat); yysize1 = yysize + yystrlen (yyf); yysize_overflow |= (yysize1 < yysize); yysize = yysize1; if (yysize_overflow) return YYSIZE_MAXIMUM; if (yyresult) { /* Avoid sprintf, as that infringes on the user's name space. Don't have undefined behavior even if the translation produced a string with the wrong number of "%s"s. */ char *yyp = yyresult; int yyi = 0; while ((*yyp = *yyf) != '\0') { if (*yyp == '%' && yyf[1] == 's' && yyi < yycount) { yyp += yytnamerr (yyp, yyarg[yyi++]); yyf += 2; } else { yyp++; yyf++; } } } return yysize; } } #endif /* YYERROR_VERBOSE */ /*-----------------------------------------------. | Release the memory associated to this symbol. | `-----------------------------------------------*/ /*ARGSUSED*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep) #else static void yydestruct (yymsg, yytype, yyvaluep) const char *yymsg; int yytype; YYSTYPE *yyvaluep; #endif { YYUSE (yyvaluep); if (!yymsg) yymsg = "Deleting"; YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); switch (yytype) { default: break; } } /* Prevent warnings from -Wmissing-prototypes. */ #ifdef YYPARSE_PARAM #if defined __STDC__ || defined __cplusplus int yyparse (void *YYPARSE_PARAM); #else int yyparse (); #endif #else /* ! YYPARSE_PARAM */ #if defined __STDC__ || defined __cplusplus int yyparse (void); #else int yyparse (); #endif #endif /* ! YYPARSE_PARAM */ /* The lookahead symbol. */ int yychar; /* The semantic value of the lookahead symbol. */ YYSTYPE yylval; /* Number of syntax errors so far. */ int yynerrs; /*-------------------------. | yyparse or yypush_parse. | `-------------------------*/ #ifdef YYPARSE_PARAM #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) int yyparse (void *YYPARSE_PARAM) #else int yyparse (YYPARSE_PARAM) void *YYPARSE_PARAM; #endif #else /* ! YYPARSE_PARAM */ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) int yyparse (void) #else int yyparse () #endif #endif { int yystate; /* Number of tokens to shift before error messages enabled. */ int yyerrstatus; /* The stacks and their tools: `yyss': related to states. `yyvs': related to semantic values. Refer to the stacks thru separate pointers, to allow yyoverflow to reallocate them elsewhere. */ /* The state stack. */ yytype_int16 yyssa[YYINITDEPTH]; yytype_int16 *yyss; yytype_int16 *yyssp; /* The semantic value stack. */ YYSTYPE yyvsa[YYINITDEPTH]; YYSTYPE *yyvs; YYSTYPE *yyvsp; YYSIZE_T yystacksize; int yyn; int yyresult; /* Lookahead token as an internal (translated) token number. */ int yytoken; /* The variables used to return semantic value and location from the action routines. */ YYSTYPE yyval; #if YYERROR_VERBOSE /* Buffer for error messages, and its allocated size. */ char yymsgbuf[128]; char *yymsg = yymsgbuf; YYSIZE_T yymsg_alloc = sizeof yymsgbuf; #endif #define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) /* The number of symbols on the RHS of the reduced rule. Keep to zero when no symbol should be popped. */ int yylen = 0; yytoken = 0; yyss = yyssa; yyvs = yyvsa; yystacksize = YYINITDEPTH; YYDPRINTF ((stderr, "Starting parse\n")); yystate = 0; yyerrstatus = 0; yynerrs = 0; yychar = YYEMPTY; /* Cause a token to be read. */ /* Initialize stack pointers. Waste one element of value and location stack so that they stay on the same level as the state stack. The wasted elements are never initialized. */ yyssp = yyss; yyvsp = yyvs; goto yysetstate; /*------------------------------------------------------------. | yynewstate -- Push a new state, which is found in yystate. | `------------------------------------------------------------*/ yynewstate: /* In all cases, when you get here, the value and location stacks have just been pushed. So pushing a state here evens the stacks. */ yyssp++; yysetstate: *yyssp = yystate; if (yyss + yystacksize - 1 <= yyssp) { /* Get the current used size of the three stacks, in elements. */ YYSIZE_T yysize = yyssp - yyss + 1; #ifdef yyoverflow { /* Give user a chance to reallocate the stack. Use copies of these so that the &'s don't force the real ones into memory. */ YYSTYPE *yyvs1 = yyvs; yytype_int16 *yyss1 = yyss; /* Each stack pointer address is followed by the size of the data in use in that stack, in bytes. This used to be a conditional around just the two extra args, but that might be undefined if yyoverflow is a macro. */ yyoverflow (YY_("memory exhausted"), &yyss1, yysize * sizeof (*yyssp), &yyvs1, yysize * sizeof (*yyvsp), &yystacksize); yyss = yyss1; yyvs = yyvs1; } #else /* no yyoverflow */ # ifndef YYSTACK_RELOCATE goto yyexhaustedlab; # else /* Extend the stack our own way. */ if (YYMAXDEPTH <= yystacksize) goto yyexhaustedlab; yystacksize *= 2; if (YYMAXDEPTH < yystacksize) yystacksize = YYMAXDEPTH; { yytype_int16 *yyss1 = yyss; union yyalloc *yyptr = (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); if (! yyptr) goto yyexhaustedlab; YYSTACK_RELOCATE (yyss_alloc, yyss); YYSTACK_RELOCATE (yyvs_alloc, yyvs); # undef YYSTACK_RELOCATE if (yyss1 != yyssa) YYSTACK_FREE (yyss1); } # endif #endif /* no yyoverflow */ yyssp = yyss + yysize - 1; yyvsp = yyvs + yysize - 1; YYDPRINTF ((stderr, "Stack size increased to %lu\n", (unsigned long int) yystacksize)); if (yyss + yystacksize - 1 <= yyssp) YYABORT; } YYDPRINTF ((stderr, "Entering state %d\n", yystate)); if (yystate == YYFINAL) YYACCEPT; goto yybackup; /*-----------. | yybackup. | `-----------*/ yybackup: /* Do appropriate processing given the current state. Read a lookahead token if we need one and don't already have one. */ /* First try to decide what to do without reference to lookahead token. */ yyn = yypact[yystate]; if (yyn == YYPACT_NINF) goto yydefault; /* Not known => get a lookahead token if don't already have one. */ /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ if (yychar == YYEMPTY) { YYDPRINTF ((stderr, "Reading a token: ")); yychar = YYLEX; } if (yychar <= YYEOF) { yychar = yytoken = YYEOF; YYDPRINTF ((stderr, "Now at end of input.\n")); } else { yytoken = YYTRANSLATE (yychar); YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); } /* If the proper action on seeing token YYTOKEN is to reduce or to detect an error, take that action. */ yyn += yytoken; if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) goto yydefault; yyn = yytable[yyn]; if (yyn <= 0) { if (yyn == 0 || yyn == YYTABLE_NINF) goto yyerrlab; yyn = -yyn; goto yyreduce; } /* Count tokens shifted since error; after three, turn off error status. */ if (yyerrstatus) yyerrstatus--; /* Shift the lookahead token. */ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); /* Discard the shifted token. */ yychar = YYEMPTY; yystate = yyn; *++yyvsp = yylval; goto yynewstate; /*-----------------------------------------------------------. | yydefault -- do the default action for the current state. | `-----------------------------------------------------------*/ yydefault: yyn = yydefact[yystate]; if (yyn == 0) goto yyerrlab; goto yyreduce; /*-----------------------------. | yyreduce -- Do a reduction. | `-----------------------------*/ yyreduce: /* yyn is the number of a rule to reduce with. */ yylen = yyr2[yyn]; /* If YYLEN is nonzero, implement the default value of the action: `$$ = $1'. Otherwise, the following line sets YYVAL to garbage. This behavior is undocumented and Bison users should not rely upon it. Assigning to YYVAL unconditionally makes the parser a bit smaller, and it avoids a GCC warning that YYVAL may be used uninitialized. */ yyval = yyvsp[1-yylen]; YY_REDUCE_PRINT (yyn); switch (yyn) { case 25: /* Line 1455 of yacc.c */ #line 181 "pars0grm.y" { (yyval) = que_node_list_add_last(NULL, (yyvsp[(1) - (1)])); } break; case 26: /* Line 1455 of yacc.c */ #line 183 "pars0grm.y" { (yyval) = que_node_list_add_last((yyvsp[(1) - (2)]), (yyvsp[(2) - (2)])); } break; case 27: /* Line 1455 of yacc.c */ #line 187 "pars0grm.y" { (yyval) = (yyvsp[(1) - (1)]);} break; case 28: /* Line 1455 of yacc.c */ #line 189 "pars0grm.y" { (yyval) = pars_func((yyvsp[(1) - (4)]), (yyvsp[(3) - (4)])); } break; case 29: /* Line 1455 of yacc.c */ #line 190 "pars0grm.y" { (yyval) = (yyvsp[(1) - (1)]);} break; case 30: /* Line 1455 of yacc.c */ #line 191 "pars0grm.y" { (yyval) = (yyvsp[(1) - (1)]);} break; case 31: /* Line 1455 of yacc.c */ #line 192 "pars0grm.y" { (yyval) = (yyvsp[(1) - (1)]);} break; case 32: /* Line 1455 of yacc.c */ #line 193 "pars0grm.y" { (yyval) = (yyvsp[(1) - (1)]);} break; case 33: /* Line 1455 of yacc.c */ #line 194 "pars0grm.y" { (yyval) = (yyvsp[(1) - (1)]);} break; case 34: /* Line 1455 of yacc.c */ #line 195 "pars0grm.y" { (yyval) = (yyvsp[(1) - (1)]);} break; case 35: /* Line 1455 of yacc.c */ #line 196 "pars0grm.y" { (yyval) = (yyvsp[(1) - (1)]);} break; case 36: /* Line 1455 of yacc.c */ #line 197 "pars0grm.y" { (yyval) = pars_op('+', (yyvsp[(1) - (3)]), (yyvsp[(3) - (3)])); } break; case 37: /* Line 1455 of yacc.c */ #line 198 "pars0grm.y" { (yyval) = pars_op('-', (yyvsp[(1) - (3)]), (yyvsp[(3) - (3)])); } break; case 38: /* Line 1455 of yacc.c */ #line 199 "pars0grm.y" { (yyval) = pars_op('*', (yyvsp[(1) - (3)]), (yyvsp[(3) - (3)])); } break; case 39: /* Line 1455 of yacc.c */ #line 200 "pars0grm.y" { (yyval) = pars_op('/', (yyvsp[(1) - (3)]), (yyvsp[(3) - (3)])); } break; case 40: /* Line 1455 of yacc.c */ #line 201 "pars0grm.y" { (yyval) = pars_op('-', (yyvsp[(2) - (2)]), NULL); } break; case 41: /* Line 1455 of yacc.c */ #line 202 "pars0grm.y" { (yyval) = (yyvsp[(2) - (3)]); } break; case 42: /* Line 1455 of yacc.c */ #line 203 "pars0grm.y" { (yyval) = pars_op('=', (yyvsp[(1) - (3)]), (yyvsp[(3) - (3)])); } break; case 43: /* Line 1455 of yacc.c */ #line 204 "pars0grm.y" { (yyval) = pars_op('<', (yyvsp[(1) - (3)]), (yyvsp[(3) - (3)])); } break; case 44: /* Line 1455 of yacc.c */ #line 205 "pars0grm.y" { (yyval) = pars_op('>', (yyvsp[(1) - (3)]), (yyvsp[(3) - (3)])); } break; case 45: /* Line 1455 of yacc.c */ #line 206 "pars0grm.y" { (yyval) = pars_op(PARS_GE_TOKEN, (yyvsp[(1) - (3)]), (yyvsp[(3) - (3)])); } break; case 46: /* Line 1455 of yacc.c */ #line 207 "pars0grm.y" { (yyval) = pars_op(PARS_LE_TOKEN, (yyvsp[(1) - (3)]), (yyvsp[(3) - (3)])); } break; case 47: /* Line 1455 of yacc.c */ #line 208 "pars0grm.y" { (yyval) = pars_op(PARS_NE_TOKEN, (yyvsp[(1) - (3)]), (yyvsp[(3) - (3)])); } break; case 48: /* Line 1455 of yacc.c */ #line 209 "pars0grm.y" { (yyval) = pars_op(PARS_AND_TOKEN, (yyvsp[(1) - (3)]), (yyvsp[(3) - (3)])); } break; case 49: /* Line 1455 of yacc.c */ #line 210 "pars0grm.y" { (yyval) = pars_op(PARS_OR_TOKEN, (yyvsp[(1) - (3)]), (yyvsp[(3) - (3)])); } break; case 50: /* Line 1455 of yacc.c */ #line 211 "pars0grm.y" { (yyval) = pars_op(PARS_NOT_TOKEN, (yyvsp[(2) - (2)]), NULL); } break; case 51: /* Line 1455 of yacc.c */ #line 213 "pars0grm.y" { (yyval) = pars_op(PARS_NOTFOUND_TOKEN, (yyvsp[(1) - (3)]), NULL); } break; case 52: /* Line 1455 of yacc.c */ #line 215 "pars0grm.y" { (yyval) = pars_op(PARS_NOTFOUND_TOKEN, (yyvsp[(1) - (3)]), NULL); } break; case 53: /* Line 1455 of yacc.c */ #line 219 "pars0grm.y" { (yyval) = &pars_to_char_token; } break; case 54: /* Line 1455 of yacc.c */ #line 220 "pars0grm.y" { (yyval) = &pars_to_number_token; } break; case 55: /* Line 1455 of yacc.c */ #line 221 "pars0grm.y" { (yyval) = &pars_to_binary_token; } break; case 56: /* Line 1455 of yacc.c */ #line 223 "pars0grm.y" { (yyval) = &pars_binary_to_number_token; } break; case 57: /* Line 1455 of yacc.c */ #line 224 "pars0grm.y" { (yyval) = &pars_substr_token; } break; case 58: /* Line 1455 of yacc.c */ #line 225 "pars0grm.y" { (yyval) = &pars_concat_token; } break; case 59: /* Line 1455 of yacc.c */ #line 226 "pars0grm.y" { (yyval) = &pars_instr_token; } break; case 60: /* Line 1455 of yacc.c */ #line 227 "pars0grm.y" { (yyval) = &pars_length_token; } break; case 61: /* Line 1455 of yacc.c */ #line 228 "pars0grm.y" { (yyval) = &pars_sysdate_token; } break; case 62: /* Line 1455 of yacc.c */ #line 229 "pars0grm.y" { (yyval) = &pars_rnd_token; } break; case 63: /* Line 1455 of yacc.c */ #line 230 "pars0grm.y" { (yyval) = &pars_rnd_str_token; } break; case 67: /* Line 1455 of yacc.c */ #line 241 "pars0grm.y" { (yyval) = pars_stored_procedure_call((yyvsp[(2) - (6)])); } break; case 68: /* Line 1455 of yacc.c */ #line 246 "pars0grm.y" { (yyval) = pars_procedure_call((yyvsp[(1) - (4)]), (yyvsp[(3) - (4)])); } break; case 69: /* Line 1455 of yacc.c */ #line 250 "pars0grm.y" { (yyval) = &pars_replstr_token; } break; case 70: /* Line 1455 of yacc.c */ #line 251 "pars0grm.y" { (yyval) = &pars_printf_token; } break; case 71: /* Line 1455 of yacc.c */ #line 252 "pars0grm.y" { (yyval) = &pars_assert_token; } break; case 72: /* Line 1455 of yacc.c */ #line 256 "pars0grm.y" { (yyval) = (yyvsp[(1) - (3)]); } break; case 73: /* Line 1455 of yacc.c */ #line 260 "pars0grm.y" { (yyval) = que_node_list_add_last(NULL, (yyvsp[(1) - (1)])); } break; case 74: /* Line 1455 of yacc.c */ #line 262 "pars0grm.y" { (yyval) = que_node_list_add_last((yyvsp[(1) - (3)]), (yyvsp[(3) - (3)])); } break; case 75: /* Line 1455 of yacc.c */ #line 266 "pars0grm.y" { (yyval) = NULL; } break; case 76: /* Line 1455 of yacc.c */ #line 267 "pars0grm.y" { (yyval) = que_node_list_add_last(NULL, (yyvsp[(1) - (1)])); } break; case 77: /* Line 1455 of yacc.c */ #line 269 "pars0grm.y" { (yyval) = que_node_list_add_last((yyvsp[(1) - (3)]), (yyvsp[(3) - (3)])); } break; case 78: /* Line 1455 of yacc.c */ #line 273 "pars0grm.y" { (yyval) = NULL; } break; case 79: /* Line 1455 of yacc.c */ #line 274 "pars0grm.y" { (yyval) = que_node_list_add_last(NULL, (yyvsp[(1) - (1)]));} break; case 80: /* Line 1455 of yacc.c */ #line 275 "pars0grm.y" { (yyval) = que_node_list_add_last((yyvsp[(1) - (3)]), (yyvsp[(3) - (3)])); } break; case 81: /* Line 1455 of yacc.c */ #line 279 "pars0grm.y" { (yyval) = (yyvsp[(1) - (1)]); } break; case 82: /* Line 1455 of yacc.c */ #line 281 "pars0grm.y" { (yyval) = pars_func(&pars_count_token, que_node_list_add_last(NULL, sym_tab_add_int_lit( pars_sym_tab_global, 1))); } break; case 83: /* Line 1455 of yacc.c */ #line 286 "pars0grm.y" { (yyval) = pars_func(&pars_count_token, que_node_list_add_last(NULL, pars_func(&pars_distinct_token, que_node_list_add_last( NULL, (yyvsp[(4) - (5)]))))); } break; case 84: /* Line 1455 of yacc.c */ #line 292 "pars0grm.y" { (yyval) = pars_func(&pars_sum_token, que_node_list_add_last(NULL, (yyvsp[(3) - (4)]))); } break; case 85: /* Line 1455 of yacc.c */ #line 298 "pars0grm.y" { (yyval) = NULL; } break; case 86: /* Line 1455 of yacc.c */ #line 299 "pars0grm.y" { (yyval) = que_node_list_add_last(NULL, (yyvsp[(1) - (1)])); } break; case 87: /* Line 1455 of yacc.c */ #line 301 "pars0grm.y" { (yyval) = que_node_list_add_last((yyvsp[(1) - (3)]), (yyvsp[(3) - (3)])); } break; case 88: /* Line 1455 of yacc.c */ #line 305 "pars0grm.y" { (yyval) = pars_select_list(&pars_star_denoter, NULL); } break; case 89: /* Line 1455 of yacc.c */ #line 308 "pars0grm.y" { (yyval) = pars_select_list((yyvsp[(1) - (3)]), (yyvsp[(3) - (3)])); } break; case 90: /* Line 1455 of yacc.c */ #line 309 "pars0grm.y" { (yyval) = pars_select_list((yyvsp[(1) - (1)]), NULL); } break; case 91: /* Line 1455 of yacc.c */ #line 313 "pars0grm.y" { (yyval) = NULL; } break; case 92: /* Line 1455 of yacc.c */ #line 314 "pars0grm.y" { (yyval) = (yyvsp[(2) - (2)]); } break; case 93: /* Line 1455 of yacc.c */ #line 318 "pars0grm.y" { (yyval) = NULL; } break; case 94: /* Line 1455 of yacc.c */ #line 320 "pars0grm.y" { (yyval) = &pars_update_token; } break; case 95: /* Line 1455 of yacc.c */ #line 324 "pars0grm.y" { (yyval) = NULL; } break; case 96: /* Line 1455 of yacc.c */ #line 326 "pars0grm.y" { (yyval) = &pars_share_token; } break; case 97: /* Line 1455 of yacc.c */ #line 330 "pars0grm.y" { (yyval) = &pars_asc_token; } break; case 98: /* Line 1455 of yacc.c */ #line 331 "pars0grm.y" { (yyval) = &pars_asc_token; } break; case 99: /* Line 1455 of yacc.c */ #line 332 "pars0grm.y" { (yyval) = &pars_desc_token; } break; case 100: /* Line 1455 of yacc.c */ #line 336 "pars0grm.y" { (yyval) = NULL; } break; case 101: /* Line 1455 of yacc.c */ #line 338 "pars0grm.y" { (yyval) = pars_order_by((yyvsp[(3) - (4)]), (yyvsp[(4) - (4)])); } break; case 102: /* Line 1455 of yacc.c */ #line 347 "pars0grm.y" { (yyval) = pars_select_statement((yyvsp[(2) - (8)]), (yyvsp[(4) - (8)]), (yyvsp[(5) - (8)]), (yyvsp[(6) - (8)]), (yyvsp[(7) - (8)]), (yyvsp[(8) - (8)])); } break; case 103: /* Line 1455 of yacc.c */ #line 353 "pars0grm.y" { (yyval) = (yyvsp[(3) - (3)]); } break; case 104: /* Line 1455 of yacc.c */ #line 358 "pars0grm.y" { (yyval) = pars_insert_statement((yyvsp[(1) - (5)]), (yyvsp[(4) - (5)]), NULL); } break; case 105: /* Line 1455 of yacc.c */ #line 360 "pars0grm.y" { (yyval) = pars_insert_statement((yyvsp[(1) - (2)]), NULL, (yyvsp[(2) - (2)])); } break; case 106: /* Line 1455 of yacc.c */ #line 364 "pars0grm.y" { (yyval) = pars_column_assignment((yyvsp[(1) - (3)]), (yyvsp[(3) - (3)])); } break; case 107: /* Line 1455 of yacc.c */ #line 368 "pars0grm.y" { (yyval) = que_node_list_add_last(NULL, (yyvsp[(1) - (1)])); } break; case 108: /* Line 1455 of yacc.c */ #line 370 "pars0grm.y" { (yyval) = que_node_list_add_last((yyvsp[(1) - (3)]), (yyvsp[(3) - (3)])); } break; case 109: /* Line 1455 of yacc.c */ #line 376 "pars0grm.y" { (yyval) = (yyvsp[(4) - (4)]); } break; case 110: /* Line 1455 of yacc.c */ #line 382 "pars0grm.y" { (yyval) = pars_update_statement_start(FALSE, (yyvsp[(2) - (4)]), (yyvsp[(4) - (4)])); } break; case 111: /* Line 1455 of yacc.c */ #line 388 "pars0grm.y" { (yyval) = pars_update_statement((yyvsp[(1) - (2)]), NULL, (yyvsp[(2) - (2)])); } break; case 112: /* Line 1455 of yacc.c */ #line 393 "pars0grm.y" { (yyval) = pars_update_statement((yyvsp[(1) - (2)]), (yyvsp[(2) - (2)]), NULL); } break; case 113: /* Line 1455 of yacc.c */ #line 398 "pars0grm.y" { (yyval) = pars_update_statement_start(TRUE, (yyvsp[(3) - (3)]), NULL); } break; case 114: /* Line 1455 of yacc.c */ #line 404 "pars0grm.y" { (yyval) = pars_update_statement((yyvsp[(1) - (2)]), NULL, (yyvsp[(2) - (2)])); } break; case 115: /* Line 1455 of yacc.c */ #line 409 "pars0grm.y" { (yyval) = pars_update_statement((yyvsp[(1) - (2)]), (yyvsp[(2) - (2)]), NULL); } break; case 116: /* Line 1455 of yacc.c */ #line 414 "pars0grm.y" { (yyval) = pars_row_printf_statement((yyvsp[(2) - (2)])); } break; case 117: /* Line 1455 of yacc.c */ #line 419 "pars0grm.y" { (yyval) = pars_assignment_statement((yyvsp[(1) - (3)]), (yyvsp[(3) - (3)])); } break; case 118: /* Line 1455 of yacc.c */ #line 425 "pars0grm.y" { (yyval) = pars_elsif_element((yyvsp[(2) - (4)]), (yyvsp[(4) - (4)])); } break; case 119: /* Line 1455 of yacc.c */ #line 429 "pars0grm.y" { (yyval) = que_node_list_add_last(NULL, (yyvsp[(1) - (1)])); } break; case 120: /* Line 1455 of yacc.c */ #line 431 "pars0grm.y" { (yyval) = que_node_list_add_last((yyvsp[(1) - (2)]), (yyvsp[(2) - (2)])); } break; case 121: /* Line 1455 of yacc.c */ #line 435 "pars0grm.y" { (yyval) = NULL; } break; case 122: /* Line 1455 of yacc.c */ #line 437 "pars0grm.y" { (yyval) = (yyvsp[(2) - (2)]); } break; case 123: /* Line 1455 of yacc.c */ #line 438 "pars0grm.y" { (yyval) = (yyvsp[(1) - (1)]); } break; case 124: /* Line 1455 of yacc.c */ #line 445 "pars0grm.y" { (yyval) = pars_if_statement((yyvsp[(2) - (7)]), (yyvsp[(4) - (7)]), (yyvsp[(5) - (7)])); } break; case 125: /* Line 1455 of yacc.c */ #line 451 "pars0grm.y" { (yyval) = pars_while_statement((yyvsp[(2) - (6)]), (yyvsp[(4) - (6)])); } break; case 126: /* Line 1455 of yacc.c */ #line 459 "pars0grm.y" { (yyval) = pars_for_statement((yyvsp[(2) - (10)]), (yyvsp[(4) - (10)]), (yyvsp[(6) - (10)]), (yyvsp[(8) - (10)])); } break; case 127: /* Line 1455 of yacc.c */ #line 463 "pars0grm.y" { (yyval) = pars_exit_statement(); } break; case 128: /* Line 1455 of yacc.c */ #line 467 "pars0grm.y" { (yyval) = pars_return_statement(); } break; case 129: /* Line 1455 of yacc.c */ #line 472 "pars0grm.y" { (yyval) = pars_open_statement( ROW_SEL_OPEN_CURSOR, (yyvsp[(2) - (2)])); } break; case 130: /* Line 1455 of yacc.c */ #line 478 "pars0grm.y" { (yyval) = pars_open_statement( ROW_SEL_CLOSE_CURSOR, (yyvsp[(2) - (2)])); } break; case 131: /* Line 1455 of yacc.c */ #line 484 "pars0grm.y" { (yyval) = pars_fetch_statement((yyvsp[(2) - (4)]), (yyvsp[(4) - (4)]), NULL); } break; case 132: /* Line 1455 of yacc.c */ #line 486 "pars0grm.y" { (yyval) = pars_fetch_statement((yyvsp[(2) - (4)]), NULL, (yyvsp[(4) - (4)])); } break; case 133: /* Line 1455 of yacc.c */ #line 491 "pars0grm.y" { (yyval) = pars_column_def((yyvsp[(1) - (5)]), (yyvsp[(2) - (5)]), (yyvsp[(3) - (5)]), (yyvsp[(4) - (5)]), (yyvsp[(5) - (5)])); } break; case 134: /* Line 1455 of yacc.c */ #line 495 "pars0grm.y" { (yyval) = que_node_list_add_last(NULL, (yyvsp[(1) - (1)])); } break; case 135: /* Line 1455 of yacc.c */ #line 497 "pars0grm.y" { (yyval) = que_node_list_add_last((yyvsp[(1) - (3)]), (yyvsp[(3) - (3)])); } break; case 136: /* Line 1455 of yacc.c */ #line 501 "pars0grm.y" { (yyval) = NULL; } break; case 137: /* Line 1455 of yacc.c */ #line 503 "pars0grm.y" { (yyval) = (yyvsp[(2) - (3)]); } break; case 138: /* Line 1455 of yacc.c */ #line 507 "pars0grm.y" { (yyval) = NULL; } break; case 139: /* Line 1455 of yacc.c */ #line 509 "pars0grm.y" { (yyval) = &pars_int_token; /* pass any non-NULL pointer */ } break; case 140: /* Line 1455 of yacc.c */ #line 514 "pars0grm.y" { (yyval) = NULL; } break; case 141: /* Line 1455 of yacc.c */ #line 516 "pars0grm.y" { (yyval) = &pars_int_token; /* pass any non-NULL pointer */ } break; case 142: /* Line 1455 of yacc.c */ #line 521 "pars0grm.y" { (yyval) = NULL; } break; case 143: /* Line 1455 of yacc.c */ #line 523 "pars0grm.y" { (yyval) = &pars_int_token; /* pass any non-NULL pointer */ } break; case 144: /* Line 1455 of yacc.c */ #line 530 "pars0grm.y" { (yyval) = pars_create_table((yyvsp[(3) - (7)]), (yyvsp[(5) - (7)]), (yyvsp[(7) - (7)])); } break; case 145: /* Line 1455 of yacc.c */ #line 534 "pars0grm.y" { (yyval) = que_node_list_add_last(NULL, (yyvsp[(1) - (1)])); } break; case 146: /* Line 1455 of yacc.c */ #line 536 "pars0grm.y" { (yyval) = que_node_list_add_last((yyvsp[(1) - (3)]), (yyvsp[(3) - (3)])); } break; case 147: /* Line 1455 of yacc.c */ #line 540 "pars0grm.y" { (yyval) = NULL; } break; case 148: /* Line 1455 of yacc.c */ #line 541 "pars0grm.y" { (yyval) = &pars_unique_token; } break; case 149: /* Line 1455 of yacc.c */ #line 545 "pars0grm.y" { (yyval) = NULL; } break; case 150: /* Line 1455 of yacc.c */ #line 546 "pars0grm.y" { (yyval) = &pars_clustered_token; } break; case 151: /* Line 1455 of yacc.c */ #line 554 "pars0grm.y" { (yyval) = pars_create_index((yyvsp[(2) - (10)]), (yyvsp[(3) - (10)]), (yyvsp[(5) - (10)]), (yyvsp[(7) - (10)]), (yyvsp[(9) - (10)])); } break; case 152: /* Line 1455 of yacc.c */ #line 559 "pars0grm.y" { (yyval) = pars_commit_statement(); } break; case 153: /* Line 1455 of yacc.c */ #line 564 "pars0grm.y" { (yyval) = pars_rollback_statement(); } break; case 154: /* Line 1455 of yacc.c */ #line 568 "pars0grm.y" { (yyval) = &pars_int_token; } break; case 155: /* Line 1455 of yacc.c */ #line 569 "pars0grm.y" { (yyval) = &pars_int_token; } break; case 156: /* Line 1455 of yacc.c */ #line 570 "pars0grm.y" { (yyval) = &pars_char_token; } break; case 157: /* Line 1455 of yacc.c */ #line 571 "pars0grm.y" { (yyval) = &pars_binary_token; } break; case 158: /* Line 1455 of yacc.c */ #line 572 "pars0grm.y" { (yyval) = &pars_blob_token; } break; case 159: /* Line 1455 of yacc.c */ #line 577 "pars0grm.y" { (yyval) = pars_parameter_declaration((yyvsp[(1) - (3)]), PARS_INPUT, (yyvsp[(3) - (3)])); } break; case 160: /* Line 1455 of yacc.c */ #line 580 "pars0grm.y" { (yyval) = pars_parameter_declaration((yyvsp[(1) - (3)]), PARS_OUTPUT, (yyvsp[(3) - (3)])); } break; case 161: /* Line 1455 of yacc.c */ #line 585 "pars0grm.y" { (yyval) = NULL; } break; case 162: /* Line 1455 of yacc.c */ #line 586 "pars0grm.y" { (yyval) = que_node_list_add_last(NULL, (yyvsp[(1) - (1)])); } break; case 163: /* Line 1455 of yacc.c */ #line 588 "pars0grm.y" { (yyval) = que_node_list_add_last((yyvsp[(1) - (3)]), (yyvsp[(3) - (3)])); } break; case 164: /* Line 1455 of yacc.c */ #line 593 "pars0grm.y" { (yyval) = pars_variable_declaration((yyvsp[(1) - (3)]), (yyvsp[(2) - (3)])); } break; case 168: /* Line 1455 of yacc.c */ #line 605 "pars0grm.y" { (yyval) = pars_cursor_declaration((yyvsp[(3) - (6)]), (yyvsp[(5) - (6)])); } break; case 169: /* Line 1455 of yacc.c */ #line 610 "pars0grm.y" { (yyval) = pars_function_declaration((yyvsp[(3) - (4)])); } break; case 175: /* Line 1455 of yacc.c */ #line 631 "pars0grm.y" { (yyval) = pars_procedure_definition((yyvsp[(2) - (11)]), (yyvsp[(4) - (11)]), (yyvsp[(10) - (11)])); } break; /* Line 1455 of yacc.c */ #line 2962 "pars/pars0grm.c" default: break; } YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); YYPOPSTACK (yylen); yylen = 0; YY_STACK_PRINT (yyss, yyssp); *++yyvsp = yyval; /* Now `shift' the result of the reduction. Determine what state that goes to, based on the state we popped back to and the rule number reduced by. */ yyn = yyr1[yyn]; yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) yystate = yytable[yystate]; else yystate = yydefgoto[yyn - YYNTOKENS]; goto yynewstate; /*------------------------------------. | yyerrlab -- here on detecting error | `------------------------------------*/ yyerrlab: /* If not already recovering from an error, report this error. */ if (!yyerrstatus) { ++yynerrs; #if ! YYERROR_VERBOSE yyerror (YY_("syntax error")); #else { YYSIZE_T yysize = yysyntax_error (0, yystate, yychar); if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM) { YYSIZE_T yyalloc = 2 * yysize; if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM)) yyalloc = YYSTACK_ALLOC_MAXIMUM; if (yymsg != yymsgbuf) YYSTACK_FREE (yymsg); yymsg = (char *) YYSTACK_ALLOC (yyalloc); if (yymsg) yymsg_alloc = yyalloc; else { yymsg = yymsgbuf; yymsg_alloc = sizeof yymsgbuf; } } if (0 < yysize && yysize <= yymsg_alloc) { (void) yysyntax_error (yymsg, yystate, yychar); yyerror (yymsg); } else { yyerror (YY_("syntax error")); if (yysize != 0) goto yyexhaustedlab; } } #endif } if (yyerrstatus == 3) { /* If just tried and failed to reuse lookahead token after an error, discard it. */ if (yychar <= YYEOF) { /* Return failure if at end of input. */ if (yychar == YYEOF) YYABORT; } else { yydestruct ("Error: discarding", yytoken, &yylval); yychar = YYEMPTY; } } /* Else will try to reuse lookahead token after shifting the error token. */ goto yyerrlab1; /*---------------------------------------------------. | yyerrorlab -- error raised explicitly by YYERROR. | `---------------------------------------------------*/ yyerrorlab: /* Pacify compilers like GCC when the user code never invokes YYERROR and the label yyerrorlab therefore never appears in user code. */ if (/*CONSTCOND*/ 0) goto yyerrorlab; /* Do not reclaim the symbols of the rule which action triggered this YYERROR. */ YYPOPSTACK (yylen); yylen = 0; YY_STACK_PRINT (yyss, yyssp); yystate = *yyssp; goto yyerrlab1; /*-------------------------------------------------------------. | yyerrlab1 -- common code for both syntax error and YYERROR. | `-------------------------------------------------------------*/ yyerrlab1: yyerrstatus = 3; /* Each real token shifted decrements this. */ for (;;) { yyn = yypact[yystate]; if (yyn != YYPACT_NINF) { yyn += YYTERROR; if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) { yyn = yytable[yyn]; if (0 < yyn) break; } } /* Pop the current state because it cannot handle the error token. */ if (yyssp == yyss) YYABORT; yydestruct ("Error: popping", yystos[yystate], yyvsp); YYPOPSTACK (1); yystate = *yyssp; YY_STACK_PRINT (yyss, yyssp); } *++yyvsp = yylval; /* Shift the error token. */ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); yystate = yyn; goto yynewstate; /*-------------------------------------. | yyacceptlab -- YYACCEPT comes here. | `-------------------------------------*/ yyacceptlab: yyresult = 0; goto yyreturn; /*-----------------------------------. | yyabortlab -- YYABORT comes here. | `-----------------------------------*/ yyabortlab: yyresult = 1; goto yyreturn; #if !defined(yyoverflow) || YYERROR_VERBOSE /*-------------------------------------------------. | yyexhaustedlab -- memory exhaustion comes here. | `-------------------------------------------------*/ yyexhaustedlab: yyerror (YY_("memory exhausted")); yyresult = 2; /* Fall through. */ #endif yyreturn: if (yychar != YYEMPTY) yydestruct ("Cleanup: discarding lookahead", yytoken, &yylval); /* Do not reclaim the symbols of the rule which action triggered this YYABORT or YYACCEPT. */ YYPOPSTACK (yylen); YY_STACK_PRINT (yyss, yyssp); while (yyssp != yyss) { yydestruct ("Cleanup: popping", yystos[*yyssp], yyvsp); YYPOPSTACK (1); } #ifndef yyoverflow if (yyss != yyssa) YYSTACK_FREE (yyss); #endif #if YYERROR_VERBOSE if (yymsg != yymsgbuf) YYSTACK_FREE (yymsg); #endif /* Make sure YYID is used. */ return YYID (yyresult); } /* Line 1675 of yacc.c */ #line 635 "pars0grm.y" /********************************************************************** Release any resources used by the parser. */ UNIV_INTERN void pars_close(void) /*============*/ { pars_lexer_close(); } haildb-2.3.2/pars/make_bison.sh0000755000175000017500000000207511513177357017262 0ustar00pcrewspcrews00000000000000#!/bin/bash # # Copyright (c) 2006, 2009, Innobase Oy. All Rights Reserved. # # This program is free software; you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free Software # Foundation; version 2 of the License. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along with # this program; if not, write to the Free Software Foundation, Inc., 59 Temple # Place, Suite 330, Boston, MA 02111-1307 USA # # generate parser files from bison input files. set -eu TMPFILE=pars0grm.tab.c OUTFILE=pars0grm.c bison -d pars0grm.y mv pars0grm.tab.h ../include/pars0grm.h sed -e ' s/'"$TMPFILE"'/'"$OUTFILE"'/; s/^\(\(YYSTYPE\|int\) yy\(char\|nerrs\)\)/static \1/; s/\(\(YYSTYPE\|int\) yy\(lval\|parse\)\)/UNIV_INTERN \1/; ' < "$TMPFILE" > "$OUTFILE" rm "$TMPFILE" haildb-2.3.2/pars/make_flex.sh0000755000175000017500000000365511513177357017113 0ustar00pcrewspcrews00000000000000#!/bin/bash # # Copyright (c) 2006, 2009, Innobase Oy. All Rights Reserved. # # This program is free software; you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free Software # Foundation; version 2 of the License. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along with # this program; if not, write to the Free Software Foundation, Inc., 59 Temple # Place, Suite 330, Boston, MA 02111-1307 USA # # generate lexer files from flex input files. set -eu TMPFILE=_flex_tmp.c OUTFILE=lexyy.c flex -o $TMPFILE pars0lex.l # AIX needs its includes done in a certain order, so include "univ.i" first # to be sure we get it right. echo '#include "univ.i"' > $OUTFILE # flex assigns a pointer to an int in one place without a cast, resulting in # a warning on Win64. Add the cast. Also define some symbols as static. sed -e ' s/'"$TMPFILE"'/'"$OUTFILE"'/; s/\(int offset = \)\((yy_c_buf_p) - (yytext_ptr)\);/\1(int)(\2);/; s/\(void yy\(restart\|_\(delete\|flush\)_buffer\)\)/static \1/; s/\(void yy_switch_to_buffer\)/__attribute__((unused)) static \1/; s/\(void yy\(push\|pop\)_buffer_state\)/__attribute__((unused)) static \1/; s/\(YY_BUFFER_STATE yy_create_buffer\)/static \1/; s/\(\(int\|void\) yy[gs]et_\)/__attribute__((unused)) static \1/; s/\(void \*\?yy\(\(re\)\?alloc\|free\)\)/static \1/; s/\(extern \)\?\(int yy\(leng\|lineno\|_flex_debug\)\)/static \2/; s/\(int yylex_destroy\)/__attribute__((unused)) static \1/; s/\(extern \)\?\(int yylex \)/UNIV_INTERN \2/; s/^\(\(FILE\|char\) *\* *yyget\)/__attribute__((unused)) static \1/; s/^\(extern \)\?\(\(FILE\|char\) *\* *yy\)/static \2/; ' < $TMPFILE >> $OUTFILE rm $TMPFILE haildb-2.3.2/pars/pars0grm.y0000644000175000017500000003770311513177357016547 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1997, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /****************************************************** SQL parser: input file for the GNU Bison parser generator Look from pars0lex.l for instructions how to generate the C files for the InnoDB parser. Created 12/14/1997 Heikki Tuuri *******************************************************/ %{ /* The value of the semantic attribute is a pointer to a query tree node que_node_t */ #include "univ.i" #include /* Can't be before univ.i */ #include "pars0pars.h" #include "mem0mem.h" #include "que0types.h" #include "que0que.h" #include "row0sel.h" #define YYSTYPE que_node_t* /* #define __STDC__ */ int yylex(void); %} %token PARS_INT_LIT %token PARS_FLOAT_LIT %token PARS_STR_LIT %token PARS_FIXBINARY_LIT %token PARS_BLOB_LIT %token PARS_NULL_LIT %token PARS_ID_TOKEN %token PARS_AND_TOKEN %token PARS_OR_TOKEN %token PARS_NOT_TOKEN %token PARS_GE_TOKEN %token PARS_LE_TOKEN %token PARS_NE_TOKEN %token PARS_PROCEDURE_TOKEN %token PARS_IN_TOKEN %token PARS_OUT_TOKEN %token PARS_BINARY_TOKEN %token PARS_BLOB_TOKEN %token PARS_INT_TOKEN %token PARS_INTEGER_TOKEN %token PARS_FLOAT_TOKEN %token PARS_CHAR_TOKEN %token PARS_IS_TOKEN %token PARS_BEGIN_TOKEN %token PARS_END_TOKEN %token PARS_IF_TOKEN %token PARS_THEN_TOKEN %token PARS_ELSE_TOKEN %token PARS_ELSIF_TOKEN %token PARS_LOOP_TOKEN %token PARS_WHILE_TOKEN %token PARS_RETURN_TOKEN %token PARS_SELECT_TOKEN %token PARS_SUM_TOKEN %token PARS_COUNT_TOKEN %token PARS_DISTINCT_TOKEN %token PARS_FROM_TOKEN %token PARS_WHERE_TOKEN %token PARS_FOR_TOKEN %token PARS_DDOT_TOKEN %token PARS_READ_TOKEN %token PARS_ORDER_TOKEN %token PARS_BY_TOKEN %token PARS_ASC_TOKEN %token PARS_DESC_TOKEN %token PARS_INSERT_TOKEN %token PARS_INTO_TOKEN %token PARS_VALUES_TOKEN %token PARS_UPDATE_TOKEN %token PARS_SET_TOKEN %token PARS_DELETE_TOKEN %token PARS_CURRENT_TOKEN %token PARS_OF_TOKEN %token PARS_CREATE_TOKEN %token PARS_TABLE_TOKEN %token PARS_INDEX_TOKEN %token PARS_UNIQUE_TOKEN %token PARS_CLUSTERED_TOKEN %token PARS_DOES_NOT_FIT_IN_MEM_TOKEN %token PARS_ON_TOKEN %token PARS_ASSIGN_TOKEN %token PARS_DECLARE_TOKEN %token PARS_CURSOR_TOKEN %token PARS_SQL_TOKEN %token PARS_OPEN_TOKEN %token PARS_FETCH_TOKEN %token PARS_CLOSE_TOKEN %token PARS_NOTFOUND_TOKEN %token PARS_TO_CHAR_TOKEN %token PARS_TO_NUMBER_TOKEN %token PARS_TO_BINARY_TOKEN %token PARS_BINARY_TO_NUMBER_TOKEN %token PARS_SUBSTR_TOKEN %token PARS_REPLSTR_TOKEN %token PARS_CONCAT_TOKEN %token PARS_INSTR_TOKEN %token PARS_LENGTH_TOKEN %token PARS_SYSDATE_TOKEN %token PARS_PRINTF_TOKEN %token PARS_ASSERT_TOKEN %token PARS_RND_TOKEN %token PARS_RND_STR_TOKEN %token PARS_ROW_PRINTF_TOKEN %token PARS_COMMIT_TOKEN %token PARS_ROLLBACK_TOKEN %token PARS_WORK_TOKEN %token PARS_UNSIGNED_TOKEN %token PARS_EXIT_TOKEN %token PARS_FUNCTION_TOKEN %token PARS_LOCK_TOKEN %token PARS_SHARE_TOKEN %token PARS_MODE_TOKEN %left PARS_AND_TOKEN PARS_OR_TOKEN %left PARS_NOT_TOKEN %left '=' '<' '>' PARS_GE_TOKEN PARS_LE_TOKEN %left '-' '+' %left '*' '/' %left NEG /* negation--unary minus */ %left '%' /* Grammar follows */ %% top_statement: procedure_definition ';' statement: stored_procedure_call | predefined_procedure_call ';' | while_statement ';' | for_statement ';' | exit_statement ';' | if_statement ';' | return_statement ';' | assignment_statement ';' | select_statement ';' | insert_statement ';' | row_printf_statement ';' | delete_statement_searched ';' | delete_statement_positioned ';' | update_statement_searched ';' | update_statement_positioned ';' | open_cursor_statement ';' | fetch_statement ';' | close_cursor_statement ';' | commit_statement ';' | rollback_statement ';' | create_table ';' | create_index ';' ; statement_list: statement { $$ = que_node_list_add_last(NULL, $1); } | statement_list statement { $$ = que_node_list_add_last($1, $2); } ; exp: PARS_ID_TOKEN { $$ = $1;} | function_name '(' exp_list ')' { $$ = pars_func($1, $3); } | PARS_INT_LIT { $$ = $1;} | PARS_FLOAT_LIT { $$ = $1;} | PARS_STR_LIT { $$ = $1;} | PARS_FIXBINARY_LIT { $$ = $1;} | PARS_BLOB_LIT { $$ = $1;} | PARS_NULL_LIT { $$ = $1;} | PARS_SQL_TOKEN { $$ = $1;} | exp '+' exp { $$ = pars_op('+', $1, $3); } | exp '-' exp { $$ = pars_op('-', $1, $3); } | exp '*' exp { $$ = pars_op('*', $1, $3); } | exp '/' exp { $$ = pars_op('/', $1, $3); } | '-' exp %prec NEG { $$ = pars_op('-', $2, NULL); } | '(' exp ')' { $$ = $2; } | exp '=' exp { $$ = pars_op('=', $1, $3); } | exp '<' exp { $$ = pars_op('<', $1, $3); } | exp '>' exp { $$ = pars_op('>', $1, $3); } | exp PARS_GE_TOKEN exp { $$ = pars_op(PARS_GE_TOKEN, $1, $3); } | exp PARS_LE_TOKEN exp { $$ = pars_op(PARS_LE_TOKEN, $1, $3); } | exp PARS_NE_TOKEN exp { $$ = pars_op(PARS_NE_TOKEN, $1, $3); } | exp PARS_AND_TOKEN exp{ $$ = pars_op(PARS_AND_TOKEN, $1, $3); } | exp PARS_OR_TOKEN exp { $$ = pars_op(PARS_OR_TOKEN, $1, $3); } | PARS_NOT_TOKEN exp { $$ = pars_op(PARS_NOT_TOKEN, $2, NULL); } | PARS_ID_TOKEN '%' PARS_NOTFOUND_TOKEN { $$ = pars_op(PARS_NOTFOUND_TOKEN, $1, NULL); } | PARS_SQL_TOKEN '%' PARS_NOTFOUND_TOKEN { $$ = pars_op(PARS_NOTFOUND_TOKEN, $1, NULL); } ; function_name: PARS_TO_CHAR_TOKEN { $$ = &pars_to_char_token; } | PARS_TO_NUMBER_TOKEN { $$ = &pars_to_number_token; } | PARS_TO_BINARY_TOKEN { $$ = &pars_to_binary_token; } | PARS_BINARY_TO_NUMBER_TOKEN { $$ = &pars_binary_to_number_token; } | PARS_SUBSTR_TOKEN { $$ = &pars_substr_token; } | PARS_CONCAT_TOKEN { $$ = &pars_concat_token; } | PARS_INSTR_TOKEN { $$ = &pars_instr_token; } | PARS_LENGTH_TOKEN { $$ = &pars_length_token; } | PARS_SYSDATE_TOKEN { $$ = &pars_sysdate_token; } | PARS_RND_TOKEN { $$ = &pars_rnd_token; } | PARS_RND_STR_TOKEN { $$ = &pars_rnd_str_token; } ; question_mark_list: /* Nothing */ | '?' | question_mark_list ',' '?' ; stored_procedure_call: '{' PARS_ID_TOKEN '(' question_mark_list ')' '}' { $$ = pars_stored_procedure_call($2); } ; predefined_procedure_call: predefined_procedure_name '(' exp_list ')' { $$ = pars_procedure_call($1, $3); } ; predefined_procedure_name: PARS_REPLSTR_TOKEN { $$ = &pars_replstr_token; } | PARS_PRINTF_TOKEN { $$ = &pars_printf_token; } | PARS_ASSERT_TOKEN { $$ = &pars_assert_token; } ; user_function_call: PARS_ID_TOKEN '(' ')' { $$ = $1; } ; table_list: PARS_ID_TOKEN { $$ = que_node_list_add_last(NULL, $1); } | table_list ',' PARS_ID_TOKEN { $$ = que_node_list_add_last($1, $3); } ; variable_list: /* Nothing */ { $$ = NULL; } | PARS_ID_TOKEN { $$ = que_node_list_add_last(NULL, $1); } | variable_list ',' PARS_ID_TOKEN { $$ = que_node_list_add_last($1, $3); } ; exp_list: /* Nothing */ { $$ = NULL; } | exp { $$ = que_node_list_add_last(NULL, $1);} | exp_list ',' exp { $$ = que_node_list_add_last($1, $3); } ; select_item: exp { $$ = $1; } | PARS_COUNT_TOKEN '(' '*' ')' { $$ = pars_func(&pars_count_token, que_node_list_add_last(NULL, sym_tab_add_int_lit( pars_sym_tab_global, 1))); } | PARS_COUNT_TOKEN '(' PARS_DISTINCT_TOKEN PARS_ID_TOKEN ')' { $$ = pars_func(&pars_count_token, que_node_list_add_last(NULL, pars_func(&pars_distinct_token, que_node_list_add_last( NULL, $4)))); } | PARS_SUM_TOKEN '(' exp ')' { $$ = pars_func(&pars_sum_token, que_node_list_add_last(NULL, $3)); } ; select_item_list: /* Nothing */ { $$ = NULL; } | select_item { $$ = que_node_list_add_last(NULL, $1); } | select_item_list ',' select_item { $$ = que_node_list_add_last($1, $3); } ; select_list: '*' { $$ = pars_select_list(&pars_star_denoter, NULL); } | select_item_list PARS_INTO_TOKEN variable_list { $$ = pars_select_list($1, $3); } | select_item_list { $$ = pars_select_list($1, NULL); } ; search_condition: /* Nothing */ { $$ = NULL; } | PARS_WHERE_TOKEN exp { $$ = $2; } ; for_update_clause: /* Nothing */ { $$ = NULL; } | PARS_FOR_TOKEN PARS_UPDATE_TOKEN { $$ = &pars_update_token; } ; lock_shared_clause: /* Nothing */ { $$ = NULL; } | PARS_LOCK_TOKEN PARS_IN_TOKEN PARS_SHARE_TOKEN PARS_MODE_TOKEN { $$ = &pars_share_token; } ; order_direction: /* Nothing */ { $$ = &pars_asc_token; } | PARS_ASC_TOKEN { $$ = &pars_asc_token; } | PARS_DESC_TOKEN { $$ = &pars_desc_token; } ; order_by_clause: /* Nothing */ { $$ = NULL; } | PARS_ORDER_TOKEN PARS_BY_TOKEN PARS_ID_TOKEN order_direction { $$ = pars_order_by($3, $4); } ; select_statement: PARS_SELECT_TOKEN select_list PARS_FROM_TOKEN table_list search_condition for_update_clause lock_shared_clause order_by_clause { $$ = pars_select_statement($2, $4, $5, $6, $7, $8); } ; insert_statement_start: PARS_INSERT_TOKEN PARS_INTO_TOKEN PARS_ID_TOKEN { $$ = $3; } ; insert_statement: insert_statement_start PARS_VALUES_TOKEN '(' exp_list ')' { $$ = pars_insert_statement($1, $4, NULL); } | insert_statement_start select_statement { $$ = pars_insert_statement($1, NULL, $2); } ; column_assignment: PARS_ID_TOKEN '=' exp { $$ = pars_column_assignment($1, $3); } ; column_assignment_list: column_assignment { $$ = que_node_list_add_last(NULL, $1); } | column_assignment_list ',' column_assignment { $$ = que_node_list_add_last($1, $3); } ; cursor_positioned: PARS_WHERE_TOKEN PARS_CURRENT_TOKEN PARS_OF_TOKEN PARS_ID_TOKEN { $$ = $4; } ; update_statement_start: PARS_UPDATE_TOKEN PARS_ID_TOKEN PARS_SET_TOKEN column_assignment_list { $$ = pars_update_statement_start(FALSE, $2, $4); } ; update_statement_searched: update_statement_start search_condition { $$ = pars_update_statement($1, NULL, $2); } ; update_statement_positioned: update_statement_start cursor_positioned { $$ = pars_update_statement($1, $2, NULL); } ; delete_statement_start: PARS_DELETE_TOKEN PARS_FROM_TOKEN PARS_ID_TOKEN { $$ = pars_update_statement_start(TRUE, $3, NULL); } ; delete_statement_searched: delete_statement_start search_condition { $$ = pars_update_statement($1, NULL, $2); } ; delete_statement_positioned: delete_statement_start cursor_positioned { $$ = pars_update_statement($1, $2, NULL); } ; row_printf_statement: PARS_ROW_PRINTF_TOKEN select_statement { $$ = pars_row_printf_statement($2); } ; assignment_statement: PARS_ID_TOKEN PARS_ASSIGN_TOKEN exp { $$ = pars_assignment_statement($1, $3); } ; elsif_element: PARS_ELSIF_TOKEN exp PARS_THEN_TOKEN statement_list { $$ = pars_elsif_element($2, $4); } ; elsif_list: elsif_element { $$ = que_node_list_add_last(NULL, $1); } | elsif_list elsif_element { $$ = que_node_list_add_last($1, $2); } ; else_part: /* Nothing */ { $$ = NULL; } | PARS_ELSE_TOKEN statement_list { $$ = $2; } | elsif_list { $$ = $1; } ; if_statement: PARS_IF_TOKEN exp PARS_THEN_TOKEN statement_list else_part PARS_END_TOKEN PARS_IF_TOKEN { $$ = pars_if_statement($2, $4, $5); } ; while_statement: PARS_WHILE_TOKEN exp PARS_LOOP_TOKEN statement_list PARS_END_TOKEN PARS_LOOP_TOKEN { $$ = pars_while_statement($2, $4); } ; for_statement: PARS_FOR_TOKEN PARS_ID_TOKEN PARS_IN_TOKEN exp PARS_DDOT_TOKEN exp PARS_LOOP_TOKEN statement_list PARS_END_TOKEN PARS_LOOP_TOKEN { $$ = pars_for_statement($2, $4, $6, $8); } ; exit_statement: PARS_EXIT_TOKEN { $$ = pars_exit_statement(); } ; return_statement: PARS_RETURN_TOKEN { $$ = pars_return_statement(); } ; open_cursor_statement: PARS_OPEN_TOKEN PARS_ID_TOKEN { $$ = pars_open_statement( ROW_SEL_OPEN_CURSOR, $2); } ; close_cursor_statement: PARS_CLOSE_TOKEN PARS_ID_TOKEN { $$ = pars_open_statement( ROW_SEL_CLOSE_CURSOR, $2); } ; fetch_statement: PARS_FETCH_TOKEN PARS_ID_TOKEN PARS_INTO_TOKEN variable_list { $$ = pars_fetch_statement($2, $4, NULL); } | PARS_FETCH_TOKEN PARS_ID_TOKEN PARS_INTO_TOKEN user_function_call { $$ = pars_fetch_statement($2, NULL, $4); } ; column_def: PARS_ID_TOKEN type_name opt_column_len opt_unsigned opt_not_null { $$ = pars_column_def($1, $2, $3, $4, $5); } ; column_def_list: column_def { $$ = que_node_list_add_last(NULL, $1); } | column_def_list ',' column_def { $$ = que_node_list_add_last($1, $3); } ; opt_column_len: /* Nothing */ { $$ = NULL; } | '(' PARS_INT_LIT ')' { $$ = $2; } ; opt_unsigned: /* Nothing */ { $$ = NULL; } | PARS_UNSIGNED_TOKEN { $$ = &pars_int_token; /* pass any non-NULL pointer */ } ; opt_not_null: /* Nothing */ { $$ = NULL; } | PARS_NOT_TOKEN PARS_NULL_LIT { $$ = &pars_int_token; /* pass any non-NULL pointer */ } ; not_fit_in_memory: /* Nothing */ { $$ = NULL; } | PARS_DOES_NOT_FIT_IN_MEM_TOKEN { $$ = &pars_int_token; /* pass any non-NULL pointer */ } ; create_table: PARS_CREATE_TOKEN PARS_TABLE_TOKEN PARS_ID_TOKEN '(' column_def_list ')' not_fit_in_memory { $$ = pars_create_table($3, $5, $7); } ; column_list: PARS_ID_TOKEN { $$ = que_node_list_add_last(NULL, $1); } | column_list ',' PARS_ID_TOKEN { $$ = que_node_list_add_last($1, $3); } ; unique_def: /* Nothing */ { $$ = NULL; } | PARS_UNIQUE_TOKEN { $$ = &pars_unique_token; } ; clustered_def: /* Nothing */ { $$ = NULL; } | PARS_CLUSTERED_TOKEN { $$ = &pars_clustered_token; } ; create_index: PARS_CREATE_TOKEN unique_def clustered_def PARS_INDEX_TOKEN PARS_ID_TOKEN PARS_ON_TOKEN PARS_ID_TOKEN '(' column_list ')' { $$ = pars_create_index($2, $3, $5, $7, $9); } ; commit_statement: PARS_COMMIT_TOKEN PARS_WORK_TOKEN { $$ = pars_commit_statement(); } ; rollback_statement: PARS_ROLLBACK_TOKEN PARS_WORK_TOKEN { $$ = pars_rollback_statement(); } ; type_name: PARS_INT_TOKEN { $$ = &pars_int_token; } | PARS_INTEGER_TOKEN { $$ = &pars_int_token; } | PARS_CHAR_TOKEN { $$ = &pars_char_token; } | PARS_BINARY_TOKEN { $$ = &pars_binary_token; } | PARS_BLOB_TOKEN { $$ = &pars_blob_token; } ; parameter_declaration: PARS_ID_TOKEN PARS_IN_TOKEN type_name { $$ = pars_parameter_declaration($1, PARS_INPUT, $3); } | PARS_ID_TOKEN PARS_OUT_TOKEN type_name { $$ = pars_parameter_declaration($1, PARS_OUTPUT, $3); } ; parameter_declaration_list: /* Nothing */ { $$ = NULL; } | parameter_declaration { $$ = que_node_list_add_last(NULL, $1); } | parameter_declaration_list ',' parameter_declaration { $$ = que_node_list_add_last($1, $3); } ; variable_declaration: PARS_ID_TOKEN type_name ';' { $$ = pars_variable_declaration($1, $2); } ; variable_declaration_list: /* Nothing */ | variable_declaration | variable_declaration_list variable_declaration ; cursor_declaration: PARS_DECLARE_TOKEN PARS_CURSOR_TOKEN PARS_ID_TOKEN PARS_IS_TOKEN select_statement ';' { $$ = pars_cursor_declaration($3, $5); } ; function_declaration: PARS_DECLARE_TOKEN PARS_FUNCTION_TOKEN PARS_ID_TOKEN ';' { $$ = pars_function_declaration($3); } ; declaration: cursor_declaration | function_declaration ; declaration_list: /* Nothing */ | declaration | declaration_list declaration ; procedure_definition: PARS_PROCEDURE_TOKEN PARS_ID_TOKEN '(' parameter_declaration_list ')' PARS_IS_TOKEN variable_declaration_list declaration_list PARS_BEGIN_TOKEN statement_list PARS_END_TOKEN { $$ = pars_procedure_definition($2, $4, $10); } ; %% /********************************************************************** Release any resources used by the parser. */ UNIV_INTERN void pars_close(void) /*============*/ { pars_lexer_close(); } haildb-2.3.2/pars/pars0sym.c0000644000175000017500000002052311513177357016534 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1997, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file pars/pars0sym.c SQL parser symbol table Created 12/15/1997 Heikki Tuuri *******************************************************/ #include "pars0sym.h" #ifdef UNIV_NONINL #include "pars0sym.ic" #endif #include "mem0mem.h" #include "data0type.h" #include "data0data.h" #include "pars0grm.h" #include "pars0pars.h" #include "que0que.h" #include "eval0eval.h" #include "row0sel.h" /******************************************************************//** Creates a symbol table for a single stored procedure or query. @return own: symbol table */ UNIV_INTERN sym_tab_t* sym_tab_create( /*===========*/ mem_heap_t* heap) /*!< in: memory heap where to create */ { sym_tab_t* sym_tab; sym_tab = mem_heap_alloc(heap, sizeof(sym_tab_t)); UT_LIST_INIT(sym_tab->sym_list); UT_LIST_INIT(sym_tab->func_node_list); sym_tab->heap = heap; return(sym_tab); } /******************************************************************//** Frees the memory allocated dynamically AFTER parsing phase for variables etc. in the symbol table. Does not free the mem heap where the table was originally created. Frees also SQL explicit cursor definitions. */ UNIV_INTERN void sym_tab_free_private( /*=================*/ sym_tab_t* sym_tab) /*!< in, own: symbol table */ { sym_node_t* sym; func_node_t* func; sym = UT_LIST_GET_FIRST(sym_tab->sym_list); while (sym) { eval_node_free_val_buf(sym); if (sym->prefetch_buf) { sel_col_prefetch_buf_free(sym->prefetch_buf); } if (sym->cursor_def) { que_graph_free_recursive(sym->cursor_def); } sym = UT_LIST_GET_NEXT(sym_list, sym); } func = UT_LIST_GET_FIRST(sym_tab->func_node_list); while (func) { eval_node_free_val_buf(func); func = UT_LIST_GET_NEXT(func_node_list, func); } } /******************************************************************//** Adds an integer literal to a symbol table. @return symbol table node */ UNIV_INTERN sym_node_t* sym_tab_add_int_lit( /*================*/ sym_tab_t* sym_tab, /*!< in: symbol table */ ulint val) /*!< in: integer value */ { sym_node_t* node; byte* data; node = mem_heap_alloc(sym_tab->heap, sizeof(sym_node_t)); node->common.type = QUE_NODE_SYMBOL; node->resolved = TRUE; node->token_type = SYM_LIT; node->indirection = NULL; dtype_set(dfield_get_type(&node->common.val), DATA_INT, 0, 4); data = mem_heap_alloc(sym_tab->heap, 4); mach_write_to_4(data, val); dfield_set_data(&(node->common.val), data, 4); node->common.val_buf_size = 0; node->prefetch_buf = NULL; node->cursor_def = NULL; UT_LIST_ADD_LAST(sym_list, sym_tab->sym_list, node); node->sym_table = sym_tab; return(node); } /******************************************************************//** Adds a string literal to a symbol table. @return symbol table node */ UNIV_INTERN sym_node_t* sym_tab_add_str_lit( /*================*/ sym_tab_t* sym_tab, /*!< in: symbol table */ byte* str, /*!< in: string with no quotes around it */ ulint len) /*!< in: string length */ { sym_node_t* node; byte* data; node = mem_heap_alloc(sym_tab->heap, sizeof(sym_node_t)); node->common.type = QUE_NODE_SYMBOL; node->resolved = TRUE; node->token_type = SYM_LIT; node->indirection = NULL; dtype_set(dfield_get_type(&node->common.val), DATA_VARCHAR, DATA_ENGLISH, 0); if (len) { data = mem_heap_alloc(sym_tab->heap, len + 1); ut_memcpy(data, str, len); } else { data = NULL; } dfield_set_data(&(node->common.val), data, len); node->common.val_buf_size = 0; node->prefetch_buf = NULL; node->cursor_def = NULL; UT_LIST_ADD_LAST(sym_list, sym_tab->sym_list, node); node->sym_table = sym_tab; return(node); } /******************************************************************//** Add a bound literal to a symbol table. @return symbol table node */ UNIV_INTERN sym_node_t* sym_tab_add_bound_lit( /*==================*/ sym_tab_t* sym_tab, /*!< in: symbol table */ const char* name, /*!< in: name of bound literal */ ulint* lit_type) /*!< out: type of literal (PARS_*_LIT) */ { sym_node_t* node; pars_bound_lit_t* blit; ulint len = 0; blit = pars_info_get_bound_lit(sym_tab->info, name); ut_a(blit); node = mem_heap_alloc(sym_tab->heap, sizeof(sym_node_t)); node->common.type = QUE_NODE_SYMBOL; node->resolved = TRUE; node->token_type = SYM_LIT; node->indirection = NULL; switch (blit->type) { case DATA_FIXBINARY: len = blit->length; *lit_type = PARS_FIXBINARY_LIT; break; case DATA_BLOB: *lit_type = PARS_BLOB_LIT; break; case DATA_VARCHAR: *lit_type = PARS_STR_LIT; break; case DATA_CHAR: ut_a(blit->length > 0); len = blit->length; *lit_type = PARS_STR_LIT; break; case DATA_INT: ut_a(blit->length > 0); ut_a(blit->length <= 8); len = blit->length; *lit_type = PARS_INT_LIT; break; default: ut_error; } dtype_set(dfield_get_type(&node->common.val), blit->type, blit->prtype, len); dfield_set_data(&(node->common.val), blit->address, blit->length); node->common.val_buf_size = 0; node->prefetch_buf = NULL; node->cursor_def = NULL; UT_LIST_ADD_LAST(sym_list, sym_tab->sym_list, node); node->sym_table = sym_tab; return(node); } /******************************************************************//** Adds an SQL null literal to a symbol table. @return symbol table node */ UNIV_INTERN sym_node_t* sym_tab_add_null_lit( /*=================*/ sym_tab_t* sym_tab) /*!< in: symbol table */ { sym_node_t* node; node = mem_heap_alloc(sym_tab->heap, sizeof(sym_node_t)); node->common.type = QUE_NODE_SYMBOL; node->resolved = TRUE; node->token_type = SYM_LIT; node->indirection = NULL; dfield_get_type(&node->common.val)->mtype = DATA_ERROR; dfield_set_null(&node->common.val); node->common.val_buf_size = 0; node->prefetch_buf = NULL; node->cursor_def = NULL; UT_LIST_ADD_LAST(sym_list, sym_tab->sym_list, node); node->sym_table = sym_tab; return(node); } /******************************************************************//** Adds an identifier to a symbol table. @return symbol table node */ UNIV_INTERN sym_node_t* sym_tab_add_id( /*===========*/ sym_tab_t* sym_tab, /*!< in: symbol table */ byte* name, /*!< in: identifier name */ ulint len) /*!< in: identifier length */ { sym_node_t* node; node = mem_heap_alloc(sym_tab->heap, sizeof(sym_node_t)); node->common.type = QUE_NODE_SYMBOL; node->resolved = FALSE; node->indirection = NULL; node->name = mem_heap_strdupl(sym_tab->heap, (char*) name, len); node->name_len = len; UT_LIST_ADD_LAST(sym_list, sym_tab->sym_list, node); dfield_set_null(&node->common.val); node->common.val_buf_size = 0; node->prefetch_buf = NULL; node->cursor_def = NULL; node->sym_table = sym_tab; return(node); } /******************************************************************//** Add a bound identifier to a symbol table. @return symbol table node */ UNIV_INTERN sym_node_t* sym_tab_add_bound_id( /*=================*/ sym_tab_t* sym_tab, /*!< in: symbol table */ const char* name) /*!< in: name of bound id */ { sym_node_t* node; pars_bound_id_t* bid; bid = pars_info_get_bound_id(sym_tab->info, name); ut_a(bid); node = mem_heap_alloc(sym_tab->heap, sizeof(sym_node_t)); node->common.type = QUE_NODE_SYMBOL; node->resolved = FALSE; node->indirection = NULL; node->name = mem_heap_strdup(sym_tab->heap, bid->id); node->name_len = strlen(node->name); UT_LIST_ADD_LAST(sym_list, sym_tab->sym_list, node); dfield_set_null(&node->common.val); node->common.val_buf_size = 0; node->prefetch_buf = NULL; node->cursor_def = NULL; node->sym_table = sym_tab; return(node); } haildb-2.3.2/pars/lexyy.c0000644000175000017500000021544711513177357016143 0ustar00pcrewspcrews00000000000000#include "univ.i" #line 2 "lexyy.c" #line 4 "lexyy.c" #define YY_INT_ALIGNED short int /* A lexical scanner generated by flex */ #define FLEX_SCANNER #define YY_FLEX_MAJOR_VERSION 2 #define YY_FLEX_MINOR_VERSION 5 #define YY_FLEX_SUBMINOR_VERSION 35 #if YY_FLEX_SUBMINOR_VERSION > 0 #define FLEX_BETA #endif /* First, we deal with platform-specific or compiler-specific issues. */ /* begin standard C headers. */ #include #include #include #include /* end standard C headers. */ /* flex integer type definitions */ #ifndef FLEXINT_H #define FLEXINT_H /* C99 systems have . Non-C99 systems may or may not. */ #if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, * if you want the limit (max/min) macros for int types. */ #ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS 1 #endif #include typedef int8_t flex_int8_t; typedef uint8_t flex_uint8_t; typedef int16_t flex_int16_t; typedef uint16_t flex_uint16_t; typedef int32_t flex_int32_t; typedef uint32_t flex_uint32_t; #else typedef signed char flex_int8_t; typedef short int flex_int16_t; typedef int flex_int32_t; typedef unsigned char flex_uint8_t; typedef unsigned short int flex_uint16_t; typedef unsigned int flex_uint32_t; /* Limits of integral types. */ #ifndef INT8_MIN #define INT8_MIN (-128) #endif #ifndef INT16_MIN #define INT16_MIN (-32767-1) #endif #ifndef INT32_MIN #define INT32_MIN (-2147483647-1) #endif #ifndef INT8_MAX #define INT8_MAX (127) #endif #ifndef INT16_MAX #define INT16_MAX (32767) #endif #ifndef INT32_MAX #define INT32_MAX (2147483647) #endif #ifndef UINT8_MAX #define UINT8_MAX (255U) #endif #ifndef UINT16_MAX #define UINT16_MAX (65535U) #endif #ifndef UINT32_MAX #define UINT32_MAX (4294967295U) #endif #endif /* ! C99 */ #endif /* ! FLEXINT_H */ #ifdef __cplusplus /* The "const" storage-class-modifier is valid. */ #define YY_USE_CONST #else /* ! __cplusplus */ /* C99 requires __STDC__ to be defined as 1. */ #if defined (__STDC__) #define YY_USE_CONST #endif /* defined (__STDC__) */ #endif /* ! __cplusplus */ #ifdef YY_USE_CONST #define yyconst const #else #define yyconst #endif /* Returned upon end-of-file. */ #define YY_NULL 0 /* Promotes a possibly negative, possibly signed char to an unsigned * integer for use as an array index. If the signed char is negative, * we want to instead treat it as an 8-bit unsigned char, hence the * double cast. */ #define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) /* Enter a start condition. This macro really ought to take a parameter, * but we do it the disgusting crufty way forced on us by the ()-less * definition of BEGIN. */ #define BEGIN (yy_start) = 1 + 2 * /* Translate the current start state into a value that can be later handed * to BEGIN to return to the state. The YYSTATE alias is for lex * compatibility. */ #define YY_START (((yy_start) - 1) / 2) #define YYSTATE YY_START /* Action number for EOF rule of a given start state. */ #define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) /* Special action meaning "start processing a new file". */ #define YY_NEW_FILE yyrestart(yyin ) #define YY_END_OF_BUFFER_CHAR 0 /* Size of default input buffer. */ #ifndef YY_BUF_SIZE #ifdef __ia64__ /* On IA-64, the buffer size is 16k, not 8k. * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. * Ditto for the __ia64__ case accordingly. */ #define YY_BUF_SIZE 32768 #else #define YY_BUF_SIZE 16384 #endif /* __ia64__ */ #endif /* The state buf must be large enough to hold one state per character in the main buffer. */ #define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) #ifndef YY_TYPEDEF_YY_BUFFER_STATE #define YY_TYPEDEF_YY_BUFFER_STATE typedef struct yy_buffer_state *YY_BUFFER_STATE; #endif static int yyleng; static FILE *yyin, *yyout; #define EOB_ACT_CONTINUE_SCAN 0 #define EOB_ACT_END_OF_FILE 1 #define EOB_ACT_LAST_MATCH 2 #define YY_LESS_LINENO(n) /* Return all but the first "n" matched characters back to the input stream. */ #define yyless(n) \ do \ { \ /* Undo effects of setting up yytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ *yy_cp = (yy_hold_char); \ YY_RESTORE_YY_MORE_OFFSET \ (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ YY_DO_BEFORE_ACTION; /* set up yytext again */ \ } \ while ( 0 ) #define unput(c) yyunput( c, (yytext_ptr) ) #ifndef YY_TYPEDEF_YY_SIZE_T #define YY_TYPEDEF_YY_SIZE_T typedef size_t yy_size_t; #endif #ifndef YY_STRUCT_YY_BUFFER_STATE #define YY_STRUCT_YY_BUFFER_STATE struct yy_buffer_state { FILE *yy_input_file; char *yy_ch_buf; /* input buffer */ char *yy_buf_pos; /* current position in input buffer */ /* Size of input buffer in bytes, not including room for EOB * characters. */ yy_size_t yy_buf_size; /* Number of characters read into yy_ch_buf, not including EOB * characters. */ int yy_n_chars; /* Whether we "own" the buffer - i.e., we know we created it, * and can realloc() it to grow it, and should free() it to * delete it. */ int yy_is_our_buffer; /* Whether this is an "interactive" input source; if so, and * if we're using stdio for input, then we want to use getc() * instead of fread(), to make sure we stop fetching input after * each newline. */ int yy_is_interactive; /* Whether we're considered to be at the beginning of a line. * If so, '^' rules will be active on the next match, otherwise * not. */ int yy_at_bol; int yy_bs_lineno; /**< The line count. */ int yy_bs_column; /**< The column count. */ /* Whether to try to fill the input buffer when we reach the * end of it. */ int yy_fill_buffer; int yy_buffer_status; #define YY_BUFFER_NEW 0 #define YY_BUFFER_NORMAL 1 /* When an EOF's been seen but there's still some text to process * then we mark the buffer as YY_EOF_PENDING, to indicate that we * shouldn't try reading from the input source any more. We might * still have a bunch of tokens to match, though, because of * possible backing-up. * * When we actually see the EOF, we change the status to "new" * (via yyrestart()), so that the user can continue scanning by * just pointing yyin at a new input file. */ #define YY_BUFFER_EOF_PENDING 2 }; #endif /* !YY_STRUCT_YY_BUFFER_STATE */ /* Stack of input buffers. */ static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */ static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */ static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */ /* We provide macros for accessing buffer states in case in the * future we want to put the buffer states in a more general * "scanner state". * * Returns the top of the stack, or NULL. */ #define YY_CURRENT_BUFFER ( (yy_buffer_stack) \ ? (yy_buffer_stack)[(yy_buffer_stack_top)] \ : NULL) /* Same as previous macro, but useful when we know that the buffer stack is not * NULL or when we need an lvalue. For internal use only. */ #define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)] /* yy_hold_char holds the character lost when yytext is formed. */ static char yy_hold_char; static int yy_n_chars; /* number of characters read into yy_ch_buf */ static int yyleng; /* Points to current character in buffer. */ static char *yy_c_buf_p = (char *) 0; static int yy_init = 0; /* whether we need to initialize */ static int yy_start = 0; /* start state number */ /* Flag which is used to allow yywrap()'s to do buffer switches * instead of setting up a fresh yyin. A bit of a hack ... */ static int yy_did_buffer_switch_on_eof; static void yyrestart (FILE *input_file ); __attribute__((unused)) static void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ); static YY_BUFFER_STATE yy_create_buffer (FILE *file,int size ); static void yy_delete_buffer (YY_BUFFER_STATE b ); static void yy_flush_buffer (YY_BUFFER_STATE b ); __attribute__((unused)) static void yypush_buffer_state (YY_BUFFER_STATE new_buffer ); __attribute__((unused)) static void yypop_buffer_state (void ); static void yyensure_buffer_stack (void ); static void yy_load_buffer_state (void ); static void yy_init_buffer (YY_BUFFER_STATE b,FILE *file ); #define YY_FLUSH_BUFFER yy_flush_buffer(YY_CURRENT_BUFFER ) YY_BUFFER_STATE yy_scan_buffer (char *base,yy_size_t size ); YY_BUFFER_STATE yy_scan_string (yyconst char *yy_str ); YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,int len ); static void *yyalloc (yy_size_t ); static void *yyrealloc (void *,yy_size_t ); static void yyfree (void * ); #define yy_new_buffer yy_create_buffer #define yy_set_interactive(is_interactive) \ { \ if ( ! YY_CURRENT_BUFFER ){ \ yyensure_buffer_stack (); \ YY_CURRENT_BUFFER_LVALUE = \ yy_create_buffer(yyin,YY_BUF_SIZE ); \ } \ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ } #define yy_set_bol(at_bol) \ { \ if ( ! YY_CURRENT_BUFFER ){\ yyensure_buffer_stack (); \ YY_CURRENT_BUFFER_LVALUE = \ yy_create_buffer(yyin,YY_BUF_SIZE ); \ } \ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ } #define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) /* Begin user sect3 */ #define yywrap(n) 1 #define YY_SKIP_YYWRAP typedef unsigned char YY_CHAR; static FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0; typedef int yy_state_type; static int yylineno; static int yylineno = 1; static char *yytext; #define yytext_ptr yytext static yy_state_type yy_get_previous_state (void ); static yy_state_type yy_try_NUL_trans (yy_state_type current_state ); static int yy_get_next_buffer (void ); static void yy_fatal_error (yyconst char msg[] ); /* Done after the current pattern has been matched and before the * corresponding action - sets up yytext. */ #define YY_DO_BEFORE_ACTION \ (yytext_ptr) = yy_bp; \ yyleng = (size_t) (yy_cp - yy_bp); \ (yy_hold_char) = *yy_cp; \ *yy_cp = '\0'; \ (yy_c_buf_p) = yy_cp; #define YY_NUM_RULES 119 #define YY_END_OF_BUFFER 120 /* This struct is not used in this scanner, but its presence is necessary. */ struct yy_trans_info { flex_int32_t yy_verify; flex_int32_t yy_nxt; }; static yyconst flex_int16_t yy_accept[399] = { 0, 0, 0, 114, 114, 0, 0, 0, 0, 120, 118, 117, 117, 8, 118, 109, 5, 98, 104, 107, 105, 102, 106, 118, 108, 1, 118, 103, 101, 99, 100, 112, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 110, 111, 114, 115, 6, 7, 9, 10, 117, 4, 93, 113, 2, 1, 3, 94, 95, 97, 96, 92, 92, 92, 92, 92, 92, 44, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 28, 17, 25, 92, 92, 92, 92, 92, 54, 61, 92, 14, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 114, 115, 115, 116, 6, 7, 9, 10, 2, 13, 45, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 27, 92, 92, 92, 41, 92, 92, 92, 92, 21, 92, 92, 92, 92, 15, 92, 92, 92, 18, 92, 92, 92, 92, 92, 80, 92, 92, 92, 51, 92, 12, 92, 36, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 20, 24, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 46, 92, 92, 30, 92, 87, 92, 92, 39, 92, 92, 92, 92, 92, 48, 92, 89, 32, 91, 92, 11, 64, 92, 92, 92, 42, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 29, 92, 92, 92, 92, 92, 92, 92, 92, 92, 85, 92, 26, 92, 66, 92, 92, 92, 37, 92, 92, 92, 92, 92, 92, 92, 31, 65, 23, 92, 57, 92, 75, 92, 92, 92, 43, 92, 92, 92, 92, 92, 92, 92, 92, 90, 92, 92, 56, 92, 92, 92, 92, 92, 92, 92, 40, 33, 79, 19, 92, 83, 74, 55, 92, 63, 92, 52, 92, 92, 92, 47, 92, 76, 92, 78, 92, 92, 34, 92, 92, 92, 35, 72, 92, 92, 92, 92, 58, 92, 50, 49, 92, 92, 53, 62, 92, 92, 92, 22, 92, 92, 73, 81, 92, 92, 77, 92, 68, 92, 92, 92, 92, 38, 92, 88, 67, 92, 84, 92, 92, 92, 86, 92, 59, 92, 16, 92, 70, 69, 92, 92, 82, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 71, 92, 92, 92, 92, 92, 92, 60, 0 } ; static yyconst flex_int32_t yy_ec[256] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 4, 1, 5, 6, 1, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 18, 19, 20, 21, 22, 1, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 32, 1, 1, 1, 1, 48, 1, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 49, 1, 50, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } ; static yyconst flex_int32_t yy_meta[51] = { 0, 1, 1, 1, 2, 1, 1, 3, 1, 1, 4, 1, 1, 1, 1, 1, 5, 1, 1, 1, 6, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 1, 1 } ; static yyconst flex_int16_t yy_base[409] = { 0, 0, 0, 437, 436, 438, 437, 439, 438, 441, 448, 49, 51, 448, 0, 448, 448, 448, 448, 448, 448, 448, 448, 426, 429, 41, 418, 448, 38, 448, 417, 448, 20, 33, 32, 46, 40, 44, 0, 54, 52, 399, 48, 60, 395, 65, 67, 81, 27, 411, 75, 448, 448, 0, 98, 0, 426, 0, 428, 113, 0, 448, 448, 415, 54, 410, 448, 448, 448, 448, 0, 403, 68, 399, 391, 389, 0, 402, 80, 84, 397, 383, 96, 381, 394, 379, 393, 387, 375, 379, 375, 377, 377, 0, 98, 0, 376, 97, 385, 368, 375, 0, 0, 381, 381, 364, 94, 103, 379, 98, 65, 381, 369, 109, 361, 377, 373, 351, 97, 372, 363, 115, 356, 0, 137, 138, 448, 0, 388, 0, 390, 377, 0, 0, 365, 360, 367, 365, 348, 346, 345, 350, 359, 347, 359, 95, 347, 353, 354, 336, 336, 123, 0, 334, 350, 351, 0, 338, 347, 344, 122, 124, 341, 336, 330, 340, 338, 331, 328, 336, 0, 326, 336, 334, 325, 315, 309, 322, 307, 327, 0, 313, 0, 311, 0, 325, 316, 313, 131, 309, 316, 323, 302, 304, 309, 309, 301, 304, 299, 0, 0, 311, 295, 305, 312, 292, 291, 305, 294, 307, 287, 0, 297, 279, 0, 298, 0, 295, 282, 0, 281, 276, 281, 280, 290, 0, 276, 0, 0, 0, 280, 0, 0, 276, 273, 287, 0, 272, 272, 270, 286, 271, 283, 280, 264, 282, 277, 0, 272, 272, 258, 257, 270, 256, 270, 269, 268, 0, 252, 0, 246, 0, 265, 249, 248, 0, 262, 252, 247, 246, 258, 248, 247, 0, 0, 0, 251, 0, 239, 0, 253, 249, 235, 0, 249, 250, 233, 238, 231, 249, 231, 228, 0, 229, 226, 0, 231, 243, 230, 237, 227, 235, 220, 0, 0, 0, 212, 219, 0, 0, 0, 216, 0, 230, 0, 231, 218, 217, 0, 213, 0, 216, 0, 208, 210, 0, 209, 223, 216, 0, 0, 219, 222, 204, 219, 0, 215, 0, 0, 199, 213, 0, 0, 197, 196, 201, 0, 210, 195, 0, 0, 201, 197, 0, 192, 0, 204, 204, 192, 202, 0, 179, 0, 0, 199, 0, 183, 177, 183, 0, 174, 0, 193, 0, 192, 0, 0, 183, 187, 0, 174, 174, 180, 166, 189, 181, 180, 166, 151, 118, 0, 130, 136, 127, 123, 119, 111, 0, 448, 167, 173, 179, 152, 181, 124, 187, 193, 199, 205 } ; static yyconst flex_int16_t yy_def[409] = { 0, 398, 1, 399, 399, 400, 400, 401, 401, 398, 398, 398, 398, 398, 402, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 403, 398, 398, 398, 398, 398, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 398, 398, 405, 406, 407, 398, 408, 398, 398, 402, 398, 398, 398, 398, 403, 398, 398, 398, 398, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 405, 406, 406, 398, 407, 398, 408, 398, 398, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, 0, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398 } ; static yyconst flex_int16_t yy_nxt[499] = { 0, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 38, 39, 38, 38, 40, 41, 42, 43, 44, 38, 45, 46, 47, 48, 49, 50, 38, 38, 38, 51, 52, 59, 59, 59, 59, 63, 71, 64, 67, 68, 73, 72, 77, 118, 74, 119, 78, 75, 63, 79, 64, 88, 80, 82, 85, 81, 86, 83, 89, 96, 76, 90, 93, 84, 91, 99, 87, 92, 101, 97, 94, 100, 107, 133, 110, 95, 102, 111, 103, 179, 104, 108, 109, 105, 115, 121, 112, 180, 125, 134, 113, 116, 122, 126, 114, 59, 59, 139, 117, 141, 142, 146, 163, 140, 159, 171, 173, 143, 189, 70, 147, 172, 177, 183, 164, 207, 208, 148, 190, 160, 161, 174, 193, 178, 184, 175, 194, 398, 125, 222, 214, 224, 398, 126, 215, 248, 249, 60, 397, 396, 395, 225, 394, 393, 223, 392, 391, 250, 53, 53, 53, 53, 53, 53, 55, 55, 55, 55, 55, 55, 57, 57, 57, 57, 57, 57, 65, 65, 123, 123, 123, 390, 123, 123, 124, 124, 124, 124, 124, 124, 127, 127, 389, 127, 127, 127, 129, 388, 129, 129, 129, 129, 387, 386, 385, 384, 383, 382, 381, 380, 379, 378, 377, 376, 375, 374, 373, 372, 371, 370, 369, 368, 367, 366, 365, 364, 363, 362, 361, 360, 359, 358, 357, 356, 355, 354, 353, 352, 351, 350, 349, 348, 347, 346, 345, 344, 343, 342, 341, 340, 339, 338, 337, 336, 335, 334, 333, 332, 331, 330, 329, 328, 327, 326, 325, 324, 323, 322, 321, 320, 319, 318, 317, 316, 315, 314, 313, 312, 311, 310, 309, 308, 307, 306, 305, 304, 303, 302, 301, 300, 299, 298, 297, 296, 295, 294, 293, 292, 291, 290, 289, 288, 287, 286, 285, 284, 283, 282, 281, 280, 279, 278, 277, 276, 275, 274, 273, 272, 271, 270, 269, 268, 267, 266, 265, 264, 263, 262, 261, 260, 259, 258, 257, 256, 255, 254, 253, 252, 251, 247, 246, 245, 244, 243, 242, 241, 240, 239, 238, 237, 236, 235, 234, 233, 232, 231, 230, 229, 228, 227, 226, 221, 220, 219, 218, 217, 216, 213, 212, 211, 210, 209, 206, 205, 204, 203, 202, 201, 200, 199, 198, 197, 196, 131, 130, 128, 195, 192, 191, 188, 187, 186, 185, 182, 181, 176, 170, 169, 168, 167, 166, 165, 162, 158, 157, 156, 155, 154, 153, 152, 151, 150, 149, 145, 144, 138, 137, 136, 135, 132, 398, 131, 130, 128, 120, 106, 98, 69, 66, 62, 61, 398, 58, 58, 56, 56, 54, 54, 9, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398 } ; static yyconst flex_int16_t yy_chk[499] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 11, 11, 12, 12, 25, 32, 25, 28, 28, 33, 32, 34, 48, 33, 48, 34, 33, 64, 34, 64, 37, 34, 35, 36, 34, 36, 35, 37, 40, 33, 37, 39, 35, 37, 42, 36, 37, 43, 40, 39, 42, 45, 72, 46, 39, 43, 46, 43, 110, 43, 45, 45, 43, 47, 50, 46, 110, 54, 72, 46, 47, 50, 54, 46, 59, 59, 78, 47, 79, 79, 82, 97, 78, 94, 106, 107, 79, 118, 404, 82, 106, 109, 113, 97, 145, 145, 82, 118, 94, 94, 107, 121, 109, 113, 107, 121, 124, 125, 160, 151, 161, 124, 125, 151, 188, 188, 402, 396, 395, 394, 161, 393, 392, 160, 391, 389, 188, 399, 399, 399, 399, 399, 399, 400, 400, 400, 400, 400, 400, 401, 401, 401, 401, 401, 401, 403, 403, 405, 405, 405, 388, 405, 405, 406, 406, 406, 406, 406, 406, 407, 407, 387, 407, 407, 407, 408, 386, 408, 408, 408, 408, 385, 384, 383, 382, 381, 380, 378, 377, 374, 372, 370, 368, 367, 366, 364, 361, 359, 358, 357, 356, 354, 352, 351, 348, 347, 345, 344, 343, 340, 339, 336, 334, 333, 332, 331, 328, 327, 326, 324, 323, 321, 319, 317, 316, 315, 313, 311, 307, 306, 302, 301, 300, 299, 298, 297, 296, 294, 293, 291, 290, 289, 288, 287, 286, 285, 284, 282, 281, 280, 278, 276, 272, 271, 270, 269, 268, 267, 266, 264, 263, 262, 260, 258, 256, 255, 254, 253, 252, 251, 250, 249, 248, 246, 245, 244, 243, 242, 241, 240, 239, 238, 237, 235, 234, 233, 230, 226, 224, 223, 222, 221, 220, 218, 217, 215, 213, 212, 210, 209, 208, 207, 206, 205, 204, 203, 202, 201, 198, 197, 196, 195, 194, 193, 192, 191, 190, 189, 187, 186, 185, 183, 181, 179, 178, 177, 176, 175, 174, 173, 172, 171, 169, 168, 167, 166, 165, 164, 163, 162, 159, 158, 157, 155, 154, 153, 150, 149, 148, 147, 146, 144, 143, 142, 141, 140, 139, 138, 137, 136, 135, 134, 131, 130, 128, 122, 120, 119, 117, 116, 115, 114, 112, 111, 108, 105, 104, 103, 100, 99, 98, 96, 92, 91, 90, 89, 88, 87, 86, 85, 84, 83, 81, 80, 77, 75, 74, 73, 71, 65, 63, 58, 56, 49, 44, 41, 30, 26, 24, 23, 9, 8, 7, 6, 5, 4, 3, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 398 } ; static yy_state_type yy_last_accepting_state; static char *yy_last_accepting_cpos; static int yy_flex_debug; static int yy_flex_debug = 0; /* The intent behind this definition is that it'll catch * any uses of REJECT which flex missed. */ #define REJECT reject_used_but_not_detected #define yymore() yymore_used_but_not_detected #define YY_MORE_ADJ 0 #define YY_RESTORE_YY_MORE_OFFSET static char *yytext; #line 1 "pars0lex.l" /***************************************************************************** Copyright (c) 1997, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /****************************************************** SQL parser lexical analyzer: input file for the GNU Flex lexer generator The InnoDB parser is frozen because MySQL takes care of SQL parsing. Therefore we normally keep the InnoDB parser C files as they are, and do not automatically generate them from pars0grm.y and pars0lex.l. How to make the InnoDB parser and lexer C files: 1. Run ./make_flex.sh to generate lexer files. 2. Run ./make_bison.sh to generate parser files. These instructions seem to work at least with bison-1.875d and flex-2.5.31 on Linux. Created 12/14/1997 Heikki Tuuri *******************************************************/ #define YY_NO_INPUT 1 #define YY_NO_UNISTD_H 1 #line 55 "pars0lex.l" #define YYSTYPE que_node_t* #include "univ.i" #include "pars0pars.h" #include "pars0grm.h" #include "pars0sym.h" #include "mem0mem.h" #include "os0proc.h" #define malloc(A) ut_malloc(A) #define free(A) ut_free(A) #define realloc(P, A) ut_realloc(P, A) #define exit(A) ut_error #define YY_INPUT(buf, result, max_size) pars_get_lex_chars(buf, &result, max_size) /* String buffer for removing quotes */ static ulint stringbuf_len_alloc = 0; /* Allocated length */ static ulint stringbuf_len = 0; /* Current length */ static char* stringbuf; /* Start of buffer */ /** Appends a string to the buffer. */ static void string_append( /*==========*/ const char* str, /*!< in: string to be appended */ ulint len) /*!< in: length of the string */ { if (stringbuf == NULL) { stringbuf = ut_malloc(1); stringbuf_len_alloc = 1; } if (stringbuf_len + len > stringbuf_len_alloc) { while (stringbuf_len + len > stringbuf_len_alloc) { stringbuf_len_alloc <<= 1; } stringbuf = ut_realloc(stringbuf, stringbuf_len_alloc); } memcpy(stringbuf + stringbuf_len, str, len); stringbuf_len += len; } /************************************************************************* Reset the lexing variables. */ UNIV_INTERN void pars_lexer_var_init(void) /*=====================*/ { stringbuf_len = 0; stringbuf_len_alloc = 0; if (stringbuf != NULL) { ut_free(stringbuf); stringbuf = NULL; } } #line 806 "lexyy.c" #define INITIAL 0 #define comment 1 #define quoted 2 #define id 3 #ifndef YY_NO_UNISTD_H /* Special case for "unistd.h", since it is non-ANSI. We include it way * down here because we want the user's section 1 to have been scanned first. * The user has a chance to override it with an option. */ #include #endif #define YY_EXTRA_TYPE que_node_t* static int yy_init_globals (void ); /* Accessor methods to globals. These are made visible to non-reentrant scanners for convenience. */ __attribute__((unused)) static int yylex_destroy (void ); __attribute__((unused)) static int yyget_debug (void ); __attribute__((unused)) static void yyset_debug (int debug_flag ); YY_EXTRA_TYPE yyget_extra (void ); __attribute__((unused)) static void yyset_extra (YY_EXTRA_TYPE user_defined ); __attribute__((unused)) static FILE *yyget_in (void ); __attribute__((unused)) static void yyset_in (FILE * in_str ); __attribute__((unused)) static FILE *yyget_out (void ); __attribute__((unused)) static void yyset_out (FILE * out_str ); __attribute__((unused)) static int yyget_leng (void ); __attribute__((unused)) static char *yyget_text (void ); __attribute__((unused)) static int yyget_lineno (void ); __attribute__((unused)) static void yyset_lineno (int line_number ); /* Macros after this point can all be overridden by user definitions in * section 1. */ #ifndef YY_SKIP_YYWRAP #ifdef __cplusplus extern "C" int yywrap (void ); #else extern int yywrap (void ); #endif #endif #ifndef yytext_ptr static void yy_flex_strncpy (char *,yyconst char *,int ); #endif #ifdef YY_NEED_STRLEN static int yy_flex_strlen (yyconst char * ); #endif #ifndef YY_NO_INPUT #ifdef __cplusplus static int yyinput (void ); #else static int input (void ); #endif #endif /* Amount of stuff to slurp up with each read. */ #ifndef YY_READ_BUF_SIZE #ifdef __ia64__ /* On IA-64, the buffer size is 16k, not 8k */ #define YY_READ_BUF_SIZE 16384 #else #define YY_READ_BUF_SIZE 8192 #endif /* __ia64__ */ #endif /* Copy whatever the last rule matched to the standard output. */ #ifndef ECHO /* This used to be an fputs(), but since the string might contain NUL's, * we now use fwrite(). */ #define ECHO do { if (fwrite( yytext, yyleng, 1, yyout )) {} } while (0) #endif /* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, * is returned in "result". */ #ifndef YY_INPUT #define YY_INPUT(buf,result,max_size) \ if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ { \ int c = '*'; \ size_t n; \ for ( n = 0; n < max_size && \ (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ buf[n] = (char) c; \ if ( c == '\n' ) \ buf[n++] = (char) c; \ if ( c == EOF && ferror( yyin ) ) \ YY_FATAL_ERROR( "input in flex scanner failed" ); \ result = n; \ } \ else \ { \ errno=0; \ while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \ { \ if( errno != EINTR) \ { \ YY_FATAL_ERROR( "input in flex scanner failed" ); \ break; \ } \ errno=0; \ clearerr(yyin); \ } \ }\ \ #endif /* No semi-colon after return; correct usage is to write "yyterminate();" - * we don't want an extra ';' after the "return" because that will cause * some compilers to complain about unreachable statements. */ #ifndef yyterminate #define yyterminate() return YY_NULL #endif /* Number of entries by which start-condition stack grows. */ #ifndef YY_START_STACK_INCR #define YY_START_STACK_INCR 25 #endif /* Report a fatal error. */ #ifndef YY_FATAL_ERROR #define YY_FATAL_ERROR(msg) yy_fatal_error( msg ) #endif /* end tables serialization structures and prototypes */ /* Default declaration of generated scanner - a define so the user can * easily add parameters. */ #ifndef YY_DECL #define YY_DECL_IS_OURS 1 UNIV_INTERN int yylex (void); #define YY_DECL UNIV_INTERN int yylex (void) #endif /* !YY_DECL */ /* Code executed at the beginning of each rule, after yytext and yyleng * have been set up. */ #ifndef YY_USER_ACTION #define YY_USER_ACTION #endif /* Code executed at the end of each rule. */ #ifndef YY_BREAK #define YY_BREAK break; #endif #define YY_RULE_SETUP \ YY_USER_ACTION /** The main scanner function which does all the work. */ YY_DECL { register yy_state_type yy_current_state; register char *yy_cp, *yy_bp; register int yy_act; #line 125 "pars0lex.l" #line 995 "lexyy.c" if ( !(yy_init) ) { (yy_init) = 1; #ifdef YY_USER_INIT YY_USER_INIT; #endif if ( ! (yy_start) ) (yy_start) = 1; /* first start state */ if ( ! yyin ) yyin = stdin; if ( ! yyout ) yyout = stdout; if ( ! YY_CURRENT_BUFFER ) { yyensure_buffer_stack (); YY_CURRENT_BUFFER_LVALUE = yy_create_buffer(yyin,YY_BUF_SIZE ); } yy_load_buffer_state( ); } while ( 1 ) /* loops until end-of-file is reached */ { yy_cp = (yy_c_buf_p); /* Support of yytext. */ *yy_cp = (yy_hold_char); /* yy_bp points to the position in yy_ch_buf of the start of * the current run. */ yy_bp = yy_cp; yy_current_state = (yy_start); yy_match: do { register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)]; if ( yy_accept[yy_current_state] ) { (yy_last_accepting_state) = yy_current_state; (yy_last_accepting_cpos) = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 399 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; ++yy_cp; } while ( yy_current_state != 398 ); yy_cp = (yy_last_accepting_cpos); yy_current_state = (yy_last_accepting_state); yy_find_action: yy_act = yy_accept[yy_current_state]; YY_DO_BEFORE_ACTION; do_action: /* This label is used only to access EOF actions. */ switch ( yy_act ) { /* beginning of action switch */ case 0: /* must back up */ /* undo the effects of YY_DO_BEFORE_ACTION */ *yy_cp = (yy_hold_char); yy_cp = (yy_last_accepting_cpos); yy_current_state = (yy_last_accepting_state); goto yy_find_action; case 1: YY_RULE_SETUP #line 127 "pars0lex.l" { yylval = sym_tab_add_int_lit(pars_sym_tab_global, atoi(yytext)); return(PARS_INT_LIT); } YY_BREAK case 2: YY_RULE_SETUP #line 133 "pars0lex.l" { ut_error; /* not implemented */ return(PARS_FLOAT_LIT); } YY_BREAK case 3: YY_RULE_SETUP #line 139 "pars0lex.l" { ulint type; yylval = sym_tab_add_bound_lit(pars_sym_tab_global, yytext + 1, &type); return((int) type); } YY_BREAK case 4: YY_RULE_SETUP #line 148 "pars0lex.l" { yylval = sym_tab_add_bound_id(pars_sym_tab_global, yytext + 1); return(PARS_ID_TOKEN); } YY_BREAK case 5: YY_RULE_SETUP #line 155 "pars0lex.l" { /* Quoted character string literals are handled in an explicit start state 'quoted'. This state is entered and the buffer for the scanned string is emptied upon encountering a starting quote. In the state 'quoted', only two actions are possible (defined below). */ BEGIN(quoted); stringbuf_len = 0; } YY_BREAK case 6: /* rule 6 can match eol */ YY_RULE_SETUP #line 164 "pars0lex.l" { /* Got a sequence of characters other than "'": append to string buffer */ string_append(yytext, yyleng); } YY_BREAK case 7: YY_RULE_SETUP #line 169 "pars0lex.l" { /* Got a sequence of "'" characters: append half of them to string buffer, as "''" represents a single "'". We apply truncating division, so that "'''" will result in "'". */ string_append(yytext, yyleng / 2); /* If we got an odd number of quotes, then the last quote we got is the terminating quote. At the end of the string, we return to the initial start state and report the scanned string literal. */ if (yyleng % 2) { BEGIN(INITIAL); yylval = sym_tab_add_str_lit( pars_sym_tab_global, (byte*) stringbuf, stringbuf_len); return(PARS_STR_LIT); } } YY_BREAK case 8: YY_RULE_SETUP #line 193 "pars0lex.l" { /* Quoted identifiers are handled in an explicit start state 'id'. This state is entered and the buffer for the scanned string is emptied upon encountering a starting quote. In the state 'id', only two actions are possible (defined below). */ BEGIN(id); stringbuf_len = 0; } YY_BREAK case 9: /* rule 9 can match eol */ YY_RULE_SETUP #line 202 "pars0lex.l" { /* Got a sequence of characters other than '"': append to string buffer */ string_append(yytext, yyleng); } YY_BREAK case 10: YY_RULE_SETUP #line 207 "pars0lex.l" { /* Got a sequence of '"' characters: append half of them to string buffer, as '""' represents a single '"'. We apply truncating division, so that '"""' will result in '"'. */ string_append(yytext, yyleng / 2); /* If we got an odd number of quotes, then the last quote we got is the terminating quote. At the end of the string, we return to the initial start state and report the scanned identifier. */ if (yyleng % 2) { BEGIN(INITIAL); yylval = sym_tab_add_id( pars_sym_tab_global, (byte*) stringbuf, stringbuf_len); return(PARS_ID_TOKEN); } } YY_BREAK case 11: YY_RULE_SETUP #line 232 "pars0lex.l" { yylval = sym_tab_add_null_lit(pars_sym_tab_global); return(PARS_NULL_LIT); } YY_BREAK case 12: YY_RULE_SETUP #line 238 "pars0lex.l" { /* Implicit cursor name */ yylval = sym_tab_add_str_lit(pars_sym_tab_global, (byte*) yytext, yyleng); return(PARS_SQL_TOKEN); } YY_BREAK case 13: YY_RULE_SETUP #line 245 "pars0lex.l" { return(PARS_AND_TOKEN); } YY_BREAK case 14: YY_RULE_SETUP #line 249 "pars0lex.l" { return(PARS_OR_TOKEN); } YY_BREAK case 15: YY_RULE_SETUP #line 253 "pars0lex.l" { return(PARS_NOT_TOKEN); } YY_BREAK case 16: YY_RULE_SETUP #line 257 "pars0lex.l" { return(PARS_PROCEDURE_TOKEN); } YY_BREAK case 17: YY_RULE_SETUP #line 261 "pars0lex.l" { return(PARS_IN_TOKEN); } YY_BREAK case 18: YY_RULE_SETUP #line 265 "pars0lex.l" { return(PARS_OUT_TOKEN); } YY_BREAK case 19: YY_RULE_SETUP #line 269 "pars0lex.l" { return(PARS_BINARY_TOKEN); } YY_BREAK case 20: YY_RULE_SETUP #line 273 "pars0lex.l" { return(PARS_BLOB_TOKEN); } YY_BREAK case 21: YY_RULE_SETUP #line 277 "pars0lex.l" { return(PARS_INT_TOKEN); } YY_BREAK case 22: YY_RULE_SETUP #line 281 "pars0lex.l" { return(PARS_INT_TOKEN); } YY_BREAK case 23: YY_RULE_SETUP #line 285 "pars0lex.l" { return(PARS_FLOAT_TOKEN); } YY_BREAK case 24: YY_RULE_SETUP #line 289 "pars0lex.l" { return(PARS_CHAR_TOKEN); } YY_BREAK case 25: YY_RULE_SETUP #line 293 "pars0lex.l" { return(PARS_IS_TOKEN); } YY_BREAK case 26: YY_RULE_SETUP #line 297 "pars0lex.l" { return(PARS_BEGIN_TOKEN); } YY_BREAK case 27: YY_RULE_SETUP #line 301 "pars0lex.l" { return(PARS_END_TOKEN); } YY_BREAK case 28: YY_RULE_SETUP #line 305 "pars0lex.l" { return(PARS_IF_TOKEN); } YY_BREAK case 29: YY_RULE_SETUP #line 309 "pars0lex.l" { return(PARS_THEN_TOKEN); } YY_BREAK case 30: YY_RULE_SETUP #line 313 "pars0lex.l" { return(PARS_ELSE_TOKEN); } YY_BREAK case 31: YY_RULE_SETUP #line 317 "pars0lex.l" { return(PARS_ELSIF_TOKEN); } YY_BREAK case 32: YY_RULE_SETUP #line 321 "pars0lex.l" { return(PARS_LOOP_TOKEN); } YY_BREAK case 33: YY_RULE_SETUP #line 325 "pars0lex.l" { return(PARS_WHILE_TOKEN); } YY_BREAK case 34: YY_RULE_SETUP #line 329 "pars0lex.l" { return(PARS_RETURN_TOKEN); } YY_BREAK case 35: YY_RULE_SETUP #line 333 "pars0lex.l" { return(PARS_SELECT_TOKEN); } YY_BREAK case 36: YY_RULE_SETUP #line 337 "pars0lex.l" { return(PARS_SUM_TOKEN); } YY_BREAK case 37: YY_RULE_SETUP #line 341 "pars0lex.l" { return(PARS_COUNT_TOKEN); } YY_BREAK case 38: YY_RULE_SETUP #line 345 "pars0lex.l" { return(PARS_DISTINCT_TOKEN); } YY_BREAK case 39: YY_RULE_SETUP #line 349 "pars0lex.l" { return(PARS_FROM_TOKEN); } YY_BREAK case 40: YY_RULE_SETUP #line 353 "pars0lex.l" { return(PARS_WHERE_TOKEN); } YY_BREAK case 41: YY_RULE_SETUP #line 357 "pars0lex.l" { return(PARS_FOR_TOKEN); } YY_BREAK case 42: YY_RULE_SETUP #line 361 "pars0lex.l" { return(PARS_READ_TOKEN); } YY_BREAK case 43: YY_RULE_SETUP #line 365 "pars0lex.l" { return(PARS_ORDER_TOKEN); } YY_BREAK case 44: YY_RULE_SETUP #line 369 "pars0lex.l" { return(PARS_BY_TOKEN); } YY_BREAK case 45: YY_RULE_SETUP #line 373 "pars0lex.l" { return(PARS_ASC_TOKEN); } YY_BREAK case 46: YY_RULE_SETUP #line 377 "pars0lex.l" { return(PARS_DESC_TOKEN); } YY_BREAK case 47: YY_RULE_SETUP #line 381 "pars0lex.l" { return(PARS_INSERT_TOKEN); } YY_BREAK case 48: YY_RULE_SETUP #line 385 "pars0lex.l" { return(PARS_INTO_TOKEN); } YY_BREAK case 49: YY_RULE_SETUP #line 389 "pars0lex.l" { return(PARS_VALUES_TOKEN); } YY_BREAK case 50: YY_RULE_SETUP #line 393 "pars0lex.l" { return(PARS_UPDATE_TOKEN); } YY_BREAK case 51: YY_RULE_SETUP #line 397 "pars0lex.l" { return(PARS_SET_TOKEN); } YY_BREAK case 52: YY_RULE_SETUP #line 401 "pars0lex.l" { return(PARS_DELETE_TOKEN); } YY_BREAK case 53: YY_RULE_SETUP #line 405 "pars0lex.l" { return(PARS_CURRENT_TOKEN); } YY_BREAK case 54: YY_RULE_SETUP #line 409 "pars0lex.l" { return(PARS_OF_TOKEN); } YY_BREAK case 55: YY_RULE_SETUP #line 413 "pars0lex.l" { return(PARS_CREATE_TOKEN); } YY_BREAK case 56: YY_RULE_SETUP #line 417 "pars0lex.l" { return(PARS_TABLE_TOKEN); } YY_BREAK case 57: YY_RULE_SETUP #line 421 "pars0lex.l" { return(PARS_INDEX_TOKEN); } YY_BREAK case 58: YY_RULE_SETUP #line 425 "pars0lex.l" { return(PARS_UNIQUE_TOKEN); } YY_BREAK case 59: YY_RULE_SETUP #line 429 "pars0lex.l" { return(PARS_CLUSTERED_TOKEN); } YY_BREAK case 60: YY_RULE_SETUP #line 433 "pars0lex.l" { return(PARS_DOES_NOT_FIT_IN_MEM_TOKEN); } YY_BREAK case 61: YY_RULE_SETUP #line 437 "pars0lex.l" { return(PARS_ON_TOKEN); } YY_BREAK case 62: YY_RULE_SETUP #line 441 "pars0lex.l" { return(PARS_DECLARE_TOKEN); } YY_BREAK case 63: YY_RULE_SETUP #line 445 "pars0lex.l" { return(PARS_CURSOR_TOKEN); } YY_BREAK case 64: YY_RULE_SETUP #line 449 "pars0lex.l" { return(PARS_OPEN_TOKEN); } YY_BREAK case 65: YY_RULE_SETUP #line 453 "pars0lex.l" { return(PARS_FETCH_TOKEN); } YY_BREAK case 66: YY_RULE_SETUP #line 457 "pars0lex.l" { return(PARS_CLOSE_TOKEN); } YY_BREAK case 67: YY_RULE_SETUP #line 461 "pars0lex.l" { return(PARS_NOTFOUND_TOKEN); } YY_BREAK case 68: YY_RULE_SETUP #line 465 "pars0lex.l" { return(PARS_TO_CHAR_TOKEN); } YY_BREAK case 69: YY_RULE_SETUP #line 469 "pars0lex.l" { return(PARS_TO_NUMBER_TOKEN); } YY_BREAK case 70: YY_RULE_SETUP #line 473 "pars0lex.l" { return(PARS_TO_BINARY_TOKEN); } YY_BREAK case 71: YY_RULE_SETUP #line 477 "pars0lex.l" { return(PARS_BINARY_TO_NUMBER_TOKEN); } YY_BREAK case 72: YY_RULE_SETUP #line 481 "pars0lex.l" { return(PARS_SUBSTR_TOKEN); } YY_BREAK case 73: YY_RULE_SETUP #line 485 "pars0lex.l" { return(PARS_REPLSTR_TOKEN); } YY_BREAK case 74: YY_RULE_SETUP #line 489 "pars0lex.l" { return(PARS_CONCAT_TOKEN); } YY_BREAK case 75: YY_RULE_SETUP #line 493 "pars0lex.l" { return(PARS_INSTR_TOKEN); } YY_BREAK case 76: YY_RULE_SETUP #line 497 "pars0lex.l" { return(PARS_LENGTH_TOKEN); } YY_BREAK case 77: YY_RULE_SETUP #line 501 "pars0lex.l" { return(PARS_SYSDATE_TOKEN); } YY_BREAK case 78: YY_RULE_SETUP #line 505 "pars0lex.l" { return(PARS_PRINTF_TOKEN); } YY_BREAK case 79: YY_RULE_SETUP #line 509 "pars0lex.l" { return(PARS_ASSERT_TOKEN); } YY_BREAK case 80: YY_RULE_SETUP #line 513 "pars0lex.l" { return(PARS_RND_TOKEN); } YY_BREAK case 81: YY_RULE_SETUP #line 517 "pars0lex.l" { return(PARS_RND_STR_TOKEN); } YY_BREAK case 82: YY_RULE_SETUP #line 521 "pars0lex.l" { return(PARS_ROW_PRINTF_TOKEN); } YY_BREAK case 83: YY_RULE_SETUP #line 525 "pars0lex.l" { return(PARS_COMMIT_TOKEN); } YY_BREAK case 84: YY_RULE_SETUP #line 529 "pars0lex.l" { return(PARS_ROLLBACK_TOKEN); } YY_BREAK case 85: YY_RULE_SETUP #line 533 "pars0lex.l" { return(PARS_WORK_TOKEN); } YY_BREAK case 86: YY_RULE_SETUP #line 537 "pars0lex.l" { return(PARS_UNSIGNED_TOKEN); } YY_BREAK case 87: YY_RULE_SETUP #line 541 "pars0lex.l" { return(PARS_EXIT_TOKEN); } YY_BREAK case 88: YY_RULE_SETUP #line 545 "pars0lex.l" { return(PARS_FUNCTION_TOKEN); } YY_BREAK case 89: YY_RULE_SETUP #line 549 "pars0lex.l" { return(PARS_LOCK_TOKEN); } YY_BREAK case 90: YY_RULE_SETUP #line 553 "pars0lex.l" { return(PARS_SHARE_TOKEN); } YY_BREAK case 91: YY_RULE_SETUP #line 557 "pars0lex.l" { return(PARS_MODE_TOKEN); } YY_BREAK case 92: YY_RULE_SETUP #line 561 "pars0lex.l" { yylval = sym_tab_add_id(pars_sym_tab_global, (byte*)yytext, ut_strlen(yytext)); return(PARS_ID_TOKEN); } YY_BREAK case 93: YY_RULE_SETUP #line 568 "pars0lex.l" { return(PARS_DDOT_TOKEN); } YY_BREAK case 94: YY_RULE_SETUP #line 572 "pars0lex.l" { return(PARS_ASSIGN_TOKEN); } YY_BREAK case 95: YY_RULE_SETUP #line 576 "pars0lex.l" { return(PARS_LE_TOKEN); } YY_BREAK case 96: YY_RULE_SETUP #line 580 "pars0lex.l" { return(PARS_GE_TOKEN); } YY_BREAK case 97: YY_RULE_SETUP #line 584 "pars0lex.l" { return(PARS_NE_TOKEN); } YY_BREAK case 98: YY_RULE_SETUP #line 588 "pars0lex.l" { return((int)(*yytext)); } YY_BREAK case 99: YY_RULE_SETUP #line 593 "pars0lex.l" { return((int)(*yytext)); } YY_BREAK case 100: YY_RULE_SETUP #line 598 "pars0lex.l" { return((int)(*yytext)); } YY_BREAK case 101: YY_RULE_SETUP #line 603 "pars0lex.l" { return((int)(*yytext)); } YY_BREAK case 102: YY_RULE_SETUP #line 608 "pars0lex.l" { return((int)(*yytext)); } YY_BREAK case 103: YY_RULE_SETUP #line 613 "pars0lex.l" { return((int)(*yytext)); } YY_BREAK case 104: YY_RULE_SETUP #line 618 "pars0lex.l" { return((int)(*yytext)); } YY_BREAK case 105: YY_RULE_SETUP #line 623 "pars0lex.l" { return((int)(*yytext)); } YY_BREAK case 106: YY_RULE_SETUP #line 628 "pars0lex.l" { return((int)(*yytext)); } YY_BREAK case 107: YY_RULE_SETUP #line 633 "pars0lex.l" { return((int)(*yytext)); } YY_BREAK case 108: YY_RULE_SETUP #line 638 "pars0lex.l" { return((int)(*yytext)); } YY_BREAK case 109: YY_RULE_SETUP #line 643 "pars0lex.l" { return((int)(*yytext)); } YY_BREAK case 110: YY_RULE_SETUP #line 648 "pars0lex.l" { return((int)(*yytext)); } YY_BREAK case 111: YY_RULE_SETUP #line 653 "pars0lex.l" { return((int)(*yytext)); } YY_BREAK case 112: YY_RULE_SETUP #line 658 "pars0lex.l" { return((int)(*yytext)); } YY_BREAK case 113: YY_RULE_SETUP #line 663 "pars0lex.l" BEGIN(comment); /* eat up comment */ YY_BREAK case 114: /* rule 114 can match eol */ YY_RULE_SETUP #line 665 "pars0lex.l" YY_BREAK case 115: /* rule 115 can match eol */ YY_RULE_SETUP #line 666 "pars0lex.l" YY_BREAK case 116: YY_RULE_SETUP #line 667 "pars0lex.l" BEGIN(INITIAL); YY_BREAK case 117: /* rule 117 can match eol */ YY_RULE_SETUP #line 669 "pars0lex.l" /* eat up whitespace */ YY_BREAK case 118: YY_RULE_SETUP #line 672 "pars0lex.l" { ib_logger(ib_stream,"Unrecognized character: %02x\n", *yytext); ut_error; return(0); } YY_BREAK case 119: YY_RULE_SETUP #line 681 "pars0lex.l" YY_FATAL_ERROR( "flex scanner jammed" ); YY_BREAK #line 1997 "lexyy.c" case YY_STATE_EOF(INITIAL): case YY_STATE_EOF(comment): case YY_STATE_EOF(quoted): case YY_STATE_EOF(id): yyterminate(); case YY_END_OF_BUFFER: { /* Amount of text matched not including the EOB char. */ int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1; /* Undo the effects of YY_DO_BEFORE_ACTION. */ *yy_cp = (yy_hold_char); YY_RESTORE_YY_MORE_OFFSET if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) { /* We're scanning a new file or input source. It's * possible that this happened because the user * just pointed yyin at a new source and called * yylex(). If so, then we have to assure * consistency between YY_CURRENT_BUFFER and our * globals. Here is the right place to do so, because * this is the first action (other than possibly a * back-up) that will match for the new input source. */ (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; } /* Note that here we test for yy_c_buf_p "<=" to the position * of the first EOB in the buffer, since yy_c_buf_p will * already have been incremented past the NUL character * (since all states make transitions on EOB to the * end-of-buffer state). Contrast this with the test * in input(). */ if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) { /* This was really a NUL. */ yy_state_type yy_next_state; (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state( ); /* Okay, we're now positioned to make the NUL * transition. We couldn't have * yy_get_previous_state() go ahead and do it * for us because it doesn't know how to deal * with the possibility of jamming (and we don't * want to build jamming into it because then it * will run more slowly). */ yy_next_state = yy_try_NUL_trans( yy_current_state ); yy_bp = (yytext_ptr) + YY_MORE_ADJ; if ( yy_next_state ) { /* Consume the NUL. */ yy_cp = ++(yy_c_buf_p); yy_current_state = yy_next_state; goto yy_match; } else { yy_cp = (yy_last_accepting_cpos); yy_current_state = (yy_last_accepting_state); goto yy_find_action; } } else switch ( yy_get_next_buffer( ) ) { case EOB_ACT_END_OF_FILE: { (yy_did_buffer_switch_on_eof) = 0; if ( yywrap( ) ) { /* Note: because we've taken care in * yy_get_next_buffer() to have set up * yytext, we can now set up * yy_c_buf_p so that if some total * hoser (like flex itself) wants to * call the scanner after we return the * YY_NULL, it'll still work - another * YY_NULL will get returned. */ (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ; yy_act = YY_STATE_EOF(YY_START); goto do_action; } else { if ( ! (yy_did_buffer_switch_on_eof) ) YY_NEW_FILE; } break; } case EOB_ACT_CONTINUE_SCAN: (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state( ); yy_cp = (yy_c_buf_p); yy_bp = (yytext_ptr) + YY_MORE_ADJ; goto yy_match; case EOB_ACT_LAST_MATCH: (yy_c_buf_p) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)]; yy_current_state = yy_get_previous_state( ); yy_cp = (yy_c_buf_p); yy_bp = (yytext_ptr) + YY_MORE_ADJ; goto yy_find_action; } break; } default: YY_FATAL_ERROR( "fatal flex scanner internal error--no action found" ); } /* end of action switch */ } /* end of scanning one token */ } /* end of yylex */ /* yy_get_next_buffer - try to read in a new buffer * * Returns a code representing an action: * EOB_ACT_LAST_MATCH - * EOB_ACT_CONTINUE_SCAN - continue scanning from current position * EOB_ACT_END_OF_FILE - end of file */ static int yy_get_next_buffer (void) { register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; register char *source = (yytext_ptr); register int number_to_move, i; int ret_val; if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] ) YY_FATAL_ERROR( "fatal flex scanner internal error--end of buffer missed" ); if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) { /* Don't try to fill the buffer, so this is an EOF. */ if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 ) { /* We matched a single character, the EOB, so * treat this as a final EOF. */ return EOB_ACT_END_OF_FILE; } else { /* We matched some text prior to the EOB, first * process it. */ return EOB_ACT_LAST_MATCH; } } /* Try to read more data. */ /* First move last chars to start of buffer. */ number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1; for ( i = 0; i < number_to_move; ++i ) *(dest++) = *(source++); if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) /* don't do the read, it's not guaranteed to return an EOF, * just force an EOF */ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0; else { int num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; while ( num_to_read <= 0 ) { /* Not enough room in the buffer - grow it. */ /* just a shorter name for the current buffer */ YY_BUFFER_STATE b = YY_CURRENT_BUFFER; int yy_c_buf_p_offset = (int) ((yy_c_buf_p) - b->yy_ch_buf); if ( b->yy_is_our_buffer ) { int new_size = b->yy_buf_size * 2; if ( new_size <= 0 ) b->yy_buf_size += b->yy_buf_size / 8; else b->yy_buf_size *= 2; b->yy_ch_buf = (char *) /* Include room in for 2 EOB chars. */ yyrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ); } else /* Can't grow it, we don't own it. */ b->yy_ch_buf = 0; if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "fatal error - scanner input buffer overflow" ); (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset]; num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; } if ( num_to_read > YY_READ_BUF_SIZE ) num_to_read = YY_READ_BUF_SIZE; /* Read in more data. */ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), (yy_n_chars), (size_t) num_to_read ); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); } if ( (yy_n_chars) == 0 ) { if ( number_to_move == YY_MORE_ADJ ) { ret_val = EOB_ACT_END_OF_FILE; yyrestart(yyin ); } else { ret_val = EOB_ACT_LAST_MATCH; YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_EOF_PENDING; } } else ret_val = EOB_ACT_CONTINUE_SCAN; if ((yy_size_t) ((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { /* Extend the array by 50%, plus the number we really need. */ yy_size_t new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1); YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ); if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); } (yy_n_chars) += number_to_move; YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR; YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR; (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; return ret_val; } /* yy_get_previous_state - get the state just before the EOB char was reached */ static yy_state_type yy_get_previous_state (void) { register yy_state_type yy_current_state; register char *yy_cp; yy_current_state = (yy_start); for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp ) { register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); if ( yy_accept[yy_current_state] ) { (yy_last_accepting_state) = yy_current_state; (yy_last_accepting_cpos) = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 399 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; } return yy_current_state; } /* yy_try_NUL_trans - try to make a transition on the NUL character * * synopsis * next_state = yy_try_NUL_trans( current_state ); */ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state ) { register int yy_is_jam; register char *yy_cp = (yy_c_buf_p); register YY_CHAR yy_c = 1; if ( yy_accept[yy_current_state] ) { (yy_last_accepting_state) = yy_current_state; (yy_last_accepting_cpos) = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 399 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; yy_is_jam = (yy_current_state == 398); return yy_is_jam ? 0 : yy_current_state; } #ifndef YY_NO_INPUT #ifdef __cplusplus static int yyinput (void) #else static int input (void) #endif { int c; *(yy_c_buf_p) = (yy_hold_char); if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR ) { /* yy_c_buf_p now points to the character we want to return. * If this occurs *before* the EOB characters, then it's a * valid NUL; if not, then we've hit the end of the buffer. */ if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) /* This was really a NUL. */ *(yy_c_buf_p) = '\0'; else { /* need more input */ int offset = (int)((yy_c_buf_p) - (yytext_ptr)); ++(yy_c_buf_p); switch ( yy_get_next_buffer( ) ) { case EOB_ACT_LAST_MATCH: /* This happens because yy_g_n_b() * sees that we've accumulated a * token and flags that we need to * try matching the token before * proceeding. But for input(), * there's no matching to consider. * So convert the EOB_ACT_LAST_MATCH * to EOB_ACT_END_OF_FILE. */ /* Reset buffer status. */ yyrestart(yyin ); /*FALLTHROUGH*/ case EOB_ACT_END_OF_FILE: { if ( yywrap( ) ) return EOF; if ( ! (yy_did_buffer_switch_on_eof) ) YY_NEW_FILE; #ifdef __cplusplus return yyinput(); #else return input(); #endif } case EOB_ACT_CONTINUE_SCAN: (yy_c_buf_p) = (yytext_ptr) + offset; break; } } } c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */ *(yy_c_buf_p) = '\0'; /* preserve yytext */ (yy_hold_char) = *++(yy_c_buf_p); return c; } #endif /* ifndef YY_NO_INPUT */ /** Immediately switch to a different input stream. * @param input_file A readable stream. * * @note This function does not reset the start condition to @c INITIAL . */ static void yyrestart (FILE * input_file ) { if ( ! YY_CURRENT_BUFFER ){ yyensure_buffer_stack (); YY_CURRENT_BUFFER_LVALUE = yy_create_buffer(yyin,YY_BUF_SIZE ); } yy_init_buffer(YY_CURRENT_BUFFER,input_file ); yy_load_buffer_state( ); } /** Switch to a different input buffer. * @param new_buffer The new input buffer. * */ __attribute__((unused)) static void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ) { /* TODO. We should be able to replace this entire function body * with * yypop_buffer_state(); * yypush_buffer_state(new_buffer); */ yyensure_buffer_stack (); if ( YY_CURRENT_BUFFER == new_buffer ) return; if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ *(yy_c_buf_p) = (yy_hold_char); YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); } YY_CURRENT_BUFFER_LVALUE = new_buffer; yy_load_buffer_state( ); /* We don't actually know whether we did this switch during * EOF (yywrap()) processing, but the only time this flag * is looked at is after yywrap() is called, so it's safe * to go ahead and always set it. */ (yy_did_buffer_switch_on_eof) = 1; } static void yy_load_buffer_state (void) { (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; (yy_hold_char) = *(yy_c_buf_p); } /** Allocate and initialize an input buffer state. * @param file A readable stream. * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. * * @return the allocated buffer state. */ static YY_BUFFER_STATE yy_create_buffer (FILE * file, int size ) { YY_BUFFER_STATE b; b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) ); if ( ! b ) YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); b->yy_buf_size = size; /* yy_ch_buf has to be 2 characters longer than the size given because * we need to put in 2 end-of-buffer characters. */ b->yy_ch_buf = (char *) yyalloc(b->yy_buf_size + 2 ); if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); b->yy_is_our_buffer = 1; yy_init_buffer(b,file ); return b; } /** Destroy the buffer. * @param b a buffer created with yy_create_buffer() * */ static void yy_delete_buffer (YY_BUFFER_STATE b ) { if ( ! b ) return; if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; if ( b->yy_is_our_buffer ) yyfree((void *) b->yy_ch_buf ); yyfree((void *) b ); } /* Initializes or reinitializes a buffer. * This function is sometimes called more than once on the same buffer, * such as during a yyrestart() or at EOF. */ static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file ) { int oerrno = errno; yy_flush_buffer(b ); b->yy_input_file = file; b->yy_fill_buffer = 1; /* If b is the current buffer, then yy_init_buffer was _probably_ * called from yyrestart() or through yy_get_next_buffer. * In that case, we don't want to reset the lineno or column. */ if (b != YY_CURRENT_BUFFER){ b->yy_bs_lineno = 1; b->yy_bs_column = 0; } b->yy_is_interactive = 0; errno = oerrno; } /** Discard all buffered characters. On the next scan, YY_INPUT will be called. * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. * */ static void yy_flush_buffer (YY_BUFFER_STATE b ) { if ( ! b ) return; b->yy_n_chars = 0; /* We always need two end-of-buffer characters. The first causes * a transition to the end-of-buffer state. The second causes * a jam in that state. */ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; b->yy_buf_pos = &b->yy_ch_buf[0]; b->yy_at_bol = 1; b->yy_buffer_status = YY_BUFFER_NEW; if ( b == YY_CURRENT_BUFFER ) yy_load_buffer_state( ); } /** Pushes the new state onto the stack. The new state becomes * the current state. This function will allocate the stack * if necessary. * @param new_buffer The new state. * */ __attribute__((unused)) static void yypush_buffer_state (YY_BUFFER_STATE new_buffer ) { if (new_buffer == NULL) return; yyensure_buffer_stack(); /* This block is copied from yy_switch_to_buffer. */ if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ *(yy_c_buf_p) = (yy_hold_char); YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); } /* Only push if top exists. Otherwise, replace top. */ if (YY_CURRENT_BUFFER) (yy_buffer_stack_top)++; YY_CURRENT_BUFFER_LVALUE = new_buffer; /* copied from yy_switch_to_buffer. */ yy_load_buffer_state( ); (yy_did_buffer_switch_on_eof) = 1; } /** Removes and deletes the top of the stack, if present. * The next element becomes the new top. * */ __attribute__((unused)) static void yypop_buffer_state (void) { if (!YY_CURRENT_BUFFER) return; yy_delete_buffer(YY_CURRENT_BUFFER ); YY_CURRENT_BUFFER_LVALUE = NULL; if ((yy_buffer_stack_top) > 0) --(yy_buffer_stack_top); if (YY_CURRENT_BUFFER) { yy_load_buffer_state( ); (yy_did_buffer_switch_on_eof) = 1; } } /* Allocates the stack if it does not exist. * Guarantees space for at least one push. */ static void yyensure_buffer_stack (void) { int num_to_alloc; if (!(yy_buffer_stack)) { /* First allocation is just for 2 elements, since we don't know if this * scanner will even need a stack. We use 2 instead of 1 to avoid an * immediate realloc on the next call. */ num_to_alloc = 1; (yy_buffer_stack) = (struct yy_buffer_state**)yyalloc (num_to_alloc * sizeof(struct yy_buffer_state*) ); if ( ! (yy_buffer_stack) ) YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*)); (yy_buffer_stack_max) = num_to_alloc; (yy_buffer_stack_top) = 0; return; } if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){ /* Increase the buffer to prepare for a possible push. */ int grow_size = 8 /* arbitrary grow size */; num_to_alloc = (yy_buffer_stack_max) + grow_size; (yy_buffer_stack) = (struct yy_buffer_state**)yyrealloc ((yy_buffer_stack), num_to_alloc * sizeof(struct yy_buffer_state*) ); if ( ! (yy_buffer_stack) ) YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); /* zero only the new slots.*/ memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*)); (yy_buffer_stack_max) = num_to_alloc; } } #ifndef YY_EXIT_FAILURE #define YY_EXIT_FAILURE 2 #endif static void yy_fatal_error (yyconst char* msg ) { (void) ib_logger(ib_stream, "%s\n", msg ); exit( YY_EXIT_FAILURE ); } /* Redefine yyless() so it works in section 3 code. */ #undef yyless #define yyless(n) \ do \ { \ /* Undo effects of setting up yytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ yytext[yyleng] = (yy_hold_char); \ (yy_c_buf_p) = yytext + yyless_macro_arg; \ (yy_hold_char) = *(yy_c_buf_p); \ *(yy_c_buf_p) = '\0'; \ yyleng = yyless_macro_arg; \ } \ while ( 0 ) /* Accessor methods (get/set functions) to struct members. */ /** Get the current line number. * */ __attribute__((unused)) static int yyget_lineno (void) { return yylineno; } /** Get the input stream. * */ __attribute__((unused)) static FILE *yyget_in (void) { return yyin; } /** Get the output stream. * */ __attribute__((unused)) static FILE *yyget_out (void) { return yyout; } /** Get the length of the current token. * */ __attribute__((unused)) static int yyget_leng (void) { return yyleng; } /** Get the current token. * */ __attribute__((unused)) static char *yyget_text (void) { return yytext; } /** Set the current line number. * @param line_number * */ __attribute__((unused)) static void yyset_lineno (int line_number ) { yylineno = line_number; } /** Set the input stream. This does not discard the current * input buffer. * @param in_str A readable stream. * * @see yy_switch_to_buffer */ __attribute__((unused)) static void yyset_in (FILE * in_str ) { yyin = in_str ; } __attribute__((unused)) static void yyset_out (FILE * out_str ) { yyout = out_str ; } __attribute__((unused)) static int yyget_debug (void) { return yy_flex_debug; } __attribute__((unused)) static void yyset_debug (int bdebug ) { yy_flex_debug = bdebug ; } static int yy_init_globals (void) { /* Initialization is the same as for the non-reentrant scanner. * This function is called from yylex_destroy(), so don't allocate here. */ (yy_buffer_stack) = 0; (yy_buffer_stack_top) = 0; (yy_buffer_stack_max) = 0; (yy_c_buf_p) = (char *) 0; (yy_init) = 0; (yy_start) = 0; /* Defined in main.c */ #ifdef YY_STDINIT yyin = stdin; yyout = stdout; #else yyin = (FILE *) 0; yyout = (FILE *) 0; #endif /* For future reference: Set errno on error, since we are called by * yylex_init() */ return 0; } /* yylex_destroy is for both reentrant and non-reentrant scanners. */ __attribute__((unused)) static int yylex_destroy (void) { /* Pop the buffer stack, destroying each element. */ while(YY_CURRENT_BUFFER){ yy_delete_buffer(YY_CURRENT_BUFFER ); YY_CURRENT_BUFFER_LVALUE = NULL; yypop_buffer_state(); } /* Destroy the stack itself. */ yyfree((yy_buffer_stack) ); (yy_buffer_stack) = NULL; /* Reset the globals. This is important in a non-reentrant scanner so the next time * yylex() is called, initialization will occur. */ yy_init_globals( ); return 0; } /* * Internal utility routines. */ #ifndef yytext_ptr static void yy_flex_strncpy (char* s1, yyconst char * s2, int n ) { register int i; for ( i = 0; i < n; ++i ) s1[i] = s2[i]; } #endif #ifdef YY_NEED_STRLEN static int yy_flex_strlen (yyconst char * s ) { register int n; for ( n = 0; s[n]; ++n ) ; return n; } #endif static void *yyalloc (yy_size_t size ) { return (void *) malloc( size ); } static void *yyrealloc (void * ptr, yy_size_t size ) { /* The cast to (char *) in the following accommodates both * implementations that use char* generic pointers, and those * that use void* generic pointers. It works with the latter * because both ANSI C and C++ allow castless assignment from * any pointer type to void*, and deal with argument conversions * as though doing an assignment. */ return (void *) realloc( (char *) ptr, size ); } static void yyfree (void * ptr ) { free( (char *) ptr ); /* see yyrealloc() for (char *) cast */ } #define YYTABLES_NAME "yytables" #line 681 "pars0lex.l" /********************************************************************** Release any resources used by the lexer. */ UNIV_INTERN void pars_lexer_close(void) /*==================*/ { yylex_destroy(); if (stringbuf != NULL) { ut_free(stringbuf); stringbuf = NULL; stringbuf_len_alloc = stringbuf_len = 0; } } /* This definition is added here to get rid of a warning. */ __attribute__((unused)) static void yyset_extra(void* p) { } haildb-2.3.2/pars/pars0opt.c0000644000175000017500000007427011513177357016536 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1997, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file pars/pars0opt.c Simple SQL optimizer Created 12/21/1997 Heikki Tuuri *******************************************************/ #include "pars0opt.h" #ifdef UNIV_NONINL #include "pars0opt.ic" #endif #include "row0sel.h" #include "row0ins.h" #include "row0upd.h" #include "dict0dict.h" #include "dict0mem.h" #include "que0que.h" #include "pars0grm.h" #include "pars0pars.h" #include "lock0lock.h" #define OPT_EQUAL 1 /* comparison by = */ #define OPT_COMPARISON 2 /* comparison by <, >, <=, or >= */ #define OPT_NOT_COND 1 #define OPT_END_COND 2 #define OPT_TEST_COND 3 #define OPT_SCROLL_COND 4 /*******************************************************************//** Inverts a comparison operator. @return the equivalent operator when the order of the arguments is switched */ static int opt_invert_cmp_op( /*==============*/ int op) /*!< in: operator */ { if (op == '<') { return('>'); } else if (op == '>') { return('<'); } else if (op == '=') { return('='); } else if (op == PARS_LE_TOKEN) { return(PARS_GE_TOKEN); } else if (op == PARS_GE_TOKEN) { return(PARS_LE_TOKEN); } else { ut_error; } return(0); } /*******************************************************************//** Checks if the value of an expression can be calculated BEFORE the nth table in a join is accessed. If this is the case, it can possibly be used in an index search for the nth table. @return TRUE if already determined */ static ibool opt_check_exp_determined_before( /*============================*/ que_node_t* exp, /*!< in: expression */ sel_node_t* sel_node, /*!< in: select node */ ulint nth_table) /*!< in: nth table will be accessed */ { func_node_t* func_node; sym_node_t* sym_node; dict_table_t* table; que_node_t* arg; ulint i; ut_ad(exp && sel_node); if (que_node_get_type(exp) == QUE_NODE_FUNC) { func_node = exp; arg = func_node->args; while (arg) { if (!opt_check_exp_determined_before(arg, sel_node, nth_table)) { return(FALSE); } arg = que_node_get_next(arg); } return(TRUE); } ut_a(que_node_get_type(exp) == QUE_NODE_SYMBOL); sym_node = exp; if (sym_node->token_type != SYM_COLUMN) { return(TRUE); } for (i = 0; i < nth_table; i++) { table = sel_node_get_nth_plan(sel_node, i)->table; if (sym_node->table == table) { return(TRUE); } } return(FALSE); } /*******************************************************************//** Looks in a comparison condition if a column value is already restricted by it BEFORE the nth table is accessed. @return expression restricting the value of the column, or NULL if not known */ static que_node_t* opt_look_for_col_in_comparison_before( /*==================================*/ ulint cmp_type, /*!< in: OPT_EQUAL, OPT_COMPARISON */ ulint col_no, /*!< in: column number */ func_node_t* search_cond, /*!< in: comparison condition */ sel_node_t* sel_node, /*!< in: select node */ ulint nth_table, /*!< in: nth table in a join (a query from a single table is considered a join of 1 table) */ ulint* op) /*!< out: comparison operator ('=', PARS_GE_TOKEN, ... ); this is inverted if the column appears on the right side */ { sym_node_t* sym_node; dict_table_t* table; que_node_t* exp; que_node_t* arg; ut_ad(search_cond); ut_a((search_cond->func == '<') || (search_cond->func == '>') || (search_cond->func == '=') || (search_cond->func == PARS_GE_TOKEN) || (search_cond->func == PARS_LE_TOKEN)); table = sel_node_get_nth_plan(sel_node, nth_table)->table; if ((cmp_type == OPT_EQUAL) && (search_cond->func != '=')) { return(NULL); } else if ((cmp_type == OPT_COMPARISON) && (search_cond->func != '<') && (search_cond->func != '>') && (search_cond->func != PARS_GE_TOKEN) && (search_cond->func != PARS_LE_TOKEN)) { return(NULL); } arg = search_cond->args; if (que_node_get_type(arg) == QUE_NODE_SYMBOL) { sym_node = arg; if ((sym_node->token_type == SYM_COLUMN) && (sym_node->table == table) && (sym_node->col_no == col_no)) { /* sym_node contains the desired column id */ /* Check if the expression on the right side of the operator is already determined */ exp = que_node_get_next(arg); if (opt_check_exp_determined_before(exp, sel_node, nth_table)) { *op = search_cond->func; return(exp); } } } exp = search_cond->args; arg = que_node_get_next(arg); if (que_node_get_type(arg) == QUE_NODE_SYMBOL) { sym_node = arg; if ((sym_node->token_type == SYM_COLUMN) && (sym_node->table == table) && (sym_node->col_no == col_no)) { if (opt_check_exp_determined_before(exp, sel_node, nth_table)) { *op = opt_invert_cmp_op(search_cond->func); return(exp); } } } return(NULL); } /*******************************************************************//** Looks in a search condition if a column value is already restricted by the search condition BEFORE the nth table is accessed. Takes into account that if we will fetch in an ascending order, we cannot utilize an upper limit for a column value; in a descending order, respectively, a lower limit. @return expression restricting the value of the column, or NULL if not known */ static que_node_t* opt_look_for_col_in_cond_before( /*============================*/ ulint cmp_type, /*!< in: OPT_EQUAL, OPT_COMPARISON */ ulint col_no, /*!< in: column number */ func_node_t* search_cond, /*!< in: search condition or NULL */ sel_node_t* sel_node, /*!< in: select node */ ulint nth_table, /*!< in: nth table in a join (a query from a single table is considered a join of 1 table) */ ulint* op) /*!< out: comparison operator ('=', PARS_GE_TOKEN, ... ) */ { func_node_t* new_cond; que_node_t* exp; if (search_cond == NULL) { return(NULL); } ut_a(que_node_get_type(search_cond) == QUE_NODE_FUNC); ut_a(search_cond->func != PARS_OR_TOKEN); ut_a(search_cond->func != PARS_NOT_TOKEN); if (search_cond->func == PARS_AND_TOKEN) { new_cond = search_cond->args; exp = opt_look_for_col_in_cond_before(cmp_type, col_no, new_cond, sel_node, nth_table, op); if (exp) { return(exp); } new_cond = que_node_get_next(new_cond); exp = opt_look_for_col_in_cond_before(cmp_type, col_no, new_cond, sel_node, nth_table, op); return(exp); } exp = opt_look_for_col_in_comparison_before(cmp_type, col_no, search_cond, sel_node, nth_table, op); if (exp == NULL) { return(NULL); } /* If we will fetch in an ascending order, we cannot utilize an upper limit for a column value; in a descending order, respectively, a lower limit */ if (sel_node->asc && ((*op == '<') || (*op == PARS_LE_TOKEN))) { return(NULL); } else if (!sel_node->asc && ((*op == '>') || (*op == PARS_GE_TOKEN))) { return(NULL); } return(exp); } /*******************************************************************//** Calculates the goodness for an index according to a select node. The goodness is 4 times the number of first fields in index whose values we already know exactly in the query. If we have a comparison condition for an additional field, 2 point are added. If the index is unique, and we know all the unique fields for the index we add 1024 points. For a clustered index we add 1 point. @return goodness */ static ulint opt_calc_index_goodness( /*====================*/ dict_index_t* index, /*!< in: index */ sel_node_t* sel_node, /*!< in: parsed select node */ ulint nth_table, /*!< in: nth table in a join */ que_node_t** index_plan, /*!< in/out: comparison expressions for this index */ ulint* last_op) /*!< out: last comparison operator, if goodness > 1 */ { que_node_t* exp; ulint goodness; ulint n_fields; ulint col_no; ulint op; ulint j; goodness = 0; /* Note that as higher level node pointers in the B-tree contain page addresses as the last field, we must not put more fields in the search tuple than dict_index_get_n_unique_in_tree(index); see the note in btr_cur_search_to_nth_level. */ n_fields = dict_index_get_n_unique_in_tree(index); for (j = 0; j < n_fields; j++) { col_no = dict_index_get_nth_col_no(index, j); exp = opt_look_for_col_in_cond_before( OPT_EQUAL, col_no, sel_node->search_cond, sel_node, nth_table, &op); if (exp) { /* The value for this column is exactly known already at this stage of the join */ index_plan[j] = exp; *last_op = op; goodness += 4; } else { /* Look for non-equality comparisons */ exp = opt_look_for_col_in_cond_before( OPT_COMPARISON, col_no, sel_node->search_cond, sel_node, nth_table, &op); if (exp) { index_plan[j] = exp; *last_op = op; goodness += 2; } break; } } if (goodness >= 4 * dict_index_get_n_unique(index)) { goodness += 1024; if (dict_index_is_clust(index)) { goodness += 1024; } } /* We have to test for goodness here, as last_op may note be set */ if (goodness && dict_index_is_clust(index)) { goodness++; } return(goodness); } /*******************************************************************//** Calculates the number of matched fields based on an index goodness. @return number of excatly or partially matched fields */ UNIV_INLINE ulint opt_calc_n_fields_from_goodness( /*============================*/ ulint goodness) /*!< in: goodness */ { return(((goodness % 1024) + 2) / 4); } /*******************************************************************//** Converts a comparison operator to the corresponding search mode PAGE_CUR_GE, ... @return search mode */ UNIV_INLINE ulint opt_op_to_search_mode( /*==================*/ ibool asc, /*!< in: TRUE if the rows should be fetched in an ascending order */ ulint op) /*!< in: operator '=', PARS_GE_TOKEN, ... */ { if (op == '=') { if (asc) { return(PAGE_CUR_GE); } else { return(PAGE_CUR_LE); } } else if (op == '<') { ut_a(!asc); return(PAGE_CUR_L); } else if (op == '>') { ut_a(asc); return(PAGE_CUR_G); } else if (op == PARS_GE_TOKEN) { ut_a(asc); return(PAGE_CUR_GE); } else if (op == PARS_LE_TOKEN) { ut_a(!asc); return(PAGE_CUR_LE); } else { ut_error; } return(0); } /*******************************************************************//** Determines if a node is an argument node of a function node. @return TRUE if is an argument */ static ibool opt_is_arg( /*=======*/ que_node_t* arg_node, /*!< in: possible argument node */ func_node_t* func_node) /*!< in: function node */ { que_node_t* arg; arg = func_node->args; while (arg) { if (arg == arg_node) { return(TRUE); } arg = que_node_get_next(arg); } return(FALSE); } /*******************************************************************//** Decides if the fetching of rows should be made in a descending order, and also checks that the chosen query plan produces a result which satisfies the order-by. */ static void opt_check_order_by( /*===============*/ sel_node_t* sel_node) /*!< in: select node; asserts an error if the plan does not agree with the order-by */ { order_node_t* order_node; dict_table_t* order_table; ulint order_col_no; plan_t* plan; ulint i; if (!sel_node->order_by) { return; } order_node = sel_node->order_by; order_col_no = order_node->column->col_no; order_table = order_node->column->table; /* If there is an order-by clause, the first non-exactly matched field in the index used for the last table in the table list should be the column defined in the order-by clause, and for all the other tables we should get only at most a single row, otherwise we cannot presently calculate the order-by, as we have no sort utility */ for (i = 0; i < sel_node->n_tables; i++) { plan = sel_node_get_nth_plan(sel_node, i); if (i < sel_node->n_tables - 1) { ut_a(dict_index_get_n_unique(plan->index) <= plan->n_exact_match); } else { ut_a(plan->table == order_table); ut_a((dict_index_get_n_unique(plan->index) <= plan->n_exact_match) || (dict_index_get_nth_col_no(plan->index, plan->n_exact_match) == order_col_no)); } } } /*******************************************************************//** Optimizes a select. Decides which indexes to tables to use. The tables are accessed in the order that they were written to the FROM part in the select statement. */ static void opt_search_plan_for_table( /*======================*/ sel_node_t* sel_node, /*!< in: parsed select node */ ulint i, /*!< in: this is the ith table */ dict_table_t* table) /*!< in: table */ { plan_t* plan; dict_index_t* index; dict_index_t* best_index; ulint n_fields; ulint goodness; ulint last_op = 75946965; /* Eliminate a Purify warning */ ulint best_goodness; ulint best_last_op = 0; /* remove warning */ que_node_t* index_plan[256]; que_node_t* best_index_plan[256]; plan = sel_node_get_nth_plan(sel_node, i); plan->table = table; plan->asc = sel_node->asc; plan->pcur_is_open = FALSE; plan->cursor_at_end = FALSE; /* Calculate goodness for each index of the table */ index = dict_table_get_first_index(table); best_index = index; /* Eliminate compiler warning */ best_goodness = 0; /* should be do ... until ? comment by Jani */ while (index) { goodness = opt_calc_index_goodness(index, sel_node, i, index_plan, &last_op); if (goodness > best_goodness) { best_index = index; best_goodness = goodness; n_fields = opt_calc_n_fields_from_goodness(goodness); ut_memcpy(best_index_plan, index_plan, n_fields * sizeof(void*)); best_last_op = last_op; } index = dict_table_get_next_index(index); } plan->index = best_index; n_fields = opt_calc_n_fields_from_goodness(best_goodness); if (n_fields == 0) { plan->tuple = NULL; plan->n_exact_match = 0; } else { plan->tuple = dtuple_create(pars_sym_tab_global->heap, n_fields); dict_index_copy_types(plan->tuple, plan->index, n_fields); plan->tuple_exps = mem_heap_alloc(pars_sym_tab_global->heap, n_fields * sizeof(void*)); ut_memcpy(plan->tuple_exps, best_index_plan, n_fields * sizeof(void*)); if (best_last_op == '=') { plan->n_exact_match = n_fields; } else { plan->n_exact_match = n_fields - 1; } plan->mode = opt_op_to_search_mode(sel_node->asc, best_last_op); } if (dict_index_is_clust(best_index) && (plan->n_exact_match >= dict_index_get_n_unique(best_index))) { plan->unique_search = TRUE; } else { plan->unique_search = FALSE; } plan->old_vers_heap = NULL; btr_pcur_init(&(plan->pcur)); btr_pcur_init(&(plan->clust_pcur)); } /*******************************************************************//** Looks at a comparison condition and decides if it can, and need, be tested for a table AFTER the table has been accessed. @return OPT_NOT_COND if not for this table, else OPT_END_COND, OPT_TEST_COND, or OPT_SCROLL_COND, where the last means that the condition need not be tested, except when scroll cursors are used */ static ulint opt_classify_comparison( /*====================*/ sel_node_t* sel_node, /*!< in: select node */ ulint i, /*!< in: ith table in the join */ func_node_t* cond) /*!< in: comparison condition */ { plan_t* plan; ulint n_fields; ulint op; ulint j; ut_ad(cond && sel_node); plan = sel_node_get_nth_plan(sel_node, i); /* Check if the condition is determined after the ith table has been accessed, but not after the i - 1:th */ if (!opt_check_exp_determined_before(cond, sel_node, i + 1)) { return(OPT_NOT_COND); } if ((i > 0) && opt_check_exp_determined_before(cond, sel_node, i)) { return(OPT_NOT_COND); } /* If the condition is an exact match condition used in constructing the search tuple, it is classified as OPT_END_COND */ if (plan->tuple) { n_fields = dtuple_get_n_fields(plan->tuple); } else { n_fields = 0; } for (j = 0; j < plan->n_exact_match; j++) { if (opt_is_arg(plan->tuple_exps[j], cond)) { return(OPT_END_COND); } } /* If the condition is an non-exact match condition used in constructing the search tuple, it is classified as OPT_SCROLL_COND. When the cursor is positioned, and if a non-scroll cursor is used, there is no need to test this condition; if a scroll cursor is used the testing is necessary when the cursor is reversed. */ if ((n_fields > plan->n_exact_match) && opt_is_arg(plan->tuple_exps[n_fields - 1], cond)) { return(OPT_SCROLL_COND); } /* If the condition is a non-exact match condition on the first field in index for which there is no exact match, and it limits the search range from the opposite side of the search tuple already BEFORE we access the table, it is classified as OPT_END_COND */ if ((dict_index_get_n_fields(plan->index) > plan->n_exact_match) && opt_look_for_col_in_comparison_before( OPT_COMPARISON, dict_index_get_nth_col_no(plan->index, plan->n_exact_match), cond, sel_node, i, &op)) { if (sel_node->asc && ((op == '<') || (op == PARS_LE_TOKEN))) { return(OPT_END_COND); } if (!sel_node->asc && ((op == '>') || (op == PARS_GE_TOKEN))) { return(OPT_END_COND); } } /* Otherwise, cond is classified as OPT_TEST_COND */ return(OPT_TEST_COND); } /*******************************************************************//** Recursively looks for test conditions for a table in a join. */ static void opt_find_test_conds( /*================*/ sel_node_t* sel_node, /*!< in: select node */ ulint i, /*!< in: ith table in the join */ func_node_t* cond) /*!< in: conjunction of search conditions or NULL */ { func_node_t* new_cond; ulint class; plan_t* plan; if (cond == NULL) { return; } if (cond->func == PARS_AND_TOKEN) { new_cond = cond->args; opt_find_test_conds(sel_node, i, new_cond); new_cond = que_node_get_next(new_cond); opt_find_test_conds(sel_node, i, new_cond); return; } plan = sel_node_get_nth_plan(sel_node, i); class = opt_classify_comparison(sel_node, i, cond); if (class == OPT_END_COND) { UT_LIST_ADD_LAST(cond_list, plan->end_conds, cond); } else if (class == OPT_TEST_COND) { UT_LIST_ADD_LAST(cond_list, plan->other_conds, cond); } } /*******************************************************************//** Normalizes a list of comparison conditions so that a column of the table appears on the left side of the comparison if possible. This is accomplished by switching the arguments of the operator. */ static void opt_normalize_cmp_conds( /*====================*/ func_node_t* cond, /*!< in: first in a list of comparison conditions, or NULL */ dict_table_t* table) /*!< in: table */ { que_node_t* arg1; que_node_t* arg2; sym_node_t* sym_node; while (cond) { arg1 = cond->args; arg2 = que_node_get_next(arg1); if (que_node_get_type(arg2) == QUE_NODE_SYMBOL) { sym_node = arg2; if ((sym_node->token_type == SYM_COLUMN) && (sym_node->table == table)) { /* Switch the order of the arguments */ cond->args = arg2; que_node_list_add_last(NULL, arg2); que_node_list_add_last(arg2, arg1); /* Invert the operator */ cond->func = opt_invert_cmp_op(cond->func); } } cond = UT_LIST_GET_NEXT(cond_list, cond); } } /*******************************************************************//** Finds out the search condition conjuncts we can, and need, to test as the ith table in a join is accessed. The search tuple can eliminate the need to test some conjuncts. */ static void opt_determine_and_normalize_test_conds( /*===================================*/ sel_node_t* sel_node, /*!< in: select node */ ulint i) /*!< in: ith table in the join */ { plan_t* plan; plan = sel_node_get_nth_plan(sel_node, i); UT_LIST_INIT(plan->end_conds); UT_LIST_INIT(plan->other_conds); /* Recursively go through the conjuncts and classify them */ opt_find_test_conds(sel_node, i, sel_node->search_cond); opt_normalize_cmp_conds(UT_LIST_GET_FIRST(plan->end_conds), plan->table); ut_a(UT_LIST_GET_LEN(plan->end_conds) >= plan->n_exact_match); } /*******************************************************************//** Looks for occurrences of the columns of the table in the query subgraph and adds them to the list of columns if an occurrence of the same column does not already exist in the list. If the column is already in the list, puts a value indirection to point to the occurrence in the column list, except if the column occurrence we are looking at is in the column list, in which case nothing is done. */ UNIV_INTERN void opt_find_all_cols( /*==============*/ ibool copy_val, /*!< in: if TRUE, new found columns are added as columns to copy */ dict_index_t* index, /*!< in: index of the table to use */ sym_node_list_t* col_list, /*!< in: base node of a list where to add new found columns */ plan_t* plan, /*!< in: plan or NULL */ que_node_t* exp) /*!< in: expression or condition or NULL */ { func_node_t* func_node; que_node_t* arg; sym_node_t* sym_node; sym_node_t* col_node; ulint col_pos; if (exp == NULL) { return; } if (que_node_get_type(exp) == QUE_NODE_FUNC) { func_node = exp; arg = func_node->args; while (arg) { opt_find_all_cols(copy_val, index, col_list, plan, arg); arg = que_node_get_next(arg); } return; } ut_a(que_node_get_type(exp) == QUE_NODE_SYMBOL); sym_node = exp; if (sym_node->token_type != SYM_COLUMN) { return; } if (sym_node->table != index->table) { return; } /* Look for an occurrence of the same column in the plan column list */ col_node = UT_LIST_GET_FIRST(*col_list); while (col_node) { if (col_node->col_no == sym_node->col_no) { if (col_node == sym_node) { /* sym_node was already in a list: do nothing */ return; } /* Put an indirection */ sym_node->indirection = col_node; sym_node->alias = col_node; return; } col_node = UT_LIST_GET_NEXT(col_var_list, col_node); } /* The same column did not occur in the list: add it */ UT_LIST_ADD_LAST(col_var_list, *col_list, sym_node); sym_node->copy_val = copy_val; /* Fill in the field_no fields in sym_node */ sym_node->field_nos[SYM_CLUST_FIELD_NO] = dict_index_get_nth_col_pos( dict_table_get_first_index(index->table), sym_node->col_no); if (!dict_index_is_clust(index)) { ut_a(plan); col_pos = dict_index_get_nth_col_pos(index, sym_node->col_no); if (col_pos == ULINT_UNDEFINED) { plan->must_get_clust = TRUE; } sym_node->field_nos[SYM_SEC_FIELD_NO] = col_pos; } } /*******************************************************************//** Looks for occurrences of the columns of the table in conditions which are not yet determined AFTER the join operation has fetched a row in the ith table. The values for these column must be copied to dynamic memory for later use. */ static void opt_find_copy_cols( /*===============*/ sel_node_t* sel_node, /*!< in: select node */ ulint i, /*!< in: ith table in the join */ func_node_t* search_cond) /*!< in: search condition or NULL */ { func_node_t* new_cond; plan_t* plan; if (search_cond == NULL) { return; } ut_ad(que_node_get_type(search_cond) == QUE_NODE_FUNC); if (search_cond->func == PARS_AND_TOKEN) { new_cond = search_cond->args; opt_find_copy_cols(sel_node, i, new_cond); new_cond = que_node_get_next(new_cond); opt_find_copy_cols(sel_node, i, new_cond); return; } if (!opt_check_exp_determined_before(search_cond, sel_node, i + 1)) { /* Any ith table columns occurring in search_cond should be copied, as this condition cannot be tested already on the fetch from the ith table */ plan = sel_node_get_nth_plan(sel_node, i); opt_find_all_cols(TRUE, plan->index, &(plan->columns), plan, search_cond); } } /*******************************************************************//** Classifies the table columns according to whether we use the column only while holding the latch on the page, or whether we have to copy the column value to dynamic memory. Puts the first occurrence of a column to either list in the plan node, and puts indirections to later occurrences of the column. */ static void opt_classify_cols( /*==============*/ sel_node_t* sel_node, /*!< in: select node */ ulint i) /*!< in: ith table in the join */ { plan_t* plan; que_node_t* exp; plan = sel_node_get_nth_plan(sel_node, i); /* The final value of the following field will depend on the environment of the select statement: */ plan->must_get_clust = FALSE; UT_LIST_INIT(plan->columns); /* All select list columns should be copied: therefore TRUE as the first argument */ exp = sel_node->select_list; while (exp) { opt_find_all_cols(TRUE, plan->index, &(plan->columns), plan, exp); exp = que_node_get_next(exp); } opt_find_copy_cols(sel_node, i, sel_node->search_cond); /* All remaining columns in the search condition are temporary columns: therefore FALSE */ opt_find_all_cols(FALSE, plan->index, &(plan->columns), plan, sel_node->search_cond); } /*******************************************************************//** Fills in the info in plan which is used in accessing a clustered index record. The columns must already be classified for the plan node. */ static void opt_clust_access( /*=============*/ sel_node_t* sel_node, /*!< in: select node */ ulint n) /*!< in: nth table in select */ { plan_t* plan; dict_table_t* table; dict_index_t* clust_index; dict_index_t* index; mem_heap_t* heap; ulint n_fields; ulint pos; ulint i; plan = sel_node_get_nth_plan(sel_node, n); index = plan->index; /* The final value of the following field depends on the environment of the select statement: */ plan->no_prefetch = FALSE; if (dict_index_is_clust(index)) { plan->clust_map = NULL; plan->clust_ref = NULL; return; } table = index->table; clust_index = dict_table_get_first_index(table); n_fields = dict_index_get_n_unique(clust_index); heap = pars_sym_tab_global->heap; plan->clust_ref = dtuple_create(heap, n_fields); dict_index_copy_types(plan->clust_ref, clust_index, n_fields); plan->clust_map = mem_heap_alloc(heap, n_fields * sizeof(ulint)); for (i = 0; i < n_fields; i++) { pos = dict_index_get_nth_field_pos(index, clust_index, i); ut_a(pos != ULINT_UNDEFINED); /* We optimize here only queries to InnoDB's internal system tables, and they should not contain column prefix indexes. */ if (dict_index_get_nth_field(index, pos)->prefix_len != 0 || dict_index_get_nth_field(clust_index, i) ->prefix_len != 0) { ib_logger(ib_stream, "InnoDB: Error in pars0opt.c:" " table %s has prefix_len != 0\n", index->table_name); } *(plan->clust_map + i) = pos; } } /*******************************************************************//** Optimizes a select. Decides which indexes to tables to use. The tables are accessed in the order that they were written to the FROM part in the select statement. */ UNIV_INTERN void opt_search_plan( /*============*/ sel_node_t* sel_node) /*!< in: parsed select node */ { sym_node_t* table_node; dict_table_t* table; order_node_t* order_by; ulint i; sel_node->plans = mem_heap_alloc(pars_sym_tab_global->heap, sel_node->n_tables * sizeof(plan_t)); /* Analyze the search condition to find out what we know at each join stage about the conditions that the columns of a table should satisfy */ table_node = sel_node->table_list; if (sel_node->order_by == NULL) { sel_node->asc = TRUE; } else { order_by = sel_node->order_by; sel_node->asc = order_by->asc; } for (i = 0; i < sel_node->n_tables; i++) { table = table_node->table; /* Choose index through which to access the table */ opt_search_plan_for_table(sel_node, i, table); /* Determine the search condition conjuncts we can test at this table; normalize the end conditions */ opt_determine_and_normalize_test_conds(sel_node, i); table_node = que_node_get_next(table_node); } table_node = sel_node->table_list; for (i = 0; i < sel_node->n_tables; i++) { /* Classify the table columns into those we only need to access but not copy, and to those we must copy to dynamic memory */ opt_classify_cols(sel_node, i); /* Calculate possible info for accessing the clustered index record */ opt_clust_access(sel_node, i); table_node = que_node_get_next(table_node); } /* Check that the plan obeys a possible order-by clause: if not, an assertion error occurs */ opt_check_order_by(sel_node); #ifdef UNIV_SQL_DEBUG opt_print_query_plan(sel_node); #endif } /********************************************************************//** Prints info of a query plan. */ UNIV_INTERN void opt_print_query_plan( /*=================*/ sel_node_t* sel_node) /*!< in: select node */ { plan_t* plan; ulint n_fields; ulint i; ib_logger(ib_stream, "QUERY PLAN FOR A SELECT NODE\n"); ib_logger(ib_stream, sel_node->asc ? "Asc. search; " : "Desc. search; "); if (sel_node->set_x_locks) { ib_logger(ib_stream, "sets row x-locks; "); ut_a(sel_node->row_lock_mode == LOCK_X); ut_a(!sel_node->consistent_read); } else if (sel_node->consistent_read) { ib_logger(ib_stream, "consistent read; "); } else { ut_a(sel_node->row_lock_mode == LOCK_S); ib_logger(ib_stream, "sets row s-locks; "); } ib_logger(ib_stream, "\n"); for (i = 0; i < sel_node->n_tables; i++) { plan = sel_node_get_nth_plan(sel_node, i); if (plan->tuple) { n_fields = dtuple_get_n_fields(plan->tuple); } else { n_fields = 0; } ib_logger(ib_stream, "Table "); dict_index_name_print(ib_stream, NULL, plan->index); ib_logger(ib_stream,"; exact m. %lu, match %lu, end conds %lu\n", (unsigned long) plan->n_exact_match, (unsigned long) n_fields, (unsigned long) UT_LIST_GET_LEN(plan->end_conds)); } } haildb-2.3.2/eval/0000755000175000017500000000000011513177437014571 5ustar00pcrewspcrews00000000000000haildb-2.3.2/eval/eval0eval.c0000644000175000017500000004304611513177357016624 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1997, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file eval/eval0eval.c SQL evaluator: evaluates simple data structures, like expressions, in a query graph Created 12/29/1997 Heikki Tuuri *******************************************************/ #include "eval0eval.h" #ifdef UNIV_NONINL #include "eval0eval.ic" #endif #include "data0data.h" #include "row0sel.h" /** The RND function seed */ static ulint eval_rnd = 128367121; /** Dummy adress used when we should allocate a buffer of size 0 in eval_node_alloc_val_buf */ static byte eval_dummy; /*****************************************************************//** Allocate a buffer from global dynamic memory for a value of a que_node. NOTE that this memory must be explicitly freed when the query graph is freed. If the node already has an allocated buffer, that buffer is freed here. NOTE that this is the only function where dynamic memory should be allocated for a query node val field. @return pointer to allocated buffer */ UNIV_INTERN byte* eval_node_alloc_val_buf( /*====================*/ que_node_t* node, /*!< in: query graph node; sets the val field data field to point to the new buffer, and len field equal to size */ ulint size) /*!< in: buffer size */ { dfield_t* dfield; byte* data; ut_ad(que_node_get_type(node) == QUE_NODE_SYMBOL || que_node_get_type(node) == QUE_NODE_FUNC); dfield = que_node_get_val(node); data = dfield_get_data(dfield); if (data && data != &eval_dummy) { mem_free(data); } if (size == 0) { data = &eval_dummy; } else { data = mem_alloc(size); } que_node_set_val_buf_size(node, size); dfield_set_data(dfield, data, size); return(data); } /*****************************************************************//** Free the buffer from global dynamic memory for a value of a que_node, if it has been allocated in the above function. The freeing for pushed column values is done in sel_col_prefetch_buf_free. */ UNIV_INTERN void eval_node_free_val_buf( /*===================*/ que_node_t* node) /*!< in: query graph node */ { dfield_t* dfield; byte* data; ut_ad(que_node_get_type(node) == QUE_NODE_SYMBOL || que_node_get_type(node) == QUE_NODE_FUNC); dfield = que_node_get_val(node); data = dfield_get_data(dfield); if (que_node_get_val_buf_size(node) > 0) { ut_a(data); mem_free(data); } } /*****************************************************************//** Evaluates a comparison node. @return the result of the comparison */ UNIV_INTERN ibool eval_cmp( /*=====*/ func_node_t* cmp_node) /*!< in: comparison node */ { que_node_t* arg1; que_node_t* arg2; int res; ibool val; int func; ut_ad(que_node_get_type(cmp_node) == QUE_NODE_FUNC); arg1 = cmp_node->args; arg2 = que_node_get_next(arg1); res = cmp_dfield_dfield( NULL, que_node_get_val(arg1), que_node_get_val(arg2)); val = TRUE; func = cmp_node->func; if (func == '=') { if (res != 0) { val = FALSE; } } else if (func == '<') { if (res != -1) { val = FALSE; } } else if (func == PARS_LE_TOKEN) { if (res == 1) { val = FALSE; } } else if (func == PARS_NE_TOKEN) { if (res == 0) { val = FALSE; } } else if (func == PARS_GE_TOKEN) { if (res == -1) { val = FALSE; } } else { ut_ad(func == '>'); if (res != 1) { val = FALSE; } } eval_node_set_ibool_val(cmp_node, val); return(val); } /*****************************************************************//** Evaluates a logical operation node. */ UNIV_INLINE void eval_logical( /*=========*/ func_node_t* logical_node) /*!< in: logical operation node */ { que_node_t* arg1; que_node_t* arg2; ibool val1; ibool val2 = 0; /* remove warning */ ibool val = 0; /* remove warning */ int func; ut_ad(que_node_get_type(logical_node) == QUE_NODE_FUNC); arg1 = logical_node->args; arg2 = que_node_get_next(arg1); /* arg2 is NULL if func is 'NOT' */ val1 = eval_node_get_ibool_val(arg1); if (arg2) { val2 = eval_node_get_ibool_val(arg2); } func = logical_node->func; if (func == PARS_AND_TOKEN) { val = val1 & val2; } else if (func == PARS_OR_TOKEN) { val = val1 | val2; } else if (func == PARS_NOT_TOKEN) { val = TRUE - val1; } else { ut_error; } eval_node_set_ibool_val(logical_node, val); } /*****************************************************************//** Evaluates an arithmetic operation node. */ UNIV_INLINE void eval_arith( /*=======*/ func_node_t* arith_node) /*!< in: arithmetic operation node */ { que_node_t* arg1; que_node_t* arg2; lint val1; lint val2 = 0; /* remove warning */ lint val; int func; ut_ad(que_node_get_type(arith_node) == QUE_NODE_FUNC); arg1 = arith_node->args; arg2 = que_node_get_next(arg1); /* arg2 is NULL if func is unary '-' */ val1 = eval_node_get_int_val(arg1); if (arg2) { val2 = eval_node_get_int_val(arg2); } func = arith_node->func; if (func == '+') { val = val1 + val2; } else if ((func == '-') && arg2) { val = val1 - val2; } else if (func == '-') { val = -val1; } else if (func == '*') { val = val1 * val2; } else { ut_ad(func == '/'); val = val1 / val2; } eval_node_set_int_val(arith_node, val); } /*****************************************************************//** Evaluates an aggregate operation node. */ UNIV_INLINE void eval_aggregate( /*===========*/ func_node_t* node) /*!< in: aggregate operation node */ { que_node_t* arg; lint val; lint arg_val; int func; ut_ad(que_node_get_type(node) == QUE_NODE_FUNC); val = eval_node_get_int_val(node); func = node->func; if (func == PARS_COUNT_TOKEN) { val = val + 1; } else { ut_ad(func == PARS_SUM_TOKEN); arg = node->args; arg_val = eval_node_get_int_val(arg); val = val + arg_val; } eval_node_set_int_val(node, val); } /*****************************************************************//** Evaluates a predefined function node where the function is not relevant in benchmarks. */ static void eval_predefined_2( /*==============*/ func_node_t* func_node) /*!< in: predefined function node */ { que_node_t* arg; que_node_t* arg1; que_node_t* arg2 = 0; /* remove warning (??? bug ???) */ lint int_val; byte* data; ulint len1; ulint len2; int func; ulint i; ut_ad(que_node_get_type(func_node) == QUE_NODE_FUNC); arg1 = func_node->args; if (arg1) { arg2 = que_node_get_next(arg1); } func = func_node->func; if (func == PARS_PRINTF_TOKEN) { arg = arg1; while (arg) { dfield_print(que_node_get_val(arg)); arg = que_node_get_next(arg); } ib_logger(ib_stream, "\n"); } else if (func == PARS_ASSERT_TOKEN) { if (!eval_node_get_ibool_val(arg1)) { ib_logger(ib_stream, "SQL assertion fails in a stored procedure!\n"); } ut_a(eval_node_get_ibool_val(arg1)); /* This function, or more precisely, a debug procedure, returns no value */ } else if (func == PARS_RND_TOKEN) { len1 = (ulint)eval_node_get_int_val(arg1); len2 = (ulint)eval_node_get_int_val(arg2); ut_ad(len2 >= len1); if (len2 > len1) { int_val = (lint) (len1 + (eval_rnd % (len2 - len1 + 1))); } else { int_val = (lint) len1; } eval_rnd = ut_rnd_gen_next_ulint(eval_rnd); eval_node_set_int_val(func_node, int_val); } else if (func == PARS_RND_STR_TOKEN) { len1 = (ulint)eval_node_get_int_val(arg1); data = eval_node_ensure_val_buf(func_node, len1); for (i = 0; i < len1; i++) { data[i] = (byte)(97 + (eval_rnd % 3)); eval_rnd = ut_rnd_gen_next_ulint(eval_rnd); } } else { ut_error; } } /*****************************************************************//** Evaluates a notfound-function node. */ UNIV_INLINE void eval_notfound( /*==========*/ func_node_t* func_node) /*!< in: function node */ { que_node_t* arg1; que_node_t* arg2; sym_node_t* cursor; sel_node_t* sel_node; ibool ibool_val; arg1 = func_node->args; arg2 = que_node_get_next(arg1); ut_ad(func_node->func == PARS_NOTFOUND_TOKEN); cursor = arg1; ut_ad(que_node_get_type(cursor) == QUE_NODE_SYMBOL); if (cursor->token_type == SYM_LIT) { ut_ad(ut_memcmp(dfield_get_data(que_node_get_val(cursor)), "SQL", 3) == 0); sel_node = cursor->sym_table->query_graph->last_sel_node; } else { sel_node = cursor->alias->cursor_def; } if (sel_node->state == SEL_NODE_NO_MORE_ROWS) { ibool_val = TRUE; } else { ibool_val = FALSE; } eval_node_set_ibool_val(func_node, ibool_val); } /*****************************************************************//** Evaluates a substr-function node. */ UNIV_INLINE void eval_substr( /*========*/ func_node_t* func_node) /*!< in: function node */ { que_node_t* arg1; que_node_t* arg2; que_node_t* arg3; dfield_t* dfield; byte* str1; ulint len1; ulint len2; arg1 = func_node->args; arg2 = que_node_get_next(arg1); ut_ad(func_node->func == PARS_SUBSTR_TOKEN); arg3 = que_node_get_next(arg2); str1 = dfield_get_data(que_node_get_val(arg1)); len1 = (ulint)eval_node_get_int_val(arg2); len2 = (ulint)eval_node_get_int_val(arg3); dfield = que_node_get_val(func_node); dfield_set_data(dfield, str1 + len1, len2); } /*****************************************************************//** Evaluates a replstr-procedure node. */ static void eval_replstr( /*=========*/ func_node_t* func_node) /*!< in: function node */ { que_node_t* arg1; que_node_t* arg2; que_node_t* arg3; que_node_t* arg4; byte* str1; byte* str2; ulint len1; ulint len2; arg1 = func_node->args; arg2 = que_node_get_next(arg1); ut_ad(que_node_get_type(arg1) == QUE_NODE_SYMBOL); arg3 = que_node_get_next(arg2); arg4 = que_node_get_next(arg3); str1 = dfield_get_data(que_node_get_val(arg1)); str2 = dfield_get_data(que_node_get_val(arg2)); len1 = (ulint)eval_node_get_int_val(arg3); len2 = (ulint)eval_node_get_int_val(arg4); if ((dfield_get_len(que_node_get_val(arg1)) < len1 + len2) || (dfield_get_len(que_node_get_val(arg2)) < len2)) { ut_error; } ut_memcpy(str1 + len1, str2, len2); } /*****************************************************************//** Evaluates an instr-function node. */ static void eval_instr( /*=======*/ func_node_t* func_node) /*!< in: function node */ { que_node_t* arg1; que_node_t* arg2; dfield_t* dfield1; dfield_t* dfield2; lint int_val; byte* str1; byte* str2; byte match_char; ulint len1; ulint len2; ulint i; ulint j; arg1 = func_node->args; arg2 = que_node_get_next(arg1); dfield1 = que_node_get_val(arg1); dfield2 = que_node_get_val(arg2); str1 = dfield_get_data(dfield1); str2 = dfield_get_data(dfield2); len1 = dfield_get_len(dfield1); len2 = dfield_get_len(dfield2); if (len2 == 0) { ut_error; } match_char = str2[0]; for (i = 0; i < len1; i++) { /* In this outer loop, the number of matched characters is 0 */ if (str1[i] == match_char) { if (i + len2 > len1) { break; } for (j = 1;; j++) { /* We have already matched j characters */ if (j == len2) { int_val = i + 1; goto match_found; } if (str1[i + j] != str2[j]) { break; } } } } int_val = 0; match_found: eval_node_set_int_val(func_node, int_val); } /*****************************************************************//** Evaluates a predefined function node. */ UNIV_INLINE void eval_binary_to_number( /*==================*/ func_node_t* func_node) /*!< in: function node */ { que_node_t* arg1; dfield_t* dfield; byte* str1; byte* str2; ulint len1; ulint int_val; arg1 = func_node->args; dfield = que_node_get_val(arg1); str1 = dfield_get_data(dfield); len1 = dfield_get_len(dfield); if (len1 > 4) { ut_error; } if (len1 == 4) { str2 = str1; } else { int_val = 0; str2 = (byte*)&int_val; ut_memcpy(str2 + (4 - len1), str1, len1); } eval_node_copy_and_alloc_val(func_node, str2, 4); } /*****************************************************************//** Evaluates a predefined function node. */ static void eval_concat( /*========*/ func_node_t* func_node) /*!< in: function node */ { que_node_t* arg; dfield_t* dfield; byte* data; ulint len; ulint len1; arg = func_node->args; len = 0; while (arg) { len1 = dfield_get_len(que_node_get_val(arg)); len += len1; arg = que_node_get_next(arg); } data = eval_node_ensure_val_buf(func_node, len); arg = func_node->args; len = 0; while (arg) { dfield = que_node_get_val(arg); len1 = dfield_get_len(dfield); ut_memcpy(data + len, dfield_get_data(dfield), len1); len += len1; arg = que_node_get_next(arg); } } /*****************************************************************//** Evaluates a predefined function node. If the first argument is an integer, this function looks at the second argument which is the integer length in bytes, and converts the integer to a VARCHAR. If the first argument is of some other type, this function converts it to BINARY. */ UNIV_INLINE void eval_to_binary( /*===========*/ func_node_t* func_node) /*!< in: function node */ { que_node_t* arg1; que_node_t* arg2; dfield_t* dfield; byte* str1; ulint len; ulint len1; arg1 = func_node->args; str1 = dfield_get_data(que_node_get_val(arg1)); if (dtype_get_mtype(que_node_get_data_type(arg1)) != DATA_INT) { len = dfield_get_len(que_node_get_val(arg1)); dfield = que_node_get_val(func_node); dfield_set_data(dfield, str1, len); return; } arg2 = que_node_get_next(arg1); len1 = (ulint)eval_node_get_int_val(arg2); if (len1 > 4) { ut_error; } dfield = que_node_get_val(func_node); dfield_set_data(dfield, str1 + (4 - len1), len1); } /*****************************************************************//** Evaluates a predefined function node. */ UNIV_INLINE void eval_predefined( /*============*/ func_node_t* func_node) /*!< in: function node */ { que_node_t* arg1; lint int_val; byte* data; int func; func = func_node->func; arg1 = func_node->args; if (func == PARS_LENGTH_TOKEN) { int_val = (lint)dfield_get_len(que_node_get_val(arg1)); } else if (func == PARS_TO_CHAR_TOKEN) { /* Convert number to character string as a signed decimal integer. */ ulint uint_val; int int_len; int_val = eval_node_get_int_val(arg1); /* Determine the length of the string. */ if (int_val == 0) { int_len = 1; /* the number 0 occupies 1 byte */ } else { int_len = 0; if (int_val < 0) { uint_val = ((ulint) -int_val - 1) + 1; int_len++; /* reserve space for minus sign */ } else { uint_val = (ulint) int_val; } for (; uint_val > 0; int_len++) { uint_val /= 10; } } /* allocate the string */ data = eval_node_ensure_val_buf(func_node, int_len + 1); /* add terminating NUL character */ data[int_len] = 0; /* convert the number */ if (int_val == 0) { data[0] = '0'; } else { int tmp; if (int_val < 0) { data[0] = '-'; /* preceding minus sign */ uint_val = ((ulint) -int_val - 1) + 1; } else { uint_val = (ulint) int_val; } for (tmp = int_len; uint_val > 0; uint_val /= 10) { data[--tmp] = (byte) ('0' + (byte)(uint_val % 10)); } } dfield_set_len(que_node_get_val(func_node), int_len); return; } else if (func == PARS_TO_NUMBER_TOKEN) { int_val = atoi((char*) dfield_get_data(que_node_get_val(arg1))); } else if (func == PARS_SYSDATE_TOKEN) { int_val = (lint)ut_time(); } else { eval_predefined_2(func_node); return; } eval_node_set_int_val(func_node, int_val); } /*****************************************************************//** Evaluates a function node. */ UNIV_INTERN void eval_func( /*======*/ func_node_t* func_node) /*!< in: function node */ { que_node_t* arg; ulint class; ulint func; ut_ad(que_node_get_type(func_node) == QUE_NODE_FUNC); class = func_node->class; func = func_node->func; arg = func_node->args; /* Evaluate first the argument list */ while (arg) { eval_exp(arg); /* The functions are not defined for SQL null argument values, except for eval_cmp and notfound */ if (dfield_is_null(que_node_get_val(arg)) && (class != PARS_FUNC_CMP) && (func != PARS_NOTFOUND_TOKEN) && (func != PARS_PRINTF_TOKEN)) { ut_error; } arg = que_node_get_next(arg); } if (class == PARS_FUNC_CMP) { eval_cmp(func_node); } else if (class == PARS_FUNC_ARITH) { eval_arith(func_node); } else if (class == PARS_FUNC_AGGREGATE) { eval_aggregate(func_node); } else if (class == PARS_FUNC_PREDEFINED) { if (func == PARS_NOTFOUND_TOKEN) { eval_notfound(func_node); } else if (func == PARS_SUBSTR_TOKEN) { eval_substr(func_node); } else if (func == PARS_REPLSTR_TOKEN) { eval_replstr(func_node); } else if (func == PARS_INSTR_TOKEN) { eval_instr(func_node); } else if (func == PARS_BINARY_TO_NUMBER_TOKEN) { eval_binary_to_number(func_node); } else if (func == PARS_CONCAT_TOKEN) { eval_concat(func_node); } else if (func == PARS_TO_BINARY_TOKEN) { eval_to_binary(func_node); } else { eval_predefined(func_node); } } else { ut_ad(class == PARS_FUNC_LOGICAL); eval_logical(func_node); } } haildb-2.3.2/eval/eval0proc.c0000644000175000017500000001475711513177357016647 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1998, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file eval/eval0proc.c Executes SQL stored procedures and their control structures Created 1/20/1998 Heikki Tuuri *******************************************************/ #include "eval0proc.h" #ifdef UNIV_NONINL #include "eval0proc.ic" #endif /**********************************************************************//** Performs an execution step of an if-statement node. @return query thread to run next or NULL */ UNIV_INTERN que_thr_t* if_step( /*====*/ que_thr_t* thr) /*!< in: query thread */ { if_node_t* node; elsif_node_t* elsif_node; ut_ad(thr); node = thr->run_node; ut_ad(que_node_get_type(node) == QUE_NODE_IF); if (thr->prev_node == que_node_get_parent(node)) { /* Evaluate the condition */ eval_exp(node->cond); if (eval_node_get_ibool_val(node->cond)) { /* The condition evaluated to TRUE: start execution from the first statement in the statement list */ thr->run_node = node->stat_list; } else if (node->else_part) { thr->run_node = node->else_part; } else if (node->elsif_list) { elsif_node = node->elsif_list; for (;;) { eval_exp(elsif_node->cond); if (eval_node_get_ibool_val( elsif_node->cond)) { /* The condition evaluated to TRUE: start execution from the first statement in the statement list */ thr->run_node = elsif_node->stat_list; break; } elsif_node = que_node_get_next(elsif_node); if (elsif_node == NULL) { thr->run_node = NULL; break; } } } else { thr->run_node = NULL; } } else { /* Move to the next statement */ ut_ad(que_node_get_next(thr->prev_node) == NULL); thr->run_node = NULL; } if (thr->run_node == NULL) { thr->run_node = que_node_get_parent(node); } return(thr); } /**********************************************************************//** Performs an execution step of a while-statement node. @return query thread to run next or NULL */ UNIV_INTERN que_thr_t* while_step( /*=======*/ que_thr_t* thr) /*!< in: query thread */ { while_node_t* node; ut_ad(thr); node = thr->run_node; ut_ad(que_node_get_type(node) == QUE_NODE_WHILE); ut_ad((thr->prev_node == que_node_get_parent(node)) || (que_node_get_next(thr->prev_node) == NULL)); /* Evaluate the condition */ eval_exp(node->cond); if (eval_node_get_ibool_val(node->cond)) { /* The condition evaluated to TRUE: start execution from the first statement in the statement list */ thr->run_node = node->stat_list; } else { thr->run_node = que_node_get_parent(node); } return(thr); } /**********************************************************************//** Performs an execution step of an assignment statement node. @return query thread to run next or NULL */ UNIV_INTERN que_thr_t* assign_step( /*========*/ que_thr_t* thr) /*!< in: query thread */ { assign_node_t* node; ut_ad(thr); node = thr->run_node; ut_ad(que_node_get_type(node) == QUE_NODE_ASSIGNMENT); /* Evaluate the value to assign */ eval_exp(node->val); eval_node_copy_val(node->var->alias, node->val); thr->run_node = que_node_get_parent(node); return(thr); } /**********************************************************************//** Performs an execution step of a for-loop node. @return query thread to run next or NULL */ UNIV_INTERN que_thr_t* for_step( /*=====*/ que_thr_t* thr) /*!< in: query thread */ { for_node_t* node; que_node_t* parent; lint loop_var_value; ut_ad(thr); node = thr->run_node; ut_ad(que_node_get_type(node) == QUE_NODE_FOR); parent = que_node_get_parent(node); if (thr->prev_node != parent) { /* Move to the next statement */ thr->run_node = que_node_get_next(thr->prev_node); if (thr->run_node != NULL) { return(thr); } /* Increment the value of loop_var */ loop_var_value = 1 + eval_node_get_int_val(node->loop_var); } else { /* Initialize the loop */ eval_exp(node->loop_start_limit); eval_exp(node->loop_end_limit); loop_var_value = eval_node_get_int_val(node->loop_start_limit); node->loop_end_value = (int) eval_node_get_int_val(node->loop_end_limit); } /* Check if we should do another loop */ if (loop_var_value > node->loop_end_value) { /* Enough loops done */ thr->run_node = parent; } else { eval_node_set_int_val(node->loop_var, loop_var_value); thr->run_node = node->stat_list; } return(thr); } /**********************************************************************//** Performs an execution step of an exit statement node. @return query thread to run next or NULL */ UNIV_INTERN que_thr_t* exit_step( /*======*/ que_thr_t* thr) /*!< in: query thread */ { exit_node_t* node; que_node_t* loop_node; ut_ad(thr); node = thr->run_node; ut_ad(que_node_get_type(node) == QUE_NODE_EXIT); /* Loops exit by setting thr->run_node as the loop node's parent, so find our containing loop node and get its parent. */ loop_node = que_node_get_containing_loop_node(node); /* If someone uses an EXIT statement outside of a loop, this will trigger. */ ut_a(loop_node); thr->run_node = que_node_get_parent(loop_node); return(thr); } /**********************************************************************//** Performs an execution step of a return-statement node. @return query thread to run next or NULL */ UNIV_INTERN que_thr_t* return_step( /*========*/ que_thr_t* thr) /*!< in: query thread */ { return_node_t* node; que_node_t* parent; ut_ad(thr); node = thr->run_node; ut_ad(que_node_get_type(node) == QUE_NODE_RETURN); parent = node; while (que_node_get_type(parent) != QUE_NODE_PROC) { parent = que_node_get_parent(parent); } ut_a(parent); thr->run_node = que_node_get_parent(parent); return(thr); } haildb-2.3.2/configure.ac0000644000175000017500000002734211513177357016141 0ustar00pcrewspcrews00000000000000dnl Copyright (C) 2008 Oracle/Innobase Oy dnl Copyright (C) 2010 Monty Taylor dnl dnl This program is free software; you can redistribute it and/or modify dnl it under the terms of the GNU General Public License as published by dnl the Free Software Foundation; version 2 of the License. dnl dnl This program is distributed in the hope that it will be useful, dnl but WITHOUT ANY WARRANTY; without even the implied warranty of dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the dnl GNU General Public License for more details. dnl dnl You should have received a copy of the GNU General Public License dnl along with this program; if not, write to the Free Software dnl Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA dnl Process this file with autoconf to produce a configure script. AC_PREREQ(2.52) dnl Minimum Autoconf version required. #################################### # change version here. --> BOTH PLACES PLEASE! # note: can't put vars in AC_INIT AC_INIT([haildb],[2.3.2],[http://bugs.launchpad.net/haildb]) MAJOR_VERSION=2 MINOR_VERSION=3 SUB_VERSION=2 HAILDB_FULL_VERSION=$MAJOR_VERSION.$MINOR_VERSION.$SUB_VERSION #################################### AC_CONFIG_SRCDIR([include/univ.i]) AC_CONFIG_AUX_DIR([config]) AC_SUBST(HAILDB_FULL_VERSION) PANDORA_CANONICAL_TARGET(less-warnings, warnings-off, force-gcc42, no-vc-changelog) AC_PATH_PROG(uname_prog, uname, no) # increment if interfaces have been added, removed or changed VERSION_CURRENT=6 # increment if source code has changed # set to zero if current is incremented VERSION_REVISION=0 # increment if interfaces have been added # set to zero if interfaces have been removed or changed VERSION_AGE=0 AC_DEFINE_UNQUOTED([IB_API_VERSION_CURRENT],[$VERSION_CURRENT],[Current Version]) AC_DEFINE_UNQUOTED([IB_API_VERSION_REVISION],[$VERSION_REVISION],[Revision Version]) AC_DEFINE_UNQUOTED([IB_API_VERSION_AGE],[$VERSION_AGE],[Age Version]) AC_SUBST(IB_API_VERSION_REVISION) AC_SUBST(IB_API_VERSION_AGE) IB_API_VERSION=$VERSION_CURRENT:$VERSION_REVISION:$VERSION_AGE AC_SUBST(IB_API_VERSION) PANDORA_REQUIRE_BISON # libhaildb versioning when linked with GNU ld. if test "$lt_cv_prog_gnu_ld" = "yes" then LD_VERSION_SCRIPT="-Wl,--version-script=\$(top_srcdir)/libhaildb.ver" fi AC_SUBST(LD_VERSION_SCRIPT) ##### ## Check for system header files used by InnoDB. ##### AC_CHECK_HEADERS([pthread.h]) AC_CHECK_HEADERS(valgrind/memcheck.h) case "$target_os" in lin*) TARGET_LINUX="true"; CFLAGS="$CFLAGS -DUNIV_LINUX";; hpux10*) CFLAGS="$CFLAGS -DUNIV_MUST_NOT_INLINE -DUNIV_HPUX -DUNIV_HPUX10";; hp*) CFLAGS="$CFLAGS -DUNIV_MUST_NOT_INLINE -DUNIV_HPUX";; aix*) CFLAGS="$CFLAGS -DUNIV_AIX";; irix*|osf*|sysv5uw7*|openbsd*) CFLAGS="$CFLAGS -DUNIV_MUST_NOT_INLINE";; *solaris*|*SunOS*) CFLAGS="$CFLAGS -DUNIV_SOLARIS";; esac # do not reindent the following lines, they are copied from InnoDB Plugin's # plug.in and this way it is easier for maintenance AC_MSG_CHECKING(whether GCC atomic builtins are available) # either define HAVE_IB_GCC_ATOMIC_BUILTINS or not AC_TRY_RUN( [ int main() { long x; long y; long res; char c; x = 10; y = 123; res = __sync_bool_compare_and_swap(&x, x, y); if (!res || x != y) { return(1); } x = 10; y = 123; res = __sync_bool_compare_and_swap(&x, x + 1, y); if (res || x != 10) { return(1); } x = 10; y = 123; res = __sync_add_and_fetch(&x, y); if (res != 123 + 10 || x != 123 + 10) { return(1); } c = 10; res = __sync_lock_test_and_set(&c, 123); if (res != 10 || c != 123) { return(1); } return(0); } ], [ AC_DEFINE([HAVE_IB_GCC_ATOMIC_BUILTINS], [1], [GCC atomic builtins are available]) AC_MSG_RESULT(yes) ], [ AC_MSG_RESULT(no) ] ) AC_MSG_CHECKING(whether pthread_t can be used by GCC atomic builtins) # either define HAVE_IB_ATOMIC_PTHREAD_T_GCC or not AC_TRY_RUN( [ #include #include int main(int argc, char** argv) { pthread_t x1; pthread_t x2; pthread_t x3; memset(&x1, 0x0, sizeof(x1)); memset(&x2, 0x0, sizeof(x2)); memset(&x3, 0x0, sizeof(x3)); __sync_bool_compare_and_swap(&x1, x2, x3); return(0); } ], [ AC_DEFINE([HAVE_IB_ATOMIC_PTHREAD_T_GCC], [1], [pthread_t can be used by GCC atomic builtins]) AC_MSG_RESULT(yes) ], [ AC_MSG_RESULT(no) ] ) AC_MSG_CHECKING(whether Solaris libc atomic functions are available) # either define HAVE_IB_SOLARIS_ATOMICS or not AC_CHECK_FUNCS(atomic_add_long \ atomic_cas_32 \ atomic_cas_64 \ atomic_cas_ulong, AC_DEFINE([HAVE_IB_SOLARIS_ATOMICS], [1], [Define to 1 if Solaris libc atomic functions \ are available]) ) AC_MSG_CHECKING(whether pthread_t can be used by Solaris libc atomic functions) # either define HAVE_IB_ATOMIC_PTHREAD_T_SOLARIS or not AC_TRY_RUN( [ #include #include int main(int argc, char** argv) { pthread_t x1; pthread_t x2; pthread_t x3; memset(&x1, 0x0, sizeof(x1)); memset(&x2, 0x0, sizeof(x2)); memset(&x3, 0x0, sizeof(x3)); if (sizeof(pthread_t) == 4) { atomic_cas_32(&x1, x2, x3); } else if (sizeof(pthread_t) == 8) { atomic_cas_64(&x1, x2, x3); } else { return(1); } return(0); } ], [ AC_DEFINE([HAVE_IB_ATOMIC_PTHREAD_T_SOLARIS], [1], [pthread_t can be used by solaris atomics]) AC_MSG_RESULT(yes) ], [ AC_MSG_RESULT(no) ] ) # this is needed to know which one of atomic_cas_32() or atomic_cas_64() # to use in the source AC_CHECK_SIZEOF([pthread_t], [], [#include ]) # Check for x86 PAUSE instruction AC_MSG_CHECKING(for x86 PAUSE instruction) # We have to actually try running the test program, because of a bug # in Solaris on x86_64, where it wrongly reports that PAUSE is not # supported when trying to run an application. See # http://bugs.opensolaris.org/bugdatabase/printableBug.do?bug_id=6478684 # We use ib_ prefix to avoid collisoins if this code is added to # mysql's configure.in. AC_TRY_RUN( [ int main() { __asm__ __volatile__ ("pause"); return(0); } ], [ AC_DEFINE([HAVE_IB_PAUSE_INSTRUCTION], [1], [Does x86 PAUSE instruction exist]) AC_MSG_RESULT(yes) ], [ AC_MSG_RESULT(no) ], [ AC_MSG_RESULT(no) ] ) # end of "do not reindent the following lines" # # The behavior of --with-atomic-ops must be: # if this switch is not specified then # if gcc atomics are present, use them # else if solaris atomics are present, use them # else use innodb atomics # else the switch is specified with some value, # check if the requested atomics are present # if present, use them # else emit error during ./configure # AC_ARG_WITH([atomic-ops], AC_HELP_STRING([--with-atomic-ops= gcc_builtins|solaris|innodb], [Implement the atomic operations using GCC builtin atomic functions or \ using the Solaris 10 atomic_ops(3C) from libc or InnoDB's own \ implementation])) case "$with_atomic_ops" in "gcc_builtins") AC_CHECK_DECLS( HAVE_IB_GCC_ATOMIC_BUILTINS, AC_MSG_NOTICE(will use the requested GCC atomic builtins), AC_MSG_ERROR(the requested GCC atomic builtins are not available) ) AC_DEFINE([IB_ATOMIC_MODE_GCC_ATOMIC_BUILTINS], [1], [Use GCC builtin atomic functions]) ;; "solaris") AC_CHECK_DECLS( HAVE_IB_SOLARIS_ATOMICS, AC_MSG_NOTICE(will use the requested Solaris atomic functions), AC_MSG_ERROR(the requested Solaris atomic functions are not available) ) AC_DEFINE([IB_ATOMIC_MODE_SOLARIS_ATOMICS], [1], [Use Solaris 10 atomic_ops(3C) from libc]) ;; "innodb") AC_MSG_NOTICE(will use the requested InnoDB's own implementation of mutexes and rw_locks) # this macro is not used but is here for clarity AC_DEFINE([IB_ATOMIC_MODE_INNODB], [1], [Use InnoDB's own implementation]) ;; "") AC_CHECK_DECLS( HAVE_IB_GCC_ATOMIC_BUILTINS, [ AC_MSG_NOTICE(will use the GCC atomic builtins (auto selected)); AC_DEFINE([IB_ATOMIC_MODE_GCC_ATOMIC_BUILTINS], [1], [Use GCC builtin atomic functions]) ], [ AC_CHECK_DECLS( HAVE_IB_SOLARIS_ATOMICS, [ AC_MSG_NOTICE(will use the Solaris atomic functions (auto selected)); AC_DEFINE([IB_ATOMIC_MODE_SOLARIS_ATOMICS], [1], [Use Solaris 10 atomic_ops(3C) from libc]) ], AC_MSG_NOTICE(will use InnoDB's own implementation of mutexes and rw_locks (auto selected)) ) ] ) ;; *) AC_MSG_ERROR(Unknown value "$with_atomic_ops" specified for --with-atomic-ops) ;; esac # For large pages support if test "$TARGET_LINUX" = "true"; then # For SHM_HUGETLB on Linux AC_CHECK_DECLS(SHM_HUGETLB, AC_DEFINE([HAVE_LARGE_PAGES], [1], [Define if you have large pages support]) AC_DEFINE([HUGETLB_USE_PROC_MEMINFO], [1], [Define if /proc/meminfo shows the huge page size \ (Linux only)]) , , [ #include ]) fi AC_C_CONST AC_C_INLINE AC_C_VOLATILE AC_CHECK_TYPES([int64_t, uint64_t, int32_t, uint32_t, int16_t, uint16_t, int8_t, uint8_t, unsigned long long int, long long int], [], [AC_MSG_ERROR([Need the C99 integer types from stdint.h])], [#include ]) AC_STRUCT_TM AC_TYPE_OFF_T AC_TYPE_SIZE_T AC_STRUCT_ST_RDEV AC_CHECK_SIZEOF(void*, 4) AC_CHECK_SIZEOF(char*, 4) AC_CHECK_SIZEOF(short, 2) AC_CHECK_SIZEOF(int, 4) AC_CHECK_SIZEOF(long, 4) AC_CHECK_SIZEOF(long long, 8) # off_t is not a builtin type AC_CHECK_SIZEOF(off_t, 4) AC_CHECK_TYPES([off_t, ulong], [], [], [#include ]) AC_CHECK_TYPES([size_t], [], [], [#include ]) AC_CHECK_TYPES([u_int32_t]) AC_CHECK_LIB(pthread, pthread_mutex_trylock) # Required on Solaris AC_CHECK_FUNC(sched_yield, , AC_CHECK_LIB(posix4, sched_yield)) AC_CHECK_FUNCS(strdup strlcpy) ##### # Check whether the user wants to disable compressed tables functionality ##### AC_ARG_ENABLE(compression, [ --disable-compression disable compressed tables support], [ ], enable_compression=yes) if test $enable_compression = yes; then # Check for ZLib functions AC_CHECK_FUNC(deflate, , AC_CHECK_LIB(z, deflate, , AC_MSG_ERROR([Zlib is required for compression to work]))) AC_CHECK_FUNC(inflate, , AC_CHECK_LIB(z, inflate, , AC_MSG_ERROR([Zlib is required for compression to work]))) AC_CHECK_HEADERS(zlib.h) AC_DEFINE([WITH_ZIP], [1], [Compressed tables support]) fi AM_CONDITIONAL(WITH_ZIP, test $enable_compression = yes) # End Zip tests ##### # Check whether the user wants to disable XA functionality ##### AC_ARG_ENABLE(xa, [ --disable-xa disable XA support], [ ], enable_xa=yes AC_DEFINE([WITH_XOPEN], [1], [XA support])) AM_CONDITIONAL(WITH_XOPEN, test $enable_xa = yes) AC_CHECK_FUNCS( \ bcmp \ fcntl \ finite \ fsync \ ftruncate \ getcwd \ getrusage \ index \ localtime_r \ locking \ memcpy \ memmove \ perror \ pread \ mmap \ getpagesize \ pthread_attr_setstacksize \ pthread_barrier_destroy \ pthread_barrier_init \ pthread_barrier_wait \ pthread_setprio \ rename \ rint \ shmget \ shmat \ shmdt \ shmctl \ sleep \ snprintf \ stpcpy \ strcasecmp \ strerror \ strstr \ strtoul \ tell) AM_CONDITIONAL([HAVE_PTHREAD_BARRIER], [test "$HAVE_PTHREAD_BARRIER_INIT" = yes]) AM_CPPFLAGS="${AM_CPPFLAGS} -I\$(top_srcdir)/include -I\$(top_builddir)/include" AM_CFLAGS="${AM_CFLAGS} ${NO_WERROR}" AC_CONFIG_FILES([ Makefile haildb.spec ]) AC_OUTPUT haildb-2.3.2/rem/0000755000175000017500000000000011513177437014425 5ustar00pcrewspcrews00000000000000haildb-2.3.2/rem/rem0rec.c0000644000175000017500000013546611513177357016146 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1994, 2010, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /********************************************************************//** @file rem/rem0rec.c Record manager Created 5/30/1994 Heikki Tuuri *************************************************************************/ #include "rem0rec.h" #ifdef UNIV_NONINL #include "rem0rec.ic" #endif #include "mtr0mtr.h" #include "mtr0log.h" /* PHYSICAL RECORD (OLD STYLE) =========================== The physical record, which is the data type of all the records found in index pages of the database, has the following format (lower addresses and more significant bits inside a byte are below represented on a higher text line): | offset of the end of the last field of data, the most significant bit is set to 1 if and only if the field is SQL-null, if the offset is 2-byte, then the second most significant bit is set to 1 if the field is stored on another page: mostly this will occur in the case of big BLOB fields | ... | offset of the end of the first field of data + the SQL-null bit | | 4 bits used to delete mark a record, and mark a predefined minimum record in alphabetical order | | 4 bits giving the number of records owned by this record (this term is explained in page0page.h) | | 13 bits giving the order number of this record in the heap of the index page | | 10 bits giving the number of fields in this record | | 1 bit which is set to 1 if the offsets above are given in one byte format, 0 if in two byte format | | two bytes giving an absolute pointer to the next record in the page | ORIGIN of the record | first field of data | ... | last field of data | The origin of the record is the start address of the first field of data. The offsets are given relative to the origin. The offsets of the data fields are stored in an inverted order because then the offset of the first fields are near the origin, giving maybe a better processor cache hit rate in searches. The offsets of the data fields are given as one-byte (if there are less than 127 bytes of data in the record) or two-byte unsigned integers. The most significant bit is not part of the offset, instead it indicates the SQL-null if the bit is set to 1. */ /* PHYSICAL RECORD (NEW STYLE) =========================== The physical record, which is the data type of all the records found in index pages of the database, has the following format (lower addresses and more significant bits inside a byte are below represented on a higher text line): | length of the last non-null variable-length field of data: if the maximum length is 255, one byte; otherwise, 0xxxxxxx (one byte, length=0..127), or 1exxxxxxxxxxxxxx (two bytes, length=128..16383, extern storage flag) | ... | length of first variable-length field of data | | SQL-null flags (1 bit per nullable field), padded to full bytes | | 4 bits used to delete mark a record, and mark a predefined minimum record in alphabetical order | | 4 bits giving the number of records owned by this record (this term is explained in page0page.h) | | 13 bits giving the order number of this record in the heap of the index page | | 3 bits record type: 000=conventional, 001=node pointer (inside B-tree), 010=infimum, 011=supremum, 1xx=reserved | | two bytes giving a relative pointer to the next record in the page | ORIGIN of the record | first field of data | ... | last field of data | The origin of the record is the start address of the first field of data. The offsets are given relative to the origin. The offsets of the data fields are stored in an inverted order because then the offset of the first fields are near the origin, giving maybe a better processor cache hit rate in searches. The offsets of the data fields are given as one-byte (if there are less than 127 bytes of data in the record) or two-byte unsigned integers. The most significant bit is not part of the offset, instead it indicates the SQL-null if the bit is set to 1. */ /* CANONICAL COORDINATES. A record can be seen as a single string of 'characters' in the following way: catenate the bytes in each field, in the order of fields. An SQL-null field is taken to be an empty sequence of bytes. Then after the position of each field insert in the string the 'character' , except that after an SQL-null field insert . Now the ordinal position of each byte in this canonical string is its canonical coordinate. So, for the record ("AA", SQL-NULL, "BB", ""), the canonical string is "AABB". We identify prefixes (= initial segments) of a record with prefixes of the canonical string. The canonical length of the prefix is the length of the corresponding prefix of the canonical string. The canonical length of a record is the length of its canonical string. For example, the maximal common prefix of records ("AA", SQL-NULL, "BB", "C") and ("AA", SQL-NULL, "B", "C") is "AAB", and its canonical length is 5. A complete-field prefix of a record is a prefix which ends at the end of some field (containing also ). A record is a complete-field prefix of another record, if the corresponding canonical strings have the same property. */ /***************************************************************//** Validates the consistency of an old-style physical record. @return TRUE if ok */ static ibool rec_validate_old( /*=============*/ const rec_t* rec); /*!< in: physical record */ /******************************************************//** Determine how many of the first n columns in a compact physical record are stored externally. @return number of externally stored columns */ UNIV_INTERN ulint rec_get_n_extern_new( /*=================*/ const rec_t* rec, /*!< in: compact physical record */ dict_index_t* index, /*!< in: record descriptor */ ulint n) /*!< in: number of columns to scan */ { const byte* nulls; const byte* lens; dict_field_t* field; ulint null_mask; ulint n_extern; ulint i; ut_ad(dict_table_is_comp(index->table)); ut_ad(rec_get_status(rec) == REC_STATUS_ORDINARY); ut_ad(n == ULINT_UNDEFINED || n <= dict_index_get_n_fields(index)); if (n == ULINT_UNDEFINED) { n = dict_index_get_n_fields(index); } nulls = rec - (REC_N_NEW_EXTRA_BYTES + 1); lens = nulls - UT_BITS_IN_BYTES(index->n_nullable); null_mask = 1; n_extern = 0; i = 0; /* read the lengths of fields 0..n */ do { ulint len; field = dict_index_get_nth_field(index, i); if (!(dict_field_get_col(field)->prtype & DATA_NOT_NULL)) { /* nullable field => read the null flag */ if (UNIV_UNLIKELY(!(byte) null_mask)) { nulls--; null_mask = 1; } if (*nulls & null_mask) { null_mask <<= 1; /* No length is stored for NULL fields. */ continue; } null_mask <<= 1; } if (UNIV_UNLIKELY(!field->fixed_len)) { /* Variable-length field: read the length */ const dict_col_t* col = dict_field_get_col(field); len = *lens--; /* If the maximum length of the field is up to 255 bytes, the actual length is always stored in one byte. If the maximum length is more than 255 bytes, the actual length is stored in one byte for 0..127. The length will be encoded in two bytes when it is 128 or more, or when the field is stored externally. */ if (UNIV_UNLIKELY(col->len > 255) || UNIV_UNLIKELY(col->mtype == DATA_BLOB)) { if (len & 0x80) { /* 1exxxxxxx xxxxxxxx */ if (len & 0x40) { n_extern++; } lens--; } } } } while (++i < n); return(n_extern); } /******************************************************//** Determine the offset to each field in a leaf-page record in ROW_FORMAT=COMPACT. This is a special case of rec_init_offsets() and rec_get_offsets_func(). */ UNIV_INTERN void rec_init_offsets_comp_ordinary( /*===========================*/ const rec_t* rec, /*!< in: physical record in ROW_FORMAT=COMPACT */ ulint extra, /*!< in: number of bytes to reserve between the record header and the data payload (usually REC_N_NEW_EXTRA_BYTES) */ const dict_index_t* index, /*!< in: record descriptor */ ulint* offsets)/*!< in/out: array of offsets; in: n=rec_offs_n_fields(offsets) */ { ulint i = 0; ulint offs = 0; ulint any_ext = 0; const byte* nulls = rec - (extra + 1); const byte* lens = nulls - UT_BITS_IN_BYTES(index->n_nullable); dict_field_t* field; ulint null_mask = 1; #ifdef UNIV_DEBUG /* We cannot invoke rec_offs_make_valid() here, because it can hold that extra != REC_N_NEW_EXTRA_BYTES. Similarly, rec_offs_validate() will fail in that case, because it invokes rec_get_status(). */ offsets[2] = (ulint) rec; offsets[3] = (ulint) index; #endif /* UNIV_DEBUG */ /* read the lengths of fields 0..n */ do { ulint len; field = dict_index_get_nth_field(index, i); if (!(dict_field_get_col(field)->prtype & DATA_NOT_NULL)) { /* nullable field => read the null flag */ if (UNIV_UNLIKELY(!(byte) null_mask)) { nulls--; null_mask = 1; } if (*nulls & null_mask) { null_mask <<= 1; /* No length is stored for NULL fields. We do not advance offs, and we set the length to zero and enable the SQL NULL flag in offsets[]. */ len = offs | REC_OFFS_SQL_NULL; goto resolved; } null_mask <<= 1; } if (UNIV_UNLIKELY(!field->fixed_len)) { /* Variable-length field: read the length */ const dict_col_t* col = dict_field_get_col(field); len = *lens--; /* If the maximum length of the field is up to 255 bytes, the actual length is always stored in one byte. If the maximum length is more than 255 bytes, the actual length is stored in one byte for 0..127. The length will be encoded in two bytes when it is 128 or more, or when the field is stored externally. */ if (UNIV_UNLIKELY(col->len > 255) || UNIV_UNLIKELY(col->mtype == DATA_BLOB)) { if (len & 0x80) { /* 1exxxxxxx xxxxxxxx */ len <<= 8; len |= *lens--; offs += len & 0x3fff; if (UNIV_UNLIKELY(len & 0x4000)) { ut_ad(dict_index_is_clust (index)); any_ext = REC_OFFS_EXTERNAL; len = offs | REC_OFFS_EXTERNAL; } else { len = offs; } goto resolved; } } len = offs += len; } else { len = offs += field->fixed_len; } resolved: rec_offs_base(offsets)[i + 1] = len; } while (++i < rec_offs_n_fields(offsets)); *rec_offs_base(offsets) = (rec - (lens + 1)) | REC_OFFS_COMPACT | any_ext; } /******************************************************//** The following function determines the offsets to each field in the record. The offsets are written to a previously allocated array of ulint, where rec_offs_n_fields(offsets) has been initialized to the number of fields in the record. The rest of the array will be initialized by this function. rec_offs_base(offsets)[0] will be set to the extra size (if REC_OFFS_COMPACT is set, the record is in the new format; if REC_OFFS_EXTERNAL is set, the record contains externally stored columns), and rec_offs_base(offsets)[1..n_fields] will be set to offsets past the end of fields 0..n_fields, or to the beginning of fields 1..n_fields+1. When the high-order bit of the offset at [i+1] is set (REC_OFFS_SQL_NULL), the field i is NULL. When the second high-order bit of the offset at [i+1] is set (REC_OFFS_EXTERNAL), the field i is being stored externally. */ static void rec_init_offsets( /*=============*/ const rec_t* rec, /*!< in: physical record */ const dict_index_t* index, /*!< in: record descriptor */ ulint* offsets)/*!< in/out: array of offsets; in: n=rec_offs_n_fields(offsets) */ { ulint i = 0; ulint offs; rec_offs_make_valid(rec, index, offsets); if (dict_table_is_comp(index->table)) { const byte* nulls; const byte* lens; dict_field_t* field; ulint null_mask; ulint status = rec_get_status(rec); ulint n_node_ptr_field = ULINT_UNDEFINED; switch (UNIV_EXPECT(status, REC_STATUS_ORDINARY)) { case REC_STATUS_INFIMUM: case REC_STATUS_SUPREMUM: /* the field is 8 bytes long */ rec_offs_base(offsets)[0] = REC_N_NEW_EXTRA_BYTES | REC_OFFS_COMPACT; rec_offs_base(offsets)[1] = 8; return; case REC_STATUS_NODE_PTR: n_node_ptr_field = dict_index_get_n_unique_in_tree(index); break; case REC_STATUS_ORDINARY: rec_init_offsets_comp_ordinary(rec, REC_N_NEW_EXTRA_BYTES, index, offsets); return; } nulls = rec - (REC_N_NEW_EXTRA_BYTES + 1); lens = nulls - UT_BITS_IN_BYTES(index->n_nullable); offs = 0; null_mask = 1; /* read the lengths of fields 0..n */ do { ulint len; if (UNIV_UNLIKELY(i == n_node_ptr_field)) { len = offs += 4; goto resolved; } field = dict_index_get_nth_field(index, i); if (!(dict_field_get_col(field)->prtype & DATA_NOT_NULL)) { /* nullable field => read the null flag */ if (UNIV_UNLIKELY(!(byte) null_mask)) { nulls--; null_mask = 1; } if (*nulls & null_mask) { null_mask <<= 1; /* No length is stored for NULL fields. We do not advance offs, and we set the length to zero and enable the SQL NULL flag in offsets[]. */ len = offs | REC_OFFS_SQL_NULL; goto resolved; } null_mask <<= 1; } if (UNIV_UNLIKELY(!field->fixed_len)) { /* Variable-length field: read the length */ const dict_col_t* col = dict_field_get_col(field); len = *lens--; /* If the maximum length of the field is up to 255 bytes, the actual length is always stored in one byte. If the maximum length is more than 255 bytes, the actual length is stored in one byte for 0..127. The length will be encoded in two bytes when it is 128 or more, or when the field is stored externally. */ if (UNIV_UNLIKELY(col->len > 255) || UNIV_UNLIKELY(col->mtype == DATA_BLOB)) { if (len & 0x80) { /* 1exxxxxxx xxxxxxxx */ len <<= 8; len |= *lens--; /* B-tree node pointers must not contain externally stored columns. Thus the "e" flag must be 0. */ ut_a(!(len & 0x4000)); offs += len & 0x3fff; len = offs; goto resolved; } } len = offs += len; } else { len = offs += field->fixed_len; } resolved: rec_offs_base(offsets)[i + 1] = len; } while (++i < rec_offs_n_fields(offsets)); *rec_offs_base(offsets) = (rec - (lens + 1)) | REC_OFFS_COMPACT; } else { /* Old-style record: determine extra size and end offsets */ offs = REC_N_OLD_EXTRA_BYTES; if (rec_get_1byte_offs_flag(rec)) { offs += rec_offs_n_fields(offsets); *rec_offs_base(offsets) = offs; /* Determine offsets to fields */ do { offs = rec_1_get_field_end_info(rec, i); if (offs & REC_1BYTE_SQL_NULL_MASK) { offs &= ~REC_1BYTE_SQL_NULL_MASK; offs |= REC_OFFS_SQL_NULL; } rec_offs_base(offsets)[1 + i] = offs; } while (++i < rec_offs_n_fields(offsets)); } else { offs += 2 * rec_offs_n_fields(offsets); *rec_offs_base(offsets) = offs; /* Determine offsets to fields */ do { offs = rec_2_get_field_end_info(rec, i); if (offs & REC_2BYTE_SQL_NULL_MASK) { offs &= ~REC_2BYTE_SQL_NULL_MASK; offs |= REC_OFFS_SQL_NULL; } if (offs & REC_2BYTE_EXTERN_MASK) { offs &= ~REC_2BYTE_EXTERN_MASK; offs |= REC_OFFS_EXTERNAL; *rec_offs_base(offsets) |= REC_OFFS_EXTERNAL; } rec_offs_base(offsets)[1 + i] = offs; } while (++i < rec_offs_n_fields(offsets)); } } } /******************************************************//** The following function determines the offsets to each field in the record. It can reuse a previously returned array. @return the new offsets */ UNIV_INTERN ulint* rec_get_offsets_func( /*=================*/ const rec_t* rec, /*!< in: physical record */ const dict_index_t* index, /*!< in: record descriptor */ ulint* offsets,/*!< in/out: array consisting of offsets[0] allocated elements, or an array from rec_get_offsets(), or NULL */ ulint n_fields,/*!< in: maximum number of initialized fields (ULINT_UNDEFINED if all fields) */ mem_heap_t** heap, /*!< in/out: memory heap */ const char* file, /*!< in: file name where called */ ulint line) /*!< in: line number where called */ { ulint n; ulint size; ut_ad(rec); ut_ad(index); ut_ad(heap); if (dict_table_is_comp(index->table)) { switch (UNIV_EXPECT(rec_get_status(rec), REC_STATUS_ORDINARY)) { case REC_STATUS_ORDINARY: n = dict_index_get_n_fields(index); break; case REC_STATUS_NODE_PTR: n = dict_index_get_n_unique_in_tree(index) + 1; break; case REC_STATUS_INFIMUM: case REC_STATUS_SUPREMUM: /* infimum or supremum record */ n = 1; break; default: ut_error; return(NULL); } } else { n = rec_get_n_fields_old(rec); } if (UNIV_UNLIKELY(n_fields < n)) { n = n_fields; } size = n + (1 + REC_OFFS_HEADER_SIZE); if (UNIV_UNLIKELY(!offsets) || UNIV_UNLIKELY(rec_offs_get_n_alloc(offsets) < size)) { if (UNIV_UNLIKELY(!*heap)) { *heap = mem_heap_create_func(size * sizeof(ulint), MEM_HEAP_DYNAMIC, file, line); } offsets = mem_heap_alloc(*heap, size * sizeof(ulint)); rec_offs_set_n_alloc(offsets, size); } rec_offs_set_n_fields(offsets, n); rec_init_offsets(rec, index, offsets); return(offsets); } /******************************************************//** The following function determines the offsets to each field in the record. It can reuse a previously allocated array. */ UNIV_INTERN void rec_get_offsets_reverse( /*====================*/ const byte* extra, /*!< in: the extra bytes of a compact record in reverse order, excluding the fixed-size REC_N_NEW_EXTRA_BYTES */ const dict_index_t* index, /*!< in: record descriptor */ ulint node_ptr,/*!< in: nonzero=node pointer, 0=leaf node */ ulint* offsets)/*!< in/out: array consisting of offsets[0] allocated elements */ { ulint n; ulint i; ulint offs; ulint any_ext; const byte* nulls; const byte* lens; dict_field_t* field; ulint null_mask; ulint n_node_ptr_field; ut_ad(extra); ut_ad(index); ut_ad(offsets); ut_ad(dict_table_is_comp(index->table)); if (UNIV_UNLIKELY(node_ptr)) { n_node_ptr_field = dict_index_get_n_unique_in_tree(index); n = n_node_ptr_field + 1; } else { n_node_ptr_field = ULINT_UNDEFINED; n = dict_index_get_n_fields(index); } ut_a(rec_offs_get_n_alloc(offsets) >= n + (1 + REC_OFFS_HEADER_SIZE)); rec_offs_set_n_fields(offsets, n); nulls = extra; lens = nulls + UT_BITS_IN_BYTES(index->n_nullable); i = offs = 0; null_mask = 1; any_ext = 0; /* read the lengths of fields 0..n */ do { ulint len; if (UNIV_UNLIKELY(i == n_node_ptr_field)) { len = offs += 4; goto resolved; } field = dict_index_get_nth_field(index, i); if (!(dict_field_get_col(field)->prtype & DATA_NOT_NULL)) { /* nullable field => read the null flag */ if (UNIV_UNLIKELY(!(byte) null_mask)) { nulls++; null_mask = 1; } if (*nulls & null_mask) { null_mask <<= 1; /* No length is stored for NULL fields. We do not advance offs, and we set the length to zero and enable the SQL NULL flag in offsets[]. */ len = offs | REC_OFFS_SQL_NULL; goto resolved; } null_mask <<= 1; } if (UNIV_UNLIKELY(!field->fixed_len)) { /* Variable-length field: read the length */ const dict_col_t* col = dict_field_get_col(field); len = *lens++; /* If the maximum length of the field is up to 255 bytes, the actual length is always stored in one byte. If the maximum length is more than 255 bytes, the actual length is stored in one byte for 0..127. The length will be encoded in two bytes when it is 128 or more, or when the field is stored externally. */ if (UNIV_UNLIKELY(col->len > 255) || UNIV_UNLIKELY(col->mtype == DATA_BLOB)) { if (len & 0x80) { /* 1exxxxxxx xxxxxxxx */ len <<= 8; len |= *lens++; offs += len & 0x3fff; if (UNIV_UNLIKELY(len & 0x4000)) { any_ext = REC_OFFS_EXTERNAL; len = offs | REC_OFFS_EXTERNAL; } else { len = offs; } goto resolved; } } len = offs += len; } else { len = offs += field->fixed_len; } resolved: rec_offs_base(offsets)[i + 1] = len; } while (++i < rec_offs_n_fields(offsets)); ut_ad(lens >= extra); *rec_offs_base(offsets) = (lens - extra + REC_N_NEW_EXTRA_BYTES) | REC_OFFS_COMPACT | any_ext; } /************************************************************//** The following function is used to get the offset to the nth data field in an old-style record. @return offset to the field */ UNIV_INTERN ulint rec_get_nth_field_offs_old( /*=======================*/ const rec_t* rec, /*!< in: record */ ulint n, /*!< in: index of the field */ ulint* len) /*!< out: length of the field; UNIV_SQL_NULL if SQL null */ { ulint os; ulint next_os; ut_ad(len); ut_a(rec); ut_a(n < rec_get_n_fields_old(rec)); if (rec_get_1byte_offs_flag(rec)) { os = rec_1_get_field_start_offs(rec, n); next_os = rec_1_get_field_end_info(rec, n); if (next_os & REC_1BYTE_SQL_NULL_MASK) { *len = UNIV_SQL_NULL; return(os); } next_os = next_os & ~REC_1BYTE_SQL_NULL_MASK; } else { os = rec_2_get_field_start_offs(rec, n); next_os = rec_2_get_field_end_info(rec, n); if (next_os & REC_2BYTE_SQL_NULL_MASK) { *len = UNIV_SQL_NULL; return(os); } next_os = next_os & ~(REC_2BYTE_SQL_NULL_MASK | REC_2BYTE_EXTERN_MASK); } *len = next_os - os; ut_ad(*len < UNIV_PAGE_SIZE); return(os); } /**********************************************************//** Determines the size of a data tuple prefix in ROW_FORMAT=COMPACT. @return total size */ UNIV_INTERN ulint rec_get_converted_size_comp_prefix( /*===============================*/ const dict_index_t* index, /*!< in: record descriptor; dict_table_is_comp() is assumed to hold, even if it does not */ const dfield_t* fields, /*!< in: array of data fields */ ulint n_fields,/*!< in: number of data fields */ ulint* extra) /*!< out: extra size */ { ulint extra_size; ulint data_size; ulint i; ut_ad(index); ut_ad(fields); ut_ad(n_fields > 0); ut_ad(n_fields <= dict_index_get_n_fields(index)); extra_size = REC_N_NEW_EXTRA_BYTES + UT_BITS_IN_BYTES(index->n_nullable); data_size = 0; /* read the lengths of fields 0..n */ for (i = 0; i < n_fields; i++) { const dict_field_t* field; ulint len; const dict_col_t* col; field = dict_index_get_nth_field(index, i); len = dfield_get_len(&fields[i]); col = dict_field_get_col(field); ut_ad(dict_col_type_assert_equal(col, dfield_get_type(&fields[i]))); if (dfield_is_null(&fields[i])) { /* No length is stored for NULL fields. */ ut_ad(!(col->prtype & DATA_NOT_NULL)); continue; } ut_ad(len <= col->len || col->mtype == DATA_BLOB); /* If the maximum length of a variable-length field is up to 255 bytes, the actual length is always stored in one byte. If the maximum length is more than 255 bytes, the actual length is stored in one byte for 0..127. The length will be encoded in two bytes when it is 128 or more, or when the field is stored externally. */ if (field->fixed_len) { ut_ad(len == field->fixed_len); /* dict_index_add_col() should guarantee this */ ut_ad(!field->prefix_len || field->fixed_len == field->prefix_len); } else if (dfield_is_ext(&fields[i])) { ut_ad(col->len >= 256 || col->mtype == DATA_BLOB); extra_size += 2; } else if (len < 128 || (col->len < 256 && col->mtype != DATA_BLOB)) { extra_size++; } else { /* For variable-length columns, we look up the maximum length from the column itself. If this is a prefix index column shorter than 256 bytes, this will waste one byte. */ extra_size += 2; } data_size += len; } if (UNIV_LIKELY_NULL(extra)) { *extra = extra_size; } return(extra_size + data_size); } /**********************************************************//** Determines the size of a data tuple in ROW_FORMAT=COMPACT. @return total size */ UNIV_INTERN ulint rec_get_converted_size_comp( /*========================*/ const dict_index_t* index, /*!< in: record descriptor; dict_table_is_comp() is assumed to hold, even if it does not */ ulint status, /*!< in: status bits of the record */ const dfield_t* fields, /*!< in: array of data fields */ ulint n_fields,/*!< in: number of data fields */ ulint* extra) /*!< out: extra size */ { ulint size; ut_ad(index); ut_ad(fields); ut_ad(n_fields > 0); switch (UNIV_EXPECT(status, REC_STATUS_ORDINARY)) { case REC_STATUS_ORDINARY: ut_ad(n_fields == dict_index_get_n_fields(index)); size = 0; break; case REC_STATUS_NODE_PTR: n_fields--; ut_ad(n_fields == dict_index_get_n_unique_in_tree(index)); ut_ad(dfield_get_len(&fields[n_fields]) == REC_NODE_PTR_SIZE); size = REC_NODE_PTR_SIZE; /* child page number */ break; case REC_STATUS_INFIMUM: case REC_STATUS_SUPREMUM: /* infimum or supremum record, 8 data bytes */ if (UNIV_LIKELY_NULL(extra)) { *extra = REC_N_NEW_EXTRA_BYTES; } return(REC_N_NEW_EXTRA_BYTES + 8); default: ut_error; return(ULINT_UNDEFINED); } return(size + rec_get_converted_size_comp_prefix(index, fields, n_fields, extra)); } /***********************************************************//** Sets the value of the ith field SQL null bit of an old-style record. */ UNIV_INTERN void rec_set_nth_field_null_bit( /*=======================*/ rec_t* rec, /*!< in: record */ ulint i, /*!< in: ith field */ ibool val) /*!< in: value to set */ { ulint info; if (rec_get_1byte_offs_flag(rec)) { info = rec_1_get_field_end_info(rec, i); if (val) { info = info | REC_1BYTE_SQL_NULL_MASK; } else { info = info & ~REC_1BYTE_SQL_NULL_MASK; } rec_1_set_field_end_info(rec, i, info); return; } info = rec_2_get_field_end_info(rec, i); if (val) { info = info | REC_2BYTE_SQL_NULL_MASK; } else { info = info & ~REC_2BYTE_SQL_NULL_MASK; } rec_2_set_field_end_info(rec, i, info); } /***********************************************************//** Sets an old-style record field to SQL null. The physical size of the field is not changed. */ UNIV_INTERN void rec_set_nth_field_sql_null( /*=======================*/ rec_t* rec, /*!< in: record */ ulint n) /*!< in: index of the field */ { ulint offset; offset = rec_get_field_start_offs(rec, n); data_write_sql_null(rec + offset, rec_get_nth_field_size(rec, n)); rec_set_nth_field_null_bit(rec, n, TRUE); } /*********************************************************//** Builds an old-style physical record out of a data tuple and stores it beginning from the start of the given buffer. @return pointer to the origin of physical record */ static rec_t* rec_convert_dtuple_to_rec_old( /*==========================*/ byte* buf, /*!< in: start address of the physical record */ const dtuple_t* dtuple, /*!< in: data tuple */ ulint n_ext) /*!< in: number of externally stored columns */ { const dfield_t* field; ulint n_fields; ulint data_size; rec_t* rec; ulint end_offset; ulint ored_offset; ulint len; ulint i; ut_ad(buf && dtuple); ut_ad(dtuple_validate(dtuple)); ut_ad(dtuple_check_typed(dtuple)); n_fields = dtuple_get_n_fields(dtuple); data_size = dtuple_get_data_size(dtuple, 0); ut_ad(n_fields > 0); /* Calculate the offset of the origin in the physical record */ rec = buf + rec_get_converted_extra_size(data_size, n_fields, n_ext); #ifdef UNIV_DEBUG /* Suppress Valgrind warnings of ut_ad() in mach_write_to_1(), mach_write_to_2() et al. */ memset(buf, 0xff, rec - buf + data_size); #endif /* UNIV_DEBUG */ /* Store the number of fields */ rec_set_n_fields_old(rec, n_fields); /* Set the info bits of the record */ rec_set_info_bits_old(rec, dtuple_get_info_bits(dtuple) & REC_INFO_BITS_MASK); /* Store the data and the offsets */ end_offset = 0; if (!n_ext && data_size <= REC_1BYTE_OFFS_LIMIT) { rec_set_1byte_offs_flag(rec, TRUE); for (i = 0; i < n_fields; i++) { field = dtuple_get_nth_field(dtuple, i); if (dfield_is_null(field)) { len = dtype_get_sql_null_size( dfield_get_type(field), 0); data_write_sql_null(rec + end_offset, len); end_offset += len; ored_offset = end_offset | REC_1BYTE_SQL_NULL_MASK; } else { /* If the data is not SQL null, store it */ len = dfield_get_len(field); memcpy(rec + end_offset, dfield_get_data(field), len); end_offset += len; ored_offset = end_offset; } rec_1_set_field_end_info(rec, i, ored_offset); } } else { rec_set_1byte_offs_flag(rec, FALSE); for (i = 0; i < n_fields; i++) { field = dtuple_get_nth_field(dtuple, i); if (dfield_is_null(field)) { len = dtype_get_sql_null_size( dfield_get_type(field), 0); data_write_sql_null(rec + end_offset, len); end_offset += len; ored_offset = end_offset | REC_2BYTE_SQL_NULL_MASK; } else { /* If the data is not SQL null, store it */ len = dfield_get_len(field); memcpy(rec + end_offset, dfield_get_data(field), len); end_offset += len; ored_offset = end_offset; if (dfield_is_ext(field)) { ored_offset |= REC_2BYTE_EXTERN_MASK; } } rec_2_set_field_end_info(rec, i, ored_offset); } } return(rec); } /*********************************************************//** Builds a ROW_FORMAT=COMPACT record out of a data tuple. */ UNIV_INTERN void rec_convert_dtuple_to_rec_comp( /*===========================*/ rec_t* rec, /*!< in: origin of record */ ulint extra, /*!< in: number of bytes to reserve between the record header and the data payload (normally REC_N_NEW_EXTRA_BYTES) */ const dict_index_t* index, /*!< in: record descriptor */ ulint status, /*!< in: status bits of the record */ const dfield_t* fields, /*!< in: array of data fields */ ulint n_fields)/*!< in: number of data fields */ { const dfield_t* field; const dtype_t* type; byte* end; byte* nulls; byte* lens; ulint len; ulint i; ulint n_node_ptr_field; ulint fixed_len; ulint null_mask = 1; ut_ad(extra == 0 || dict_table_is_comp(index->table)); ut_ad(extra == 0 || extra == REC_N_NEW_EXTRA_BYTES); ut_ad(n_fields > 0); switch (UNIV_EXPECT(status, REC_STATUS_ORDINARY)) { case REC_STATUS_ORDINARY: ut_ad(n_fields <= dict_index_get_n_fields(index)); n_node_ptr_field = ULINT_UNDEFINED; break; case REC_STATUS_NODE_PTR: ut_ad(n_fields == dict_index_get_n_unique_in_tree(index) + 1); n_node_ptr_field = n_fields - 1; break; case REC_STATUS_INFIMUM: case REC_STATUS_SUPREMUM: ut_ad(n_fields == 1); n_node_ptr_field = ULINT_UNDEFINED; break; default: ut_error; return; } end = rec; nulls = rec - (extra + 1); lens = nulls - UT_BITS_IN_BYTES(index->n_nullable); /* clear the SQL-null flags */ memset(lens + 1, 0, nulls - lens); /* Store the data and the offsets */ for (i = 0, field = fields; i < n_fields; i++, field++) { const dict_field_t* ifield; type = dfield_get_type(field); len = dfield_get_len(field); if (UNIV_UNLIKELY(i == n_node_ptr_field)) { ut_ad(dtype_get_prtype(type) & DATA_NOT_NULL); ut_ad(len == 4); memcpy(end, dfield_get_data(field), len); end += 4; break; } if (!(dtype_get_prtype(type) & DATA_NOT_NULL)) { /* nullable field */ ut_ad(index->n_nullable > 0); if (UNIV_UNLIKELY(!(byte) null_mask)) { nulls--; null_mask = 1; } ut_ad(*nulls < null_mask); /* set the null flag if necessary */ if (dfield_is_null(field)) { *nulls |= null_mask; null_mask <<= 1; continue; } null_mask <<= 1; } /* only nullable fields can be null */ ut_ad(!dfield_is_null(field)); ifield = dict_index_get_nth_field(index, i); fixed_len = ifield->fixed_len; /* If the maximum length of a variable-length field is up to 255 bytes, the actual length is always stored in one byte. If the maximum length is more than 255 bytes, the actual length is stored in one byte for 0..127. The length will be encoded in two bytes when it is 128 or more, or when the field is stored externally. */ if (fixed_len) { ut_ad(len == fixed_len); ut_ad(!dfield_is_ext(field)); } else if (dfield_is_ext(field)) { ut_ad(ifield->col->len >= 256 || ifield->col->mtype == DATA_BLOB); ut_ad(len <= REC_MAX_INDEX_COL_LEN + BTR_EXTERN_FIELD_REF_SIZE); *lens-- = (byte) (len >> 8) | 0xc0; *lens-- = (byte) len; } else { ut_ad(len <= dtype_get_len(type) || dtype_get_mtype(type) == DATA_BLOB); if (len < 128 || (dtype_get_len(type) < 256 && dtype_get_mtype(type) != DATA_BLOB)) { *lens-- = (byte) len; } else { ut_ad(len < 16384); *lens-- = (byte) (len >> 8) | 0x80; *lens-- = (byte) len; } } memcpy(end, dfield_get_data(field), len); end += len; } } /*********************************************************//** Builds a new-style physical record out of a data tuple and stores it beginning from the start of the given buffer. @return pointer to the origin of physical record */ static rec_t* rec_convert_dtuple_to_rec_new( /*==========================*/ byte* buf, /*!< in: start address of the physical record */ const dict_index_t* index, /*!< in: record descriptor */ const dtuple_t* dtuple) /*!< in: data tuple */ { ulint extra_size; ulint status; rec_t* rec; status = dtuple_get_info_bits(dtuple) & REC_NEW_STATUS_MASK; rec_get_converted_size_comp(index, status, dtuple->fields, dtuple->n_fields, &extra_size); rec = buf + extra_size; rec_convert_dtuple_to_rec_comp( rec, REC_N_NEW_EXTRA_BYTES, index, status, dtuple->fields, dtuple->n_fields); /* Set the info bits of the record */ rec_set_info_and_status_bits(rec, dtuple_get_info_bits(dtuple)); return(rec); } /*********************************************************//** Builds a physical record out of a data tuple and stores it beginning from the start of the given buffer. @return pointer to the origin of physical record */ UNIV_INTERN rec_t* rec_convert_dtuple_to_rec( /*======================*/ byte* buf, /*!< in: start address of the physical record */ const dict_index_t* index, /*!< in: record descriptor */ const dtuple_t* dtuple, /*!< in: data tuple */ ulint n_ext) /*!< in: number of externally stored columns */ { rec_t* rec; ut_ad(buf && index && dtuple); ut_ad(dtuple_validate(dtuple)); ut_ad(dtuple_check_typed(dtuple)); if (dict_table_is_comp(index->table)) { rec = rec_convert_dtuple_to_rec_new(buf, index, dtuple); } else { rec = rec_convert_dtuple_to_rec_old(buf, dtuple, n_ext); } #ifdef UNIV_DEBUG { mem_heap_t* heap = NULL; ulint offsets_[REC_OFFS_NORMAL_SIZE]; const ulint* offsets; ulint i; rec_offs_init(offsets_); offsets = rec_get_offsets(rec, index, offsets_, ULINT_UNDEFINED, &heap); ut_ad(rec_validate(rec, offsets)); ut_ad(dtuple_get_n_fields(dtuple) == rec_offs_n_fields(offsets)); for (i = 0; i < rec_offs_n_fields(offsets); i++) { ut_ad(!dfield_is_ext(dtuple_get_nth_field(dtuple, i)) == !rec_offs_nth_extern(offsets, i)); } if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } } #endif /* UNIV_DEBUG */ return(rec); } /**************************************************************//** Copies the first n fields of a physical record to a data tuple. The fields are copied to the memory heap. */ UNIV_INTERN void rec_copy_prefix_to_dtuple( /*======================*/ dtuple_t* tuple, /*!< out: data tuple */ const rec_t* rec, /*!< in: physical record */ const dict_index_t* index, /*!< in: record descriptor */ ulint n_fields, /*!< in: number of fields to copy */ mem_heap_t* heap) /*!< in: memory heap */ { ulint i; ulint offsets_[REC_OFFS_NORMAL_SIZE]; ulint* offsets = offsets_; rec_offs_init(offsets_); offsets = rec_get_offsets(rec, index, offsets, n_fields, &heap); ut_ad(rec_validate(rec, offsets)); ut_ad(dtuple_check_typed(tuple)); dtuple_set_info_bits(tuple, rec_get_info_bits( rec, dict_table_is_comp(index->table))); for (i = 0; i < n_fields; i++) { dfield_t* field; const byte* data; ulint len; field = dtuple_get_nth_field(tuple, i); data = rec_get_nth_field(rec, offsets, i, &len); if (len != UNIV_SQL_NULL) { dfield_set_data(field, mem_heap_dup(heap, data, len), len); ut_ad(!rec_offs_nth_extern(offsets, i)); } else { dfield_set_null(field); } } } /**************************************************************//** Copies the first n fields of an old-style physical record to a new physical record in a buffer. @return own: copied record */ static rec_t* rec_copy_prefix_to_buf_old( /*=======================*/ const rec_t* rec, /*!< in: physical record */ ulint n_fields, /*!< in: number of fields to copy */ ulint area_end, /*!< in: end of the prefix data */ byte** buf, /*!< in/out: memory buffer for the copied prefix, or NULL */ ulint* buf_size) /*!< in/out: buffer size */ { rec_t* copy_rec; ulint area_start; ulint prefix_len; if (rec_get_1byte_offs_flag(rec)) { area_start = REC_N_OLD_EXTRA_BYTES + n_fields; } else { area_start = REC_N_OLD_EXTRA_BYTES + 2 * n_fields; } prefix_len = area_start + area_end; if ((*buf == NULL) || (*buf_size < prefix_len)) { if (*buf != NULL) { mem_free(*buf); } *buf = mem_alloc2(prefix_len, buf_size); } ut_memcpy(*buf, rec - area_start, prefix_len); copy_rec = *buf + area_start; rec_set_n_fields_old(copy_rec, n_fields); return(copy_rec); } /**************************************************************//** Copies the first n fields of a physical record to a new physical record in a buffer. @return own: copied record */ UNIV_INTERN rec_t* rec_copy_prefix_to_buf( /*===================*/ const rec_t* rec, /*!< in: physical record */ const dict_index_t* index, /*!< in: record descriptor */ ulint n_fields, /*!< in: number of fields to copy */ byte** buf, /*!< in/out: memory buffer for the copied prefix, or NULL */ ulint* buf_size) /*!< in/out: buffer size */ { const byte* nulls; const byte* lens; ulint i; ulint prefix_len; ulint null_mask; ulint status; UNIV_PREFETCH_RW(*buf); if (!dict_table_is_comp(index->table)) { ut_ad(rec_validate_old(rec)); return(rec_copy_prefix_to_buf_old( rec, n_fields, rec_get_field_start_offs(rec, n_fields), buf, buf_size)); } status = rec_get_status(rec); switch (status) { case REC_STATUS_ORDINARY: ut_ad(n_fields <= dict_index_get_n_fields(index)); break; case REC_STATUS_NODE_PTR: /* it doesn't make sense to copy the child page number field */ ut_ad(n_fields <= dict_index_get_n_unique_in_tree(index)); break; case REC_STATUS_INFIMUM: case REC_STATUS_SUPREMUM: /* infimum or supremum record: no sense to copy anything */ default: ut_error; return(NULL); } nulls = rec - (REC_N_NEW_EXTRA_BYTES + 1); lens = nulls - UT_BITS_IN_BYTES(index->n_nullable); UNIV_PREFETCH_R(lens); prefix_len = 0; null_mask = 1; /* read the lengths of fields 0..n */ for (i = 0; i < n_fields; i++) { const dict_field_t* field; const dict_col_t* col; field = dict_index_get_nth_field(index, i); col = dict_field_get_col(field); if (!(col->prtype & DATA_NOT_NULL)) { /* nullable field => read the null flag */ if (UNIV_UNLIKELY(!(byte) null_mask)) { nulls--; null_mask = 1; } if (*nulls & null_mask) { null_mask <<= 1; continue; } null_mask <<= 1; } if (field->fixed_len) { prefix_len += field->fixed_len; } else { ulint len = *lens--; /* If the maximum length of the column is up to 255 bytes, the actual length is always stored in one byte. If the maximum length is more than 255 bytes, the actual length is stored in one byte for 0..127. The length will be encoded in two bytes when it is 128 or more, or when the column is stored externally. */ if (col->len > 255 || col->mtype == DATA_BLOB) { if (len & 0x80) { /* 1exxxxxx */ len &= 0x3f; len <<= 8; len |= *lens--; UNIV_PREFETCH_R(lens); } } prefix_len += len; } } UNIV_PREFETCH_R(rec + prefix_len); prefix_len += rec - (lens + 1); if ((*buf == NULL) || (*buf_size < prefix_len)) { if (*buf != NULL) { mem_free(*buf); } *buf = mem_alloc2(prefix_len, buf_size); } memcpy(*buf, lens + 1, prefix_len); return(*buf + (rec - (lens + 1))); } /***************************************************************//** Validates the consistency of an old-style physical record. @return TRUE if ok */ static ibool rec_validate_old( /*=============*/ const rec_t* rec) /*!< in: physical record */ { const byte* data; ulint len; ulint n_fields; ulint len_sum = 0; ulint sum = 0; ulint i; ut_a(rec); n_fields = rec_get_n_fields_old(rec); if ((n_fields == 0) || (n_fields > REC_MAX_N_FIELDS)) { ib_logger(ib_stream, "InnoDB: Error: record has %lu fields\n", (ulong) n_fields); return(FALSE); } for (i = 0; i < n_fields; i++) { data = rec_get_nth_field_old(rec, i, &len); if (!((len < UNIV_PAGE_SIZE) || (len == UNIV_SQL_NULL))) { ib_logger(ib_stream, "InnoDB: Error: record field %lu len %lu\n", (ulong) i, (ulong) len); return(FALSE); } if (len != UNIV_SQL_NULL) { len_sum += len; sum += *(data + len -1); /* dereference the end of the field to cause a memory trap if possible */ } else { len_sum += rec_get_nth_field_size(rec, i); } } if (len_sum != rec_get_data_size_old(rec)) { ib_logger(ib_stream, "InnoDB: Error: record len should be %lu, len %lu\n", (ulong) len_sum, rec_get_data_size_old(rec)); return(FALSE); } return(TRUE); } /***************************************************************//** Validates the consistency of a physical record. @return TRUE if ok */ UNIV_INTERN ibool rec_validate( /*=========*/ const rec_t* rec, /*!< in: physical record */ const ulint* offsets)/*!< in: array returned by rec_get_offsets() */ { const byte* data; ulint len; ulint n_fields; ulint len_sum = 0; ulint sum = 0; ulint i; ut_a(rec); n_fields = rec_offs_n_fields(offsets); if ((n_fields == 0) || (n_fields > REC_MAX_N_FIELDS)) { ib_logger(ib_stream, "InnoDB: Error: record has %lu fields\n", (ulong) n_fields); return(FALSE); } ut_a(rec_offs_comp(offsets) || n_fields <= rec_get_n_fields_old(rec)); for (i = 0; i < n_fields; i++) { data = rec_get_nth_field(rec, offsets, i, &len); if (!((len < UNIV_PAGE_SIZE) || (len == UNIV_SQL_NULL))) { ib_logger(ib_stream, "InnoDB: Error: record field %lu len %lu\n", (ulong) i, (ulong) len); return(FALSE); } if (len != UNIV_SQL_NULL) { len_sum += len; sum += *(data + len -1); /* dereference the end of the field to cause a memory trap if possible */ } else if (!rec_offs_comp(offsets)) { len_sum += rec_get_nth_field_size(rec, i); } } if (len_sum != rec_offs_data_size(offsets)) { ib_logger(ib_stream, "InnoDB: Error: record len should be %lu, len %lu\n", (ulong) len_sum, (ulong) rec_offs_data_size(offsets)); return(FALSE); } if (!rec_offs_comp(offsets)) { ut_a(rec_validate_old(rec)); } return(TRUE); } /***************************************************************//** Prints an old-style physical record. */ UNIV_INTERN void rec_print_old( /*==========*/ ib_stream_t ib_stream, /*!< in: stream where to print */ const rec_t* rec) /*!< in: physical record */ { const byte* data; ulint len; ulint n; ulint i; ut_ad(rec); n = rec_get_n_fields_old(rec); ib_logger(ib_stream, "PHYSICAL RECORD: n_fields %lu;" " %u-byte offsets; info bits %lu\n", (ulong) n, rec_get_1byte_offs_flag(rec) ? 1 : 2, (ulong) rec_get_info_bits(rec, FALSE)); for (i = 0; i < n; i++) { data = rec_get_nth_field_old(rec, i, &len); ib_logger(ib_stream, " %lu:", (ulong) i); if (len != UNIV_SQL_NULL) { if (len <= 30) { ut_print_buf(ib_stream, data, len); } else { ut_print_buf(ib_stream, data, 30); ib_logger(ib_stream, " (total %lu bytes)", (ulong) len); } } else { ib_logger(ib_stream, " SQL NULL, size %lu ", rec_get_nth_field_size(rec, i)); } ib_logger(ib_stream, ";\n"); } rec_validate_old(rec); } #ifndef UNIV_HOTBACKUP /***************************************************************//** Prints a physical record in ROW_FORMAT=COMPACT. Ignores the record header. */ UNIV_INTERN void rec_print_comp( /*===========*/ ib_stream_t ib_stream, /*!< in: streamwhere to print */ const rec_t* rec, /*!< in: physical record */ const ulint* offsets) /*!< in: array returned by rec_get_offsets() */ { ulint i; for (i = 0; i < rec_offs_n_fields(offsets); i++) { const byte* data; ulint len; data = rec_get_nth_field(rec, offsets, i, &len); ib_logger(ib_stream, " %lu:", (ulong) i); if (len != UNIV_SQL_NULL) { if (len <= 30) { ut_print_buf(ib_stream, data, len); } else { ut_print_buf(ib_stream, data, 30); ib_logger(ib_stream, " (total %lu bytes)", (ulong) len); } } else { ib_logger(ib_stream, " SQL NULL"); } ib_logger(ib_stream, ";\n"); } } /***************************************************************//** Prints a physical record. */ UNIV_INTERN void rec_print_new( /*==========*/ ib_stream_t ib_stream, /*!< in: stream where to print */ const rec_t* rec, /*!< in: physical record */ const ulint* offsets) /*!< in: array returned by rec_get_offsets() */ { ut_ad(rec); ut_ad(offsets); ut_ad(rec_offs_validate(rec, NULL, offsets)); if (!rec_offs_comp(offsets)) { rec_print_old(ib_stream, rec); return; } ib_logger(ib_stream, "PHYSICAL RECORD: n_fields %lu;" " compact format; info bits %lu\n", (ulong) rec_offs_n_fields(offsets), (ulong) rec_get_info_bits(rec, TRUE)); rec_print_comp(ib_stream, rec, offsets); rec_validate(rec, offsets); } /***************************************************************//** Prints a physical record. */ UNIV_INTERN void rec_print( /*======*/ ib_stream_t ib_stream, /*!< in: stream where to print */ const rec_t* rec, /*!< in: physical record */ dict_index_t* index) /*!< in: record descriptor */ { ut_ad(index); if (!dict_table_is_comp(index->table)) { rec_print_old(ib_stream, rec); return; } else { mem_heap_t* heap = NULL; ulint offsets_[REC_OFFS_NORMAL_SIZE]; rec_offs_init(offsets_); rec_print_new(ib_stream, rec, rec_get_offsets(rec, index, offsets_, ULINT_UNDEFINED, &heap)); if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } } } #endif /* !UNIV_HOTBACKUP */ haildb-2.3.2/rem/rem0cmp.c0000644000175000017500000007530711513177357016151 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1994, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /*******************************************************************//** @file rem/rem0cmp.c Comparison services for records Created 7/1/1994 Heikki Tuuri ************************************************************************/ #include "rem0cmp.h" #ifdef UNIV_NONINL #include "rem0cmp.ic" #endif #include "srv0srv.h" #include "api0ucode.h" #include "api0api.h" /* ALPHABETICAL ORDER ================== The records are put into alphabetical order in the following way: let F be the first field where two records disagree. If there is a character in some position n where the records disagree, the order is determined by comparison of the characters at position n, possibly after collating transformation. If there is no such character, but the corresponding fields have different lengths, then if the data type of the fields is paddable, shorter field is padded with a padding character. If the data type is not paddable, longer field is considered greater. Finally, the SQL null is bigger than any other value. At the present, the comparison functions return 0 in the case, where two records disagree only in the way that one has more fields than the other. */ #ifdef UNIV_DEBUG /*************************************************************//** Used in debug checking of cmp_dtuple_... . This function is used to compare a data tuple to a physical record. If dtuple has n fields then rec must have either m >= n fields, or it must differ from dtuple in some of the m fields rec has. @return 1, 0, -1, if dtuple is greater, equal, less than rec, respectively, when only the common first fields are compared */ static int cmp_debug_dtuple_rec_with_match( /*============================*/ void* cmp_ctx,/*!< in: client compare context */ const dtuple_t* dtuple, /*!< in: data tuple */ const rec_t* rec, /*!< in: physical record which differs from dtuple in some of the common fields, or which has an equal number or more fields than dtuple */ const ulint* offsets,/*!< in: array returned by rec_get_offsets() */ ulint* matched_fields);/*!< in/out: number of already completely matched fields; when function returns, contains the value for current comparison */ #endif /* UNIV_DEBUG */ /*********************************************************************//** Transforms the character code so that it is ordered appropriately for the language. This is only used for the latin1 char set. The client does the comparisons for other char sets. @return collation order position */ UNIV_INLINE ulint cmp_collate( /*========*/ ulint code) /*!< in: code of a character stored in database record */ { //return((ulint) srv_latin1_ordering[code]); /* FIXME: Default to ASCII */ return(code); } /*************************************************************//** Returns TRUE if two columns are equal for comparison purposes. @return TRUE if the columns are considered equal in comparisons */ UNIV_INTERN ibool cmp_cols_are_equal( /*===============*/ const dict_col_t* col1, /*!< in: column 1 */ const dict_col_t* col2, /*!< in: column 2 */ ibool check_charsets) /*!< in: whether to check charsets */ { if (dtype_is_non_binary_string_type(col1->mtype, col1->prtype) && dtype_is_non_binary_string_type(col2->mtype, col2->prtype)) { /* Both are non-binary string types: they can be compared if and only if the charset-collation is the same */ if (check_charsets) { return(dtype_get_charset_coll(col1->prtype) == dtype_get_charset_coll(col2->prtype)); } else { return(TRUE); } } if (dtype_is_binary_string_type(col1->mtype, col1->prtype) && dtype_is_binary_string_type(col2->mtype, col2->prtype)) { /* Both are binary string types: they can be compared */ return(TRUE); } if (col1->mtype != col2->mtype) { return(FALSE); } if (col1->mtype == DATA_INT && (col1->prtype & DATA_UNSIGNED) != (col2->prtype & DATA_UNSIGNED)) { /* The storage format of an unsigned integer is different from a signed integer: in a signed integer we OR 0x8000... to the value of positive integers. */ return(FALSE); } return(col1->mtype != DATA_INT || col1->len == col2->len); } /*************************************************************//** Innobase uses this function to compare two data fields for which the data type is such that we must compare whole fields or call the client to do the comparison @return 1, 0, -1, if a is greater, equal, less than b, respectively */ static int cmp_whole_field( /*============*/ void* cmp_ctx, /*!< in: client compare context */ ulint mtype, /*!< in: main type */ ib_u16_t prtype, /*!< in: precise type */ const byte* a, /*!< in: data field */ unsigned int a_length, /*!< in: data field length, not UNIV_SQL_NULL */ const byte* b, /*!< in: data field */ unsigned int b_length) /*!< in: data field length, not UNIV_SQL_NULL */ { float f_1; float f_2; double d_1; double d_2; int swap_flag = 1; switch (mtype) { case DATA_DECIMAL: /* Remove preceding spaces */ for (; a_length && *a == ' '; a++, a_length--); for (; b_length && *b == ' '; b++, b_length--); if (*a == '-') { if (*b != '-') { return(-1); } a++; b++; a_length--; b_length--; swap_flag = -1; } else if (*b == '-') { return(1); } while (a_length > 0 && (*a == '+' || *a == '0')) { a++; a_length--; } while (b_length > 0 && (*b == '+' || *b == '0')) { b++; b_length--; } if (a_length != b_length) { if (a_length < b_length) { return(-swap_flag); } return(swap_flag); } while (a_length > 0 && *a == *b) { a++; b++; a_length--; } if (a_length == 0) { return(0); } if (*a > *b) { return(swap_flag); } return(-swap_flag); case DATA_DOUBLE: d_1 = mach_double_read(a); d_2 = mach_double_read(b); if (d_1 > d_2) { return(1); } else if (d_2 > d_1) { return(-1); } return(0); case DATA_FLOAT: f_1 = mach_float_read(a); f_2 = mach_float_read(b); if (f_1 > f_2) { return(1); } else if (f_2 > f_1) { return(-1); } return(0); case DATA_BLOB: if (prtype & DATA_BINARY_TYPE) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Error: comparing a binary BLOB" " with a character set sensitive\n" "InnoDB: comparison!\n"); } /* fall through */ case DATA_VARCLIENT: case DATA_CLIENT: { ib_col_meta_t ib_col_meta; /* FIXME: We should do this once at a higher level. Current impact is on perfromance. */ ib_col_meta.type = mtype; /* FIXME: Set the length where it's known. */ ib_col_meta.type_len = 0; ib_col_meta.attr = IB_COL_NONE; ib_col_meta.client_type = prtype & DATA_CLIENT_TYPE_MASK; if (prtype & DATA_NOT_NULL) { ib_col_meta.attr |= IB_COL_NOT_NULL; } if (prtype & DATA_UNSIGNED) { ib_col_meta.attr |= IB_COL_UNSIGNED; } if (prtype & DATA_CUSTOM_TYPE) { ib_col_meta.attr |= IB_COL_CUSTOM1; } if (prtype & (DATA_CUSTOM_TYPE << 1)) { ib_col_meta.attr |= IB_COL_CUSTOM2; } if (prtype & (DATA_CUSTOM_TYPE << 2)) { ib_col_meta.attr |= IB_COL_CUSTOM3; } return(ib_client_compare( &ib_col_meta, a, a_length, b, b_length)); } default: ib_logger(ib_stream, "InnoDB: unknown type number %lu\n", (ulong) mtype); ut_error; } return(0); } /*************************************************************//** This function is used to compare two data fields for which we know the data type. @return 1, 0, -1, if data1 is greater, equal, less than data2, respectively */ UNIV_INTERN int cmp_data_data_slow( /*===============*/ void* cmp_ctx,/*!< in: client compare context */ ulint mtype, /*!< in: main type */ ulint prtype, /*!< in: precise type */ const byte* data1, /*!< in: data field (== a pointer to a memory buffer) */ ulint len1, /*!< in: data field length or UNIV_SQL_NULL */ const byte* data2, /*!< in: data field (== a pointer to a memory buffer) */ ulint len2) /*!< in: data field length or UNIV_SQL_NULL */ { ulint data1_byte; ulint data2_byte; ulint cur_bytes; if (len1 == UNIV_SQL_NULL || len2 == UNIV_SQL_NULL) { if (len1 == len2) { return(0); } if (len1 == UNIV_SQL_NULL) { /* We define the SQL null to be the smallest possible value of a field in the alphabetical order */ return(-1); } return(1); } if (mtype >= DATA_FLOAT || (mtype == DATA_BLOB && 0 == (prtype & DATA_BINARY_TYPE) && dtype_get_charset_coll(prtype) != DATA_CLIENT_LATIN1_SWEDISH_CHARSET_COLL)) { /* prtype is really a 16 unsigned type. */ return(cmp_whole_field( cmp_ctx, mtype, (ib_u16_t) prtype, data1, (unsigned) len1, data2, (unsigned) len2)); } /* Compare then the fields */ cur_bytes = 0; for (;;) { if (len1 <= cur_bytes) { if (len2 <= cur_bytes) { return(0); } data1_byte = dtype_get_pad_char(mtype, prtype); if (data1_byte == ULINT_UNDEFINED) { return(-1); } } else { data1_byte = *data1; } if (len2 <= cur_bytes) { data2_byte = dtype_get_pad_char(mtype, prtype); if (data2_byte == ULINT_UNDEFINED) { return(1); } } else { data2_byte = *data2; } if (data1_byte == data2_byte) { /* If the bytes are equal, they will remain such even after the collation transformation below */ goto next_byte; } if (mtype <= DATA_CHAR || (mtype == DATA_BLOB && 0 == (prtype & DATA_BINARY_TYPE))) { data1_byte = cmp_collate(data1_byte); data2_byte = cmp_collate(data2_byte); } if (data1_byte > data2_byte) { return(1); } else if (data1_byte < data2_byte) { return(-1); } next_byte: /* Next byte */ cur_bytes++; data1++; data2++; } return(0); /* Not reached */ } /*************************************************************//** This function is used to compare a data tuple to a physical record. Only dtuple->n_fields_cmp first fields are taken into account for the data tuple! If we denote by n = n_fields_cmp, then rec must have either m >= n fields, or it must differ from dtuple in some of the m fields rec has. If rec has an externally stored field we do not compare it but return with value 0 if such a comparison should be made. @return 1, 0, -1, if dtuple is greater, equal, less than rec, respectively, when only the common first fields are compared, or until the first externally stored field in rec */ UNIV_INTERN int cmp_dtuple_rec_with_match( /*======================*/ void* cmp_ctx,/*!< in: client compare context */ const dtuple_t* dtuple, /*!< in: data tuple */ const rec_t* rec, /*!< in: physical record which differs from dtuple in some of the common fields, or which has an equal number or more fields than dtuple */ const ulint* offsets,/*!< in: array returned by rec_get_offsets() */ ulint* matched_fields, /*!< in/out: number of already completely matched fields; when function returns, contains the value for current comparison */ ulint* matched_bytes) /*!< in/out: number of already matched bytes within the first field not completely matched; when function returns, contains the value for current comparison */ { const dfield_t* dtuple_field; /* current field in logical record */ ulint dtuple_f_len; /* the length of the current field in the logical record */ const byte* dtuple_b_ptr; /* pointer to the current byte in logical field data */ ulint dtuple_byte; /* value of current byte to be compared in dtuple*/ ulint rec_f_len; /* length of current field in rec */ const byte* rec_b_ptr; /* pointer to the current byte in rec field */ ulint rec_byte; /* value of current byte to be compared in rec */ ulint cur_field; /* current field number */ ulint cur_bytes; /* number of already matched bytes in current field */ int ret = 3333; /* return value */ ut_ad(dtuple && rec && matched_fields && matched_bytes); ut_ad(dtuple_check_typed(dtuple)); ut_ad(rec_offs_validate(rec, NULL, offsets)); cur_field = *matched_fields; cur_bytes = *matched_bytes; ut_ad(cur_field <= dtuple_get_n_fields_cmp(dtuple)); ut_ad(cur_field <= rec_offs_n_fields(offsets)); if (cur_bytes == 0 && cur_field == 0) { ulint rec_info = rec_get_info_bits(rec, rec_offs_comp(offsets)); ulint tup_info = dtuple_get_info_bits(dtuple); if (UNIV_UNLIKELY(rec_info & REC_INFO_MIN_REC_FLAG)) { ret = !(tup_info & REC_INFO_MIN_REC_FLAG); goto order_resolved; } else if (UNIV_UNLIKELY(tup_info & REC_INFO_MIN_REC_FLAG)) { ret = -1; goto order_resolved; } } /* Match fields in a loop; stop if we run out of fields in dtuple or find an externally stored field */ while (cur_field < dtuple_get_n_fields_cmp(dtuple)) { ulint mtype; ib_u16_t prtype; dtuple_field = dtuple_get_nth_field(dtuple, cur_field); { const dtype_t* type = dfield_get_type(dtuple_field); mtype = type->mtype; prtype = type->prtype; } dtuple_f_len = dfield_get_len(dtuple_field); rec_b_ptr = rec_get_nth_field(rec, offsets, cur_field, &rec_f_len); /* If we have matched yet 0 bytes, it may be that one or both the fields are SQL null, or the record or dtuple may be the predefined minimum record, or the field is externally stored */ if (UNIV_LIKELY(cur_bytes == 0)) { if (rec_offs_nth_extern(offsets, cur_field)) { /* We do not compare to an externally stored field */ ret = 0; goto order_resolved; } if (dtuple_f_len == UNIV_SQL_NULL) { if (rec_f_len == UNIV_SQL_NULL) { goto next_field; } ret = -1; goto order_resolved; } else if (rec_f_len == UNIV_SQL_NULL) { /* We define the SQL null to be the smallest possible value of a field in the alphabetical order */ ret = 1; goto order_resolved; } } if (mtype >= DATA_FLOAT || (mtype == DATA_BLOB && 0 == (prtype & DATA_BINARY_TYPE) && dtype_get_charset_coll(prtype) != DATA_CLIENT_LATIN1_SWEDISH_CHARSET_COLL)) { ret = cmp_whole_field( cmp_ctx, mtype, prtype, dfield_get_data(dtuple_field), (unsigned) dtuple_f_len, rec_b_ptr, (unsigned) rec_f_len); if (ret != 0) { cur_bytes = 0; goto order_resolved; } else { goto next_field; } } /* Set the pointers at the current byte */ rec_b_ptr = rec_b_ptr + cur_bytes; dtuple_b_ptr = (byte*)dfield_get_data(dtuple_field) + cur_bytes; /* Compare then the fields */ for (;;) { if (UNIV_UNLIKELY(rec_f_len <= cur_bytes)) { if (dtuple_f_len <= cur_bytes) { goto next_field; } rec_byte = dtype_get_pad_char(mtype, prtype); if (rec_byte == ULINT_UNDEFINED) { ret = 1; goto order_resolved; } } else { rec_byte = *rec_b_ptr; } if (UNIV_UNLIKELY(dtuple_f_len <= cur_bytes)) { dtuple_byte = dtype_get_pad_char(mtype, prtype); if (dtuple_byte == ULINT_UNDEFINED) { ret = -1; goto order_resolved; } } else { dtuple_byte = *dtuple_b_ptr; } if (dtuple_byte == rec_byte) { /* If the bytes are equal, they will remain such even after the collation transformation below */ goto next_byte; } if (mtype <= DATA_CHAR || (mtype == DATA_BLOB && !(prtype & DATA_BINARY_TYPE))) { rec_byte = cmp_collate(rec_byte); dtuple_byte = cmp_collate(dtuple_byte); } ret = (int) (dtuple_byte - rec_byte); if (UNIV_LIKELY(ret)) { if (ret < 0) { ret = -1; goto order_resolved; } else { ret = 1; goto order_resolved; } } next_byte: /* Next byte */ cur_bytes++; rec_b_ptr++; dtuple_b_ptr++; } next_field: cur_field++; cur_bytes = 0; } ut_ad(cur_bytes == 0); ret = 0; /* If we ran out of fields, dtuple was equal to rec up to the common fields */ order_resolved: ut_ad((ret >= - 1) && (ret <= 1)); ut_ad(ret == cmp_debug_dtuple_rec_with_match( cmp_ctx, dtuple, rec, offsets, matched_fields)); ut_ad(*matched_fields == cur_field); /* In the debug version, the above cmp_debug_... sets *matched_fields to a value */ *matched_fields = cur_field; *matched_bytes = cur_bytes; return(ret); } /**************************************************************//** Compares a data tuple to a physical record. @see cmp_dtuple_rec_with_match @return 1, 0, -1, if dtuple is greater, equal, less than rec, respectively */ UNIV_INTERN int cmp_dtuple_rec( /*===========*/ void* cmp_ctx,/*!< in: client compare context */ const dtuple_t* dtuple, /*!< in: data tuple */ const rec_t* rec, /*!< in: physical record */ const ulint* offsets)/*!< in: array returned by rec_get_offsets() */ { ulint matched_fields = 0; ulint matched_bytes = 0; ut_ad(rec_offs_validate(rec, NULL, offsets)); return(cmp_dtuple_rec_with_match( cmp_ctx, dtuple, rec, offsets, &matched_fields, &matched_bytes)); } /**************************************************************//** Checks if a dtuple is a prefix of a record. The last field in dtuple is allowed to be a prefix of the corresponding field in the record. @return TRUE if prefix */ UNIV_INTERN ibool cmp_dtuple_is_prefix_of_rec( /*========================*/ void* cmp_ctx,/*!< in: client compare context */ const dtuple_t* dtuple, /*!< in: data tuple */ const rec_t* rec, /*!< in: physical record */ const ulint* offsets)/*!< in: array returned by rec_get_offsets() */ { ulint n_fields; ulint matched_fields = 0; ulint matched_bytes = 0; ut_ad(rec_offs_validate(rec, NULL, offsets)); n_fields = dtuple_get_n_fields(dtuple); if (n_fields > rec_offs_n_fields(offsets)) { return(FALSE); } cmp_dtuple_rec_with_match( cmp_ctx, dtuple, rec, offsets, &matched_fields, &matched_bytes); if (matched_fields == n_fields) { return(TRUE); } if (matched_fields == n_fields - 1 && matched_bytes == dfield_get_len( dtuple_get_nth_field(dtuple, n_fields - 1))) { return(TRUE); } return(FALSE); } /*************************************************************//** Compare two physical records that contain the same number of columns, none of which are stored externally. @return 1, 0, -1 if rec1 is greater, equal, less, respectively, than rec2 */ UNIV_INTERN int cmp_rec_rec_simple( /*===============*/ const rec_t* rec1, /*!< in: physical record */ const rec_t* rec2, /*!< in: physical record */ const ulint* offsets1,/*!< in: rec_get_offsets(rec1, ...) */ const ulint* offsets2,/*!< in: rec_get_offsets(rec2, ...) */ const dict_index_t* index) /*!< in: data dictionary index */ { ulint rec1_f_len; /*!< length of current field in rec1 */ const byte* rec1_b_ptr; /*!< pointer to the current byte in rec1 field */ ulint rec1_byte; /*!< value of current byte to be compared in rec1 */ ulint rec2_f_len; /*!< length of current field in rec2 */ const byte* rec2_b_ptr; /*!< pointer to the current byte in rec2 field */ ulint rec2_byte; /*!< value of current byte to be compared in rec2 */ ulint cur_field; /*!< current field number */ ulint n_uniq; n_uniq = dict_index_get_n_unique(index); ut_ad(rec_offs_n_fields(offsets1) >= n_uniq); ut_ad(rec_offs_n_fields(offsets2) >= n_uniq); ut_ad(rec_offs_comp(offsets1) == rec_offs_comp(offsets2)); for (cur_field = 0; cur_field < n_uniq; cur_field++) { ulint mtype; ib_u16_t prtype; ulint cur_bytes; { const dict_col_t* col = dict_index_get_nth_col(index, cur_field); mtype = col->mtype; prtype = col->prtype; } ut_ad(!rec_offs_nth_extern(offsets1, cur_field)); ut_ad(!rec_offs_nth_extern(offsets2, cur_field)); rec1_b_ptr = rec_get_nth_field(rec1, offsets1, cur_field, &rec1_f_len); rec2_b_ptr = rec_get_nth_field(rec2, offsets2, cur_field, &rec2_f_len); if (rec1_f_len == UNIV_SQL_NULL || rec2_f_len == UNIV_SQL_NULL) { if (rec1_f_len == rec2_f_len) { goto next_field; } else if (rec2_f_len == UNIV_SQL_NULL) { /* We define the SQL null to be the smallest possible value of a field in the alphabetical order */ return(1); } else { return(-1); } } if (mtype >= DATA_FLOAT || (mtype == DATA_BLOB && 0 == (prtype & DATA_BINARY_TYPE) && dtype_get_charset_coll(prtype) != DATA_CLIENT_LATIN1_SWEDISH_CHARSET_COLL)) { int ret; ret = cmp_whole_field( index->cmp_ctx, mtype, prtype, rec1_b_ptr, (unsigned) rec1_f_len, rec2_b_ptr, (unsigned) rec2_f_len); if (ret) { return(ret); } goto next_field; } /* Compare the fields */ for (cur_bytes = 0;; cur_bytes++, rec1_b_ptr++, rec2_b_ptr++) { if (rec2_f_len <= cur_bytes) { if (rec1_f_len <= cur_bytes) { goto next_field; } rec2_byte = dtype_get_pad_char(mtype, prtype); if (rec2_byte == ULINT_UNDEFINED) { return(1); } } else { rec2_byte = *rec2_b_ptr; } if (rec1_f_len <= cur_bytes) { rec1_byte = dtype_get_pad_char(mtype, prtype); if (rec1_byte == ULINT_UNDEFINED) { return(-1); } } else { rec1_byte = *rec1_b_ptr; } if (rec1_byte == rec2_byte) { /* If the bytes are equal, they will remain such even after the collation transformation below */ continue; } if (mtype <= DATA_CHAR || (mtype == DATA_BLOB && !(prtype & DATA_BINARY_TYPE))) { rec1_byte = cmp_collate(rec1_byte); rec2_byte = cmp_collate(rec2_byte); } if (rec1_byte < rec2_byte) { return(-1); } else if (rec1_byte > rec2_byte) { return(1); } } next_field: continue; } /* If we ran out of fields, rec1 was equal to rec2. */ return(0); } /*************************************************************//** This function is used to compare two physical records. Only the common first fields are compared, and if an externally stored field is encountered, then 0 is returned. @return 1, 0, -1 if rec1 is greater, equal, less, respectively */ UNIV_INTERN int cmp_rec_rec_with_match( /*===================*/ const rec_t* rec1, /*!< in: physical record */ const rec_t* rec2, /*!< in: physical record */ const ulint* offsets1,/*!< in: rec_get_offsets(rec1, index) */ const ulint* offsets2,/*!< in: rec_get_offsets(rec2, index) */ dict_index_t* index, /*!< in: data dictionary index */ ulint* matched_fields, /*!< in/out: number of already completely matched fields; when the function returns, contains the value the for current comparison */ ulint* matched_bytes) /*!< in/out: number of already matched bytes within the first field not completely matched; when the function returns, contains the value for the current comparison */ { ulint rec1_n_fields; /* the number of fields in rec */ ulint rec1_f_len; /* length of current field in rec */ const byte* rec1_b_ptr; /* pointer to the current byte in rec field */ ulint rec1_byte; /* value of current byte to be compared in rec */ ulint rec2_n_fields; /* the number of fields in rec */ ulint rec2_f_len; /* length of current field in rec */ const byte* rec2_b_ptr; /* pointer to the current byte in rec field */ ulint rec2_byte; /* value of current byte to be compared in rec */ ulint cur_field; /* current field number */ ulint cur_bytes; /* number of already matched bytes in current field */ int ret = 0; /* return value */ ulint comp; ut_ad(rec1 && rec2 && index); ut_ad(rec_offs_validate(rec1, index, offsets1)); ut_ad(rec_offs_validate(rec2, index, offsets2)); ut_ad(rec_offs_comp(offsets1) == rec_offs_comp(offsets2)); comp = rec_offs_comp(offsets1); rec1_n_fields = rec_offs_n_fields(offsets1); rec2_n_fields = rec_offs_n_fields(offsets2); cur_field = *matched_fields; cur_bytes = *matched_bytes; /* Match fields in a loop */ while ((cur_field < rec1_n_fields) && (cur_field < rec2_n_fields)) { ulint mtype; ib_u16_t prtype; if (UNIV_UNLIKELY(index->type & DICT_UNIVERSAL)) { /* This is for the insert buffer B-tree. */ mtype = DATA_BINARY; prtype = 0; } else { const dict_col_t* col = dict_index_get_nth_col(index, cur_field); mtype = col->mtype; prtype = col->prtype; } rec1_b_ptr = rec_get_nth_field(rec1, offsets1, cur_field, &rec1_f_len); rec2_b_ptr = rec_get_nth_field(rec2, offsets2, cur_field, &rec2_f_len); if (cur_bytes == 0) { if (cur_field == 0) { /* Test if rec is the predefined minimum record */ if (UNIV_UNLIKELY(rec_get_info_bits(rec1, comp) & REC_INFO_MIN_REC_FLAG)) { if (!(rec_get_info_bits(rec2, comp) & REC_INFO_MIN_REC_FLAG)) { ret = -1; } goto order_resolved; } else if (UNIV_UNLIKELY (rec_get_info_bits(rec2, comp) & REC_INFO_MIN_REC_FLAG)) { ret = 1; goto order_resolved; } } if (rec_offs_nth_extern(offsets1, cur_field) || rec_offs_nth_extern(offsets2, cur_field)) { /* We do not compare to an externally stored field */ goto order_resolved; } if (rec1_f_len == UNIV_SQL_NULL || rec2_f_len == UNIV_SQL_NULL) { if (rec1_f_len == rec2_f_len) { goto next_field; } else if (rec2_f_len == UNIV_SQL_NULL) { /* We define the SQL null to be the smallest possible value of a field in the alphabetical order */ ret = 1; } else { ret = -1; } goto order_resolved; } } if (mtype >= DATA_FLOAT || (mtype == DATA_BLOB && 0 == (prtype & DATA_BINARY_TYPE) && dtype_get_charset_coll(prtype) != DATA_CLIENT_LATIN1_SWEDISH_CHARSET_COLL)) { ret = cmp_whole_field( index->cmp_ctx, mtype, prtype, rec1_b_ptr, (unsigned) rec1_f_len, rec2_b_ptr, (unsigned) rec2_f_len); if (ret != 0) { cur_bytes = 0; goto order_resolved; } else { goto next_field; } } /* Set the pointers at the current byte */ rec1_b_ptr = rec1_b_ptr + cur_bytes; rec2_b_ptr = rec2_b_ptr + cur_bytes; /* Compare then the fields */ for (;;) { if (rec2_f_len <= cur_bytes) { if (rec1_f_len <= cur_bytes) { goto next_field; } rec2_byte = dtype_get_pad_char(mtype, prtype); if (rec2_byte == ULINT_UNDEFINED) { ret = 1; goto order_resolved; } } else { rec2_byte = *rec2_b_ptr; } if (rec1_f_len <= cur_bytes) { rec1_byte = dtype_get_pad_char(mtype, prtype); if (rec1_byte == ULINT_UNDEFINED) { ret = -1; goto order_resolved; } } else { rec1_byte = *rec1_b_ptr; } if (rec1_byte == rec2_byte) { /* If the bytes are equal, they will remain such even after the collation transformation below */ goto next_byte; } if (mtype <= DATA_CHAR || (mtype == DATA_BLOB && !(prtype & DATA_BINARY_TYPE))) { rec1_byte = cmp_collate(rec1_byte); rec2_byte = cmp_collate(rec2_byte); } if (rec1_byte < rec2_byte) { ret = -1; goto order_resolved; } else if (rec1_byte > rec2_byte) { ret = 1; goto order_resolved; } next_byte: /* Next byte */ cur_bytes++; rec1_b_ptr++; rec2_b_ptr++; } next_field: cur_field++; cur_bytes = 0; } ut_ad(cur_bytes == 0); /* If we ran out of fields, rec1 was equal to rec2 up to the common fields */ ut_ad(ret == 0); order_resolved: ut_ad((ret >= - 1) && (ret <= 1)); *matched_fields = cur_field; *matched_bytes = cur_bytes; return(ret); } #ifdef UNIV_DEBUG /*************************************************************//** Used in debug checking of cmp_dtuple_... . This function is used to compare a data tuple to a physical record. If dtuple has n fields then rec must have either m >= n fields, or it must differ from dtuple in some of the m fields rec has. If encounters an externally stored field, returns 0. @return 1, 0, -1, if dtuple is greater, equal, less than rec, respectively, when only the common first fields are compared */ static int cmp_debug_dtuple_rec_with_match( /*============================*/ void* cmp_ctx, /*!< in: client compare context */ const dtuple_t* dtuple, /*!< in: data tuple */ const rec_t* rec, /*!< in: physical record which differs from dtuple in some of the common fields, or which has an equal number or more fields than dtuple */ const ulint* offsets,/*!< in: array returned by rec_get_offsets() */ ulint* matched_fields) /*!< in/out: number of already completely matched fields; when function returns, contains the value for current comparison */ { const dfield_t* dtuple_field; /* current field in logical record */ ulint dtuple_f_len; /* the length of the current field in the logical record */ const byte* dtuple_f_data; /* pointer to the current logical field data */ ulint rec_f_len; /* length of current field in rec */ const byte* rec_f_data; /* pointer to the current rec field */ int ret = 3333; /* return value */ ulint cur_field; /* current field number */ ut_ad(dtuple && rec && matched_fields); ut_ad(dtuple_check_typed(dtuple)); ut_ad(rec_offs_validate(rec, NULL, offsets)); ut_ad(*matched_fields <= dtuple_get_n_fields_cmp(dtuple)); ut_ad(*matched_fields <= rec_offs_n_fields(offsets)); cur_field = *matched_fields; if (cur_field == 0) { if (UNIV_UNLIKELY (rec_get_info_bits(rec, rec_offs_comp(offsets)) & REC_INFO_MIN_REC_FLAG)) { ret = !(dtuple_get_info_bits(dtuple) & REC_INFO_MIN_REC_FLAG); goto order_resolved; } if (UNIV_UNLIKELY (dtuple_get_info_bits(dtuple) & REC_INFO_MIN_REC_FLAG)) { ret = -1; goto order_resolved; } } /* Match fields in a loop; stop if we run out of fields in dtuple */ while (cur_field < dtuple_get_n_fields_cmp(dtuple)) { ulint mtype; ulint prtype; dtuple_field = dtuple_get_nth_field(dtuple, cur_field); { const dtype_t* type = dfield_get_type(dtuple_field); mtype = type->mtype; prtype = type->prtype; } dtuple_f_data = dfield_get_data(dtuple_field); dtuple_f_len = dfield_get_len(dtuple_field); rec_f_data = rec_get_nth_field(rec, offsets, cur_field, &rec_f_len); if (rec_offs_nth_extern(offsets, cur_field)) { /* We do not compare to an externally stored field */ ret = 0; goto order_resolved; } ret = cmp_data_data( cmp_ctx, mtype, prtype, dtuple_f_data, dtuple_f_len, rec_f_data, rec_f_len); if (ret != 0) { goto order_resolved; } cur_field++; } ret = 0; /* If we ran out of fields, dtuple was equal to rec up to the common fields */ order_resolved: ut_ad((ret >= - 1) && (ret <= 1)); *matched_fields = cur_field; return(ret); } #endif /* UNIV_DEBUG */ haildb-2.3.2/.quickly0000644000175000017500000000015511513177357015326 0ustar00pcrewspcrews00000000000000project = haildb version = 0.4.2 template = pandora-build project-type = application pandora-version = 0.171 haildb-2.3.2/data/0000755000175000017500000000000011513177437014553 5ustar00pcrewspcrews00000000000000haildb-2.3.2/data/data0type.c0000644000175000017500000001734711513177357016627 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file data/data0type.c Data types Created 1/16/1996 Heikki Tuuri *******************************************************/ #include "data0type.h" #ifdef UNIV_NONINL #include "data0type.ic" #endif #ifndef UNIV_HOTBACKUP /* At the database startup we store the default-charset collation number of this installation to this global variable. If we have < 4.1.2 format column definitions, or records in the insert buffer, we use this charset-collation code for them. */ UNIV_INTERN ulint data_client_default_charset_coll; /*********************************************************************//** Reset dtype variables. */ UNIV_INTERN void dtype_var_init(void) /*================*/ { data_client_default_charset_coll = 0; } /************************************************************************* Determine how many bytes the first n characters of the given string occupy. If the string is shorter than n characters, returns the number of bytes the characters in the string occupy. @return length of the prefix, in bytes */ UNIV_INTERN ulint dtype_get_at_most_n_mbchars( /*========================*/ ulint prtype, /*!< in: precise type */ ulint mbminlen, /*!< in: minimum length of a multi-byte character */ ulint mbmaxlen, /*!< in: maximum length of a multi-byte character */ ulint prefix_len, /*!< in: length of the requested prefix, in characters, multiplied by dtype_get_mbmaxlen(dtype) */ ulint data_len, /*!< in: length of str (in bytes) */ const char* str) /*!< in: the string whose prefix length is being determined */ { ut_a(data_len != UNIV_SQL_NULL); ut_ad(!mbmaxlen || !(prefix_len % mbmaxlen)); if (mbminlen != mbmaxlen) { const charset_t*cs; ut_a(!(prefix_len % mbmaxlen)); cs = ib_ucode_get_charset(dtype_get_charset_coll(prtype)); return(ib_ucode_get_storage_size( cs, prefix_len, data_len, str)); } if (prefix_len < data_len) { return(prefix_len); } return(data_len); } #endif /* UNIV_HOTBACKUP */ /*********************************************************************//** Checks if a data main type is a string type. Also a BLOB is considered a string type. @return TRUE if string type */ UNIV_INTERN ibool dtype_is_string_type( /*=================*/ ulint mtype) /*!< in: InnoDB main data type code: DATA_CHAR, ... */ { if (mtype <= DATA_BLOB || mtype == DATA_CLIENT || mtype == DATA_VARCLIENT) { return(TRUE); } return(FALSE); } /*********************************************************************//** Checks if a type is a binary string type. Note that for tables created with < 4.0.14, we do not know if a DATA_BLOB column is a BLOB or a TEXT column. For those DATA_BLOB columns this function currently returns FALSE. @return TRUE if binary string type */ UNIV_INTERN ibool dtype_is_binary_string_type( /*========================*/ ulint mtype, /*!< in: main data type */ ulint prtype) /*!< in: precise type */ { if ((mtype == DATA_FIXBINARY) || (mtype == DATA_BINARY) || (mtype == DATA_BLOB && (prtype & DATA_BINARY_TYPE))) { return(TRUE); } return(FALSE); } /*********************************************************************//** Checks if a type is a non-binary string type. That is, dtype_is_string_type is TRUE and dtype_is_binary_string_type is FALSE. Note that for tables created with < 4.0.14, we do not know if a DATA_BLOB column is a BLOB or a TEXT column. For those DATA_BLOB columns this function currently returns TRUE. @return TRUE if non-binary string type */ UNIV_INTERN ibool dtype_is_non_binary_string_type( /*============================*/ ulint mtype, /*!< in: main data type */ ulint prtype) /*!< in: precise type */ { if (dtype_is_string_type(mtype) == TRUE && dtype_is_binary_string_type(mtype, prtype) == FALSE) { return(TRUE); } return(FALSE); } /*********************************************************************//** Forms a precise type from the < 4.1.2 format precise type plus the charset-collation code. @return precise type, including the charset-collation code */ UNIV_INTERN ulint dtype_form_prtype( /*==============*/ ulint old_prtype, /*!< in: the user type code and the flags DATA_BINARY_TYPE etc. */ ulint charset_coll) /*!< in: user charset-collation code */ { ut_a(old_prtype < 256 * 256); ut_a(charset_coll < 256); return(old_prtype + (charset_coll << 16)); } /*********************************************************************//** Validates a data type structure. @return TRUE if ok */ UNIV_INTERN ibool dtype_validate( /*===========*/ const dtype_t* type) /*!< in: type struct to validate */ { ut_a(type); ut_a(type->mtype >= DATA_VARCHAR); ut_a(type->mtype <= DATA_CLIENT); if (type->mtype == DATA_SYS) { ut_a((type->prtype & DATA_CLIENT_TYPE_MASK) < DATA_N_SYS_COLS); } #ifndef UNIV_HOTBACKUP ut_a(type->mbminlen <= type->mbmaxlen); #endif /* !UNIV_HOTBACKUP */ return(TRUE); } #ifndef UNIV_HOTBACKUP /*********************************************************************//** Prints a data type structure. */ UNIV_INTERN void dtype_print( /*========*/ const dtype_t* type) /*!< in: type */ { ulint mtype; ulint prtype; ulint len; ut_a(type); mtype = type->mtype; prtype = type->prtype; switch (mtype) { case DATA_VARCHAR: ib_logger(ib_stream, "DATA_VARCHAR"); break; case DATA_CHAR: ib_logger(ib_stream, "DATA_CHAR"); break; case DATA_BINARY: ib_logger(ib_stream, "DATA_BINARY"); break; case DATA_FIXBINARY: ib_logger(ib_stream, "DATA_FIXBINARY"); break; case DATA_BLOB: ib_logger(ib_stream, "DATA_BLOB"); break; case DATA_INT: ib_logger(ib_stream, "DATA_INT"); break; case DATA_CLIENT: ib_logger(ib_stream, "DATA_CLIENT"); break; case DATA_SYS: ib_logger(ib_stream, "DATA_SYS"); break; case DATA_FLOAT: ib_logger(ib_stream, "DATA_FLOAT"); break; case DATA_DOUBLE: ib_logger(ib_stream, "DATA_DOUBLE"); break; case DATA_DECIMAL: ib_logger(ib_stream, "DATA_DECIMAL"); break; default: ib_logger(ib_stream, "type %lu", (ulong) mtype); break; } len = type->len; if ((type->mtype == DATA_SYS) || (type->mtype == DATA_VARCHAR) || (type->mtype == DATA_CHAR)) { ib_logger(ib_stream, " "); if (prtype == DATA_ROW_ID) { ib_logger(ib_stream, "DATA_ROW_ID"); len = DATA_ROW_ID_LEN; } else if (prtype == DATA_ROLL_PTR) { ib_logger(ib_stream, "DATA_ROLL_PTR"); len = DATA_ROLL_PTR_LEN; } else if (prtype == DATA_TRX_ID) { ib_logger(ib_stream, "DATA_TRX_ID"); len = DATA_TRX_ID_LEN; } else if (prtype == DATA_ENGLISH) { ib_logger(ib_stream, "DATA_ENGLISH"); } else { ib_logger(ib_stream, "prtype %lu", (ulong) prtype); } } else { if (prtype & DATA_UNSIGNED) { ib_logger(ib_stream, " DATA_UNSIGNED"); } if (prtype & DATA_BINARY_TYPE) { ib_logger(ib_stream, " DATA_BINARY_TYPE"); } if (prtype & DATA_NOT_NULL) { ib_logger(ib_stream, " DATA_NOT_NULL"); } } ib_logger(ib_stream, " len %lu", (ulong) len); } #endif /* !UNIV_HOTBACKUP */ haildb-2.3.2/data/data0data.c0000644000175000017500000004571711513177357016561 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1994, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /********************************************************************//** @file data/data0data.c SQL data field and tuple Created 5/30/1994 Heikki Tuuri *************************************************************************/ #include "data0data.h" #ifdef UNIV_NONINL #include "data0data.ic" #endif #ifndef UNIV_HOTBACKUP #include "rem0rec.h" #include "rem0cmp.h" #include "page0page.h" #include "page0zip.h" #include "dict0dict.h" #include "btr0cur.h" #include #endif /* !UNIV_HOTBACKUP */ #ifdef UNIV_DEBUG /** Dummy variable to catch access to uninitialized fields. In the debug version, dtuple_create() will make all fields of dtuple_t point to data_error. */ UNIV_INTERN byte data_error; # ifndef UNIV_DEBUG_VALGRIND /** this is used to fool the compiler in dtuple_validate */ UNIV_INTERN ulint data_dummy; # endif /* !UNIV_DEBUG_VALGRIND */ #endif /* UNIV_DEBUG */ #ifndef UNIV_HOTBACKUP /*********************************************************************//** Reset dfield variables. */ UNIV_INTERN void dfield_var_init(void) /*=================*/ { #ifdef UNIV_DEBUG data_error = 0; # ifndef UNIV_DEBUG_VALGRIND data_dummy = 0; # endif /* !UNIV_DEBUG_VALGRIND */ #endif /* UNIV_DEBUG */ } /************************************************************************* Tests if dfield data length and content is equal to the given. @return TRUE if equal */ UNIV_INTERN ibool dfield_data_is_binary_equal( /*========================*/ const dfield_t* field, /*!< in: field */ ulint len, /*!< in: data length or UNIV_SQL_NULL */ const byte* data) /*!< in: data */ { if (len != dfield_get_len(field)) { return(FALSE); } if (len == UNIV_SQL_NULL) { return(TRUE); } if (0 != memcmp(dfield_get_data(field), data, len)) { return(FALSE); } return(TRUE); } /************************************************************//** Compare two data tuples, respecting the collation of character fields. @return 1, 0 , -1 if tuple1 is greater, equal, less, respectively, than tuple2 */ UNIV_INTERN int dtuple_coll_cmp( /*============*/ void* cmp_ctx,/*!< in: client compare context */ const dtuple_t* tuple1, /*!< in: tuple 1 */ const dtuple_t* tuple2) /*!< in: tuple 2 */ { ulint n_fields; ulint i; ut_ad(tuple1 && tuple2); ut_ad(tuple1->magic_n == DATA_TUPLE_MAGIC_N); ut_ad(tuple2->magic_n == DATA_TUPLE_MAGIC_N); ut_ad(dtuple_check_typed(tuple1)); ut_ad(dtuple_check_typed(tuple2)); n_fields = dtuple_get_n_fields(tuple1); if (n_fields != dtuple_get_n_fields(tuple2)) { return(n_fields < dtuple_get_n_fields(tuple2) ? -1 : 1); } for (i = 0; i < n_fields; i++) { int cmp; const dfield_t* field1 = dtuple_get_nth_field(tuple1, i); const dfield_t* field2 = dtuple_get_nth_field(tuple2, i); cmp = cmp_dfield_dfield(cmp_ctx, field1, field2); if (cmp) { return(cmp); } } return(0); } /*********************************************************************//** Sets number of fields used in a tuple. Normally this is set in dtuple_create, but if you want later to set it smaller, you can use this. */ UNIV_INTERN void dtuple_set_n_fields( /*================*/ dtuple_t* tuple, /*!< in: tuple */ ulint n_fields) /*!< in: number of fields */ { ut_ad(tuple); tuple->n_fields = n_fields; tuple->n_fields_cmp = n_fields; } /**********************************************************//** Checks that a data field is typed. @return TRUE if ok */ static ibool dfield_check_typed_no_assert( /*=========================*/ const dfield_t* field) /*!< in: data field */ { if (dfield_get_type(field)->mtype > DATA_CLIENT || dfield_get_type(field)->mtype < DATA_VARCHAR) { ib_logger(ib_stream, "InnoDB: Error: data field type %lu, len %lu\n", (ulong) dfield_get_type(field)->mtype, (ulong) dfield_get_len(field)); return(FALSE); } return(TRUE); } /**********************************************************//** Checks that a data tuple is typed. @return TRUE if ok */ UNIV_INTERN ibool dtuple_check_typed_no_assert( /*=========================*/ const dtuple_t* tuple) /*!< in: tuple */ { const dfield_t* field; ulint i; if (dtuple_get_n_fields(tuple) > REC_MAX_N_FIELDS) { ib_logger(ib_stream, "InnoDB: Error: index entry has %lu fields\n", (ulong) dtuple_get_n_fields(tuple)); dump: ib_logger(ib_stream, "InnoDB: Tuple contents: "); dtuple_print(ib_stream, tuple); ib_logger(ib_stream,"\n"); return(FALSE); } for (i = 0; i < dtuple_get_n_fields(tuple); i++) { field = dtuple_get_nth_field(tuple, i); if (!dfield_check_typed_no_assert(field)) { goto dump; } } return(TRUE); } #endif /* !UNIV_HOTBACKUP */ #ifdef UNIV_DEBUG /**********************************************************//** Checks that a data field is typed. Asserts an error if not. @return TRUE if ok */ UNIV_INTERN ibool dfield_check_typed( /*===============*/ const dfield_t* field) /*!< in: data field */ { if (dfield_get_type(field)->mtype > DATA_CLIENT || dfield_get_type(field)->mtype < DATA_VARCHAR) { ib_logger(ib_stream, "InnoDB: Error: data field type %lu, len %lu\n", (ulong) dfield_get_type(field)->mtype, (ulong) dfield_get_len(field)); ut_error; } return(TRUE); } /**********************************************************//** Checks that a data tuple is typed. Asserts an error if not. @return TRUE if ok */ UNIV_INTERN ibool dtuple_check_typed( /*===============*/ const dtuple_t* tuple) /*!< in: tuple */ { const dfield_t* field; ulint i; for (i = 0; i < dtuple_get_n_fields(tuple); i++) { field = dtuple_get_nth_field(tuple, i); ut_a(dfield_check_typed(field)); } return(TRUE); } /**********************************************************//** Validates the consistency of a tuple which must be complete, i.e, all fields must have been set. @return TRUE if ok */ UNIV_INTERN ibool dtuple_validate( /*============*/ const dtuple_t* tuple) /*!< in: tuple */ { const dfield_t* field; ulint n_fields; ulint len; ulint i; ut_ad(tuple->magic_n == DATA_TUPLE_MAGIC_N); n_fields = dtuple_get_n_fields(tuple); /* We dereference all the data of each field to test for memory traps */ for (i = 0; i < n_fields; i++) { field = dtuple_get_nth_field(tuple, i); len = dfield_get_len(field); if (!dfield_is_null(field)) { const byte* data = dfield_get_data(field); #ifndef UNIV_DEBUG_VALGRIND ulint j; for (j = 0; j < len; j++) { data_dummy += *data; /* fool the compiler not to optimize out this code */ data++; } #endif /* !UNIV_DEBUG_VALGRIND */ UNIV_MEM_ASSERT_RW(data, len); } } ut_a(dtuple_check_typed(tuple)); return(TRUE); } #endif /* UNIV_DEBUG */ #ifndef UNIV_HOTBACKUP /*************************************************************//** Pretty prints a dfield value according to its data type. */ UNIV_INTERN void dfield_print( /*=========*/ const dfield_t* dfield) /*!< in: dfield */ { const byte* data; ulint len; ulint i; len = dfield_get_len(dfield); data = dfield_get_data(dfield); if (dfield_is_null(dfield)) { ib_logger(ib_stream, "NULL"); return; } switch (dtype_get_mtype(dfield_get_type(dfield))) { case DATA_CHAR: case DATA_VARCHAR: for (i = 0; i < len; i++) { int c = *data++; ib_logger(ib_stream, "%c", isprint(c) ? c : ' '); } if (dfield_is_ext(dfield)) { ib_logger(ib_stream, "(external)"); } break; case DATA_INT: ut_a(len == 4); /* only works for 32-bit integers */ ib_logger(ib_stream, "%d", (int)mach_read_from_4(data)); break; default: ut_error; } } /*************************************************************//** Pretty prints a dfield value according to its data type. Also the hex string is printed if a string contains non-printable characters. */ UNIV_INTERN void dfield_print_also_hex( /*==================*/ const dfield_t* dfield) /*!< in: dfield */ { const byte* data; ulint len; ulint prtype; ulint i; ibool print_also_hex; len = dfield_get_len(dfield); data = dfield_get_data(dfield); if (dfield_is_null(dfield)) { ib_logger(ib_stream, "NULL"); return; } prtype = dtype_get_prtype(dfield_get_type(dfield)); switch (dtype_get_mtype(dfield_get_type(dfield))) { dulint id; case DATA_INT: switch (len) { ulint val; case 1: val = mach_read_from_1(data); if (!(prtype & DATA_UNSIGNED)) { val &= ~0x80; ib_logger(ib_stream, "%ld", (long) val); } else { ib_logger(ib_stream, "%lu", (ulong) val); } break; case 2: val = mach_read_from_2(data); if (!(prtype & DATA_UNSIGNED)) { val &= ~0x8000; ib_logger(ib_stream, "%ld", (long) val); } else { ib_logger(ib_stream, "%lu", (ulong) val); } break; case 3: val = mach_read_from_3(data); if (!(prtype & DATA_UNSIGNED)) { val &= ~0x800000; ib_logger(ib_stream, "%ld", (long) val); } else { ib_logger(ib_stream, "%lu", (ulong) val); } break; case 4: val = mach_read_from_4(data); if (!(prtype & DATA_UNSIGNED)) { val &= ~0x80000000; ib_logger(ib_stream, "%ld", (long) val); } else { ib_logger(ib_stream, "%lu", (ulong) val); } break; case 6: id = mach_read_from_6(data); ib_logger(ib_stream, "{%lu %lu}", ut_dulint_get_high(id), ut_dulint_get_low(id)); break; case 7: id = mach_read_from_7(data); ib_logger(ib_stream, "{%lu %lu}", ut_dulint_get_high(id), ut_dulint_get_low(id)); break; case 8: id = mach_read_from_8(data); ib_logger(ib_stream, "{%lu %lu}", ut_dulint_get_high(id), ut_dulint_get_low(id)); break; default: goto print_hex; } break; case DATA_SYS: switch (prtype & DATA_SYS_PRTYPE_MASK) { case DATA_TRX_ID: id = mach_read_from_6(data); ib_logger(ib_stream, "trx_id " TRX_ID_FMT, TRX_ID_PREP_PRINTF(id)); break; case DATA_ROLL_PTR: id = mach_read_from_7(data); ib_logger(ib_stream, "roll_ptr {%lu %lu}", ut_dulint_get_high(id), ut_dulint_get_low(id)); break; case DATA_ROW_ID: id = mach_read_from_6(data); ib_logger(ib_stream, "row_id {%lu %lu}", ut_dulint_get_high(id), ut_dulint_get_low(id)); break; default: id = mach_dulint_read_compressed(data); ib_logger(ib_stream, "mix_id {%lu %lu}", ut_dulint_get_high(id), ut_dulint_get_low(id)); } break; case DATA_CHAR: case DATA_VARCHAR: print_also_hex = FALSE; for (i = 0; i < len; i++) { int c = *data++; if (!isprint(c)) { print_also_hex = TRUE; ib_logger(ib_stream, "\\x%02x", (unsigned char) c); } else { ib_logger(ib_stream,"%c", c); } } if (dfield_is_ext(dfield)) { ib_logger(ib_stream, "(external)"); } if (!print_also_hex) { break; } data = dfield_get_data(dfield); /* fall through */ case DATA_BINARY: default: print_hex: ib_logger(ib_stream, " Hex: "); for (i = 0; i < len; i++) { ib_logger(ib_stream, "%02lx", (ulint) *data++); } if (dfield_is_ext(dfield)) { ib_logger(ib_stream, "(external)"); } } } /*************************************************************//** Print a dfield value using ut_print_buf. */ static void dfield_print_raw( /*=============*/ ib_stream_t ib_stream, /*!< in: output stream */ const dfield_t* dfield) /*!< in: dfield */ { ulint len = dfield_get_len(dfield); if (!dfield_is_null(dfield)) { ulint print_len = ut_min(len, 1000); ut_print_buf(ib_stream, dfield_get_data(dfield), print_len); if (len != print_len) { ib_logger(ib_stream, "(total %lu bytes%s)", (ulong) len, dfield_is_ext(dfield) ? ", external" : ""); } } else { ib_logger(ib_stream, " SQL NULL"); } } /**********************************************************//** The following function prints the contents of a tuple. */ UNIV_INTERN void dtuple_print( /*=========*/ ib_stream_t ib_stream, /*!< in: output stream */ const dtuple_t* tuple) /*!< in: tuple */ { ulint n_fields; ulint i; n_fields = dtuple_get_n_fields(tuple); ib_logger(ib_stream, "DATA TUPLE: %lu fields;\n", (ulong) n_fields); for (i = 0; i < n_fields; i++) { ib_logger(ib_stream, " %lu:", (ulong) i); dfield_print_raw(ib_stream, dtuple_get_nth_field(tuple, i)); ib_logger(ib_stream, ";\n"); } ut_ad(dtuple_validate(tuple)); } /**************************************************************//** Moves parts of long fields in entry to the big record vector so that the size of tuple drops below the maximum record size allowed in the database. Moves data only from those fields which are not necessary to determine uniquely the insertion place of the tuple in the index. @return own: created big record vector, NULL if we are not able to shorten the entry enough, i.e., if there are too many fixed-length or short fields in entry or the index is clustered */ UNIV_INTERN big_rec_t* dtuple_convert_big_rec( /*===================*/ dict_index_t* index, /*!< in: index */ dtuple_t* entry, /*!< in/out: index entry */ ulint* n_ext) /*!< in/out: number of externally stored columns */ { mem_heap_t* heap; big_rec_t* vector; dfield_t* dfield; dict_field_t* ifield; ulint size; ulint n_fields; ulint local_len; ulint local_prefix_len; if (UNIV_UNLIKELY(!dict_index_is_clust(index))) { return(NULL); } if (dict_table_get_format(index->table) < DICT_TF_FORMAT_ZIP) { /* up to v5.1: store a 768-byte prefix locally */ local_len = BTR_EXTERN_FIELD_REF_SIZE + DICT_MAX_INDEX_COL_LEN; } else { /* new-format table: do not store any BLOB prefix locally */ local_len = BTR_EXTERN_FIELD_REF_SIZE; } ut_a(dtuple_check_typed_no_assert(entry)); size = rec_get_converted_size(index, entry, *n_ext); if (UNIV_UNLIKELY(size > 1000000000)) { ib_logger(ib_stream, "InnoDB: Warning: tuple size very big: %lu\n", (ulong) size); ib_logger(ib_stream, "InnoDB: Tuple contents: "); dtuple_print(ib_stream, entry); ib_logger(ib_stream, "\n"); } heap = mem_heap_create(size + dtuple_get_n_fields(entry) * sizeof(big_rec_field_t) + 1000); vector = mem_heap_alloc(heap, sizeof(big_rec_t)); vector->heap = heap; vector->fields = mem_heap_alloc(heap, dtuple_get_n_fields(entry) * sizeof(big_rec_field_t)); /* Decide which fields to shorten: the algorithm is to look for a variable-length field that yields the biggest savings when stored externally */ n_fields = 0; while (page_rec_needs_ext(rec_get_converted_size(index, entry, *n_ext), dict_table_is_comp(index->table), dict_index_get_n_fields(index), dict_table_zip_size(index->table))) { ulint i; ulint longest = 0; ulint longest_i = ULINT_MAX; byte* data; big_rec_field_t* b; for (i = dict_index_get_n_unique_in_tree(index); i < dtuple_get_n_fields(entry); i++) { ulint savings; dfield = dtuple_get_nth_field(entry, i); ifield = dict_index_get_nth_field(index, i); /* Skip fixed-length, NULL, externally stored, or short columns */ if (ifield->fixed_len || dfield_is_null(dfield) || dfield_is_ext(dfield) || dfield_get_len(dfield) <= local_len || dfield_get_len(dfield) <= BTR_EXTERN_FIELD_REF_SIZE * 2) { goto skip_field; } savings = dfield_get_len(dfield) - local_len; /* Check that there would be savings */ if (longest >= savings) { goto skip_field; } /* In DYNAMIC and COMPRESSED format, store locally any non-BLOB columns whose maximum length does not exceed 256 bytes. This is because there is no room for the "external storage" flag when the maximum length is 255 bytes or less. This restriction trivially holds in REDUNDANT and COMPACT format, because there we always store locally columns whose length is up to local_len == 788 bytes. @see rec_init_offsets_comp_ordinary */ if (ifield->col->mtype != DATA_BLOB && ifield->col->len < 256) { goto skip_field; } longest_i = i; longest = savings; skip_field: continue; } if (!longest) { /* Cannot shorten more */ mem_heap_free(heap); return(NULL); } /* Move data from field longest_i to big rec vector. We store the first bytes locally to the record. Then we can calculate all ordering fields in all indexes from locally stored data. */ dfield = dtuple_get_nth_field(entry, longest_i); ifield = dict_index_get_nth_field(index, longest_i); local_prefix_len = local_len - BTR_EXTERN_FIELD_REF_SIZE; b = &vector->fields[n_fields]; b->field_no = longest_i; b->len = dfield_get_len(dfield) - local_prefix_len; b->data = (char*) dfield_get_data(dfield) + local_prefix_len; /* Allocate the locally stored part of the column. */ data = mem_heap_alloc(heap, local_len); /* Copy the local prefix. */ memcpy(data, dfield_get_data(dfield), local_prefix_len); /* Clear the extern field reference (BLOB pointer). */ memset(data + local_prefix_len, 0, BTR_EXTERN_FIELD_REF_SIZE); #if 0 /* The following would fail the Valgrind checks in page_cur_insert_rec_low() and page_cur_insert_rec_zip(). The BLOB pointers in the record will be initialized after the record and the BLOBs have been written. */ UNIV_MEM_ALLOC(data + local_prefix_len, BTR_EXTERN_FIELD_REF_SIZE); #endif dfield_set_data(dfield, data, local_len); dfield_set_ext(dfield); n_fields++; (*n_ext)++; ut_ad(n_fields < dtuple_get_n_fields(entry)); } vector->n_fields = n_fields; return(vector); } /**************************************************************//** Puts back to entry the data stored in vector. Note that to ensure the fields in entry can accommodate the data, vector must have been created from entry with dtuple_convert_big_rec. */ UNIV_INTERN void dtuple_convert_back_big_rec( /*========================*/ dict_index_t* index __attribute__((unused)), /*!< in: index */ dtuple_t* entry, /*!< in: entry whose data was put to vector */ big_rec_t* vector) /*!< in, own: big rec vector; it is freed in this function */ { big_rec_field_t* b = vector->fields; const big_rec_field_t* const end = b + vector->n_fields; for (; b < end; b++) { dfield_t* dfield; ulint local_len; dfield = dtuple_get_nth_field(entry, b->field_no); local_len = dfield_get_len(dfield); ut_ad(dfield_is_ext(dfield)); ut_ad(local_len >= BTR_EXTERN_FIELD_REF_SIZE); local_len -= BTR_EXTERN_FIELD_REF_SIZE; ut_ad(local_len <= DICT_MAX_INDEX_COL_LEN); dfield_set_data(dfield, (char*) b->data - local_len, b->len + local_len); } mem_heap_free(vector->heap); } #endif /* !UNIV_HOTBACKUP */ haildb-2.3.2/aclocal.m40000644000175000017500000011321511513177415015501 0ustar00pcrewspcrews00000000000000# generated automatically by aclocal 1.11.1 -*- Autoconf -*- # Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, # 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc. # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.67],, [m4_warning([this file was generated for autoconf 2.67. You have another version of autoconf. It may work, but is not guaranteed to. If you have problems, you may need to regenerate the build system entirely. To do so, use the procedure documented by the package, typically `autoreconf'.])]) # Copyright (C) 2002, 2003, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_AUTOMAKE_VERSION(VERSION) # ---------------------------- # Automake X.Y traces this macro to ensure aclocal.m4 has been # generated from the m4 files accompanying Automake X.Y. # (This private macro should not be called outside this file.) AC_DEFUN([AM_AUTOMAKE_VERSION], [am__api_version='1.11' dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to dnl require some minimum version. Point them to the right macro. m4_if([$1], [1.11.1], [], [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl ]) # _AM_AUTOCONF_VERSION(VERSION) # ----------------------------- # aclocal traces this macro to find the Autoconf version. # This is a private macro too. Using m4_define simplifies # the logic in aclocal, which can simply ignore this definition. m4_define([_AM_AUTOCONF_VERSION], []) # AM_SET_CURRENT_AUTOMAKE_VERSION # ------------------------------- # Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced. # This function is AC_REQUIREd by AM_INIT_AUTOMAKE. AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], [AM_AUTOMAKE_VERSION([1.11.1])dnl m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) # AM_AUX_DIR_EXPAND -*- Autoconf -*- # Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets # $ac_aux_dir to `$srcdir/foo'. In other projects, it is set to # `$srcdir', `$srcdir/..', or `$srcdir/../..'. # # Of course, Automake must honor this variable whenever it calls a # tool from the auxiliary directory. The problem is that $srcdir (and # therefore $ac_aux_dir as well) can be either absolute or relative, # depending on how configure is run. This is pretty annoying, since # it makes $ac_aux_dir quite unusable in subdirectories: in the top # source directory, any form will work fine, but in subdirectories a # relative path needs to be adjusted first. # # $ac_aux_dir/missing # fails when called from a subdirectory if $ac_aux_dir is relative # $top_srcdir/$ac_aux_dir/missing # fails if $ac_aux_dir is absolute, # fails when called from a subdirectory in a VPATH build with # a relative $ac_aux_dir # # The reason of the latter failure is that $top_srcdir and $ac_aux_dir # are both prefixed by $srcdir. In an in-source build this is usually # harmless because $srcdir is `.', but things will broke when you # start a VPATH build or use an absolute $srcdir. # # So we could use something similar to $top_srcdir/$ac_aux_dir/missing, # iff we strip the leading $srcdir from $ac_aux_dir. That would be: # am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"` # and then we would define $MISSING as # MISSING="\${SHELL} $am_aux_dir/missing" # This will work as long as MISSING is not called from configure, because # unfortunately $(top_srcdir) has no meaning in configure. # However there are other variables, like CC, which are often used in # configure, and could therefore not use this "fixed" $ac_aux_dir. # # Another solution, used here, is to always expand $ac_aux_dir to an # absolute PATH. The drawback is that using absolute paths prevent a # configured tree to be moved without reconfiguration. AC_DEFUN([AM_AUX_DIR_EXPAND], [dnl Rely on autoconf to set up CDPATH properly. AC_PREREQ([2.50])dnl # expand $ac_aux_dir to an absolute path am_aux_dir=`cd $ac_aux_dir && pwd` ]) # AM_CONDITIONAL -*- Autoconf -*- # Copyright (C) 1997, 2000, 2001, 2003, 2004, 2005, 2006, 2008 # Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # serial 9 # AM_CONDITIONAL(NAME, SHELL-CONDITION) # ------------------------------------- # Define a conditional. AC_DEFUN([AM_CONDITIONAL], [AC_PREREQ(2.52)dnl ifelse([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])], [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl AC_SUBST([$1_TRUE])dnl AC_SUBST([$1_FALSE])dnl _AM_SUBST_NOTMAKE([$1_TRUE])dnl _AM_SUBST_NOTMAKE([$1_FALSE])dnl m4_define([_AM_COND_VALUE_$1], [$2])dnl if $2; then $1_TRUE= $1_FALSE='#' else $1_TRUE='#' $1_FALSE= fi AC_CONFIG_COMMANDS_PRE( [if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then AC_MSG_ERROR([[conditional "$1" was never defined. Usually this means the macro was only invoked conditionally.]]) fi])]) # Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2009 # Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # serial 10 # There are a few dirty hacks below to avoid letting `AC_PROG_CC' be # written in clear, in which case automake, when reading aclocal.m4, # will think it sees a *use*, and therefore will trigger all it's # C support machinery. Also note that it means that autoscan, seeing # CC etc. in the Makefile, will ask for an AC_PROG_CC use... # _AM_DEPENDENCIES(NAME) # ---------------------- # See how the compiler implements dependency checking. # NAME is "CC", "CXX", "GCJ", or "OBJC". # We try a few techniques and use that to set a single cache variable. # # We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was # modified to invoke _AM_DEPENDENCIES(CC); we would have a circular # dependency, and given that the user is not expected to run this macro, # just rely on AC_PROG_CC. AC_DEFUN([_AM_DEPENDENCIES], [AC_REQUIRE([AM_SET_DEPDIR])dnl AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl AC_REQUIRE([AM_MAKE_INCLUDE])dnl AC_REQUIRE([AM_DEP_TRACK])dnl ifelse([$1], CC, [depcc="$CC" am_compiler_list=], [$1], CXX, [depcc="$CXX" am_compiler_list=], [$1], OBJC, [depcc="$OBJC" am_compiler_list='gcc3 gcc'], [$1], UPC, [depcc="$UPC" am_compiler_list=], [$1], GCJ, [depcc="$GCJ" am_compiler_list='gcc3 gcc'], [depcc="$$1" am_compiler_list=]) AC_CACHE_CHECK([dependency style of $depcc], [am_cv_$1_dependencies_compiler_type], [if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then # We make a subdir and do the tests there. Otherwise we can end up # making bogus files that we don't know about and never remove. For # instance it was reported that on HP-UX the gcc test will end up # making a dummy file named `D' -- because `-MD' means `put the output # in D'. mkdir conftest.dir # Copy depcomp to subdir because otherwise we won't find it if we're # using a relative directory. cp "$am_depcomp" conftest.dir cd conftest.dir # We will build objects and dependencies in a subdirectory because # it helps to detect inapplicable dependency modes. For instance # both Tru64's cc and ICC support -MD to output dependencies as a # side effect of compilation, but ICC will put the dependencies in # the current directory while Tru64 will put them in the object # directory. mkdir sub am_cv_$1_dependencies_compiler_type=none if test "$am_compiler_list" = ""; then am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp` fi am__universal=false m4_case([$1], [CC], [case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac], [CXX], [case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac]) for depmode in $am_compiler_list; do # Setup a source with many dependencies, because some compilers # like to wrap large dependency lists on column 80 (with \), and # we should not choose a depcomp mode which is confused by this. # # We need to recreate these files for each test, as the compiler may # overwrite some of them when testing with obscure command lines. # This happens at least with the AIX C compiler. : > sub/conftest.c for i in 1 2 3 4 5 6; do echo '#include "conftst'$i'.h"' >> sub/conftest.c # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with # Solaris 8's {/usr,}/bin/sh. touch sub/conftst$i.h done echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf # We check with `-c' and `-o' for the sake of the "dashmstdout" # mode. It turns out that the SunPro C++ compiler does not properly # handle `-M -o', and we need to detect this. Also, some Intel # versions had trouble with output in subdirs am__obj=sub/conftest.${OBJEXT-o} am__minus_obj="-o $am__obj" case $depmode in gcc) # This depmode causes a compiler race in universal mode. test "$am__universal" = false || continue ;; nosideeffect) # after this tag, mechanisms are not by side-effect, so they'll # only be used when explicitly requested if test "x$enable_dependency_tracking" = xyes; then continue else break fi ;; msvisualcpp | msvcmsys) # This compiler won't grok `-c -o', but also, the minuso test has # not run yet. These depmodes are late enough in the game, and # so weak that their functioning should not be impacted. am__obj=conftest.${OBJEXT-o} am__minus_obj= ;; none) break ;; esac if depmode=$depmode \ source=sub/conftest.c object=$am__obj \ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ >/dev/null 2>conftest.err && grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && grep $am__obj sub/conftest.Po > /dev/null 2>&1 && ${MAKE-make} -s -f confmf > /dev/null 2>&1; then # icc doesn't choke on unknown options, it will just issue warnings # or remarks (even with -Werror). So we grep stderr for any message # that says an option was ignored or not supported. # When given -MP, icc 7.0 and 7.1 complain thusly: # icc: Command line warning: ignoring option '-M'; no argument required # The diagnosis changed in icc 8.0: # icc: Command line remark: option '-MP' not supported if (grep 'ignoring option' conftest.err || grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else am_cv_$1_dependencies_compiler_type=$depmode break fi fi done cd .. rm -rf conftest.dir else am_cv_$1_dependencies_compiler_type=none fi ]) AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type]) AM_CONDITIONAL([am__fastdep$1], [ test "x$enable_dependency_tracking" != xno \ && test "$am_cv_$1_dependencies_compiler_type" = gcc3]) ]) # AM_SET_DEPDIR # ------------- # Choose a directory name for dependency files. # This macro is AC_REQUIREd in _AM_DEPENDENCIES AC_DEFUN([AM_SET_DEPDIR], [AC_REQUIRE([AM_SET_LEADING_DOT])dnl AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl ]) # AM_DEP_TRACK # ------------ AC_DEFUN([AM_DEP_TRACK], [AC_ARG_ENABLE(dependency-tracking, [ --disable-dependency-tracking speeds up one-time build --enable-dependency-tracking do not reject slow dependency extractors]) if test "x$enable_dependency_tracking" != xno; then am_depcomp="$ac_aux_dir/depcomp" AMDEPBACKSLASH='\' fi AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno]) AC_SUBST([AMDEPBACKSLASH])dnl _AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl ]) # Generate code to set up dependency tracking. -*- Autoconf -*- # Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2008 # Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. #serial 5 # _AM_OUTPUT_DEPENDENCY_COMMANDS # ------------------------------ AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], [{ # Autoconf 2.62 quotes --file arguments for eval, but not when files # are listed without --file. Let's play safe and only enable the eval # if we detect the quoting. case $CONFIG_FILES in *\'*) eval set x "$CONFIG_FILES" ;; *) set x $CONFIG_FILES ;; esac shift for mf do # Strip MF so we end up with the name of the file. mf=`echo "$mf" | sed -e 's/:.*$//'` # Check whether this is an Automake generated Makefile or not. # We used to match only the files named `Makefile.in', but # some people rename them; so instead we look at the file content. # Grep'ing the first line is not enough: some people post-process # each Makefile.in and add a new line on top of each file to say so. # Grep'ing the whole file is not good either: AIX grep has a line # limit of 2048, but all sed's we know have understand at least 4000. if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then dirpart=`AS_DIRNAME("$mf")` else continue fi # Extract the definition of DEPDIR, am__include, and am__quote # from the Makefile without running `make'. DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` test -z "$DEPDIR" && continue am__include=`sed -n 's/^am__include = //p' < "$mf"` test -z "am__include" && continue am__quote=`sed -n 's/^am__quote = //p' < "$mf"` # When using ansi2knr, U may be empty or an underscore; expand it U=`sed -n 's/^U = //p' < "$mf"` # Find all dependency output files, they are included files with # $(DEPDIR) in their names. We invoke sed twice because it is the # simplest approach to changing $(DEPDIR) to its actual value in the # expansion. for file in `sed -n " s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do # Make sure the directory exists. test -f "$dirpart/$file" && continue fdir=`AS_DIRNAME(["$file"])` AS_MKDIR_P([$dirpart/$fdir]) # echo "creating $dirpart/$file" echo '# dummy' > "$dirpart/$file" done done } ])# _AM_OUTPUT_DEPENDENCY_COMMANDS # AM_OUTPUT_DEPENDENCY_COMMANDS # ----------------------------- # This macro should only be invoked once -- use via AC_REQUIRE. # # This code is only required when automatic dependency tracking # is enabled. FIXME. This creates each `.P' file that we will # need in order to bootstrap the dependency handling code. AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], [AC_CONFIG_COMMANDS([depfiles], [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS], [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"]) ]) # Do all the work for Automake. -*- Autoconf -*- # Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, # 2005, 2006, 2008, 2009 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # serial 16 # This macro actually does too much. Some checks are only needed if # your package does certain things. But this isn't really a big deal. # AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE]) # AM_INIT_AUTOMAKE([OPTIONS]) # ----------------------------------------------- # The call with PACKAGE and VERSION arguments is the old style # call (pre autoconf-2.50), which is being phased out. PACKAGE # and VERSION should now be passed to AC_INIT and removed from # the call to AM_INIT_AUTOMAKE. # We support both call styles for the transition. After # the next Automake release, Autoconf can make the AC_INIT # arguments mandatory, and then we can depend on a new Autoconf # release and drop the old call support. AC_DEFUN([AM_INIT_AUTOMAKE], [AC_PREREQ([2.62])dnl dnl Autoconf wants to disallow AM_ names. We explicitly allow dnl the ones we care about. m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl AC_REQUIRE([AC_PROG_INSTALL])dnl if test "`cd $srcdir && pwd`" != "`pwd`"; then # Use -I$(srcdir) only when $(srcdir) != ., so that make's output # is not polluted with repeated "-I." AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl # test to see if srcdir already configured if test -f $srcdir/config.status; then AC_MSG_ERROR([source directory already configured; run "make distclean" there first]) fi fi # test whether we have cygpath if test -z "$CYGPATH_W"; then if (cygpath --version) >/dev/null 2>/dev/null; then CYGPATH_W='cygpath -w' else CYGPATH_W=echo fi fi AC_SUBST([CYGPATH_W]) # Define the identity of the package. dnl Distinguish between old-style and new-style calls. m4_ifval([$2], [m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl AC_SUBST([PACKAGE], [$1])dnl AC_SUBST([VERSION], [$2])], [_AM_SET_OPTIONS([$1])dnl dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT. m4_if(m4_ifdef([AC_PACKAGE_NAME], 1)m4_ifdef([AC_PACKAGE_VERSION], 1), 11,, [m4_fatal([AC_INIT should be called with package and version arguments])])dnl AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl _AM_IF_OPTION([no-define],, [AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of package]) AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version number of package])])dnl # Some tools Automake needs. AC_REQUIRE([AM_SANITY_CHECK])dnl AC_REQUIRE([AC_ARG_PROGRAM])dnl AM_MISSING_PROG(ACLOCAL, aclocal-${am__api_version}) AM_MISSING_PROG(AUTOCONF, autoconf) AM_MISSING_PROG(AUTOMAKE, automake-${am__api_version}) AM_MISSING_PROG(AUTOHEADER, autoheader) AM_MISSING_PROG(MAKEINFO, makeinfo) AC_REQUIRE([AM_PROG_INSTALL_SH])dnl AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl AC_REQUIRE([AM_PROG_MKDIR_P])dnl # We need awk for the "check" target. The system "awk" is bad on # some platforms. AC_REQUIRE([AC_PROG_AWK])dnl AC_REQUIRE([AC_PROG_MAKE_SET])dnl AC_REQUIRE([AM_SET_LEADING_DOT])dnl _AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])], [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])], [_AM_PROG_TAR([v7])])]) _AM_IF_OPTION([no-dependencies],, [AC_PROVIDE_IFELSE([AC_PROG_CC], [_AM_DEPENDENCIES(CC)], [define([AC_PROG_CC], defn([AC_PROG_CC])[_AM_DEPENDENCIES(CC)])])dnl AC_PROVIDE_IFELSE([AC_PROG_CXX], [_AM_DEPENDENCIES(CXX)], [define([AC_PROG_CXX], defn([AC_PROG_CXX])[_AM_DEPENDENCIES(CXX)])])dnl AC_PROVIDE_IFELSE([AC_PROG_OBJC], [_AM_DEPENDENCIES(OBJC)], [define([AC_PROG_OBJC], defn([AC_PROG_OBJC])[_AM_DEPENDENCIES(OBJC)])])dnl ]) _AM_IF_OPTION([silent-rules], [AC_REQUIRE([AM_SILENT_RULES])])dnl dnl The `parallel-tests' driver may need to know about EXEEXT, so add the dnl `am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This macro dnl is hooked onto _AC_COMPILER_EXEEXT early, see below. AC_CONFIG_COMMANDS_PRE(dnl [m4_provide_if([_AM_COMPILER_EXEEXT], [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl ]) dnl Hook into `_AC_COMPILER_EXEEXT' early to learn its expansion. Do not dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further dnl mangled by Autoconf and run in a shell conditional statement. m4_define([_AC_COMPILER_EXEEXT], m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])]) # When config.status generates a header, we must update the stamp-h file. # This file resides in the same directory as the config header # that is generated. The stamp files are numbered to have different names. # Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the # loop where config.status creates the headers, so we can generate # our stamp files there. AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK], [# Compute $1's index in $config_headers. _am_arg=$1 _am_stamp_count=1 for _am_header in $config_headers :; do case $_am_header in $_am_arg | $_am_arg:* ) break ;; * ) _am_stamp_count=`expr $_am_stamp_count + 1` ;; esac done echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count]) # Copyright (C) 2001, 2003, 2005, 2008 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_PROG_INSTALL_SH # ------------------ # Define $install_sh. AC_DEFUN([AM_PROG_INSTALL_SH], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl if test x"${install_sh}" != xset; then case $am_aux_dir in *\ * | *\ *) install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; *) install_sh="\${SHELL} $am_aux_dir/install-sh" esac fi AC_SUBST(install_sh)]) # Copyright (C) 2003, 2005 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # serial 2 # Check whether the underlying file-system supports filenames # with a leading dot. For instance MS-DOS doesn't. AC_DEFUN([AM_SET_LEADING_DOT], [rm -rf .tst 2>/dev/null mkdir .tst 2>/dev/null if test -d .tst; then am__leading_dot=. else am__leading_dot=_ fi rmdir .tst 2>/dev/null AC_SUBST([am__leading_dot])]) # Check to see how 'make' treats includes. -*- Autoconf -*- # Copyright (C) 2001, 2002, 2003, 2005, 2009 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # serial 4 # AM_MAKE_INCLUDE() # ----------------- # Check to see how make treats includes. AC_DEFUN([AM_MAKE_INCLUDE], [am_make=${MAKE-make} cat > confinc << 'END' am__doit: @echo this is the am__doit target .PHONY: am__doit END # If we don't find an include directive, just comment out the code. AC_MSG_CHECKING([for style of include used by $am_make]) am__include="#" am__quote= _am_result=none # First try GNU make style include. echo "include confinc" > confmf # Ignore all kinds of additional output from `make'. case `$am_make -s -f confmf 2> /dev/null` in #( *the\ am__doit\ target*) am__include=include am__quote= _am_result=GNU ;; esac # Now try BSD make style include. if test "$am__include" = "#"; then echo '.include "confinc"' > confmf case `$am_make -s -f confmf 2> /dev/null` in #( *the\ am__doit\ target*) am__include=.include am__quote="\"" _am_result=BSD ;; esac fi AC_SUBST([am__include]) AC_SUBST([am__quote]) AC_MSG_RESULT([$_am_result]) rm -f confinc confmf ]) # Copyright (C) 1999, 2000, 2001, 2003, 2004, 2005, 2008 # Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # serial 6 # AM_PROG_CC_C_O # -------------- # Like AC_PROG_CC_C_O, but changed for automake. AC_DEFUN([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC_C_O])dnl AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl AC_REQUIRE_AUX_FILE([compile])dnl # FIXME: we rely on the cache variable name because # there is no other way. set dummy $CC am_cc=`echo $[2] | sed ['s/[^a-zA-Z0-9_]/_/g;s/^[0-9]/_/']` eval am_t=\$ac_cv_prog_cc_${am_cc}_c_o if test "$am_t" != yes; then # Losing compiler, so override with the script. # FIXME: It is wrong to rewrite CC. # But if we don't then we get into trouble of one sort or another. # A longer-term fix would be to have automake use am__CC in this case, # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" CC="$am_aux_dir/compile $CC" fi dnl Make sure AC_PROG_CC is never called again, or it will override our dnl setting of CC. m4_define([AC_PROG_CC], [m4_fatal([AC_PROG_CC cannot be called after AM_PROG_CC_C_O])]) ]) # Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- # Copyright (C) 1997, 1999, 2000, 2001, 2003, 2004, 2005, 2008 # Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # serial 6 # AM_MISSING_PROG(NAME, PROGRAM) # ------------------------------ AC_DEFUN([AM_MISSING_PROG], [AC_REQUIRE([AM_MISSING_HAS_RUN]) $1=${$1-"${am_missing_run}$2"} AC_SUBST($1)]) # AM_MISSING_HAS_RUN # ------------------ # Define MISSING if not defined so far and test if it supports --run. # If it does, set am_missing_run to use it, otherwise, to nothing. AC_DEFUN([AM_MISSING_HAS_RUN], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl AC_REQUIRE_AUX_FILE([missing])dnl if test x"${MISSING+set}" != xset; then case $am_aux_dir in *\ * | *\ *) MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; *) MISSING="\${SHELL} $am_aux_dir/missing" ;; esac fi # Use eval to expand $SHELL if eval "$MISSING --run true"; then am_missing_run="$MISSING --run " else am_missing_run= AC_MSG_WARN([`missing' script is too old or missing]) fi ]) # Copyright (C) 2003, 2004, 2005, 2006 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_PROG_MKDIR_P # --------------- # Check for `mkdir -p'. AC_DEFUN([AM_PROG_MKDIR_P], [AC_PREREQ([2.60])dnl AC_REQUIRE([AC_PROG_MKDIR_P])dnl dnl Automake 1.8 to 1.9.6 used to define mkdir_p. We now use MKDIR_P, dnl while keeping a definition of mkdir_p for backward compatibility. dnl @MKDIR_P@ is magic: AC_OUTPUT adjusts its value for each Makefile. dnl However we cannot define mkdir_p as $(MKDIR_P) for the sake of dnl Makefile.ins that do not define MKDIR_P, so we do our own dnl adjustment using top_builddir (which is defined more often than dnl MKDIR_P). AC_SUBST([mkdir_p], ["$MKDIR_P"])dnl case $mkdir_p in [[\\/$]]* | ?:[[\\/]]*) ;; */*) mkdir_p="\$(top_builddir)/$mkdir_p" ;; esac ]) # Helper functions for option handling. -*- Autoconf -*- # Copyright (C) 2001, 2002, 2003, 2005, 2008 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # serial 4 # _AM_MANGLE_OPTION(NAME) # ----------------------- AC_DEFUN([_AM_MANGLE_OPTION], [[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])]) # _AM_SET_OPTION(NAME) # ------------------------------ # Set option NAME. Presently that only means defining a flag for this option. AC_DEFUN([_AM_SET_OPTION], [m4_define(_AM_MANGLE_OPTION([$1]), 1)]) # _AM_SET_OPTIONS(OPTIONS) # ---------------------------------- # OPTIONS is a space-separated list of Automake options. AC_DEFUN([_AM_SET_OPTIONS], [m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])]) # _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET]) # ------------------------------------------- # Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. AC_DEFUN([_AM_IF_OPTION], [m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) # Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_RUN_LOG(COMMAND) # ------------------- # Run COMMAND, save the exit status in ac_status, and log it. # (This has been adapted from Autoconf's _AC_RUN_LOG macro.) AC_DEFUN([AM_RUN_LOG], [{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD ($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD (exit $ac_status); }]) # Check to make sure that the build environment is sane. -*- Autoconf -*- # Copyright (C) 1996, 1997, 2000, 2001, 2003, 2005, 2008 # Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # serial 5 # AM_SANITY_CHECK # --------------- AC_DEFUN([AM_SANITY_CHECK], [AC_MSG_CHECKING([whether build environment is sane]) # Just in case sleep 1 echo timestamp > conftest.file # Reject unsafe characters in $srcdir or the absolute working directory # name. Accept space and tab only in the latter. am_lf=' ' case `pwd` in *[[\\\"\#\$\&\'\`$am_lf]]*) AC_MSG_ERROR([unsafe absolute working directory name]);; esac case $srcdir in *[[\\\"\#\$\&\'\`$am_lf\ \ ]]*) AC_MSG_ERROR([unsafe srcdir value: `$srcdir']);; esac # Do `set' in a subshell so we don't clobber the current shell's # arguments. Must try -L first in case configure is actually a # symlink; some systems play weird games with the mod time of symlinks # (eg FreeBSD returns the mod time of the symlink's containing # directory). if ( set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` if test "$[*]" = "X"; then # -L didn't work. set X `ls -t "$srcdir/configure" conftest.file` fi rm -f conftest.file if test "$[*]" != "X $srcdir/configure conftest.file" \ && test "$[*]" != "X conftest.file $srcdir/configure"; then # If neither matched, then we have a broken ls. This can happen # if, for instance, CONFIG_SHELL is bash and it inherits a # broken ls alias from the environment. This has actually # happened. Such a system could not be considered "sane". AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken alias in your environment]) fi test "$[2]" = conftest.file ) then # Ok. : else AC_MSG_ERROR([newly created file is older than distributed files! Check your system clock]) fi AC_MSG_RESULT(yes)]) # Copyright (C) 2009 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # serial 1 # AM_SILENT_RULES([DEFAULT]) # -------------------------- # Enable less verbose build rules; with the default set to DEFAULT # (`yes' being less verbose, `no' or empty being verbose). AC_DEFUN([AM_SILENT_RULES], [AC_ARG_ENABLE([silent-rules], [ --enable-silent-rules less verbose build output (undo: `make V=1') --disable-silent-rules verbose build output (undo: `make V=0')]) case $enable_silent_rules in yes) AM_DEFAULT_VERBOSITY=0;; no) AM_DEFAULT_VERBOSITY=1;; *) AM_DEFAULT_VERBOSITY=m4_if([$1], [yes], [0], [1]);; esac AC_SUBST([AM_DEFAULT_VERBOSITY])dnl AM_BACKSLASH='\' AC_SUBST([AM_BACKSLASH])dnl _AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl ]) # Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_PROG_INSTALL_STRIP # --------------------- # One issue with vendor `install' (even GNU) is that you can't # specify the program used to strip binaries. This is especially # annoying in cross-compiling environments, where the build's strip # is unlikely to handle the host's binaries. # Fortunately install-sh will honor a STRIPPROG variable, so we # always use install-sh in `make install-strip', and initialize # STRIPPROG with the value of the STRIP variable (set by the user). AC_DEFUN([AM_PROG_INSTALL_STRIP], [AC_REQUIRE([AM_PROG_INSTALL_SH])dnl # Installed binaries are usually stripped using `strip' when the user # run `make install-strip'. However `strip' might not be the right # tool to use in cross-compilation environments, therefore Automake # will honor the `STRIP' environment variable to overrule this program. dnl Don't test for $cross_compiling = yes, because it might be `maybe'. if test "$cross_compiling" != no; then AC_CHECK_TOOL([STRIP], [strip], :) fi INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" AC_SUBST([INSTALL_STRIP_PROGRAM])]) # Copyright (C) 2006, 2008 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # serial 2 # _AM_SUBST_NOTMAKE(VARIABLE) # --------------------------- # Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in. # This macro is traced by Automake. AC_DEFUN([_AM_SUBST_NOTMAKE]) # AM_SUBST_NOTMAKE(VARIABLE) # --------------------------- # Public sister of _AM_SUBST_NOTMAKE. AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)]) # Check how to create a tarball. -*- Autoconf -*- # Copyright (C) 2004, 2005 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # serial 2 # _AM_PROG_TAR(FORMAT) # -------------------- # Check how to create a tarball in format FORMAT. # FORMAT should be one of `v7', `ustar', or `pax'. # # Substitute a variable $(am__tar) that is a command # writing to stdout a FORMAT-tarball containing the directory # $tardir. # tardir=directory && $(am__tar) > result.tar # # Substitute a variable $(am__untar) that extract such # a tarball read from stdin. # $(am__untar) < result.tar AC_DEFUN([_AM_PROG_TAR], [# Always define AMTAR for backward compatibility. AM_MISSING_PROG([AMTAR], [tar]) m4_if([$1], [v7], [am__tar='${AMTAR} chof - "$$tardir"'; am__untar='${AMTAR} xf -'], [m4_case([$1], [ustar],, [pax],, [m4_fatal([Unknown tar format])]) AC_MSG_CHECKING([how to create a $1 tar archive]) # Loop over all known methods to create a tar archive until one works. _am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none' _am_tools=${am_cv_prog_tar_$1-$_am_tools} # Do not fold the above two line into one, because Tru64 sh and # Solaris sh will not grok spaces in the rhs of `-'. for _am_tool in $_am_tools do case $_am_tool in gnutar) for _am_tar in tar gnutar gtar; do AM_RUN_LOG([$_am_tar --version]) && break done am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"' am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"' am__untar="$_am_tar -xf -" ;; plaintar) # Must skip GNU tar: if it does not support --format= it doesn't create # ustar tarball either. (tar --version) >/dev/null 2>&1 && continue am__tar='tar chf - "$$tardir"' am__tar_='tar chf - "$tardir"' am__untar='tar xf -' ;; pax) am__tar='pax -L -x $1 -w "$$tardir"' am__tar_='pax -L -x $1 -w "$tardir"' am__untar='pax -r' ;; cpio) am__tar='find "$$tardir" -print | cpio -o -H $1 -L' am__tar_='find "$tardir" -print | cpio -o -H $1 -L' am__untar='cpio -i -H $1 -d' ;; none) am__tar=false am__tar_=false am__untar=false ;; esac # If the value was cached, stop now. We just wanted to have am__tar # and am__untar set. test -n "${am_cv_prog_tar_$1}" && break # tar/untar a dummy directory, and stop if the command works rm -rf conftest.dir mkdir conftest.dir echo GrepMe > conftest.dir/file AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar]) rm -rf conftest.dir if test -s conftest.tar; then AM_RUN_LOG([$am__untar /dev/null 2>&1 && break fi done rm -rf conftest.dir AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool]) AC_MSG_RESULT([$am_cv_prog_tar_$1])]) AC_SUBST([am__tar]) AC_SUBST([am__untar]) ]) # _AM_PROG_TAR m4_include([m4/lib-prefix.m4]) m4_include([m4/libtool.m4]) m4_include([m4/ltoptions.m4]) m4_include([m4/ltsugar.m4]) m4_include([m4/ltversion.m4]) m4_include([m4/lt~obsolete.m4]) m4_include([m4/pandora_64bit.m4]) m4_include([m4/pandora_bison.m4]) m4_include([m4/pandora_canonical.m4]) m4_include([m4/pandora_check_compiler_version.m4]) m4_include([m4/pandora_check_cxx_standard.m4]) m4_include([m4/pandora_cinttypes.m4]) m4_include([m4/pandora_clock_gettime.m4]) m4_include([m4/pandora_cstdint.m4]) m4_include([m4/pandora_enable_dtrace.m4]) m4_include([m4/pandora_ensure_gcc_version.m4]) m4_include([m4/pandora_extensions.m4]) m4_include([m4/pandora_have_better_malloc.m4]) m4_include([m4/pandora_have_gcc_atomics.m4]) m4_include([m4/pandora_header_assert.m4]) m4_include([m4/pandora_header_stdcxx_98.m4]) m4_include([m4/pandora_libtool.m4]) m4_include([m4/pandora_optimize.m4]) m4_include([m4/pandora_platform.m4]) m4_include([m4/pandora_use_pipe.m4]) m4_include([m4/pandora_vc_build.m4]) m4_include([m4/pandora_version.m4]) m4_include([m4/pandora_visibility.m4]) m4_include([m4/pandora_warnings.m4]) haildb-2.3.2/COPYING.Sun_Microsystems0000644000175000017500000000324211513177357020224 0ustar00pcrewspcrews00000000000000Portions of this software contain modifications contributed by Sun Microsystems, Inc. These contributions are used with the following license: Copyright (c) 2009, Sun Microsystems, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Sun Microsystems, Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. haildb-2.3.2/api/0000755000175000017500000000000011513177437014413 5ustar00pcrewspcrews00000000000000haildb-2.3.2/api/api0misc.c0000644000175000017500000002162711513177357016275 0ustar00pcrewspcrews00000000000000/*********************************************************************** Copyright (c) 2008 Innobase Oy. All rights reserved. Copyright (c) 2008 Oracle. All rights reserved. Copyright (c) 2010 Stewart Smith This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ************************************************************************/ #include "config.h" #include #ifdef HAVE_UNISTD_H #include #endif /* HAVE_UNISTD_H */ #include "univ.i" #include "api0misc.h" #include "trx0roll.h" #include "srv0srv.h" #include "dict0mem.h" #include "dict0dict.h" #include "pars0pars.h" #include "row0sel.h" #include "lock0lock.h" /* If set then we rollback the transaction on DB_LOCK_WAIT_TIMEOUT error. */ UNIV_INTERN ibool ses_rollback_on_timeout = FALSE; #ifdef __WIN__ #include /* Required for mapping file system errors */ void __cdecl _dosmaperr(unsigned long); /* The length was chosen arbitrarily and should be generous. */ #define WIN_MAX_PATH 512 /******************************************************************//** @file api/api0misc.c Create a temporary file in Windows. @return file descriptor, or -1 */ static int ib_win_create_tempfile( /*===================*/ const char* prefix) /*!< in: temp file prefix */ { ulint ret; TCHAR path[WIN_MAX_PATH]; TCHAR tempname[WIN_MAX_PATH]; int fh = -1; ret = GetTempPath(sizeof(path), path); if (ret > sizeof(path) || ret == 0) { _dosmaperr(GetLastError()); } else if (!GetTempFileName(path, prefix, 0, tempname)) { _dosmaperr(GetLastError()); } else { HANDLE handle; /* Create the actual file with the relevant attributes. */ handle = CreateFile( tempname, GENERIC_READ | GENERIC_WRITE | DELETE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE | FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_SEQUENTIAL_SCAN, NULL); if (handle == INVALID_HANDLE_VALUE) { _dosmaperr(GetLastError()); } else { do { DWORD oserr; fh = _open_osfhandle((intptr_t) handle, 0); /* We need to map the Windows OS error number (if any) to errno. */ oserr = GetLastError(); if (oserr) { _dosmaperr(oserr); } } while (fh == -1 && errno == EINTR); if (fh == -1) { _dosmaperr(GetLastError()); CloseHandle(handle); } } } return(fh); } #endif /******************************************************************//** Create a temporary file. FIXME: This is a Q&D solution. */ int ib_create_tempfile( /*===============*/ const char* prefix) /*!< in: temp filename prefix */ { int fh = -1; #ifdef __WIN__ fh = ib_win_create_tempfile(prefix); #else FILE* file; (void) prefix; file = tmpfile(); if (file != NULL) { fh = dup(fileno(file)); fclose(file); } #endif return(fh); } /**********************************************************************//** Determines if the currently running transaction has been interrupted. @return TRUE if interrupted */ ib_trx_is_interrupted_handler_t ib_trx_is_interrupted = NULL; ibool trx_is_interrupted( /*===============*/ const trx_t* trx) /*!< in: transaction */ { if (trx->client_thd && ib_trx_is_interrupted != NULL) { return(ib_trx_is_interrupted(trx->client_thd)); } return(FALSE); } /****************************************************************//** Handles user errors and lock waits detected by the database engine. @return TRUE if it was a lock wait and we should continue running the query thread */ ibool ib_handle_errors( /*=============*/ enum db_err* new_err,/*!< out: possible new error encountered in lock wait, or if no new error, the value of trx->error_state at the entry of this function */ trx_t* trx, /*!< in: transaction */ que_thr_t* thr, /*!< in: query thread */ trx_savept_t* savept) /*!< in: savepoint or NULL */ { enum db_err err; handle_new_error: err = trx->error_state; ut_a(err != DB_SUCCESS); trx->error_state = DB_SUCCESS; switch (err) { case DB_LOCK_WAIT_TIMEOUT: if (ses_rollback_on_timeout) { trx_general_rollback(trx, FALSE, NULL); break; } /* fall through */ case DB_DUPLICATE_KEY: case DB_FOREIGN_DUPLICATE_KEY: case DB_TOO_BIG_RECORD: case DB_ROW_IS_REFERENCED: case DB_NO_REFERENCED_ROW: case DB_CANNOT_ADD_CONSTRAINT: case DB_TOO_MANY_CONCURRENT_TRXS: case DB_OUT_OF_FILE_SPACE: if (savept) { /* Roll back the latest, possibly incomplete insertion or update */ trx_general_rollback(trx, TRUE, savept); } break; case DB_LOCK_WAIT: srv_suspend_user_thread(thr); if (trx->error_state != DB_SUCCESS) { que_thr_stop_client(thr); goto handle_new_error; } *new_err = err; return(TRUE); /* Operation needs to be retried. */ case DB_DEADLOCK: case DB_LOCK_TABLE_FULL: /* Roll back the whole transaction; this resolution was added to version 3.23.43 */ trx_general_rollback(trx, FALSE, NULL); break; case DB_MUST_GET_MORE_FILE_SPACE: srv_panic(DB_ERROR, "InnoDB: The database cannot continue" " operation because of\n" "InnoDB: lack of space. You must add" " a new data file\n" "InnoDB: and restart the database.\n"); break; case DB_CORRUPTION: ib_logger(ib_stream, "InnoDB: We detected index corruption" " in an InnoDB type table.\n" "InnoDB: You have to dump + drop + reimport" " the table or, in\n" "InnoDB: a case of widespread corruption," " dump all InnoDB\n" "InnoDB: tables and recreate the" " whole InnoDB tablespace.\n" "InnoDB: If the server crashes" " after the startup or when\n" "InnoDB: you dump the tables, check the \n" "InnoDB: InnoDB website for help.\n"); break; default: ib_logger(ib_stream, "InnoDB: unknown error code %lu\n", (ulong) err); ut_error; } if (trx->error_state != DB_SUCCESS) { *new_err = trx->error_state; } else { *new_err = err; } trx->error_state = DB_SUCCESS; return(FALSE); } /*********************************************************************//** Sets a lock on a table. @return error code or DB_SUCCESS */ enum db_err ib_trx_lock_table_with_retry( /*=========================*/ trx_t* trx, /*!< in/out: transaction */ dict_table_t* table, /*!< in: table to lock */ enum lock_mode mode) /*!< in: LOCK_X or LOCK_S */ { que_thr_t* thr; enum db_err err; mem_heap_t* heap; sel_node_t* node; ut_ad(trx->client_thread_id == os_thread_get_curr_id()); heap = mem_heap_create(512); trx->op_info = "setting table lock"; node = sel_node_create(heap); thr = pars_complete_graph_for_exec(node, trx, heap); thr->graph->state = QUE_FORK_ACTIVE; /* We use the select query graph as the dummy graph needed in the lock module call */ thr = que_fork_get_first_thr(que_node_get_parent(thr)); que_thr_move_to_run_state(thr); run_again: thr->run_node = thr; thr->prev_node = thr->common.parent; err = lock_table(0, table, mode, thr); trx->error_state = err; if (UNIV_LIKELY(err == DB_SUCCESS)) { que_thr_stop_for_client_no_error(thr, trx); } else { que_thr_stop_client(thr); if (err != DB_QUE_THR_SUSPENDED) { ibool was_lock_wait; was_lock_wait = ib_handle_errors(&err, trx, thr, NULL); if (was_lock_wait) { goto run_again; } } else { que_thr_t* run_thr; que_node_t* parent; parent = que_node_get_parent(thr); run_thr = que_fork_start_command(parent); ut_a(run_thr == thr); /* There was a lock wait but the thread was not in a ready to run or running state. */ trx->error_state = DB_LOCK_WAIT; goto run_again; } } que_graph_free(thr->graph); trx->op_info = ""; return(err); } /*********************************************************************//** Updates the table modification counter and calculates new estimates for table and index statistics if necessary. */ void ib_update_statistics_if_needed( /*===========================*/ dict_table_t* table) /*!< in/out: table */ { ulint counter; counter = table->stat_modified_counter++; /* Calculate new statistics if 1 / 16 of table has been modified since the last time a statistics batch was run, or if stat_modified_counter > 2 000 000 000 (to avoid wrap-around). We calculate statistics at most every 16th round, since we may have a counter table which is very small and updated very often. */ if (counter > 2000000000 || ((ib_int64_t)counter > 16 + table->stat_n_rows / 16)) { dict_update_statistics(table); } } haildb-2.3.2/api/api0cfg.c0000644000175000017500000011412311513177357016073 0ustar00pcrewspcrews00000000000000/*********************************************************************** Copyright (c) 2008 Innobase Oy. All rights reserved. Copyright (c) 2008 Oracle. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ************************************************************************/ #include #ifdef HAVE_STRINGS_H #include #endif #include "univ.i" #include "api0api.h" #include "btr0sea.h" #include "buf0buf.h" /* for buf_pool */ #include "buf0lru.h" /* for buf_LRU_* */ #include "db0err.h" #include "log0recv.h" #include "srv0srv.h" #include "srv0start.h" #include "trx0sys.h" /* for trx_sys_file_format_name_to_id() */ #include "os0sync.h" static char* srv_file_flush_method_str = NULL; /* A point in the LRU list (expressed as a percent), all blocks from this point onwards (inclusive) are considered "old" blocks. */ static ulint lru_old_blocks_pct; /** We copy the argument passed to ib_cfg_set_text("data_file_path") because srv_parse_data_file_paths_and_sizes() parses it's input argument destructively. The copy is done using ut_malloc(). */ static char* srv_data_file_paths_and_sizes = NULL; /* enum ib_cfg_flag_t @{ */ typedef enum ib_cfg_flag { IB_CFG_FLAG_NONE = 0x1, IB_CFG_FLAG_READONLY_AFTER_STARTUP = 0x2,/* can be modified only before innodb is started */ IB_CFG_FLAG_READONLY = 0x4 /* cannot be modified */ } ib_cfg_flag_t; /* @} */ /* struct ib_cfg_var_t @{ */ typedef struct ib_cfg_var { const char* name; /* config var name */ ib_cfg_type_t type; /* config var type */ ib_cfg_flag_t flag; /* config var flag */ ib_uint64_t min_val;/* minimum allowed value for numeric types, ignored for other types */ ib_uint64_t max_val;/* maximum allowed value for numeric types, ignored for other types */ ib_err_t (*validate)(const struct ib_cfg_var*, const void*); /* function used to validate a new variable's value when setting it */ ib_err_t (*set)(struct ib_cfg_var*, const void*); /* function used to set the variable's value */ ib_err_t (*get)(const struct ib_cfg_var*, void*); /* function used to get the variable's value */ void* tank; /* opaque storage that may be used by the set() and get() functions */ } ib_cfg_var_t; /* @} */ /*******************************************************************//** @file api/api0cfg.c Assign src to dst according to type. If this is a string variable (char*) the string itself is not copied. ib_cfg_assign() @{ @return DB_SUCCESS if assigned (type is known) */ static ib_err_t ib_cfg_assign( /*==========*/ ib_cfg_type_t type, /*!< in: type of src and dst */ void* dst, /*!< out: destination */ const void* src) /*!< in: source */ { switch (type) { case IB_CFG_IBOOL: { *(ib_bool_t*) dst = *(const ib_bool_t*) src; return(DB_SUCCESS); } case IB_CFG_ULINT: { *(ulint*) dst = *(const ulint*) src; return(DB_SUCCESS); } case IB_CFG_ULONG: { *(ulong*) dst = *(const ulong*) src; return(DB_SUCCESS); } case IB_CFG_TEXT: { *(char**) dst = *(char**) src; return(DB_SUCCESS); } case IB_CFG_CB: { *(ib_cb_t*) dst = *(ib_cb_t*) src; return(DB_SUCCESS); } /* do not add default: in order to produce a compilation warning if new type is added which is not handled here */ } /* NOT REACHED */ return(DB_ERROR); } /* @} */ /*******************************************************************//** A generic function used for ib_cfg_var_t::validate() to check a numeric type for min/max allowed value overflow. ib_cfg_var_validate_numeric() @{ @return DB_SUCCESS if value is in range */ static ib_err_t ib_cfg_var_validate_numeric( /*========================*/ const struct ib_cfg_var* cfg_var,/*!< in/out: configuration variable to check */ const void* value) /*!< in: value to check */ { switch (cfg_var->type) { case IB_CFG_ULINT: { ulint v; v = *(ulint*) value; if ((ulint) cfg_var->min_val <= v && v <= (ulint) cfg_var->max_val) { return(DB_SUCCESS); } else { return(DB_INVALID_INPUT); } } case IB_CFG_ULONG: { ulong v; v = *(ulong*) value; if ((ulong) cfg_var->min_val <= v && v <= (ulong) cfg_var->max_val) { return(DB_SUCCESS); } else { return(DB_INVALID_INPUT); } } default: ut_error; } /* NOT REACHED */ return(DB_ERROR); } /* @} */ /* generic and specific ib_cfg_var_(set|get)_* functions @{ */ /*******************************************************************//** A generic function used for ib_cfg_var_t::set() that stores the value of the configuration parameter in the location pointed by ib_cfg_var_t::tank. If this is a string variable (char*) then the string is not copied and a reference to "value" is made. It should not be freed or modified until InnoDB is running or a new value is set. ib_cfg_var_set_generic() @{ @return DB_SUCCESS if set successfully */ static ib_err_t ib_cfg_var_set_generic( /*===================*/ struct ib_cfg_var* cfg_var,/*!< in/out: configuration variable to manipulate */ const void* value) /*!< in: value to set */ { ib_err_t ret; if (cfg_var->validate != NULL) { ret = cfg_var->validate(cfg_var, value); if (ret != DB_SUCCESS) { return(ret); } } ret = ib_cfg_assign(cfg_var->type, cfg_var->tank, value); return(ret); } /* @} */ /*******************************************************************//** A generic function used for ib_cfg_var_t::get() that retrieves the value of the configuration parameter from the location pointed by ib_cfg_var_t::tank and stores it in "value". The variable is not copied to "value" even if it is a string variable (char*). Only a pointer to the storage is written in "value". It should not be freed unless it was allocated by the user and set with ib_cfg_set(). ib_cfg_var_get_generic() @{ @return DB_SUCCESS if retrieved successfully */ static ib_err_t ib_cfg_var_get_generic( /*===================*/ const struct ib_cfg_var* cfg_var,/*!< in: configuration variable whose value to retrieve */ void* value) /*!< out: place to store the retrieved value */ { return(ib_cfg_assign(cfg_var->type, value, cfg_var->tank)); } /* @} */ /*******************************************************************//** Set the value of the config variable "adaptive_hash_index". ib_cfg_var_set_adaptive_hash_index() @{ @return DB_SUCCESS if set successfully */ static ib_err_t ib_cfg_var_set_adaptive_hash_index( /*===============================*/ struct ib_cfg_var* cfg_var,/*!< in/out: configuration variable to manipulate, must be "adaptive_hash_index" */ const void* value) /*!< in: value to set, must point to ib_bool_t variable */ { ut_a(strcasecmp(cfg_var->name, "adaptive_hash_index") == 0); ut_a(cfg_var->type == IB_CFG_IBOOL); btr_search_enabled = !(*(const ib_bool_t*) value); return(DB_SUCCESS); } /* @} */ /*******************************************************************//** Retrieve the value of the config variable "adaptive_hash_index". ib_cfg_var_get_adaptive_hash_index() @{ @return DB_SUCCESS if retrieved successfully */ static ib_err_t ib_cfg_var_get_adaptive_hash_index( /*===============================*/ const struct ib_cfg_var* cfg_var,/*!< in: configuration variable whose value to retrieve, must be "adaptive_hash_index" */ void* value) /*!< out: place to store the retrieved value, must point to ib_bool_t variable */ { ut_a(strcasecmp(cfg_var->name, "adaptive_hash_index") == 0); ut_a(cfg_var->type == IB_CFG_IBOOL); *(ib_bool_t*) value = !btr_search_enabled; return(DB_SUCCESS); } /* @} */ /*******************************************************************//** Set the value of the config variable "data_file_path". ib_cfg_var_set_data_file_path() @{ @return DB_SUCCESS if set successfully */ static ib_err_t ib_cfg_var_set_data_file_path( /*==========================*/ struct ib_cfg_var* cfg_var,/*!< in/out: configuration variable to manipulate, must be "data_file_path" */ const void* value) /*!< in: value to set, must point to char* variable */ { const char* value_str; ut_a(strcasecmp(cfg_var->name, "data_file_path") == 0); ut_a(cfg_var->type == IB_CFG_TEXT); value_str = *(char**) value; if (srv_parse_data_file_paths_and_sizes(value_str)) { return(DB_SUCCESS); } else { return(DB_INVALID_INPUT); } } /* @} */ /*******************************************************************//** Retrieve the value of the config variable "data_file_path". ib_cfg_var_get_data_file_path() @{ @return DB_SUCCESS if retrieved successfully */ static ib_err_t ib_cfg_var_get_data_file_path( /*==========================*/ const struct ib_cfg_var* cfg_var,/*!< in: configuration variable whose value to retrieve, must be "data_file_path" */ void* value) /*!< out: place to store the retrieved value, must point to char* variable */ { ut_a(strcasecmp(cfg_var->name, "data_file_path") == 0); ut_a(cfg_var->type == IB_CFG_TEXT); *(char**) value = srv_data_file_paths_and_sizes; return(DB_SUCCESS); } /* @} */ /*******************************************************************//** Set the value of the config variable "file_format". ib_cfg_var_set_file_format() @{ @return DB_SUCCESS if set successfully */ static ib_err_t ib_cfg_var_set_file_format( /*=======================*/ struct ib_cfg_var* cfg_var,/*!< in/out: configuration variable to manipulate, must be "file_format" */ const void* value) /*!< in: value to set, must point to char* variable */ { ulint format_id; ut_a(strcasecmp(cfg_var->name, "file_format") == 0); ut_a(cfg_var->type == IB_CFG_TEXT); format_id = trx_sys_file_format_name_to_id(*(char**) value); if (format_id > DICT_TF_FORMAT_MAX) { return(DB_INVALID_INPUT); } srv_file_format = format_id; return(DB_SUCCESS); } /* @} */ /*******************************************************************//** Retrieve the value of the config variable "file_format". ib_cfg_var_get_file_format @{ @return DB_SUCCESS if retrieved successfully */ static ib_err_t ib_cfg_var_get_file_format( /*=======================*/ const struct ib_cfg_var* cfg_var,/*!< in: configuration variable whose value to retrieve, must be "file_format" */ void* value) /*!< out: place to store the retrieved value, must point to char* variable */ { ut_a(strcasecmp(cfg_var->name, "file_format") == 0); ut_a(cfg_var->type == IB_CFG_TEXT); *(const char**) value = trx_sys_file_format_id_to_name( srv_file_format); return(DB_SUCCESS); } /* @} */ /*******************************************************************//** Check the value of the config variable "data_home_dir". We need to ensure that the value ends with a path separator. ib_cfg_var_validate_data_home_dir() @{ @return DB_SUCCESS if value is valid */ static ib_err_t ib_cfg_var_validate_data_home_dir( /*==============================*/ const struct ib_cfg_var*cfg_var,/*!< in/out: configuration variable to check, must be "data_home_dir" */ const void* value) /*!< in: value to check, must point to char* variable */ { ulint len; char* value_str; ut_a(strcasecmp(cfg_var->name, "data_home_dir") == 0); ut_a(cfg_var->type == IB_CFG_TEXT); value_str = *(char**) value; len = ut_strlen(value_str); /* We simply require that this variable end in a path separator. We will normalize it before use internally. */ if (len == 0 || (value_str[len - 1] != '/' && value_str[len - 1] != '\\')) { return(DB_INVALID_INPUT); } return(DB_SUCCESS); } /* @} */ /*******************************************************************//** Set the value of the config variable "log_group_home_dir". ib_cfg_var_set_log_group_home_dir @{ @return DB_SUCCESS if set successfully */ static ib_err_t ib_cfg_var_set_log_group_home_dir( /*==============================*/ struct ib_cfg_var* cfg_var,/*!< in/out: configuration variable to manipulate, must be "log_group_home_dir" */ const void* value) /*!< in: value to set, must point to char* variable */ { const char* value_str; ut_a(strcasecmp(cfg_var->name, "log_group_home_dir") == 0); ut_a(cfg_var->type == IB_CFG_TEXT); ut_a(srv_log_group_home_dir == NULL); value_str = *(char**) value; if (srv_parse_log_group_home_dirs(value_str)) { return(DB_SUCCESS); } else { return(DB_INVALID_INPUT); } } /* @} */ /*******************************************************************//** Set the value of the config variable "flush_method". ib_cfg_var_set_flush_method@{ @return DB_SUCCESS if set successfully */ static ib_err_t ib_cfg_var_set_flush_method( /*========================*/ struct ib_cfg_var* cfg_var,/*!< in/out: configuration variable to manipulate, must be "log_group_home_dir" */ const void* value) /*!< in: value to set, must point to char* variable */ { const char* value_str; ib_err_t err = DB_SUCCESS; ut_a(strcasecmp(cfg_var->name, "flush_method") == 0); ut_a(cfg_var->type == IB_CFG_TEXT); value_str = *(const char**) value; os_aio_use_native_aio = FALSE; #ifndef __WIN__ if (0 == ut_strcmp(value_str, "fsync")) { srv_unix_file_flush_method = SRV_UNIX_FSYNC; } else if (0 == ut_strcmp(value_str, "O_DSYNC")) { srv_unix_file_flush_method = SRV_UNIX_O_DSYNC; } else if (0 == ut_strcmp(value_str, "O_DIRECT")) { srv_unix_file_flush_method = SRV_UNIX_O_DIRECT; } else if (0 == ut_strcmp(value_str, "littlesync")) { srv_unix_file_flush_method = SRV_UNIX_LITTLESYNC; } else if (0 == ut_strcmp(value_str, "nosync")) { srv_unix_file_flush_method = SRV_UNIX_NOSYNC; } else { err = DB_INVALID_INPUT; } #else if (0 == ut_strcmp(value_str, "normal")) { srv_win_file_flush_method = SRV_WIN_IO_NORMAL; } else if (0 == ut_strcmp(value_str, "unbuffered")) { srv_win_file_flush_method = SRV_WIN_IO_UNBUFFERED; } else if (0 == ut_strcmp(value_str, "async_unbuffered")) { srv_win_file_flush_method = SRV_WIN_IO_UNBUFFERED; } else { err = DB_INVALID_INPUT; } if (err == DB_SUCCESS) { switch (os_get_os_version()) { case OS_WIN95: case OS_WIN31: case OS_WINNT: /* On Win 95, 98, ME, Win32 subsystem for Windows 3.1, and NT use simulated aio. In NT Windows provides async i/o, but when run in conjunction with InnoDB Hot Backup, it seemed to corrupt the data files. */ os_aio_use_native_aio = FALSE; break; default: /* On Win 2000 and XP use async i/o */ os_aio_use_native_aio = TRUE; break; } } #endif if (err == DB_SUCCESS) { *(const char**) cfg_var->tank = value_str; } else { *(const char**) cfg_var->tank = NULL; } return(err); } /* @} */ /*******************************************************************//** Retrieve the value of the config variable "log_group_home_dir". ib_cfg_var_get_log_group_home_dir() @{ @return DB_SUCCESS if retrieved successfully */ static ib_err_t ib_cfg_var_get_log_group_home_dir( /*==============================*/ const struct ib_cfg_var* cfg_var,/*!< in: configuration variable whose value to retrieve, must be "log_group_home_dir" */ void* value) /*!< out: place to store the retrieved value, must point to char* variable */ { ut_a(strcasecmp(cfg_var->name, "log_group_home_dir") == 0); ut_a(cfg_var->type == IB_CFG_TEXT); *(const char**) value = NULL; return(DB_SUCCESS); } /* @} */ /*******************************************************************//** Set the value of the config variable "lru_old_blocks_pct". ib_cfg_var_set_lru_old_blocks_pct() @{ @return DB_SUCCESS if set successfully */ static ib_err_t ib_cfg_var_set_lru_old_blocks_pct( /*==============================*/ struct ib_cfg_var* cfg_var,/*!< in/out: configuration variable to manipulate, must be "lru_old_blocks_pct" */ const void* value) /*!< in: value to set, must point to ulint variable */ { ibool adjust_buf_pool; ut_a(strcasecmp(cfg_var->name, "lru_old_blocks_pct") == 0); ut_a(cfg_var->type == IB_CFG_ULINT); if (cfg_var->validate != NULL) { ib_err_t ret; ret = cfg_var->validate(cfg_var, value); if (ret != DB_SUCCESS) { return(ret); } } if (buf_pool != NULL) { /* buffer pool has been created */ adjust_buf_pool = TRUE; } else { /* buffer pool not yet created, do not attempt to modify it */ adjust_buf_pool = FALSE; } lru_old_blocks_pct = buf_LRU_old_ratio_update( *(ulint*) value, adjust_buf_pool); return(DB_SUCCESS); } /* @} */ /* ib_cfg_var_get_generic() is used to get the value of lru_old_blocks_pct */ /* There is no ib_cfg_var_set_version() */ /*******************************************************************//** Retrieve the value of the config variable "version". ib_cfg_var_get_version() @{ @return DB_SUCCESS if retrieved successfully */ static ib_err_t ib_cfg_var_get_version( /*===================*/ const struct ib_cfg_var* cfg_var,/*!< in: configuration variable whose value to retrieve, must be "version" */ void* value) /*!< out: place to store the retrieved value, must point to char* variable */ { ut_a(strcasecmp(cfg_var->name, "version") == 0); ut_a(cfg_var->type == IB_CFG_TEXT); *(const char**) value = VERSION; return(DB_SUCCESS); } /* @} */ /* @} */ /* cfg_vars_defaults[] @{ */ static const ib_cfg_var_t cfg_vars_defaults[] = { {STRUCT_FLD(name, "adaptive_hash_index"), STRUCT_FLD(type, IB_CFG_IBOOL), STRUCT_FLD(flag, IB_CFG_FLAG_READONLY_AFTER_STARTUP), STRUCT_FLD(min_val, 0), STRUCT_FLD(max_val, 0), STRUCT_FLD(validate, NULL), STRUCT_FLD(set, ib_cfg_var_set_adaptive_hash_index), STRUCT_FLD(get, ib_cfg_var_get_adaptive_hash_index), STRUCT_FLD(tank, NULL)}, {STRUCT_FLD(name, "adaptive_flushing"), STRUCT_FLD(type, IB_CFG_IBOOL), STRUCT_FLD(flag, IB_CFG_FLAG_READONLY_AFTER_STARTUP), STRUCT_FLD(min_val, 0), STRUCT_FLD(max_val, 0), STRUCT_FLD(validate, NULL), STRUCT_FLD(set, ib_cfg_var_set_generic), STRUCT_FLD(get, ib_cfg_var_get_generic), STRUCT_FLD(tank, &srv_adaptive_flushing)}, {STRUCT_FLD(name, "additional_mem_pool_size"), STRUCT_FLD(type, IB_CFG_ULINT), STRUCT_FLD(flag, IB_CFG_FLAG_READONLY_AFTER_STARTUP), STRUCT_FLD(min_val, 512 * 1024), STRUCT_FLD(max_val, IB_UINT64_T_MAX), STRUCT_FLD(validate, ib_cfg_var_validate_numeric), STRUCT_FLD(set, ib_cfg_var_set_generic), STRUCT_FLD(get, ib_cfg_var_get_generic), STRUCT_FLD(tank, &srv_mem_pool_size)}, {STRUCT_FLD(name, "autoextend_increment"), STRUCT_FLD(type, IB_CFG_ULONG), STRUCT_FLD(flag, IB_CFG_FLAG_NONE), STRUCT_FLD(min_val, 1), STRUCT_FLD(max_val, 1000), STRUCT_FLD(validate, ib_cfg_var_validate_numeric), STRUCT_FLD(set, ib_cfg_var_set_generic), STRUCT_FLD(get, ib_cfg_var_get_generic), STRUCT_FLD(tank, &srv_auto_extend_increment)}, {STRUCT_FLD(name, "buffer_pool_size"), STRUCT_FLD(type, IB_CFG_ULINT), STRUCT_FLD(flag, IB_CFG_FLAG_READONLY_AFTER_STARTUP), STRUCT_FLD(min_val, 5 * 1024 * 1024), STRUCT_FLD(max_val, ULINT_MAX), STRUCT_FLD(validate, ib_cfg_var_validate_numeric), STRUCT_FLD(set, ib_cfg_var_set_generic), STRUCT_FLD(get, ib_cfg_var_get_generic), STRUCT_FLD(tank, &srv_buf_pool_size)}, {STRUCT_FLD(name, "checksums"), STRUCT_FLD(type, IB_CFG_IBOOL), STRUCT_FLD(flag, IB_CFG_FLAG_READONLY_AFTER_STARTUP), STRUCT_FLD(min_val, 0), STRUCT_FLD(max_val, 0), STRUCT_FLD(validate, NULL), STRUCT_FLD(set, ib_cfg_var_set_generic), STRUCT_FLD(get, ib_cfg_var_get_generic), STRUCT_FLD(tank, &srv_use_checksums)}, {STRUCT_FLD(name, "data_file_path"), STRUCT_FLD(type, IB_CFG_TEXT), STRUCT_FLD(flag, IB_CFG_FLAG_READONLY_AFTER_STARTUP), STRUCT_FLD(min_val, 0), STRUCT_FLD(max_val, 0), STRUCT_FLD(validate, NULL), STRUCT_FLD(set, ib_cfg_var_set_data_file_path), STRUCT_FLD(get, ib_cfg_var_get_data_file_path), STRUCT_FLD(tank, NULL)}, {STRUCT_FLD(name, "data_home_dir"), STRUCT_FLD(type, IB_CFG_TEXT), STRUCT_FLD(flag, IB_CFG_FLAG_READONLY_AFTER_STARTUP), STRUCT_FLD(min_val, 0), STRUCT_FLD(max_val, 0), STRUCT_FLD(validate, ib_cfg_var_validate_data_home_dir), STRUCT_FLD(set, ib_cfg_var_set_generic), STRUCT_FLD(get, ib_cfg_var_get_generic), STRUCT_FLD(tank, &srv_data_home)}, {STRUCT_FLD(name, "doublewrite"), STRUCT_FLD(type, IB_CFG_IBOOL), STRUCT_FLD(flag, IB_CFG_FLAG_READONLY_AFTER_STARTUP), STRUCT_FLD(min_val, 0), STRUCT_FLD(max_val, 0), STRUCT_FLD(validate, NULL), STRUCT_FLD(set, ib_cfg_var_set_generic), STRUCT_FLD(get, ib_cfg_var_get_generic), STRUCT_FLD(tank, &srv_use_doublewrite_buf)}, {STRUCT_FLD(name, "file_format"), STRUCT_FLD(type, IB_CFG_TEXT), STRUCT_FLD(flag, IB_CFG_FLAG_NONE), STRUCT_FLD(min_val, 0), STRUCT_FLD(max_val, 0), STRUCT_FLD(validate, NULL /* validation is done inside ib_cfg_var_set_file_format */), STRUCT_FLD(set, ib_cfg_var_set_file_format), STRUCT_FLD(get, ib_cfg_var_get_file_format), STRUCT_FLD(tank, NULL)}, {STRUCT_FLD(name, "file_io_threads"), STRUCT_FLD(type, IB_CFG_ULINT), STRUCT_FLD(flag, IB_CFG_FLAG_READONLY), STRUCT_FLD(min_val, 0), STRUCT_FLD(max_val, 0), STRUCT_FLD(validate, NULL), STRUCT_FLD(set, NULL /* The ::set() function should never be called because this variable is flagged as IB_CFG_FLAG_READONLY */), STRUCT_FLD(get, ib_cfg_var_get_generic), STRUCT_FLD(tank, &srv_n_file_io_threads)}, {STRUCT_FLD(name, "file_per_table"), STRUCT_FLD(type, IB_CFG_IBOOL), STRUCT_FLD(flag, IB_CFG_FLAG_NONE), STRUCT_FLD(min_val, 0), STRUCT_FLD(max_val, 0), STRUCT_FLD(validate, NULL), STRUCT_FLD(set, ib_cfg_var_set_generic), STRUCT_FLD(get, ib_cfg_var_get_generic), STRUCT_FLD(tank, &srv_file_per_table)}, {STRUCT_FLD(name, "flush_log_at_trx_commit"), STRUCT_FLD(type, IB_CFG_ULONG), STRUCT_FLD(flag, IB_CFG_FLAG_NONE), STRUCT_FLD(min_val, 0), STRUCT_FLD(max_val, 2), STRUCT_FLD(validate, ib_cfg_var_validate_numeric), STRUCT_FLD(set, ib_cfg_var_set_generic), STRUCT_FLD(get, ib_cfg_var_get_generic), STRUCT_FLD(tank, &srv_flush_log_at_trx_commit)}, {STRUCT_FLD(name, "flush_method"), STRUCT_FLD(type, IB_CFG_TEXT), STRUCT_FLD(flag, IB_CFG_FLAG_READONLY_AFTER_STARTUP), STRUCT_FLD(min_val, 0), STRUCT_FLD(max_val, 0), STRUCT_FLD(validate, NULL), STRUCT_FLD(set, ib_cfg_var_set_flush_method), STRUCT_FLD(get, ib_cfg_var_get_generic), STRUCT_FLD(tank, &srv_file_flush_method_str)}, {STRUCT_FLD(name, "force_recovery"), STRUCT_FLD(type, IB_CFG_ULINT), STRUCT_FLD(flag, IB_CFG_FLAG_READONLY_AFTER_STARTUP), STRUCT_FLD(min_val, IB_RECOVERY_DEFAULT), STRUCT_FLD(max_val, IB_RECOVERY_NO_LOG_REDO), STRUCT_FLD(validate, ib_cfg_var_validate_numeric), STRUCT_FLD(set, ib_cfg_var_set_generic), STRUCT_FLD(get, ib_cfg_var_get_generic), STRUCT_FLD(tank, &srv_force_recovery)}, {STRUCT_FLD(name, "io_capacity"), STRUCT_FLD(type, IB_CFG_ULONG), STRUCT_FLD(flag, IB_CFG_FLAG_NONE), STRUCT_FLD(min_val, 100), STRUCT_FLD(max_val, 1000000), STRUCT_FLD(validate, ib_cfg_var_validate_numeric), STRUCT_FLD(set, ib_cfg_var_set_generic), STRUCT_FLD(get, ib_cfg_var_get_generic), STRUCT_FLD(tank, &srv_io_capacity)}, {STRUCT_FLD(name, "lock_wait_timeout"), STRUCT_FLD(type, IB_CFG_ULINT), STRUCT_FLD(flag, IB_CFG_FLAG_NONE), STRUCT_FLD(min_val, 1), STRUCT_FLD(max_val, 1024 * 1024 * 1024), STRUCT_FLD(validate, ib_cfg_var_validate_numeric), STRUCT_FLD(set, ib_cfg_var_set_generic), STRUCT_FLD(get, ib_cfg_var_get_generic), STRUCT_FLD(tank, &ses_lock_wait_timeout)}, {STRUCT_FLD(name, "log_buffer_size"), STRUCT_FLD(type, IB_CFG_ULINT), STRUCT_FLD(flag, IB_CFG_FLAG_READONLY_AFTER_STARTUP), STRUCT_FLD(min_val, 256 * 1024), STRUCT_FLD(max_val, IB_UINT64_T_MAX), STRUCT_FLD(validate, ib_cfg_var_validate_numeric), STRUCT_FLD(set, ib_cfg_var_set_generic), STRUCT_FLD(get, ib_cfg_var_get_generic), STRUCT_FLD(tank, &srv_log_buffer_curr_size)}, {STRUCT_FLD(name, "log_file_size"), STRUCT_FLD(type, IB_CFG_ULINT), STRUCT_FLD(flag, IB_CFG_FLAG_READONLY_AFTER_STARTUP), STRUCT_FLD(min_val, 1024 * 1024), STRUCT_FLD(max_val, ULINT_MAX), STRUCT_FLD(validate, ib_cfg_var_validate_numeric), STRUCT_FLD(set, ib_cfg_var_set_generic), STRUCT_FLD(get, ib_cfg_var_get_generic), STRUCT_FLD(tank, &srv_log_file_curr_size)}, {STRUCT_FLD(name, "log_files_in_group"), STRUCT_FLD(type, IB_CFG_ULINT), STRUCT_FLD(flag, IB_CFG_FLAG_READONLY_AFTER_STARTUP), STRUCT_FLD(min_val, 2), STRUCT_FLD(max_val, 100), STRUCT_FLD(validate, ib_cfg_var_validate_numeric), STRUCT_FLD(set, ib_cfg_var_set_generic), STRUCT_FLD(get, ib_cfg_var_get_generic), STRUCT_FLD(tank, &srv_n_log_files)}, {STRUCT_FLD(name, "log_group_home_dir"), STRUCT_FLD(type, IB_CFG_TEXT), STRUCT_FLD(flag, IB_CFG_FLAG_READONLY_AFTER_STARTUP), STRUCT_FLD(min_val, 0), STRUCT_FLD(max_val, 0), STRUCT_FLD(validate, NULL), STRUCT_FLD(set, ib_cfg_var_set_log_group_home_dir), STRUCT_FLD(get, ib_cfg_var_get_log_group_home_dir), STRUCT_FLD(tank, NULL)}, {STRUCT_FLD(name, "max_dirty_pages_pct"), STRUCT_FLD(type, IB_CFG_ULONG), STRUCT_FLD(flag, IB_CFG_FLAG_NONE), STRUCT_FLD(min_val, 0), STRUCT_FLD(max_val, 100), STRUCT_FLD(validate, ib_cfg_var_validate_numeric), STRUCT_FLD(set, ib_cfg_var_set_generic), STRUCT_FLD(get, ib_cfg_var_get_generic), STRUCT_FLD(tank, &srv_max_buf_pool_modified_pct)}, {STRUCT_FLD(name, "max_purge_lag"), STRUCT_FLD(type, IB_CFG_ULONG), STRUCT_FLD(flag, IB_CFG_FLAG_NONE), STRUCT_FLD(min_val, 0), STRUCT_FLD(max_val, IB_UINT64_T_MAX), STRUCT_FLD(validate, ib_cfg_var_validate_numeric), STRUCT_FLD(set, ib_cfg_var_set_generic), STRUCT_FLD(get, ib_cfg_var_get_generic), STRUCT_FLD(tank, &srv_max_purge_lag)}, {STRUCT_FLD(name, "lru_old_blocks_pct"), STRUCT_FLD(type, IB_CFG_ULINT), STRUCT_FLD(flag, IB_CFG_FLAG_NONE), STRUCT_FLD(min_val, 5), STRUCT_FLD(max_val, 95), STRUCT_FLD(validate, ib_cfg_var_validate_numeric), STRUCT_FLD(set, ib_cfg_var_set_lru_old_blocks_pct), STRUCT_FLD(get, ib_cfg_var_get_generic), STRUCT_FLD(tank, &lru_old_blocks_pct)}, {STRUCT_FLD(name, "lru_block_access_recency"), STRUCT_FLD(type, IB_CFG_ULINT), STRUCT_FLD(flag, IB_CFG_FLAG_NONE), STRUCT_FLD(min_val, 0), STRUCT_FLD(max_val, 0xFFFFFFFFUL), STRUCT_FLD(validate, ib_cfg_var_validate_numeric), STRUCT_FLD(set, ib_cfg_var_set_generic), STRUCT_FLD(get, ib_cfg_var_get_generic), STRUCT_FLD(tank, &buf_LRU_old_threshold_ms)}, {STRUCT_FLD(name, "open_files"), STRUCT_FLD(type, IB_CFG_ULINT), STRUCT_FLD(flag, IB_CFG_FLAG_READONLY_AFTER_STARTUP), STRUCT_FLD(min_val, 10), STRUCT_FLD(max_val, IB_UINT64_T_MAX), STRUCT_FLD(validate, ib_cfg_var_validate_numeric), STRUCT_FLD(set, ib_cfg_var_set_generic), STRUCT_FLD(get, ib_cfg_var_get_generic), STRUCT_FLD(tank, &srv_max_n_open_files)}, {STRUCT_FLD(name, "read_io_threads"), STRUCT_FLD(type, IB_CFG_ULINT), STRUCT_FLD(flag, IB_CFG_FLAG_READONLY_AFTER_STARTUP), STRUCT_FLD(min_val, 1), STRUCT_FLD(max_val, 64), STRUCT_FLD(validate, ib_cfg_var_validate_numeric), STRUCT_FLD(set, ib_cfg_var_set_generic), STRUCT_FLD(get, ib_cfg_var_get_generic), STRUCT_FLD(tank, &srv_n_read_io_threads)}, {STRUCT_FLD(name, "write_io_threads"), STRUCT_FLD(type, IB_CFG_ULINT), STRUCT_FLD(flag, IB_CFG_FLAG_READONLY_AFTER_STARTUP), STRUCT_FLD(min_val, 1), STRUCT_FLD(max_val, 64), STRUCT_FLD(validate, ib_cfg_var_validate_numeric), STRUCT_FLD(set, ib_cfg_var_set_generic), STRUCT_FLD(get, ib_cfg_var_get_generic), STRUCT_FLD(tank, &srv_n_write_io_threads)}, /* New, not present in InnoDB/MySQL */ {STRUCT_FLD(name, "pre_rollback_hook"), STRUCT_FLD(type, IB_CFG_CB), STRUCT_FLD(flag, IB_CFG_FLAG_NONE), STRUCT_FLD(min_val, 0), STRUCT_FLD(max_val, 0), STRUCT_FLD(validate, NULL), STRUCT_FLD(set, ib_cfg_var_set_generic), STRUCT_FLD(get, ib_cfg_var_get_generic), STRUCT_FLD(tank, &recv_pre_rollback_hook)}, /* New, not present in InnoDB/MySQL */ {STRUCT_FLD(name, "print_verbose_log"), STRUCT_FLD(type, IB_CFG_IBOOL), STRUCT_FLD(flag, IB_CFG_FLAG_NONE), STRUCT_FLD(min_val, 0), STRUCT_FLD(max_val, 0), STRUCT_FLD(validate, NULL), STRUCT_FLD(set, ib_cfg_var_set_generic), STRUCT_FLD(get, ib_cfg_var_get_generic), STRUCT_FLD(tank, &srv_print_verbose_log)}, /* New, not present in InnoDB/MySQL */ {STRUCT_FLD(name, "rollback_on_timeout"), STRUCT_FLD(type, IB_CFG_IBOOL), STRUCT_FLD(flag, IB_CFG_FLAG_NONE), STRUCT_FLD(min_val, 0), STRUCT_FLD(max_val, 0), STRUCT_FLD(validate, NULL), STRUCT_FLD(set, ib_cfg_var_set_generic), STRUCT_FLD(get, ib_cfg_var_get_generic), STRUCT_FLD(tank, &ses_rollback_on_timeout)}, {STRUCT_FLD(name, "stats_sample_pages"), STRUCT_FLD(type, IB_CFG_ULINT), STRUCT_FLD(flag, IB_CFG_FLAG_NONE), STRUCT_FLD(min_val, 1), STRUCT_FLD(max_val, ULINT_MAX), STRUCT_FLD(validate, ib_cfg_var_validate_numeric), STRUCT_FLD(set, ib_cfg_var_set_generic), STRUCT_FLD(get, ib_cfg_var_get_generic), STRUCT_FLD(tank, &srv_stats_sample_pages)}, {STRUCT_FLD(name, "status_file"), STRUCT_FLD(type, IB_CFG_IBOOL), STRUCT_FLD(flag, IB_CFG_FLAG_NONE), STRUCT_FLD(min_val, 0), STRUCT_FLD(max_val, 0), STRUCT_FLD(validate, NULL), STRUCT_FLD(set, ib_cfg_var_set_generic), STRUCT_FLD(get, ib_cfg_var_get_generic), STRUCT_FLD(tank, &srv_innodb_status)}, {STRUCT_FLD(name, "sync_spin_loops"), STRUCT_FLD(type, IB_CFG_ULONG), STRUCT_FLD(flag, IB_CFG_FLAG_NONE), STRUCT_FLD(min_val, 0), STRUCT_FLD(max_val, IB_UINT64_T_MAX), STRUCT_FLD(validate, ib_cfg_var_validate_numeric), STRUCT_FLD(set, ib_cfg_var_set_generic), STRUCT_FLD(get, ib_cfg_var_get_generic), STRUCT_FLD(tank, &srv_n_spin_wait_rounds)}, {STRUCT_FLD(name, "use_sys_malloc"), STRUCT_FLD(type, IB_CFG_IBOOL), STRUCT_FLD(flag, IB_CFG_FLAG_NONE), STRUCT_FLD(min_val, 0), STRUCT_FLD(max_val, 0), STRUCT_FLD(validate, NULL), STRUCT_FLD(set, ib_cfg_var_set_generic), STRUCT_FLD(get, ib_cfg_var_get_generic), STRUCT_FLD(tank, &srv_use_sys_malloc)}, {STRUCT_FLD(name, "version"), STRUCT_FLD(type, IB_CFG_TEXT), STRUCT_FLD(flag, IB_CFG_FLAG_READONLY), STRUCT_FLD(min_val, 0), STRUCT_FLD(max_val, 0), STRUCT_FLD(validate, NULL), STRUCT_FLD(set, NULL /* The ::set() function should never be called because this variable is flagged as IB_CFG_FLAG_READONLY */), STRUCT_FLD(get, ib_cfg_var_get_version), STRUCT_FLD(tank, NULL)}, }; /* @} */ /** This mutex has to work even if the InnoDB latching infrastructure hasn't been initialized. */ static os_fast_mutex_t cfg_vars_mutex; static ib_cfg_var_t cfg_vars[UT_ARR_SIZE(cfg_vars_defaults)]; /* public API functions and some auxiliary ones @{ */ /*********************************************************************//** Lookup a variable name. ib_cfg_lookup_var() @{ @return config variable instance if found else NULL */ static ib_cfg_var_t* ib_cfg_lookup_var( /*==============*/ const char* var) /*!< in: variable name */ { ulint i; for (i = 0; i < UT_ARR_SIZE(cfg_vars); ++i) { ib_cfg_var_t* cfg_var; cfg_var = &cfg_vars[i]; if (strcasecmp(var, cfg_var->name) == 0) { return(cfg_var); } } return(NULL); } /* @} */ /*********************************************************************//** Get the type of a configuration variable. Returns DB_SUCCESS if the variable with name "name" was found and "type" was set. ib_cfg_var_get_type() @{ @return DB_SUCCESS if successful */ ib_err_t ib_cfg_var_get_type( /*================*/ const char* name, /*!< in: variable name */ ib_cfg_type_t* type) /*!< out: variable type */ { ib_cfg_var_t* cfg_var; ib_err_t ret; os_fast_mutex_lock(&cfg_vars_mutex); cfg_var = ib_cfg_lookup_var(name); if (cfg_var != NULL) { *type = cfg_var->type; ret = DB_SUCCESS; } else { ret = DB_NOT_FOUND; } os_fast_mutex_unlock(&cfg_vars_mutex); return(ret); } /* @} */ /*********************************************************************//** Set a configuration variable. "ap" must contain one argument whose type depends on the type of the variable with the given "name". Returns DB_SUCCESS if the variable with name "name" was found and if its value was set. ib_cfg_set_ap() @{ @return DB_SUCCESS if set */ static ib_err_t ib_cfg_set_ap( /*==========*/ const char* name, /*!< in: variable name */ va_list ap) /*!< in: variable value */ { ib_cfg_var_t* cfg_var; ib_err_t ret = DB_NOT_FOUND; os_fast_mutex_lock(&cfg_vars_mutex); cfg_var = ib_cfg_lookup_var(name); if (cfg_var != NULL) { /* check whether setting the variable is appropriate, according to its flag */ if (cfg_var->flag & IB_CFG_FLAG_READONLY || (cfg_var->flag & IB_CFG_FLAG_READONLY_AFTER_STARTUP && srv_was_started)) { ret = DB_READONLY; } else { /* Get the parameter according to its type and call ::set() */ switch (cfg_var->type) { case IB_CFG_IBOOL: { ib_bool_t value; value = va_arg(ap, ib_bool_t); ret = cfg_var->set(cfg_var, &value); break; } case IB_CFG_ULINT: { ulint value; value = va_arg(ap, ulint); ret = cfg_var->set(cfg_var, &value); break; } case IB_CFG_ULONG: { ulong value; value = va_arg(ap, ulong); ret = cfg_var->set(cfg_var, &value); break; } case IB_CFG_TEXT: { const char* value; value = va_arg(ap, const char*); ret = cfg_var->set(cfg_var, &value); break; } case IB_CFG_CB: { ib_cb_t value; value = va_arg(ap, ib_cb_t); ret = cfg_var->set(cfg_var, &value); break; } /* do not add default: in order to produce a compilation warning if new type is added which is not handled here */ } } } os_fast_mutex_unlock(&cfg_vars_mutex); return(ret); } /* @} */ /*********************************************************************//** Set a configuration variable. The second argument's type depends on the type of the variable with the given "name". Returns DB_SUCCESS if the variable with name "name" was found and if its value was set. Strings are not copied, be sure not to destroy or modify your string after it has been given to this function. Memory management is left to the caller. E.g. if you malloc() a string and give it to this function and then malloc() another string and give it again to this function for the same config variable, then you are responsible to free the first string. ib_cfg_set() @{ @return DB_SUCCESS if set */ ib_err_t ib_cfg_set( /*=======*/ const char* name, /*!< in: variable name */ ...) /*!< in: variable value */ { va_list ap; ib_bool_t ret; va_start(ap, name); ret = ib_cfg_set_ap(name, ap); va_end(ap); return(ret); } /* @} */ /*********************************************************************//** Get the value of a configuration variable. The type of the returned value depends on the type of the configuration variable. DB_SUCCESS is returned if the variable with name "name" was found and "value" was set. Strings are not copied and a reference to the internal storage is returned. Be sure not to modify the returned value or you may confuse InnoDB or cause a crash. ib_cfg_get() @{ @return DB_SUCCESS if retrieved successfully */ ib_err_t ib_cfg_get( /*=======*/ const char* name, /*!< in: variable name */ void* value) /*!< out: pointer to the place to store the retrieved value */ { ib_cfg_var_t* cfg_var; ib_err_t ret; os_fast_mutex_lock(&cfg_vars_mutex); cfg_var = ib_cfg_lookup_var(name); if (cfg_var != NULL) { ret = cfg_var->get(cfg_var, value); } else { ret = DB_NOT_FOUND; } os_fast_mutex_unlock(&cfg_vars_mutex); return(ret); } /* @} */ /*********************************************************************//** Get a list of the names of all configuration variables. The caller is responsible for free(3)ing the returned array of strings when it is not needed anymore and for not modifying the individual strings. ib_cfg_get_all() @{ @return DB_SUCCESS or error code */ ib_err_t ib_cfg_get_all( /*===========*/ const char*** names, /*!< out: pointer to array of strings */ ib_u32_t* names_num) /*!< out: number of strings returned */ { ib_u32_t i; *names_num = UT_ARR_SIZE(cfg_vars_defaults); *names = (const char**) malloc(*names_num * sizeof(const char*)); if (*names == NULL) { return(DB_OUT_OF_MEMORY); } for (i = 0; i < *names_num; i++) { (*names)[i] = cfg_vars_defaults[i].name; } return(DB_SUCCESS); } /* @} */ /*********************************************************************//** Initialize the config system. ib_cfg_init() @{ @return DB_SUCCESS or error code */ ib_err_t ib_cfg_init(void) /*=============*/ { /* Initialize the mutex that protects cfg_vars[]. */ os_fast_mutex_init(&cfg_vars_mutex); ut_memcpy(cfg_vars, cfg_vars_defaults, sizeof(cfg_vars)); /* Set the default options. */ srv_file_flush_method_str = NULL; srv_unix_file_flush_method = SRV_UNIX_FSYNC; srv_win_file_flush_method = SRV_WIN_IO_UNBUFFERED; os_aio_print_debug = FALSE; os_aio_use_native_aio = FALSE; #define IB_CFG_SET(name, var) \ if (ib_cfg_set(name, var) != DB_SUCCESS) ut_error IB_CFG_SET("additional_mem_pool_size", 4 * 1024 * 1024); IB_CFG_SET("buffer_pool_size", 8 * 1024 * 1024); IB_CFG_SET("data_file_path", "ibdata1:32M:autoextend"); IB_CFG_SET("data_home_dir", "./"); IB_CFG_SET("file_per_table", IB_TRUE); #ifndef __WIN__ IB_CFG_SET("flush_method", "fsync"); #endif IB_CFG_SET("lock_wait_timeout", 60); IB_CFG_SET("log_buffer_size", 384 * 1024); IB_CFG_SET("log_file_size", 16 * 1024 * 1024); IB_CFG_SET("log_files_in_group", 2); IB_CFG_SET("log_group_home_dir", "."); IB_CFG_SET("lru_old_blocks_pct", 3 * 100 / 8); IB_CFG_SET("lru_block_access_recency", 0); IB_CFG_SET("rollback_on_timeout", IB_TRUE); IB_CFG_SET("read_io_threads", 4); IB_CFG_SET("write_io_threads", 4); #undef IB_CFG_SET return(DB_SUCCESS); } /* @} */ /*********************************************************************//** Shutdown the config system. ib_cfg_shutdown() @{ @return DB_SUCCESS or error code */ ib_err_t ib_cfg_shutdown(void) /*=================*/ { os_fast_mutex_lock(&cfg_vars_mutex); /* TODO: Check for NULL values of allocated config variables. */ memset(cfg_vars, 0x0, sizeof(cfg_vars)); os_fast_mutex_unlock(&cfg_vars_mutex); os_fast_mutex_free(&cfg_vars_mutex); return(DB_SUCCESS); } /* @} */ /* @} */ /* vim: set foldmethod=marker foldmarker=@{,@}: */ haildb-2.3.2/api/api0status.c0000644000175000017500000001671211513177357016664 0ustar00pcrewspcrews00000000000000/*********************************************************************** Copyright (c) 2009 Innobase Oy. All rights reserved. Copyright (c) 2009 Oracle. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ************************************************************************/ #ifdef HAVE_STRINGS_H #include #endif #include "univ.i" #include "srv0srv.h" #include "api0api.h" #include "api0ucode.h" /** InnoDB status variables types. */ typedef enum { IB_STATUS_IBOOL, /*!< Boolean status variable, ibool */ IB_STATUS_I64, /*!< ib_int64_t status variable */ IB_STATUS_ULINT /*!< uling status variable */ } ib_status_type_t; /** InnoDB status variables */ typedef struct { const char* name; /*!< Status variable name */ ib_status_type_t type; /*!< Status varable type */ const void* val; /*!< Pointer to status value */ } ib_status_t; /* All status variables that a user can query. */ static const ib_status_t status_vars[] = { /* IO system related */ {"read_req_pending", IB_STATUS_ULINT, &export_vars.innodb_data_pending_reads}, {"write_req_pending", IB_STATUS_ULINT, &export_vars.innodb_data_pending_writes}, {"fsync_req_pending", IB_STATUS_ULINT, &export_vars.innodb_data_pending_fsyncs}, {"write_req_done", IB_STATUS_ULINT, &export_vars.innodb_data_writes}, {"read_req_done", IB_STATUS_ULINT, &export_vars.innodb_data_reads}, {"fsync_req_done", IB_STATUS_ULINT, &export_vars.innodb_data_fsyncs}, {"bytes_total_written", IB_STATUS_ULINT, &export_vars.innodb_data_written}, {"bytes_total_read", IB_STATUS_ULINT, &export_vars.innodb_data_read}, /* Buffer pool related */ {"buffer_pool_current_size", IB_STATUS_ULINT, &export_vars.innodb_buffer_pool_pages_total}, {"buffer_pool_data_pages", IB_STATUS_ULINT, &export_vars.innodb_buffer_pool_pages_data}, {"buffer_pool_dirty_pages", IB_STATUS_ULINT, &export_vars.innodb_buffer_pool_pages_dirty}, {"buffer_pool_misc_pages", IB_STATUS_ULINT, &export_vars.innodb_buffer_pool_pages_misc}, {"buffer_pool_free_pages", IB_STATUS_ULINT, &export_vars.innodb_buffer_pool_pages_free}, {"buffer_pool_read_reqs", IB_STATUS_ULINT, &export_vars.innodb_buffer_pool_read_requests}, {"buffer_pool_reads", IB_STATUS_ULINT, &export_vars.innodb_buffer_pool_reads}, {"buffer_pool_waited_for_free", IB_STATUS_ULINT, &export_vars.innodb_buffer_pool_wait_free}, {"buffer_pool_pages_flushed", IB_STATUS_ULINT, &export_vars.innodb_buffer_pool_pages_flushed}, {"buffer_pool_write_reqs", IB_STATUS_ULINT, &export_vars.innodb_buffer_pool_write_requests}, {"buffer_pool_total_pages", IB_STATUS_ULINT, &export_vars.innodb_pages_created}, {"buffer_pool_pages_read", IB_STATUS_ULINT, &export_vars.innodb_pages_read}, {"buffer_pool_pages_written", IB_STATUS_ULINT, &export_vars.innodb_pages_written}, /* Double write buffer related */ {"double_write_pages_written", IB_STATUS_ULINT, &export_vars.innodb_dblwr_pages_written}, {"double_write_invoked", IB_STATUS_ULINT, &export_vars.innodb_dblwr_writes}, /* Log related */ {"log_buffer_slot_waits", IB_STATUS_ULINT, &export_vars.innodb_log_waits}, {"log_write_reqs", IB_STATUS_ULINT, &export_vars.innodb_log_write_requests}, {"log_write_flush_count", IB_STATUS_ULINT, &export_vars.innodb_log_writes}, {"log_bytes_written", IB_STATUS_ULINT, &export_vars.innodb_os_log_written}, {"log_fsync_req_done", IB_STATUS_ULINT, &export_vars.innodb_os_log_fsyncs}, {"log_write_req_pending", IB_STATUS_ULINT, &export_vars.innodb_os_log_pending_writes}, {"log_fsync_req_pending", IB_STATUS_ULINT, &export_vars.innodb_os_log_pending_fsyncs}, /* Lock related */ {"lock_row_waits", IB_STATUS_ULINT, &export_vars.innodb_row_lock_waits}, {"lock_row_waiting", IB_STATUS_ULINT, &export_vars.innodb_row_lock_current_waits}, {"lock_total_wait_time_in_secs",IB_STATUS_ULINT, &export_vars.innodb_row_lock_time}, {"lock_wait_time_avg_in_secs", IB_STATUS_ULINT, &export_vars.innodb_row_lock_time_avg}, {"lock_max_wait_time_in_secs",IB_STATUS_ULINT, &export_vars.innodb_row_lock_time_max}, /* Row operations */ {"row_total_read", IB_STATUS_ULINT, &export_vars.innodb_rows_read}, {"row_total_inserted", IB_STATUS_ULINT, &export_vars.innodb_rows_inserted}, {"row_total_updated", IB_STATUS_ULINT, &export_vars.innodb_rows_updated}, {"row_total_deleted", IB_STATUS_ULINT, &export_vars.innodb_rows_deleted}, /* Miscellaneous */ {"page_size", IB_STATUS_ULINT, &export_vars.innodb_page_size}, {"have_atomic_builtins", IB_STATUS_IBOOL, &export_vars.innodb_have_atomic_builtins}, { NULL, 0, 0}}; /*********************************************************************//** Get a list of the names of all status variables. The caller is responsible for free(3)ing the returned array of strings when it is not needed anymore and for not modifying the individual strings. ib_status_get_all() @{ @return DB_SUCCESS or error code */ ib_err_t ib_status_get_all( /*===========*/ const char*** names, /*!< out: pointer to array of strings */ ib_u32_t* names_num) /*!< out: number of strings returned */ { ib_u32_t i; *names_num = UT_ARR_SIZE(status_vars); *names = (const char**) malloc(*names_num * sizeof(const char*)); if (*names == NULL) { return(DB_OUT_OF_MEMORY); } for (i = 0; i < *names_num; i++) { (*names)[i] = status_vars[i].name; } return(DB_SUCCESS); } /* @} */ /*******************************************************************//* Get the status variable that matches name. @return DB_SUCCESS if found else DB_NOT_FOUND */ static ib_err_t ib_status_lookup( /*=============*/ const char* name, /*!< in: Variable to lookup */ const ib_status_t** var) /*!< out: pointer to entry */ { const ib_status_t* ptr; *var = NULL; for (ptr = status_vars; ptr && ptr->name != NULL; ++ptr) { if (ib_utf8_strcasecmp(name, ptr->name) == 0) { *var = ptr; return(DB_SUCCESS); } } return(DB_NOT_FOUND); } /*******************************************************************//** Get the value of an INT status variable. @file api/api0status.c @return DB_SUCCESS if found and type is INT, DB_DATA_MISMATCH if found but type is not INT, DB_NOT_FOUND otherwise. */ ib_err_t ib_status_get_i64( /*==============*/ const char* name, /*!< in: Status variable name */ ib_i64_t* dst) /*!< out: Variable value */ { ib_err_t err; const ib_status_t* var; err = ib_status_lookup(name, &var); if (err == DB_SUCCESS) { /* Read the latest values into export_vars. */ srv_export_innodb_status(); switch (var->type) { case IB_STATUS_ULINT: *dst = *(ulint*) var->val; break; case IB_STATUS_IBOOL: *dst = *(ibool*) var->val; break; case IB_STATUS_I64: *dst = *(ib_int64_t*) var->val; break; default: /* Currently the status variables are all INTs. If we add other types then this will signal to the user that the variable type is different. */ err = DB_DATA_MISMATCH; break; } } return(err); } haildb-2.3.2/api/api0api.c0000644000175000017500000047474611513177357016131 0ustar00pcrewspcrews00000000000000/*********************************************************************** Copyright (c) 2008 Innobase Oy. All rights reserved. Copyright (c) 2008 Oracle. All rights reserved. Copyright (c) 2010 Stewart Smith This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ************************************************************************/ #include "config.h" #include #include #include #include #ifdef HAVE_UNISTD_H #include #endif #include "univ.i" #include "api0api.h" #include "api0misc.h" #include "srv0start.h" #include "dict0dict.h" #include "btr0pcur.h" #include "row0ins.h" #include "row0upd.h" #include "row0vers.h" #include "row0prebuilt.h" #include "trx0roll.h" #include "ddl0ddl.h" #include "dict0crea.h" #include "row0merge.h" #include "pars0pars.h" #include "api0ucode.h" #include "lock0types.h" #include "row0sel.h" #include "lock0lock.h" #include "rem0cmp.h" #include "ut0dbg.h" /* for UT_DBG_ENTER_FUNC */ static const char* GEN_CLUST_INDEX = "GEN_CLUST_INDEX"; /* All InnoDB error messages are written to this stream. */ ib_logger_t ib_logger; ib_stream_t ib_stream; /* This must hold. */ #if IB_TRUE != TRUE || IB_FALSE != FALSE #error IB_TRUE != TRUE or IB_FALSE != FALSE #endif static int api_api_enter_func_enabled = 0; #define UT_DBG_ENTER_FUNC_ENABLED api_api_enter_func_enabled /* Protected by the schema lock */ typedef struct ib_db_format_t { ulint id; /* Numeric representation of database format */ const char* name; /* Text representation of name, allocated using ut_malloc() and should be automatically freed at InnoDB shutdown */ } ib_db_format_t; /* This value is read at database startup. */ static ib_db_format_t db_format; /*************************************************************//** @file api/api0api.c Does a simple memcmp(3). @return 1, 0, -1, if a is greater, equal, less than b, respectively */ static int ib_default_compare( /*===============*/ const ib_col_meta_t* col_meta, /*!< in: column meta data */ const ib_byte_t*p1, /*!< in: packed key */ ib_ulint_t p1_len, /*!< in: packed key length */ const ib_byte_t*p2, /*!< in: packed key */ ib_ulint_t p2_len); /*!< in: packed key length */ ib_client_cmp_t ib_client_compare = ib_default_compare; /* InnoDB tuple types. */ typedef enum ib_tuple_type_enum { TPL_ROW, /* Data row tuple */ TPL_KEY /* Index key tuple */ } ib_tuple_type_t; /* Query types supported. */ typedef enum ib_qry_type_enum { QRY_NON, /* None/Sentinel */ QRY_INS, /* Insert operation */ QRY_UPD, /* Update operation */ QRY_SEL /* Select operation */ } ib_qry_type_t; /* Query graph types. */ typedef struct ib_qry_grph_struct { que_fork_t* ins; /* Innobase SQL query graph used in inserts */ que_fork_t* upd; /* Innobase SQL query graph used in updates or deletes */ que_fork_t* sel; /* dummy query graph used in selects */ } ib_qry_grph_t; /* Query node types. */ typedef struct ib_qry_node_struct { ins_node_t* ins; /* Innobase SQL insert node used to perform inserts to the table */ upd_node_t* upd; /* Innobase SQL update node used to perform updates and deletes */ sel_node_t* sel; /* Innobase SQL select node used to perform selects on the table */ } ib_qry_node_t; /* Query processing fields. */ typedef struct ib_qry_proc_struct { ib_qry_node_t node; /* Query node*/ ib_qry_grph_t grph; /* Query graph */ } ib_qry_proc_t; /* Cursor instance for traversing tables/indexes. This will eventually become row_prebuilt_t. */ typedef struct ib_cursor_struct { mem_heap_t* heap; /* Instance heap */ mem_heap_t* query_heap; /* Heap to use for query graphs */ ib_qry_proc_t q_proc; /* Query processing info */ ib_match_mode_t match_mode; /* ib_cursor_moveto match mode */ row_prebuilt_t* prebuilt; /* For reading rows */ } ib_cursor_t; /* InnoDB table columns used during table and index schema creation. */ typedef struct ib_col_struct { const char* name; /* Name of column */ ib_col_type_t ib_col_type; /* Main type of the column */ ulint len; /* Length of the column */ ib_col_attr_t ib_col_attr; /* Column attributes */ } ib_col_t; /* InnoDB index columns used during index and index schema creation. */ typedef struct ib_key_col_struct { const char* name; /* Name of column */ ulint prefix_len; /* Column index prefix len or 0 */ } ib_key_col_t; typedef struct ib_table_def_struct ib_table_def_t; /* InnoDB index schema used during index creation */ typedef struct ib_index_def_struct { mem_heap_t* heap; /* Heap used to build this and all its columns in the list */ const char* name; /* Index name */ dict_table_t* table; /* Parent InnoDB table */ ib_table_def_t* schema; /* Parent table schema that owns this instance */ ibool clustered; /* True if clustered index */ ibool unique; /* True if unique index */ ib_vector_t* cols; /* Vector of columns */ trx_t* usr_trx; /* User transacton covering the DDL operations */ } ib_index_def_t; /* InnoDB table schema used during table creation */ struct ib_table_def_struct { mem_heap_t* heap; /* Heap used to build this and all its columns in the list */ const char* name; /* Table name */ ib_tbl_fmt_t ib_tbl_fmt; /* Row format */ ulint page_size; /* Page size */ ib_vector_t* cols; /* Vector of columns */ ib_vector_t* indexes; /* Vector of indexes */ dict_table_t* table; /* Table read from or NULL */ }; /* InnoDB tuple used for key operations. */ typedef struct ib_tuple_struct { mem_heap_t* heap; /* Heap used to build this and for copying the column values. */ ib_tuple_type_t type; /* Tuple discriminitor. */ const dict_index_t* index; /* Index for tuple can be either secondary or cluster index. */ dtuple_t* ptr; /* The internal tuple instance */ } ib_tuple_t; /* I can't see what merge has to do with creating an Index. */ typedef merge_index_def_t index_def_t; typedef merge_index_field_t index_field_t; /* The following counter is used to convey information to InnoDB about server activity: in selects it is not sensible to call srv_active_wake_master_thread after each fetch or search, we only do it every INNOBASE_WAKE_INTERVAL'th step. */ #define INNOBASE_WAKE_INTERVAL 32 /*************************************************************//** Does a simple memcmp(3). @return 1, 0, -1, if a is greater, equal, less than b, respectively */ static int ib_default_compare( /*===============*/ const ib_col_meta_t* ib_col_meta, /*!< in: column meta data */ const ib_byte_t*p1, /*!< in: packed key */ ib_ulint_t p1_len, /*!< in: packed key length */ const ib_byte_t*p2, /*!< in: packed key */ ib_ulint_t p2_len) /*!< in: packed key length */ { int ret; (void)ib_col_meta; UT_DBG_ENTER_FUNC; ret = memcmp(p1, p2, ut_min(p1_len, p2_len)); if (ret == 0) { ret = p1_len - p2_len; } return(ret < 0 ? -1 : ((ret > 0) ? 1 : 0)); } /*****************************************************************//** Check if the Innodb persistent cursor is positioned. @return IB_TRUE if positioned */ UNIV_INLINE ib_bool_t ib_btr_cursor_is_positioned( /*========================*/ btr_pcur_t* pcur) /*!< in: InnoDB persistent cursor */ { return(pcur->old_stored == BTR_PCUR_OLD_STORED && (pcur->pos_state == BTR_PCUR_IS_POSITIONED || pcur->pos_state == BTR_PCUR_WAS_POSITIONED)); } /*******************************************************************//** Delays an INSERT, DELETE or UPDATE operation if the purge is lagging. */ static void ib_delay_dml_if_needed(void) /*========================*/ { if (srv_dml_needed_delay) { os_thread_sleep(srv_dml_needed_delay); } } /********************************************************************//** Open a table using the table id, if found then increment table ref count. @return table instance if found */ static dict_table_t* ib_open_table_by_id( /*================*/ ib_id_t tid, /*!< in: table id to lookup */ ib_bool_t locked) /*!< in: TRUE if own dict mutex */ { dict_table_t* table; dulint table_id; UT_DBG_ENTER_FUNC; /* We only return the lower 32 bits of the dulint. */ ut_a(tid < 0xFFFFFFFFULL); table_id = ut_dulint_create(0, (ulint) tid); if (!locked) { dict_mutex_enter(); } table = dict_table_get_using_id(srv_force_recovery, table_id, TRUE); if (table != NULL && table->ibd_file_missing) { ib_logger(ib_stream, "The .ibd file for table %s is missing.\n", table->name); dict_table_decrement_handle_count(table, TRUE); table = NULL; } if (!locked) { dict_mutex_exit(); } return(table); } /********************************************************************//** Open a table using the table name, if found then increment table ref count. @return table instance if found */ static dict_table_t* ib_open_table_by_name( /*==================*/ const char* name) /*!< in: table name to lookup */ { dict_table_t* table; UT_DBG_ENTER_FUNC; table = dict_table_get(name, TRUE); if (table != NULL && table->ibd_file_missing) { ib_logger(ib_stream, "The .ibd file for table %s is missing.\n", name); dict_table_decrement_handle_count(table, FALSE); table = NULL; } return(table); } /********************************************************************//** Find table using table name. @return table instance if found */ static dict_table_t* ib_lookup_table_by_name( /*====================*/ const char* name) /*!< in: table name to lookup */ { dict_table_t* table; UT_DBG_ENTER_FUNC; table = dict_table_get_low(name); if (table != NULL && table->ibd_file_missing) { ib_logger(ib_stream, "The .ibd file for table %s is missing.\n", name); table = NULL; } return(table); } /********************************************************************//** Increments innobase_active_counter and every INNOBASE_WAKE_INTERVALth time calls srv_active_wake_master_thread. This function should be used when a single database operation may introduce a small need for server utility activity, like checkpointing. */ UNIV_INLINE void ib_wake_master_thread(void) /*=======================*/ { static ulint ib_signal_counter = 0; UT_DBG_ENTER_FUNC; ++ib_signal_counter; if ((ib_signal_counter % INNOBASE_WAKE_INTERVAL) == 0) { srv_active_wake_master_thread(); } } #if 0 /*****************************************************************//** Calculate the length of the column data less trailing space. */ static ulint ib_varchar_len( /*===========*/ const dtype_t* dtype, ib_byte_t* ptr, ulint len) { /* Handle UCS2 strings differently. */ ulint mbminlen = dtype_get_mbminlen(dtype); if (mbminlen == 2) { /* SPACE = 0x0020. */ /* Trim "half-chars", just in case. */ len &= ~1; while (len >= 2 && ptr[len - 2] == 0x00 && ptr[len - 1] == 0x20) { len -= 2; } } else { ut_a(mbminlen == 1); /* SPACE = 0x20. */ while (len > 0 && ptr[len - 1] == 0x20) { --len; } } return(len); } #endif /*********************************************************************//** Calculate the max row size of the columns in a cluster index. @return max row length */ UNIV_INLINE ulint ib_get_max_row_len( /*===============*/ dict_index_t* cluster) /*!< in: cluster index */ { ulint i; ulint max_len = 0; ulint n_fields = cluster->n_fields; UT_DBG_ENTER_FUNC; /* Add the size of the ordering columns in the clustered index. */ for (i = 0; i < n_fields; ++i) { const dict_col_t* col; col = dict_index_get_nth_col(cluster, i); /* Use the maximum output size of mach_write_compressed(), although the encoded length should always fit in 2 bytes. */ max_len += dict_col_get_max_size(col); } return(max_len); } /*****************************************************************//** Read the colums from a rec into a tuple. */ UNIV_INLINE void ib_read_tuple( /*==========*/ const rec_t* rec, /*!< in: Record to read */ ib_bool_t page_format, /*!< in: IB_TRUE if compressed format */ ib_tuple_t* tuple) /*!< in: tuple to read into */ { ulint i; void* ptr; rec_t* copy; ulint rec_meta_data; ulint n_index_fields; ulint offsets_[REC_OFFS_NORMAL_SIZE]; ulint* offsets = offsets_; dtuple_t* dtuple = tuple->ptr; const dict_index_t* dindex = tuple->index; UT_DBG_ENTER_FUNC; rec_offs_init(offsets_); offsets = rec_get_offsets( rec, dindex, offsets, ULINT_UNDEFINED, &tuple->heap); rec_meta_data = rec_get_info_bits(rec, page_format); dtuple_set_info_bits(dtuple, rec_meta_data); /* Make a copy of the rec. */ ptr = mem_heap_alloc(tuple->heap, rec_offs_size(offsets)); copy = rec_copy(ptr, rec, offsets); /* Avoid a debug assertion in rec_offs_validate(). */ rec_offs_make_valid(rec, dindex, (ulint*) offsets); n_index_fields = ut_min( rec_offs_n_fields(offsets), dtuple_get_n_fields(dtuple)); for (i = 0; i < n_index_fields; ++i) { ulint len; const byte* data; dfield_t* dfield; if (tuple->type == TPL_ROW) { const dict_col_t* col; ulint col_no; const dict_field_t* index_field; index_field = dict_index_get_nth_field(dindex, i); col = dict_field_get_col(index_field); col_no = dict_col_get_no(col); dfield = dtuple_get_nth_field(dtuple, col_no); } else { dfield = dtuple_get_nth_field(dtuple, i); } data = rec_get_nth_field(copy, offsets, i, &len); /* Fetch and copy any externally stored column. */ if (rec_offs_nth_extern(offsets, i)) { ulint zip_size; zip_size = dict_table_zip_size(dindex->table); data = btr_rec_copy_externally_stored_field( copy, offsets, zip_size, i, &len, tuple->heap); ut_a(len != UNIV_SQL_NULL); } dfield_set_data(dfield, data, len); } } /*****************************************************************//** Create an InnoDB key tuple. @return tuple instance created, or NULL */ static ib_tpl_t ib_key_tuple_new_low( /*=================*/ const dict_index_t* dict_index,/*!< in: index for which tuple required */ ulint n_cols, /*!< in: no. of user defined cols */ mem_heap_t* heap) /*!< in: memory heap */ { ib_tuple_t* tuple; ulint i; ulint n_cmp_cols; UT_DBG_ENTER_FUNC; tuple = mem_heap_alloc(heap, sizeof(*tuple)); if (tuple == NULL) { mem_heap_free(heap); return(NULL); } tuple->heap = heap; tuple->index = dict_index; tuple->type = TPL_KEY; /* Is it a generated clustered index ? */ if (n_cols == 0) { ++n_cols; } tuple->ptr = dtuple_create(heap, n_cols); /* Copy types and set to SQL_NULL. */ dict_index_copy_types(tuple->ptr, dict_index, n_cols); for (i = 0; i < n_cols; i++) { dfield_t* dfield; dfield = dtuple_get_nth_field(tuple->ptr, i); dfield_set_null(dfield); } n_cmp_cols = dict_index_get_n_ordering_defined_by_user(dict_index); dtuple_set_n_fields_cmp(tuple->ptr, n_cmp_cols); return((ib_tpl_t) tuple); } /*****************************************************************//** Create an InnoDB key tuple. @return tuple instance created, or NULL */ static ib_tpl_t ib_key_tuple_new( /*=============*/ const dict_index_t* dict_index, /*!< in: index of tuple */ ulint n_cols) /*!< in: no. of user defined cols */ { mem_heap_t* heap; UT_DBG_ENTER_FUNC; heap = mem_heap_create(64); if (heap == NULL) { return(NULL); } return(ib_key_tuple_new_low(dict_index, n_cols, heap)); } /*****************************************************************//** Create an InnoDB row tuple. @return tuple instance, or NULL */ static ib_tpl_t ib_row_tuple_new_low( /*=================*/ const dict_index_t* dict_index, /*!< in: index of tuple */ ulint n_cols, /*!< in: no. of cols in tuple */ mem_heap_t* heap) /*!< in: memory heap */ { ib_tuple_t* tuple; UT_DBG_ENTER_FUNC; tuple = mem_heap_alloc(heap, sizeof(*tuple)); if (tuple == NULL) { mem_heap_free(heap); return(NULL); } tuple->heap = heap; tuple->index = dict_index; tuple->type = TPL_ROW; tuple->ptr = dtuple_create(heap, n_cols); /* Copy types and set to SQL_NULL. */ dict_table_copy_types(tuple->ptr, dict_index->table); return((ib_tpl_t) tuple); } /*****************************************************************//** Create an InnoDB row tuple. @return tuple instance, or NULL */ static ib_tpl_t ib_row_tuple_new( /*=============*/ const dict_index_t* dict_index, /*!< in: index of tuple */ ulint n_cols) /*!< in: no. of cols in tuple */ { mem_heap_t* heap; UT_DBG_ENTER_FUNC; heap = mem_heap_create(64); if (heap == NULL) { return(NULL); } return(ib_row_tuple_new_low(dict_index, n_cols, heap)); } /*****************************************************************//** Return the API version number, the version number format is: | 16 bits future use | 16 bits current | 16 bits revision | 16 bits age | - If the library source code has changed at all since the last release, then revision will be incremented (`c:r:a' becomes `c:r+1:a'). - If any interfaces have been added, removed, or changed since the last update, current will be incremented, and revision will be set to 0. - If any interfaces have been added (but not changed or removed) since the last release, then age will be incremented. - If any interfaces have been changed or removed since the last release, then age will be set to 0. @return API version number */ ib_u64_t ib_api_version(void) /*================*/ { return(((ib_u64_t) IB_API_VERSION_CURRENT << 32) | (IB_API_VERSION_REVISION << 16) | IB_API_VERSION_AGE); } /*****************************************************************//** Initialize the InnoDB engine. This must be called prior to calling any other InnoDB API function. @return DB_SUCCESS or error code */ ib_err_t ib_init(void) /*=========*/ { ib_err_t ib_err; IB_CHECK_PANIC(); ut_mem_init(); ib_logger = (ib_logger_t) fprintf; ib_stream = stderr; ib_err = ib_cfg_init(); return(ib_err); } /*****************************************************************//** Startup the InnoDB engine. @return DB_SUCCESS or error code */ ib_err_t ib_startup( /*=======*/ const char* format) /*!< in: file format name */ { ib_err_t err = DB_SUCCESS; UT_DBG_ENTER_FUNC; db_format.id = 0; db_format.name = NULL; /* Validate the file format if set by the user. */ if (format != NULL) { db_format.id = trx_sys_file_format_name_to_id(format); /* Check if format name was found. */ if (db_format.id > DICT_TF_FORMAT_MAX) { err = DB_UNSUPPORTED; ib_logger(ib_stream, "InnoDB: format '%s' unknown.", format); } } if (err == DB_SUCCESS) { db_format.name = trx_sys_file_format_id_to_name(db_format.id); /* Set the highest file format id supported. */ srv_file_format = db_format.id; err = innobase_start_or_create(); } return(err); } /*****************************************************************//** Shutdown the InnoDB engine. @return DB_SUCCESS or error code */ ib_err_t ib_shutdown( /*========*/ ib_shutdown_t flag) /*!< in: Shutdown flags */ { ib_err_t err; IB_CHECK_PANIC(); err = ib_cfg_shutdown(); if (err != DB_SUCCESS) { ib_logger(ib_stream, "ib_cfg_shutdown(): %s; " "continuing shutdown anyway\n", ib_strerror(err)); } db_format.id = 0; db_format.name = NULL; err = innobase_shutdown(flag); return(err); } /*****************************************************************//** Begin a transaction. @return innobase txn handle */ ib_err_t ib_trx_start( /*=========*/ ib_trx_t ib_trx, /*!< in: transaction to restart */ ib_trx_level_t ib_trx_level) /*!< in: trx isolation level */ { ib_err_t err = DB_SUCCESS; trx_t* trx = (trx_t*) ib_trx; UT_DBG_ENTER_FUNC; ut_a(ib_trx_level >= IB_TRX_READ_UNCOMMITTED); ut_a(ib_trx_level <= IB_TRX_SERIALIZABLE); ut_ad(trx->client_thread_id == os_thread_get_curr_id()); if (trx->conc_state == TRX_NOT_STARTED) { ib_bool_t started; started = trx_start(trx, ULINT_UNDEFINED); ut_a(started); trx->isolation_level = ib_trx_level; } else { err = DB_ERROR; } trx->client_thd = NULL; return(err); } void ib_trx_set_client_data( /*=========*/ ib_trx_t ib_trx, /*!< in: transaction */ void* client_data) { trx_t* trx = (trx_t*) ib_trx; trx->client_thd = client_data; } /*****************************************************************//** Begin a transaction. This will allocate a new transaction handle. @return innobase txn handle */ ib_trx_t ib_trx_begin( /*=========*/ ib_trx_level_t ib_trx_level) /*!< in: trx isolation level */ { trx_t* trx; ib_bool_t started; UT_DBG_ENTER_FUNC; trx = trx_allocate_for_client(NULL); started = ib_trx_start((ib_trx_t) trx, ib_trx_level); ut_a(started); return((ib_trx_t) trx); } /*****************************************************************//** Get the transaction's state. @return transaction state */ ib_trx_state_t ib_trx_state( /*=========*/ ib_trx_t ib_trx) /*!< in: trx handle */ { trx_t* trx = (trx_t*) ib_trx; UT_DBG_ENTER_FUNC; return((ib_trx_state_t) trx->conc_state); } /*****************************************************************//** Release the resources of the transaction. @return DB_SUCCESS or err code */ ib_err_t ib_trx_release( /*===========*/ ib_trx_t ib_trx) /*!< in: trx handle */ { trx_t* trx = (trx_t*) ib_trx; IB_CHECK_PANIC(); UT_DBG_ENTER_FUNC; ut_ad(trx != NULL); trx_free_for_client(trx); return(DB_SUCCESS); } /*****************************************************************//** Commit a transaction. This function will also release the schema latches too. @return DB_SUCCESS or err code */ ib_err_t ib_trx_commit( /*==========*/ ib_trx_t ib_trx) /*!< in: trx handle */ { ib_err_t err; trx_t* trx = (trx_t*) ib_trx; IB_CHECK_PANIC(); UT_DBG_ENTER_FUNC; err = trx_commit(trx); /* It should always succeed */ ut_a(err == DB_SUCCESS); ib_schema_unlock(ib_trx); err = ib_trx_release(ib_trx); ut_a(err == DB_SUCCESS); ib_wake_master_thread(); return(DB_SUCCESS); } /*****************************************************************//** Rollback a transaction. This function will also release the schema latches too. @return DB_SUCCESS or err code */ ib_err_t ib_trx_rollback( /*============*/ ib_trx_t ib_trx) /*!< in: trx handle */ { ib_err_t err; trx_t* trx = (trx_t*) ib_trx; IB_CHECK_PANIC(); UT_DBG_ENTER_FUNC; err = trx_general_rollback(trx, FALSE, NULL); /* It should always succeed */ ut_a(err == DB_SUCCESS); ib_schema_unlock(ib_trx); err = ib_trx_release(ib_trx); ut_a(err == DB_SUCCESS); ib_wake_master_thread(); return(err); } /*****************************************************************//** Check that the combination of values and name makes sense. @return TRUE if OK */ UNIV_INLINE ibool ib_check_col_is_ok( /*===============*/ const char* name, /*!< in: column name */ ib_col_type_t ib_col_type, /*!< in: column type */ ib_col_attr_t ib_col_attr, /*!< in: column attribute */ ib_ulint_t len) /*!< in: length of the column */ { UT_DBG_ENTER_FUNC; if (ut_strlen(name) > IB_MAX_COL_NAME_LEN) { return(FALSE); } else if ((ib_col_type == IB_VARCHAR || ib_col_type == IB_CHAR || ib_col_type == IB_BINARY) && len == 0) { return(FALSE); } else if (ib_col_type == IB_INT) { switch (len) { case 1: case 2: case 4: case 8: break; default: return(FALSE); } } else if (ib_col_type == IB_FLOAT && len != 4) { return(FALSE); } else if (ib_col_type == IB_DOUBLE && len != 8) { return(FALSE); } return(TRUE); } /*****************************************************************//** Find an index definition from the index vector using index name. @return index def. if found else NULL */ UNIV_INLINE const ib_index_def_t* ib_table_find_index( /*================*/ ib_vector_t* indexes, /*!< in: vector of indexes */ const char* name) /*!< in: index name */ { ulint i; UT_DBG_ENTER_FUNC; for (i = 0; i < ib_vector_size(indexes); ++i) { const ib_index_def_t* index_def; index_def = (ib_index_def_t*) ib_vector_get(indexes, i); if (ib_utf8_strcasecmp(name, index_def->name) == 0) { return(index_def); } } return(NULL); } /*****************************************************************//** Get the InnoDB internal precise type from the schema column definition. @return precise type in api format */ UNIV_INLINE ulint ib_col_get_prtype( /*==============*/ const ib_col_t* ib_col) /*!< in: column definition */ { ulint prtype = 0; UT_DBG_ENTER_FUNC; if (ib_col->ib_col_attr & IB_COL_UNSIGNED) { prtype |= DATA_UNSIGNED; ut_a(ib_col->ib_col_type == IB_INT); } if (ib_col->ib_col_attr & IB_COL_NOT_NULL) { prtype |= DATA_NOT_NULL; } if (ib_col->ib_col_attr & IB_COL_CUSTOM1) { prtype |= DATA_CUSTOM_TYPE; } if (ib_col->ib_col_attr & IB_COL_CUSTOM2) { prtype |= (DATA_CUSTOM_TYPE << 1); } if (ib_col->ib_col_attr & IB_COL_CUSTOM3) { prtype |= (DATA_CUSTOM_TYPE << 2); } return(prtype); } /*****************************************************************//** Get the InnoDB internal main type from the schema column definition. @return column main type */ UNIV_INLINE ulint ib_col_get_mtype( /*==============*/ const ib_col_t* ib_col) /*!< in: column definition */ { UT_DBG_ENTER_FUNC; /* Note: The api0api.h types should map directly to the internal numeric codes. */ return(ib_col->ib_col_type); } /*****************************************************************//** Find a column in the the column vector with the same name. @return col. def. if found else NULL */ UNIV_INLINE const ib_col_t* ib_table_find_col( /*==============*/ const ib_vector_t* cols, /*!< in: column list head */ const char* name) /*!< in: column name to find */ { ulint i; UT_DBG_ENTER_FUNC; for (i = 0; i < ib_vector_size(cols); ++i) { const ib_col_t* ib_col; ib_col = ib_vector_get((ib_vector_t*) cols, i); if (ib_utf8_strcasecmp(ib_col->name, name) == 0) { return(ib_col); } } return(NULL); } /*****************************************************************//** Find a column in the the column list with the same name. @return col. def. if found else NULL */ UNIV_INLINE const ib_key_col_t* ib_index_find_col( /*==============*/ ib_vector_t* cols, /*!< in: column list head */ const char* name) /*!< in: column name to find */ { ulint i; UT_DBG_ENTER_FUNC; for (i = 0; i < ib_vector_size(cols); ++i) { const ib_key_col_t* ib_col; ib_col = ib_vector_get(cols, i); if (ib_utf8_strcasecmp(ib_col->name, name) == 0) { return(ib_col); } } return(NULL); } /*****************************************************************//** Add columns to a table schema. @return DB_SUCCESS or err code */ ib_err_t ib_table_schema_add_col( /*====================*/ ib_tbl_sch_t ib_tbl_sch, /*!< in: schema instance */ const char* name, /*!< in: name of column */ ib_col_type_t ib_col_type, /*!< in: column main type */ ib_col_attr_t ib_col_attr, /*!< in: column attributes */ ib_u16_t client_type, /*!< in: any 16 bit number relevant only to the client */ ib_ulint_t len) /*!< in: max length of column */ { ib_col_t* ib_col; ib_err_t err = DB_SUCCESS; ib_table_def_t* table_def = (ib_table_def_t*) ib_tbl_sch; IB_CHECK_PANIC(); UT_DBG_ENTER_FUNC; if (table_def->table != NULL) { err = DB_ERROR; } else if (ib_table_find_col(table_def->cols, name) != NULL) { err = DB_DUPLICATE_KEY; } else if (!ib_check_col_is_ok(name, ib_col_type, ib_col_attr, len)) { err = DB_ERROR; } else { mem_heap_t* heap; heap = table_def->heap; ib_col = (ib_col_t*) mem_heap_zalloc(heap, sizeof(*ib_col)); if (ib_col == NULL) { err = DB_OUT_OF_MEMORY; } else { ib_col->name = mem_heap_strdup(heap, name); ib_col->ib_col_type = ib_col_type; ib_col->ib_col_attr = ib_col_attr; ib_col->len = len; ib_vector_push(table_def->cols, ib_col); } } return(err); } /*****************************************************************//** Create and add an index key definition to a table schema. The index schema is owned by the table schema instance and will be freed when the table schema instance is freed. @return DB_SUCCESS or err code */ ib_err_t ib_table_schema_add_index( /*======================*/ ib_tbl_sch_t ib_tbl_sch, /*!< in/out: schema instance */ const char* name, /*!< in: key defn. name to create */ ib_idx_sch_t* ib_idx_sch) /*!< out: key definition instance */ { ib_err_t err = DB_SUCCESS; ib_table_def_t* table_def = (ib_table_def_t*) ib_tbl_sch; IB_CHECK_PANIC(); UT_DBG_ENTER_FUNC; if (table_def->table != NULL) { err = DB_ERROR; } else if (ib_utf8_strcasecmp(name, GEN_CLUST_INDEX) == 0) { return(DB_INVALID_INPUT); } else if (name[0] == TEMP_INDEX_PREFIX) { return(DB_INVALID_INPUT); } if (ib_table_find_index(table_def->indexes, name) != NULL) { err = DB_DUPLICATE_KEY; } else { ib_index_def_t* index_def; mem_heap_t* heap = table_def->heap; index_def = (ib_index_def_t*) mem_heap_zalloc( heap, sizeof(*index_def)); if (index_def == NULL) { err = DB_OUT_OF_MEMORY; } else { index_def->heap = heap; index_def->schema = table_def; index_def->name = mem_heap_strdup(heap, name); index_def->cols = ib_vector_create(heap, 8); ib_vector_push(table_def->indexes, index_def); *ib_idx_sch = (ib_idx_sch_t) index_def; } } return(err); } /*****************************************************************//** Destroy a schema. */ void ib_table_schema_delete( /*===================*/ ib_tbl_sch_t ib_tbl_sch) /*!< in, own: table schema to delete */ { ulint i; ib_table_def_t* table_def = (ib_table_def_t*) ib_tbl_sch; UT_DBG_ENTER_FUNC; /* Check that all indexes are owned by the table schema. */ for (i = 0; i < ib_vector_size(table_def->indexes); ++i) { ib_index_def_t* index_def; index_def = (ib_index_def_t*) ib_vector_get( table_def->indexes, i); ut_a(index_def->schema != NULL); } if (table_def->table != NULL) { dict_table_decrement_handle_count(table_def->table, FALSE); } mem_heap_free(table_def->heap); } /*****************************************************************//** Do some table page size validation. It should be set only when ib_tbl_fmt == IB_TBL_COMPRESSED. */ static ib_err_t ib_table_schema_check( /*==================*/ ib_tbl_fmt_t ib_tbl_fmt, /*!< in: table format */ ib_ulint_t* page_size) /*!< in,out: page size requested */ { ib_err_t err = DB_SUCCESS; IB_CHECK_PANIC(); #ifndef WITH_ZIP if (ib_tbl_fmt == IB_TBL_COMPRESSED) { return(DB_UNSUPPORTED); } #endif /* WITH_ZIP */ if (ib_tbl_fmt != IB_TBL_COMPRESSED) { /* Page size set but table format is not set to compressed. Reset to 0 since we ignore such values. */ *page_size = 0; } switch (*page_size) { case 0: /* The page size value will be ignored for uncompressed tables. */ if (ib_tbl_fmt == IB_TBL_COMPRESSED) { /* Set to the system default of 8K page size. Better to be conservative here. */ *page_size = 8; /* Fall through. */ } else { break; } case 1: case 2: case 4: case 8: case 16: if (!srv_file_per_table) { /* Compressed tables require file per table. */ err = DB_UNSUPPORTED; } else if (srv_file_format < DICT_TF_FORMAT_ZIP) { /* File format unsuitable for compressed tables. */ err = DB_UNSUPPORTED; } break; default: /* Unknown page size. */ err = DB_UNSUPPORTED; break; } return(err); } #ifdef __WIN__ /*****************************************************************//** Convert a string to lower case. */ static void ib_to_lower_case( /*=============*/ char* ptr) /*!< string to convert to lower case */ { while (*ptr) { *ptr = tolower(*ptr); ++ptr; } } #endif /* __WIN__ */ /*****************************************************************//** Normalizes a table name string. A normalized name consists of the database name catenated to '/' and table name. An example: test/mytable. On Windows normalization puts both the database name and the table name always to lower case. This function can be called for system tables and they don't have a database component. For tables that don't have a database component, we don't normalize them to lower case on Windows. The assumption is that they are system tables that reside in the system table space. */ static void ib_normalize_table_name( /*====================*/ char* norm_name, /*!< out: normalized name as a null-terminated string */ const char* name) /*!< in: table name string */ { const char* ptr = name; /* Scan name from the end */ ptr += ut_strlen(name) - 1; /* Find the start of the table name. */ while (ptr >= name && *ptr != '\\' && *ptr != '/' && ptr > name) { --ptr; } /* For system tables there is no '/' or dbname. */ ut_a(ptr >= name); if (ptr > name) { const char* db_name; const char* table_name; table_name = ptr + 1; --ptr; while (ptr >= name && *ptr != '\\' && *ptr != '/') { ptr--; } db_name = ptr + 1; memcpy(norm_name, db_name, ut_strlen(name) + 1 - (db_name - name)); norm_name[table_name - db_name - 1] = '/'; #ifdef __WIN__ ib_to_lower_case(norm_name); #endif } else { ut_strcpy(norm_name, name); } } /*****************************************************************//** Check whether the table name conforms to our requirements. Currently we only do a simple check for the presence of a '/'. @return DB_SUCCESS or err code */ static ib_err_t ib_table_name_check( /*================*/ const char* name) /*!< in: table name to check */ { const char* slash = NULL; ulint len = ut_strlen(name); if (len < 2 || *name == '/' || name[len - 1] == '/' || (name[0] == '.' && name[1] == '/') || (name[0] == '.' && name[1] == '.' && name[2] == '/')) { return(DB_DATA_MISMATCH); } for ( ; *name; ++name) { #ifdef __WIN__ /* Check for reserved characters in DOS filenames. */ switch (*name) { case ':': case '|': case '"': case '*': case '<': case '>': return(DB_DATA_MISMATCH); } #endif /* __WIN__ */ if (*name == '/') { if (slash) { return(DB_DATA_MISMATCH); } slash = name; } } return(slash ? DB_SUCCESS : DB_DATA_MISMATCH); } /*****************************************************************//** Create a table schema. @return DB_SUCCESS or err code */ ib_err_t ib_table_schema_create( /*===================*/ const char* name, /*!< in: table name to create */ ib_tbl_sch_t* ib_tbl_sch, /*!< out: schema instance */ ib_tbl_fmt_t ib_tbl_fmt, /*!< in: table format */ ib_ulint_t page_size) /*!< in: Page size or 0 for default */ { ib_err_t err = DB_SUCCESS; mem_heap_t* heap = mem_heap_create(1024); IB_CHECK_PANIC(); UT_DBG_ENTER_FUNC; err = ib_table_name_check(name); if (err != DB_SUCCESS) { return(err); } err = ib_table_schema_check(ib_tbl_fmt, &page_size); if (err != DB_SUCCESS) { return(err); } else if (heap == NULL) { err = DB_OUT_OF_MEMORY; } else { ib_table_def_t* table_def; table_def = (ib_table_def_t*) mem_heap_zalloc( heap, sizeof(*table_def)); if (table_def == NULL) { err = DB_OUT_OF_MEMORY; mem_heap_free(heap); } else { char* normalized_name; table_def->heap = heap; normalized_name = mem_heap_strdup(heap, name); ib_normalize_table_name(normalized_name, name); table_def->name = normalized_name; table_def->page_size = page_size; table_def->ib_tbl_fmt = ib_tbl_fmt; table_def->cols = ib_vector_create(heap, 8); table_def->indexes = ib_vector_create(heap, 4); *ib_tbl_sch = (ib_tbl_sch_t) table_def; } } return(err); } /*****************************************************************//** Get the column number within the index defnintion. @return -1 or column number */ static int ib_index_get_col_no( /*================*/ const ib_index_def_t* ib_index_def, /*!< in: index definition */ const char* name) /*!< in: column name to search */ { int col_no; UT_DBG_ENTER_FUNC; /* Is this column definition for an existing table ? */ if (ib_index_def->table != NULL) { col_no = dict_table_get_col_no(ib_index_def->table, name); } else { ib_vector_t* cols; const ib_col_t* ib_col; cols = ib_index_def->schema->cols; ib_col = ib_table_find_col(cols, name); if (ib_col != NULL) { /* We simply note that we've found the column. */ col_no = 0; } else { col_no = -1; } } return(col_no); } /*****************************************************************//** Check whether a prefix length index is allowed on the column. @return TRUE if allowed. */ static int ib_index_is_prefix_allowed( /*=======================*/ const ib_index_def_t* ib_index_def, /*!< in: index definition */ const char* name) /*!< in: column name to check */ { ib_bool_t allowed = TRUE; ulint mtype = ULINT_UNDEFINED; UT_DBG_ENTER_FUNC; /* Is this column definition for an existing table ? */ if (ib_index_def->table != NULL) { const dict_col_t* col; int col_no; col_no = dict_table_get_col_no(ib_index_def->table, name); ut_a(col_no != -1); col = dict_table_get_nth_col(ib_index_def->table, col_no); ut_a(col != NULL); mtype = col->mtype; } else { const ib_vector_t* cols; const ib_col_t* ib_col; cols = ib_index_def->schema->cols; ib_col = ib_table_find_col(cols, name); ut_a(ib_col != NULL); mtype = (ulint) ib_col->ib_col_type; } /* The following column types can't have prefix column indexes. */ switch (mtype) { case DATA_INT: case DATA_FLOAT: case DATA_DOUBLE: case DATA_DECIMAL: allowed = FALSE; break; case ULINT_UNDEFINED: ut_error; } return(allowed); } /*****************************************************************//** Add columns to a schema definition. @return DB_SUCCESS or err code */ ib_err_t ib_index_schema_add_col( /*====================*/ ib_idx_sch_t ib_idx_sch, /*!< in/out: index schema instance */ const char* name, /*!< in: name of column */ ib_ulint_t prefix_len) /*!< in: length of prefix or 0 */ { ib_err_t err = DB_SUCCESS; ib_index_def_t* index_def = (ib_index_def_t*) ib_idx_sch; IB_CHECK_PANIC(); UT_DBG_ENTER_FUNC; /* Check for duplicates. */ if (ib_index_find_col(index_def->cols, name) != NULL) { err = DB_COL_APPEARS_TWICE_IN_INDEX; /* Check if the column exists in the table definition. */ } else if (ib_index_get_col_no(index_def, name) == -1) { err = DB_NOT_FOUND; /* Some column types can't have prefix length indexes. */ } else if (prefix_len > 0 && !ib_index_is_prefix_allowed(index_def, name)) { err = DB_SCHEMA_ERROR; } else { mem_heap_t* heap; ib_key_col_t* ib_col; heap = index_def->heap; ib_col = (ib_key_col_t*) mem_heap_zalloc(heap, sizeof(*ib_col)); if (ib_col == NULL) { err = DB_OUT_OF_MEMORY; } else { ib_col->name = mem_heap_strdup(heap, name); ib_col->prefix_len = prefix_len; ib_vector_push(index_def->cols, ib_col); } } return(err); } /*****************************************************************//** Create an index schema instance. @return DB_SUCCESS or err code */ ib_err_t ib_index_schema_create( /*===================*/ ib_trx_t ib_usr_trx, /*!< in: transaction */ const char* name, /*!< in: index name in schema */ const char* table_name, /*!< in: table name */ ib_idx_sch_t* ib_idx_sch) /*!< out: index schema instance */ { dict_table_t* table = NULL; char* normalized_name; ib_err_t err = DB_SUCCESS; IB_CHECK_PANIC(); UT_DBG_ENTER_FUNC; if (!ib_schema_lock_is_exclusive(ib_usr_trx)) { return(DB_SCHEMA_NOT_LOCKED); } else if (name[0] == TEMP_INDEX_PREFIX) { return(DB_INVALID_INPUT); } else if (ib_utf8_strcasecmp(name, GEN_CLUST_INDEX) == 0) { return(DB_INVALID_INPUT); } normalized_name = mem_alloc(ut_strlen(table_name) + 1); ib_normalize_table_name(normalized_name, table_name); table = ib_lookup_table_by_name(normalized_name); mem_free(normalized_name); normalized_name = NULL; if (table == NULL) { err = DB_TABLE_NOT_FOUND; } else if (dict_table_get_index_on_name(table, name) != NULL) { err = DB_DUPLICATE_KEY; } else { mem_heap_t* heap = mem_heap_create(1024); if (heap == NULL) { err = DB_OUT_OF_MEMORY; } else { ib_index_def_t* index_def; index_def = (ib_index_def_t*) mem_heap_zalloc( heap, sizeof(*index_def)); if (index_def == NULL) { err = DB_OUT_OF_MEMORY; mem_heap_free(heap); } else { index_def->heap = heap; index_def->table = table; index_def->name = mem_heap_strdup(heap, name); index_def->cols = ib_vector_create(heap, 8); index_def->usr_trx = (trx_t*) ib_usr_trx; *ib_idx_sch = (ib_idx_sch_t) index_def; } } } return(err); } /*****************************************************************//** Get an index definition that is tagged as a clustered index. @return cluster index schema */ UNIV_INLINE ib_index_def_t* ib_find_clustered_index( /*====================*/ ib_vector_t* indexes) /*!< in: index defs. to search */ { ulint i; ulint n_indexes; UT_DBG_ENTER_FUNC; n_indexes = ib_vector_size(indexes); for (i = 0; i < n_indexes; ++i) { ib_index_def_t* ib_index_def; ib_index_def = ib_vector_get(indexes, i); if (ib_index_def->clustered) { return(ib_index_def); } } return(NULL); } /*****************************************************************//** Set index as clustered index. Implies UNIQUE. @return DB_SUCCESS or err code */ ib_err_t ib_index_schema_set_clustered( /*==========================*/ ib_idx_sch_t ib_idx_sch) /*!< in/out: index definition */ { ib_index_def_t* index_def; ib_err_t err = DB_SUCCESS; IB_CHECK_PANIC(); UT_DBG_ENTER_FUNC; index_def = (ib_index_def_t*) ib_idx_sch; /* If this index schema is part of a table schema then we need to check the state of the other indexes. */ if (index_def->schema != NULL) { ib_index_def_t* ib_clust_index_def; ib_clust_index_def = ib_find_clustered_index( index_def->schema->indexes); if (ib_clust_index_def != NULL) { ut_a(ib_clust_index_def->clustered); ib_clust_index_def->clustered = FALSE; } } index_def->unique = TRUE; index_def->clustered = TRUE; return(err); } /*****************************************************************//** Set index as a unique index. @return DB_SUCCESS or err code */ ib_err_t ib_index_schema_set_unique( /*=======================*/ ib_idx_sch_t ib_idx_sch) /*!< in/out: index definition */ { ib_index_def_t* index_def; ib_err_t err = DB_SUCCESS; IB_CHECK_PANIC(); UT_DBG_ENTER_FUNC; index_def = (ib_index_def_t*) ib_idx_sch; index_def->unique = TRUE; return(err); } /*****************************************************************//** Destroy an index schema. */ void ib_index_schema_delete( /*===================*/ ib_idx_sch_t ib_idx_sch) /*!< in, own: index schema to delete */ { ib_index_def_t* index_def = (ib_index_def_t*) ib_idx_sch; UT_DBG_ENTER_FUNC; ut_a(index_def->schema == NULL); mem_heap_free(index_def->heap); } /*****************************************************************//** Convert the table definition table attributes to the internal format. @return flags */ static ulint ib_table_def_get_flags( /*===================*/ const ib_table_def_t* table_def) /*!< in: table definition */ { ulint flags = 0; UT_DBG_ENTER_FUNC; switch(table_def->ib_tbl_fmt) { case IB_TBL_REDUNDANT: /* Old row format */ break; case IB_TBL_COMPACT: /* The compact row format */ flags = DICT_TF_COMPACT; break; case IB_TBL_DYNAMIC: /* The dynamic row format */ /* Dynamic format implies a page size of 0. */ flags = DICT_TF_COMPACT | DICT_TF_FORMAT_ZIP << DICT_TF_FORMAT_SHIFT; break; case IB_TBL_COMPRESSED: { /* Compact row format and compressed page */ ulint i; ulint j; for (i = j = 1; i <= DICT_TF_ZSSIZE_MAX; ++i, j <<= 1) { if (j == table_def->page_size) { flags = i << DICT_TF_ZSSIZE_SHIFT | DICT_TF_COMPACT | DICT_TF_FORMAT_ZIP << DICT_TF_FORMAT_SHIFT; break; } } ut_a(flags != 0); ut_a(i <= DICT_TF_ZSSIZE_MAX); break; } default: ut_error; } return(flags); } /*****************************************************************//** Copy the index definition to row0merge.c format. @return converted index definition */ static const index_def_t* ib_copy_index_definition( /*=====================*/ ib_index_def_t* ib_index_def, /*!< in: key definition for index */ ibool clustered) /*!< in: IB_TRUE if clustered index */ { ulint i; ib_key_col_t* ib_col; ulint name_len; index_def_t* index_def; char* index_name; UT_DBG_ENTER_FUNC; index_def = (index_def_t*) mem_heap_zalloc( ib_index_def->heap, sizeof(*index_def)); name_len = ut_strlen(ib_index_def->name); index_name = mem_heap_zalloc(ib_index_def->heap, name_len + 2); /* The TEMP_INDEX_PREFIX is only needed if we are rebuilding an index or creating a new index on a table that has records. If the definition is owned by a table schema then we can be sure that this index definition is part of a CREATE TABLE. */ if (ib_index_def->schema == NULL) { *index_name = TEMP_INDEX_PREFIX; memcpy(index_name + 1, ib_index_def->name, name_len + 1); } else { memcpy(index_name, ib_index_def->name, name_len); } index_def->name = index_name; index_def->n_fields = ib_vector_size(ib_index_def->cols); if (ib_index_def->unique) { index_def->ind_type = DICT_UNIQUE; } else { index_def->ind_type = 0; } if (clustered) { index_def->ind_type |= DICT_CLUSTERED; } index_def->fields = (index_field_t*) mem_heap_zalloc( ib_index_def->heap, sizeof(index_field_t) * index_def->n_fields); for (i = 0; i < ib_vector_size(ib_index_def->cols); ++i) { ib_col = ib_vector_get(ib_index_def->cols, i); index_def->fields[i].field_name = ib_col->name; index_def->fields[i].prefix_len = ib_col->prefix_len; } return(index_def); } /*****************************************************************//** (Re)Create a secondary index. @return DB_SUCCESS or err code */ static ib_err_t ib_build_secondary_index( /*=====================*/ trx_t* usr_trx, /*!< in: transaction */ dict_table_t* table, /*!< in: parent table of index */ ib_index_def_t* ib_index_def, /*!< in: index definition */ ib_bool_t create, /*!< in: TRUE if part of table create */ dict_index_t** dict_index) /*!< out: index created */ { ib_err_t err; trx_t* ddl_trx; const index_def_t* index_def; IB_CHECK_PANIC(); UT_DBG_ENTER_FUNC; ut_a(usr_trx->conc_state != TRX_NOT_STARTED); if (!create) { ib_bool_t started; ddl_trx = trx_allocate_for_client(NULL); started = trx_start(ddl_trx, ULINT_UNDEFINED); ut_a(started); } else { ddl_trx = usr_trx; } /* Set the CLUSTERED flag to FALSE. */ index_def = ib_copy_index_definition(ib_index_def, FALSE); ut_a(!(index_def->ind_type & DICT_CLUSTERED)); ddl_trx->op_info = "creating secondary index"; if (!(create || ib_schema_lock_is_exclusive((ib_trx_t) usr_trx))) { err = ib_schema_lock_exclusive((ib_trx_t) usr_trx); if (err != DB_SUCCESS) { return(err); } } if (!create) { /* Flag this transaction as a dictionary operation, so that the data dictionary will be locked in crash recovery. */ trx_set_dict_operation(ddl_trx, TRX_DICT_OP_INDEX); } *dict_index = row_merge_create_index(ddl_trx, table, index_def); if (!create) { /* Even if the user locked the schema, we release it here and build the index without holding the dictionary lock. */ ib_schema_unlock((ib_trx_t) usr_trx); } err = ddl_trx->error_state; if (!create) { /* Commit the data dictionary transaction in order to release the table locks on the system tables. */ trx_commit(ddl_trx); trx_free_for_client(ddl_trx); } ut_a(usr_trx->conc_state != TRX_NOT_STARTED); if (*dict_index != NULL) { ut_a(err == DB_SUCCESS); (*dict_index)->cmp_ctx = NULL; /* Read the clustered index records and build the index. */ err = row_merge_build_indexes( usr_trx, table, table, dict_index, 1, NULL); } return(err); } /*******************************************************************//** Create a temporary tablename using table name and id @return temporary tablename */ static char* ib_table_create_temp_name( /*======================*/ mem_heap_t* heap, /*!< in: memory heap */ char id, /*!< in: identifier [0-9a-zA-Z] */ const char* table_name) /*!< in: table name */ { ulint len; char* name; static const char suffix[] = "# "; len = ut_strlen(table_name); name = (char*) mem_heap_zalloc(heap, len + sizeof(suffix)); ut_memcpy(name, table_name, len); ut_memcpy(name + len, suffix, sizeof(suffix)); name[len + (sizeof(suffix) - 2)] = id; return(name); } /*******************************************************************//** Create an index definition from the index. */ static void ib_index_create_def( /*================*/ const dict_index_t* dict_index, /*!< in: index definition to copy */ index_def_t* index_def,/*!< out: index definition */ mem_heap_t* heap) /*!< in: heap where allocated */ { ulint i; const dict_field_t* dfield; ulint n_fields; n_fields = dict_index->n_user_defined_cols; index_def->fields = (merge_index_field_t*) mem_heap_zalloc( heap, n_fields * sizeof(*index_def->fields)); index_def->name = dict_index->name; index_def->n_fields = n_fields; index_def->ind_type = dict_index->type & ~DICT_CLUSTERED; for (i = 0, dfield = dict_index->fields; i < n_fields; ++i, ++dfield) { merge_index_field_t* def_field; def_field = &index_def->fields[i]; def_field->field_name = dfield->name; def_field->prefix_len = dfield->prefix_len; } } /*******************************************************************//** Create and return an array of index definitions on a table. Skip the old clustered index if it's a generated clustered index. If there is a user defined clustered index on the table its CLUSTERED flag will be unset. @return index definitions or NULL */ static index_def_t* ib_table_create_index_defs( /*=======================*/ trx_t* trx, /*!< in: transaction */ const dict_table_t* table, /*!< in: table definition */ mem_heap_t* heap, /*!< in: heap where space for index definitions are allocated */ ulint* n_indexes) /*!< out: number of indexes retuned */ { ulint sz; ib_err_t err; const dict_index_t* dict_index; index_def_t* index_defs; UT_DBG_ENTER_FUNC; sz = sizeof(*index_defs) * UT_LIST_GET_LEN(table->indexes); index_defs = (index_def_t*) mem_heap_zalloc(heap, sz); err = ib_schema_lock_exclusive((ib_trx_t) trx); ut_a(err == DB_SUCCESS); dict_index = dict_table_get_first_index(table); /* Skip a generated cluster index. */ if (ib_utf8_strcasecmp(dict_index->name, GEN_CLUST_INDEX) == 0) { ut_a(dict_index_get_nth_col(dict_index, 0)->mtype == DATA_SYS); dict_index = dict_table_get_next_index(dict_index); } while (dict_index) { ib_index_create_def(dict_index, index_defs++, heap); dict_index = dict_table_get_next_index(dict_index); } ib_schema_unlock((ib_trx_t) trx); return(index_defs); } /*****************************************************************//** Create a cluster index specified by the user . The cluster index shouldn't already exist. @return DB_SUCCESS or err code */ static ib_err_t ib_create_cluster_index( /*====================*/ trx_t* trx, /*!< in: transaction */ dict_table_t* table, /*!< in: parent table of index */ ib_index_def_t* ib_index_def, /*!< in: index definition */ dict_index_t** dict_index) /*!< out: index created */ { ib_err_t err; const index_def_t* index_def; IB_CHECK_PANIC(); UT_DBG_ENTER_FUNC; ut_a(!ib_vector_is_empty(ib_index_def->cols)); /* Set the CLUSTERED flag to TRUE. */ index_def = ib_copy_index_definition(ib_index_def, TRUE); trx->op_info = "creating clustered index"; trx_set_dict_operation(trx, TRX_DICT_OP_TABLE); err = ib_trx_lock_table_with_retry(trx, table, LOCK_X); if (err == DB_SUCCESS) { *dict_index = row_merge_create_index(trx, table, index_def); err = trx->error_state; } trx->op_info = ""; return(err); } /*****************************************************************//** Create the secondary indexes on new table using the index definitions from the src table. The assumption is that a cluster index on the new table already exists. All the indexes in the source table will be copied with the exception of any generated clustered indexes. */ static ib_err_t ib_table_clone_indexes( /*===================*/ trx_t* trx, /*!< in: transaction */ dict_table_t* src_table, /*!< in: table to clone from */ dict_table_t* new_table, /*!< in: table to clone to */ mem_heap_t* heap) /*!< in: memory heap to use */ { ulint i; index_def_t* index_defs; ulint n_index_defs = 0; index_defs = ib_table_create_index_defs( trx, src_table, heap, &n_index_defs); ut_a(index_defs != NULL); for (i = 0; i < n_index_defs; ++i) { dict_index_t* dict_index; ut_a(!(index_defs[i].ind_type & DICT_CLUSTERED)); dict_index = row_merge_create_index(trx, new_table, &index_defs[i]); if (dict_index == NULL) { return(trx->error_state); } } return(DB_SUCCESS); } /*****************************************************************//** Clone the indexes definitions from src_table to dst_table. The cluster index is not cloned. If it was generated then it's dropped else it's demoted to a secondary index. A new cluster index is created for the new table. */ static ib_err_t ib_table_clone( /*===========*/ trx_t* trx, /*!< in: transaction */ dict_table_t* src_table, /*!< in: table to clone from */ dict_table_t** new_table, /*!< out: cloned table */ ib_index_def_t* ib_index_def, /*!< in: new cluster index definition */ mem_heap_t* heap) /*!< in: memory heap to use */ { ib_err_t err; const index_def_t* index_def; char* new_table_name; new_table_name = ib_table_create_temp_name(heap, '1', src_table->name); err = ib_schema_lock_exclusive((ib_trx_t) trx); if (err != DB_SUCCESS) { return(err); } /* Set the CLUSTERED flag to TRUE. */ index_def = ib_copy_index_definition(ib_index_def, TRUE); /* Create the new table and the cluster index. */ *new_table = row_merge_create_temporary_table( new_table_name, index_def, src_table, trx); if (!new_table) { err = trx->error_state; } else { trx->table_id = (*new_table)->id; err = ib_table_clone_indexes(trx, src_table, *new_table, heap); } ib_schema_unlock((ib_trx_t) trx); return(err); } /*****************************************************************//** Copy the data from the source table to dst table. */ static ib_err_t ib_table_copy( /*==========*/ trx_t* trx, /*!< in: transaction */ dict_table_t* src_table, /*!< in: table to copy from */ dict_table_t* dst_table, /*!< in: table to copy to */ mem_heap_t* heap) /*!< in: heap to use */ { ib_err_t err; dict_index_t* dict_index; dict_index_t** indexes; ulint n_indexes; err = ib_schema_lock_exclusive((ib_trx_t) trx); if (err != DB_SUCCESS) { return(err); } n_indexes = UT_LIST_GET_LEN(dst_table->indexes); indexes = (dict_index_t**) mem_heap_zalloc( heap, n_indexes * sizeof(dict_index)); n_indexes = 0; dict_index = dict_table_get_first_index(dst_table); /* Copy the indexes to an array. */ while (dict_index) { indexes[n_indexes++] = dict_index; dict_index = dict_table_get_next_index(dict_index); } ut_a(n_indexes == UT_LIST_GET_LEN(dst_table->indexes)); ib_schema_unlock((ib_trx_t) trx); /* Build the actual indexes. */ err = row_merge_build_indexes( trx, src_table, dst_table, indexes, n_indexes, NULL); return(err); } /*****************************************************************//** Create a default cluster index, this usually means the user didn't create a table with a primary key. @return DB_SUCCESS or err code */ static ib_err_t ib_create_default_cluster_index( /*============================*/ trx_t* trx, /*!< in: transaction */ dict_table_t* table, /*!< in: parent table of index */ dict_index_t** dict_index) /*!< out: index created */ { ib_err_t err; index_def_t index_def; UT_DBG_ENTER_FUNC; index_def.name = GEN_CLUST_INDEX; index_def.ind_type = DICT_CLUSTERED; index_def.n_fields = 0; index_def.fields = NULL; trx->op_info = "creating default clustered index"; trx_set_dict_operation(trx, TRX_DICT_OP_TABLE); ut_a(ib_schema_lock_is_exclusive((ib_trx_t) trx)); err = ib_trx_lock_table_with_retry(trx, table, LOCK_X); if (err == DB_SUCCESS) { *dict_index = row_merge_create_index(trx, table, &index_def); err = trx->error_state; } trx->op_info = ""; return(err); } /*****************************************************************//** Create the indexes for the table. Each index is created in a separate transaction. The caller is responsible for dropping any indexes that exist if there is a failure. @return DB_SUCCESS or err code */ static ib_err_t ib_create_indexes( /*==============*/ trx_t* ddl_trx, /*!< in: transaction */ dict_table_t* table, /*!< in: table where index created */ ib_vector_t* indexes) /*!< in: index defs. to create */ { ulint i = 0; ulint n_indexes; ib_index_def_t* ib_index_def; dict_index_t* dict_index = NULL; ib_err_t err = DB_ERROR; ib_index_def_t* ib_clust_index_def = NULL; UT_DBG_ENTER_FUNC; n_indexes = ib_vector_size(indexes); if (n_indexes > 0) { ib_clust_index_def = ib_find_clustered_index(indexes); if (ib_clust_index_def != NULL) { ut_a(ib_clust_index_def->clustered); err = ib_create_cluster_index( ddl_trx, table, ib_clust_index_def, &dict_index); } } if (ib_clust_index_def == NULL) { err = ib_create_default_cluster_index(ddl_trx, table, &dict_index); } for (i= 0; err == DB_SUCCESS && i < n_indexes; ++i) { ib_index_def = ib_vector_get(indexes, i); ut_a(!ib_vector_is_empty(ib_index_def->cols)); if (!ib_index_def->clustered) { /* Since this is part of CREATE TABLE, set the create flag to IB_TRUE. */ err = ib_build_secondary_index( ddl_trx, table, ib_index_def, IB_TRUE, &dict_index); } else { /* There can be at most one cluster definition. */ ut_a(ib_clust_index_def == ib_index_def); } } return(err); } /*****************************************************************//** Get a table id. The caller must have acquired the dictionary mutex. @return DB_SUCCESS if found */ static ib_err_t ib_table_get_id_low( /*================*/ const char* table_name, /*!< in: table to find */ ib_id_t* table_id) /*!< out: table id if found */ { dict_table_t* table; ib_err_t err = DB_TABLE_NOT_FOUND; UT_DBG_ENTER_FUNC; *table_id = 0; table = ib_lookup_table_by_name(table_name); if (table != NULL) { *table_id = ut_conv_dulint_to_longlong(table->id); err = DB_SUCCESS; } return(err); } /*****************************************************************//** Create a table. If the table exists in the database then this function will return DB_TABLE_IS_BEING_USED and id will contain that tables id. @return DB_SUCCESS or err code */ ib_err_t ib_table_create( /*============*/ ib_trx_t ib_trx, /*!< in/out: transaction */ const ib_tbl_sch_t ib_tbl_sch, /*!< in: table schema */ ib_id_t* id) /*!< out: table id */ { ulint i; ib_err_t err; mem_heap_t* heap; dict_table_t* table; ulint n_cols = 0; ulint n_cluster = 0; ib_col_t* ib_col = NULL; trx_t* ddl_trx = (trx_t*) ib_trx; const ib_table_def_t* table_def = (const ib_table_def_t*) ib_tbl_sch; IB_CHECK_PANIC(); UT_DBG_ENTER_FUNC; /* Another thread may have created the table already when we get here. We need to search the data dictionary before we attempt to create the table. */ if (!ib_schema_lock_is_exclusive((ib_trx_t)ddl_trx)) { return(DB_SCHEMA_NOT_LOCKED); } err = ib_table_get_id_low(table_def->name, id); if (err == DB_SUCCESS) { return(DB_TABLE_IS_BEING_USED); } *id = 0; n_cols = ib_vector_size(table_def->cols); if (n_cols == 0) { return(DB_SCHEMA_ERROR); } /* Check that all index definitions are valid. */ for (i = 0; i < ib_vector_size(table_def->indexes); ++i) { ib_index_def_t* ib_index_def; ib_index_def = ib_vector_get(table_def->indexes, i); /* Check that the index definition has at least one column. */ if (ib_vector_is_empty(ib_index_def->cols)) { return(DB_SCHEMA_ERROR); } /* Check for duplicate cluster definitions. */ if (ib_index_def->clustered) { ++n_cluster; if (n_cluster > 1) { return(DB_SCHEMA_ERROR); } } } /* Create the table prototype. */ table = dict_mem_table_create( table_def->name, 0, n_cols, ib_table_def_get_flags(table_def)); heap = table->heap; /* Create the columns defined by the user. */ for (i = 0; i < n_cols; i++) { ib_col = ib_vector_get(table_def->cols, i); dict_mem_table_add_col( table, heap, ib_col->name, ib_col_get_mtype(ib_col), ib_col_get_prtype(ib_col), ib_col->len); } /* Create the table using the prototype in the data dictionary. */ err = ddl_create_table(table, ddl_trx); table = NULL; if (err == DB_SUCCESS) { table = ib_lookup_table_by_name(table_def->name); ut_a(table != NULL); /* Bump up the reference count, so that another transaction doesn't delete it behind our back. */ dict_table_increment_handle_count(table, TRUE); err = ib_create_indexes(ddl_trx, table, table_def->indexes); } /* FIXME: If ib_create_indexes() fails, it's unclear what state the data dictionary is in. */ if (err == DB_SUCCESS) { *id = ut_dulint_get_low(table->id); } if (table != NULL) { ulint format_id; const char* format = NULL; /* We update the highest file format in the system table space, if this table has a higher file format setting. */ format_id = dict_table_get_format(table); trx_sys_file_format_max_upgrade(&format, format_id); if (format != NULL && format_id > db_format.id) { db_format.name = trx_sys_file_format_id_to_name( format_id); db_format.id = trx_sys_file_format_name_to_id(format); ut_a(db_format.id <= DICT_TF_FORMAT_MAX); } dict_table_decrement_handle_count(table, TRUE); } return(err); } /*****************************************************************//** Rename a table. @return DB_SUCCESS or err code */ ib_err_t ib_table_rename( /*============*/ ib_trx_t ib_trx, /*!< in/out: transaction */ const char* old_name, /*!< in: old name*/ const char* new_name) /*!< in: old name*/ { ib_err_t err; char* normalized_old_name; char* normalized_new_name; trx_t* trx = (trx_t*) ib_trx; IB_CHECK_PANIC(); UT_DBG_ENTER_FUNC; if (!ib_schema_lock_is_exclusive(ib_trx)) { err = ib_schema_lock_exclusive(ib_trx); if (err != DB_SUCCESS) { return(err); } } normalized_old_name = mem_alloc(ut_strlen(old_name) + 1); ib_normalize_table_name(normalized_old_name, old_name); normalized_new_name = mem_alloc(ut_strlen(new_name) + 1); ib_normalize_table_name(normalized_new_name, new_name); err = ddl_rename_table(normalized_old_name, normalized_new_name, trx); mem_free(normalized_old_name); mem_free(normalized_new_name); return(err); } /*****************************************************************//** Create a primary index. The index id encodes the table id in the high 4 bytes and the index id in the lower 4 bytes. @return DB_SUCCESS or err code */ static ib_err_t ib_create_primary_index( /*====================*/ ib_idx_sch_t ib_idx_sch, /*!< in: key definition for index */ ib_id_t* index_id) /*!< out: index id */ { ib_err_t err; mem_heap_t* heap; const index_def_t* index_def; dict_table_t* new_table = NULL; ib_index_def_t* ib_index_def = (ib_index_def_t*) ib_idx_sch; trx_t* usr_trx = ib_index_def->usr_trx; dict_table_t* table = ib_index_def->table; IB_CHECK_PANIC(); UT_DBG_ENTER_FUNC; /* This should only be called on index schema instances created outside of table schemas. */ ut_a(ib_index_def->schema == NULL); ut_a(ib_index_def->clustered); /* Recreate the cluster index and all the secondary indexes on a table. If there was a user defined cluster index on the table, it will be recreated a secondary index. The InnoDB generated cluster index if one exists will be dropped. */ usr_trx->op_info = "recreating clustered index"; heap = mem_heap_create(1024); /* This transaction should be the only one operating on the table. */ ut_a(table->n_handles_opened == 1); trx_set_dict_operation(usr_trx, TRX_DICT_OP_TABLE); ut_a(!ib_vector_is_empty(ib_index_def->cols)); /* Set the CLUSTERED flag to TRUE. */ index_def = ib_copy_index_definition(ib_index_def, TRUE); err = ib_trx_lock_table_with_retry(usr_trx, table, LOCK_X); if (err == DB_SUCCESS) { err = ib_table_clone( usr_trx, table, &new_table, ib_index_def, heap); } if (err == DB_SUCCESS) { err = ib_trx_lock_table_with_retry(usr_trx, new_table, LOCK_X); } if (err == DB_SUCCESS) { err = ib_table_copy(usr_trx, table, new_table, heap); } if (err == DB_SUCCESS) { char* tmp_name; const char* old_name; /* Swap the cloned table with the original table. On success drop the original table. */ old_name = table->name; tmp_name = ib_table_create_temp_name(heap, '2', old_name); err = row_merge_rename_tables( table, new_table, tmp_name, usr_trx); if (err != DB_SUCCESS) { row_merge_drop_table(usr_trx, new_table); } } mem_heap_free(heap); usr_trx->op_info = ""; return(err); } /*****************************************************************//** Create a secondary index. The index id encodes the table id in the high 4 bytes and the index id in the lower 4 bytes. @return DB_SUCCESS or err code */ static ib_err_t ib_create_secondary_index( /*======================*/ ib_idx_sch_t ib_idx_sch, /*!< in: key definition for index */ ib_id_t* index_id) /*!< out: index id */ { ib_err_t err; dict_index_t* dict_index = NULL; trx_t* ddl_trx = NULL; ib_index_def_t* ib_index_def = (ib_index_def_t*) ib_idx_sch; trx_t* usr_trx = ib_index_def->usr_trx; dict_table_t* table = ib_index_def->table; IB_CHECK_PANIC(); UT_DBG_ENTER_FUNC; /* This should only be called on index schema instances created outside of table schemas. */ ut_a(ib_index_def->schema == NULL); ut_a(!ib_index_def->clustered); err = ib_trx_lock_table_with_retry(usr_trx, table, LOCK_S); if (err == DB_SUCCESS) { /* Since this is part of ALTER TABLE set the create flag to IB_FALSE. */ err = ib_build_secondary_index( usr_trx, table, ib_index_def, IB_FALSE, &dict_index); err = ib_schema_lock_exclusive((ib_trx_t) usr_trx); ut_a(err == DB_SUCCESS); if (dict_index != NULL && err != DB_SUCCESS) { row_merge_drop_indexes(usr_trx, table, &dict_index, 1); dict_index = NULL; } else { ib_bool_t started; ddl_trx = trx_allocate_for_client(NULL); started = trx_start(ddl_trx, ULINT_UNDEFINED); ut_a(started); } } ut_a(!(ddl_trx == NULL && err == DB_SUCCESS)); /* Rename from the TEMP new index to the actual name. */ if (dict_index != NULL && err == DB_SUCCESS) { err = row_merge_rename_indexes(usr_trx, table); if (err != DB_SUCCESS) { row_merge_drop_indexes(usr_trx, table, &dict_index, 1); dict_index = NULL; } } if (dict_index != NULL && err == DB_SUCCESS) { /* We only support 32 bit table and index ids. Because we need to pack the table id into the index id. */ ut_a(ut_dulint_get_high(table->id) == 0); ut_a(ut_dulint_get_high(dict_index->id) == 0); *index_id = ut_dulint_get_low(table->id); *index_id <<= 32; *index_id |= ut_dulint_get_low(dict_index->id); trx_commit(ddl_trx); } else if (ddl_trx != NULL) { trx_general_rollback(ddl_trx, FALSE, NULL); } if (ddl_trx != NULL) { ddl_trx->op_info = ""; trx_free_for_client(ddl_trx); } return(err); } /*****************************************************************//** Create a secondary index. The index id encodes the table id in the high 4 bytes and the index id in the lower 4 bytes. @return DB_SUCCESS or err code */ ib_err_t ib_index_create( /*============*/ ib_idx_sch_t ib_idx_sch, /*!< in/out: key definition for index */ ib_id_t* index_id) /*!< out: index id */ { ib_err_t err; ib_index_def_t* ib_index_def = (ib_index_def_t*) ib_idx_sch; IB_CHECK_PANIC(); if (!ib_schema_lock_is_exclusive((ib_trx_t) ib_index_def->usr_trx)) { return(DB_SCHEMA_NOT_LOCKED); } else if (ib_index_def->clustered) { err = ib_create_primary_index(ib_idx_sch, index_id); } else { err = ib_create_secondary_index(ib_idx_sch, index_id); } return(err); } /*****************************************************************//** Drop a table. @return DB_SUCCESS or err code */ ib_err_t ib_table_drop( /*==========*/ ib_trx_t ib_trx, /*!< in: transaction */ const char* name) /*!< in: table name to drop*/ { ib_err_t err; char* normalized_name; IB_CHECK_PANIC(); UT_DBG_ENTER_FUNC; if (!ib_schema_lock_is_exclusive(ib_trx)) { return(DB_SCHEMA_NOT_LOCKED); } normalized_name = mem_alloc(ut_strlen(name) + 1); ib_normalize_table_name(normalized_name, name); err = ddl_drop_table(normalized_name, (trx_t*) ib_trx, FALSE); mem_free(normalized_name); return(err); } /*****************************************************************//** Drop a secondary index. @return DB_SUCCESS or err code */ ib_err_t ib_index_drop( /*==========*/ ib_trx_t ib_trx, /*!< in: transaction */ ib_id_t index_id) /*!< in: index id to drop */ { ib_err_t err; dict_table_t* table; dict_index_t* dict_index; ulint table_id = (ulint) (index_id >> 32); IB_CHECK_PANIC(); UT_DBG_ENTER_FUNC; if (!ib_schema_lock_is_exclusive(ib_trx)) { return(DB_SCHEMA_NOT_LOCKED); } table = ib_open_table_by_id(table_id, TRUE); if (table == NULL) { return(DB_TABLE_NOT_FOUND); } /* We use only the lower 32 bits of the dulint. */ index_id &= 0xFFFFFFFFULL; dict_index = dict_index_get_on_id_low( table, ut_dulint_create(0, (ulint) index_id)); if (dict_index != NULL) { err = ddl_drop_index(table, dict_index, (trx_t*) ib_trx); } else { err = DB_TABLE_NOT_FOUND; } dict_table_decrement_handle_count(table, FALSE); return(err); } /*****************************************************************//** Create an internal cursor instance. @return DB_SUCCESS or err code */ static ib_err_t ib_create_cursor( /*=============*/ ib_crsr_t* ib_crsr, /*!< out: InnoDB cursor */ dict_table_t* table, /*!< in: table instance */ ib_id_t index_id, /*!< in: index id or 0 */ trx_t* trx) /*!< in: transaction */ { mem_heap_t* heap; ib_cursor_t* cursor; ib_err_t err = DB_SUCCESS; dulint id = ut_dulint_create(0, (ulint) index_id); IB_CHECK_PANIC(); UT_DBG_ENTER_FUNC; heap = mem_heap_create(sizeof(*cursor) * 2); if (heap != NULL) { row_prebuilt_t* prebuilt; cursor = mem_heap_zalloc(heap, sizeof(*cursor)); cursor->heap = heap; cursor->query_heap = mem_heap_create(64); if (cursor->query_heap == NULL) { mem_heap_free(heap); return(DB_OUT_OF_MEMORY); } cursor->prebuilt = row_prebuilt_create(table); prebuilt = cursor->prebuilt; prebuilt->trx = trx; prebuilt->table = table; prebuilt->select_lock_type = LOCK_NONE; if (index_id > 0) { prebuilt->index = dict_index_get_on_id_low(table, id); } else { prebuilt->index = dict_table_get_first_index(table); } ut_a(prebuilt->index != NULL); if (prebuilt->trx != NULL) { ++prebuilt->trx->n_client_tables_in_use; prebuilt->index_usable = row_merge_is_index_usable( prebuilt->trx, prebuilt->index); /* Assign a read view if the transaction does not have it yet */ trx_assign_read_view(prebuilt->trx); } *ib_crsr = (ib_crsr_t) cursor; } else { err = DB_OUT_OF_MEMORY; } return(err); } /*****************************************************************//** Open an InnoDB table and return a cursor handle to it. @return DB_SUCCESS or err code */ ib_err_t ib_cursor_open_table_using_id( /*==========================*/ ib_id_t table_id, /*!< in: table id of table to open */ ib_trx_t ib_trx, /*!< in: Current transaction handle can be NULL */ ib_crsr_t* ib_crsr) /*!< out,own: InnoDB cursor */ { ib_err_t err; dict_table_t* table; IB_CHECK_PANIC(); UT_DBG_ENTER_FUNC; if (ib_trx == NULL || !ib_schema_lock_is_exclusive(ib_trx)) { table = ib_open_table_by_id(table_id, FALSE); } else { table = ib_open_table_by_id(table_id, TRUE); } if (table == NULL) { return(DB_TABLE_NOT_FOUND); } err = ib_create_cursor(ib_crsr, table, 0, (trx_t*) ib_trx); return(err); } /*****************************************************************//** Open an InnoDB index and return a cursor handle to it. @return DB_SUCCESS or err code */ ib_err_t ib_cursor_open_index_using_id( /*==========================*/ ib_id_t index_id, /*!< in: index id of index to open */ ib_trx_t ib_trx, /*!< in: Current transaction handle can be NULL */ ib_crsr_t* ib_crsr) /*!< out: InnoDB cursor */ { ib_err_t err; dict_table_t* table; ulint table_id = (ulint)( index_id >> 32); IB_CHECK_PANIC(); UT_DBG_ENTER_FUNC; if (ib_trx == NULL || !ib_schema_lock_is_exclusive(ib_trx)) { table = ib_open_table_by_id(table_id, FALSE); } else { table = ib_open_table_by_id(table_id, TRUE); } if (table == NULL) { return(DB_TABLE_NOT_FOUND); } /* We only return the lower 32 bits of the dulint. */ err = ib_create_cursor( ib_crsr, table, index_id & 0xFFFFFFFFULL, (trx_t*) ib_trx); if (ib_crsr != NULL) { const ib_cursor_t* cursor; cursor = *(ib_cursor_t**) ib_crsr; if (cursor->prebuilt->index == NULL) { ib_err_t crsr_err; crsr_err = ib_cursor_close(*ib_crsr); ut_a(crsr_err == DB_SUCCESS); *ib_crsr = NULL; } } return(err); } /*****************************************************************//** Open an InnoDB secondary index cursor and return a cursor handle to it. @return DB_SUCCESS or err code */ ib_err_t ib_cursor_open_index_using_name( /*============================*/ ib_crsr_t ib_open_crsr, /*!< in: open/active cursor */ const char* index_name, /*!< in: secondary index name */ ib_crsr_t* ib_crsr) /*!< out,own: InnoDB index cursor */ { dict_table_t* table; dict_index_t* dict_index; ib_id_t index_id = 0; ib_err_t err = DB_TABLE_NOT_FOUND; ib_cursor_t* cursor = (ib_cursor_t*) ib_open_crsr; trx_t* trx = cursor->prebuilt->trx; IB_CHECK_PANIC(); UT_DBG_ENTER_FUNC; if (trx != NULL && !ib_schema_lock_is_exclusive((ib_trx_t) trx)) { dict_mutex_enter(); } /* We want to increment the ref count, so we do a redundant search. */ table = dict_table_get_using_id( srv_force_recovery, cursor->prebuilt->table->id, TRUE); ut_a(table != NULL); if (trx != NULL && !ib_schema_lock_is_exclusive((ib_trx_t) trx)) { dict_mutex_exit(); } /* The first index is always the cluster index. */ dict_index = dict_table_get_first_index(table); /* Traverse the user defined indexes. */ while (dict_index != NULL) { if (strcmp(dict_index->name, index_name) == 0) { index_id = ut_conv_dulint_to_longlong(dict_index->id); } dict_index = UT_LIST_GET_NEXT(indexes, dict_index); } *ib_crsr = NULL; if (index_id > 0) { err = ib_create_cursor( ib_crsr, table, index_id, cursor->prebuilt->trx); } if (*ib_crsr != NULL) { const ib_cursor_t* cursor; cursor = *(ib_cursor_t**) ib_crsr; if (cursor->prebuilt->index == NULL) { err = ib_cursor_close(*ib_crsr); ut_a(err == DB_SUCCESS); *ib_crsr = NULL; } } else { dict_table_decrement_handle_count(table, TRUE); } return(err); } /*****************************************************************//** Open an InnoDB table and return a cursor handle to it. @return DB_SUCCESS or err code */ ib_err_t ib_cursor_open_table( /*=================*/ const char* name, /*!< in: table name */ ib_trx_t ib_trx, /*!< in: Current transaction handle can be NULL */ ib_crsr_t* ib_crsr) /*!< out,own: InnoDB cursor */ { ib_err_t err; dict_table_t* table; char* normalized_name; IB_CHECK_PANIC(); UT_DBG_ENTER_FUNC; normalized_name = mem_alloc(ut_strlen(name) + 1); ib_normalize_table_name(normalized_name, name); if (ib_trx != NULL) { if (!ib_schema_lock_is_exclusive(ib_trx)) { table = ib_open_table_by_name(normalized_name); } else { table = ib_lookup_table_by_name(normalized_name); if (table != NULL) { dict_table_increment_handle_count(table, TRUE); } } } else { table = ib_open_table_by_name(normalized_name); } mem_free(normalized_name); normalized_name = NULL; /* It can happen that another thread has created the table but not the cluster index or it's a broken table definition. Refuse to open if that's the case. */ if (table != NULL && dict_table_get_first_index(table) == NULL) { dict_table_decrement_handle_count(table, FALSE); table = NULL; } if (table != NULL) { err = ib_create_cursor(ib_crsr, table, 0, (trx_t*) ib_trx); } else { err = DB_TABLE_NOT_FOUND; } return(err); } /********************************************************************//** Free a context struct for a table handle. */ static void ib_qry_proc_free( /*=============*/ ib_qry_proc_t* q_proc) /*!< in, own: qproc struct */ { UT_DBG_ENTER_FUNC; que_graph_free_recursive(q_proc->grph.ins); que_graph_free_recursive(q_proc->grph.upd); que_graph_free_recursive(q_proc->grph.sel); memset(q_proc, 0x0, sizeof(*q_proc)); } /*****************************************************************//** Reset the cursor. @return DB_SUCCESS or err code */ ib_err_t ib_cursor_reset( /*============*/ ib_crsr_t ib_crsr) /*!< in/out: InnoDB cursor */ { ib_cursor_t* cursor = (ib_cursor_t*) ib_crsr; row_prebuilt_t* prebuilt = cursor->prebuilt; IB_CHECK_PANIC(); UT_DBG_ENTER_FUNC; if (prebuilt->trx != NULL && prebuilt->trx->n_client_tables_in_use > 0) { --prebuilt->trx->n_client_tables_in_use; } /* The fields in this data structure are allocated from the query heap and so need to be reset too. */ ib_qry_proc_free(&cursor->q_proc); mem_heap_empty(cursor->query_heap); row_prebuilt_reset(prebuilt); return(DB_SUCCESS); } /*****************************************************************//** Close the cursor. @return DB_SUCCESS or err code */ ib_err_t ib_cursor_close( /*============*/ ib_crsr_t ib_crsr) /*!< in,own: InnoDB cursor */ { ib_cursor_t* cursor = (ib_cursor_t*) ib_crsr; row_prebuilt_t* prebuilt = cursor->prebuilt; trx_t* trx = prebuilt->trx; IB_CHECK_PANIC(); UT_DBG_ENTER_FUNC; ib_qry_proc_free(&cursor->q_proc); /* The transaction could have been detached from the cursor. */ if (trx != NULL && trx->n_client_tables_in_use > 0) { --trx->n_client_tables_in_use; } if (trx && ib_schema_lock_is_exclusive((ib_trx_t) trx)) { row_prebuilt_free(cursor->prebuilt, TRUE); } else { row_prebuilt_free(cursor->prebuilt, FALSE); } mem_heap_free(cursor->query_heap); mem_heap_free(cursor->heap); return(DB_SUCCESS); } /**********************************************************************//** Run the insert query and do error handling. @return DB_SUCCESS or error code */ UNIV_INLINE ib_err_t ib_insert_row_with_lock_retry( /*==========================*/ que_thr_t* thr, /*!< in: insert query graph */ ins_node_t* node, /*!< in: insert node for the query */ trx_savept_t* savept) /*!< in: savepoint to rollback to in case of an error */ { trx_t* trx; ib_err_t err; ib_bool_t lock_wait; trx = thr_get_trx(thr); do { thr->run_node = node; thr->prev_node = node; row_ins_step(thr); err = trx->error_state; if (err != DB_SUCCESS) { que_thr_stop_client(thr); thr->lock_state = QUE_THR_LOCK_ROW; lock_wait = ib_handle_errors(&err, trx, thr, savept); thr->lock_state = QUE_THR_LOCK_NOLOCK; } else { lock_wait = FALSE; } } while (lock_wait); return(err); } /*****************************************************************//** Write a row. @return DB_SUCCESS or err code */ static ib_err_t ib_execute_insert_query_graph( /*==========================*/ dict_table_t* table, /*!< in: table where to insert */ que_fork_t* ins_graph, /*!< in: query graph */ ins_node_t* node) /*!< in: insert node */ { trx_t* trx; que_thr_t* thr; trx_savept_t savept; ib_err_t err = DB_SUCCESS; UT_DBG_ENTER_FUNC; /* This is a short term solution to fix the purge lag. */ ib_delay_dml_if_needed(); trx = ins_graph->trx; savept = trx_savept_take(trx); thr = que_fork_get_first_thr(ins_graph); que_thr_move_to_run_state(thr); err = ib_insert_row_with_lock_retry(thr, node, &savept); if (err == DB_SUCCESS) { que_thr_stop_for_client_no_error(thr, trx); table->stat_n_rows++; srv_n_rows_inserted++; ib_update_statistics_if_needed(table); ib_wake_master_thread(); } trx->op_info = ""; return(err); } /*****************************************************************//** Create an insert query graph node. */ static void ib_insert_query_graph_create( /*==========================*/ ib_cursor_t* cursor) /*!< in: Cursor instance */ { ib_qry_proc_t* q_proc = &cursor->q_proc; ib_qry_node_t* node = &q_proc->node; trx_t* trx = cursor->prebuilt->trx; UT_DBG_ENTER_FUNC; ut_a(trx->conc_state != TRX_NOT_STARTED); if (node->ins == NULL) { dtuple_t* row; ib_qry_grph_t* grph = &q_proc->grph; mem_heap_t* heap = cursor->query_heap; dict_table_t* table = cursor->prebuilt->table; node->ins = row_ins_node_create(INS_DIRECT, table, heap); node->ins->select = NULL; node->ins->values_list = NULL; row = dtuple_create(heap, dict_table_get_n_cols(table)); dict_table_copy_types(row, table); row_ins_node_set_new_row(node->ins, row); grph->ins = que_node_get_parent( pars_complete_graph_for_exec(node->ins, trx, heap)); grph->ins->state = QUE_FORK_ACTIVE; } } /*****************************************************************//** Insert a row to a table. @return DB_SUCCESS or err code */ ib_err_t ib_cursor_insert_row( /*=================*/ ib_crsr_t ib_crsr, /*!< in/out: InnoDB cursor instance */ const ib_tpl_t ib_tpl) /*!< in: tuple to insert */ { ib_ulint_t i; ib_qry_node_t* node; ib_qry_proc_t* q_proc; ulint n_fields; dtuple_t* dst_dtuple; ib_err_t err = DB_SUCCESS; ib_cursor_t* cursor = (ib_cursor_t*) ib_crsr; const ib_tuple_t* src_tuple = (const ib_tuple_t*) ib_tpl; IB_CHECK_PANIC(); UT_DBG_ENTER_FUNC; ib_insert_query_graph_create(cursor); ut_ad(src_tuple->type == TPL_ROW); q_proc = &cursor->q_proc; node = &q_proc->node; node->ins->state = INS_NODE_ALLOC_ROW_ID; dst_dtuple = node->ins->row; n_fields = dtuple_get_n_fields(src_tuple->ptr); ut_ad(n_fields == dtuple_get_n_fields(dst_dtuple)); /* Do a shallow copy of the data fields and check for NULL constraints on columns. */ for (i = 0; i < n_fields; i++) { ulint mtype; dfield_t* src_field; dfield_t* dst_field; src_field = dtuple_get_nth_field(src_tuple->ptr, i); mtype = dtype_get_mtype(dfield_get_type(src_field)); /* Don't touch the system columns. */ if (mtype != DATA_SYS) { ulint prtype; prtype = dtype_get_prtype(dfield_get_type(src_field)); if ((prtype & DATA_NOT_NULL) && dfield_is_null(src_field)) { err = DB_DATA_MISMATCH; break; } dst_field = dtuple_get_nth_field(dst_dtuple, i); ut_ad(mtype == dtype_get_mtype(dfield_get_type(dst_field))); /* Do a shallow copy. */ dfield_set_data( dst_field, src_field->data, src_field->len); UNIV_MEM_ASSERT_RW(src_field->data, src_field->len); UNIV_MEM_ASSERT_RW(dst_field->data, dst_field->len); } } if (err == DB_SUCCESS) { err = ib_execute_insert_query_graph( src_tuple->index->table, q_proc->grph.ins, node->ins); } return(err); } /*********************************************************************//** Gets pointer to a prebuilt update vector used in updates. @return update vector */ UNIV_INLINE upd_t* ib_update_vector_create( /*====================*/ ib_cursor_t* cursor) /*!< in: current cursor */ { trx_t* trx = cursor->prebuilt->trx; mem_heap_t* heap = cursor->query_heap; dict_table_t* table = cursor->prebuilt->table; ib_qry_proc_t* q_proc = &cursor->q_proc; ib_qry_grph_t* grph = &q_proc->grph; ib_qry_node_t* node = &q_proc->node; UT_DBG_ENTER_FUNC; ut_a(trx->conc_state != TRX_NOT_STARTED); if (node->upd == NULL) { node->upd = row_create_update_node(table, heap); } grph->upd = que_node_get_parent( pars_complete_graph_for_exec(node->upd, trx, heap)); grph->upd->state = QUE_FORK_ACTIVE; return(node->upd->update); } /**********************************************************************//** Note that a column has changed. */ static void ib_update_col( /*==========*/ ib_cursor_t* cursor, /*!< in: current cursor */ upd_field_t* upd_field, /*!< in/out: update field */ ulint col_no, /*!< in: column number */ dfield_t* dfield) /*!< in: updated dfield */ { ulint data_len; dict_table_t* table = cursor->prebuilt->table; dict_index_t* dict_index = dict_table_get_first_index(table); UT_DBG_ENTER_FUNC; data_len = dfield_get_len(dfield); if (data_len == UNIV_SQL_NULL) { dfield_set_null(&upd_field->new_val); } else { dfield_copy_data(&upd_field->new_val, dfield); } upd_field->exp = NULL; upd_field->orig_len = 0; upd_field->field_no = dict_col_get_clust_pos( &table->cols[col_no], dict_index); } /**********************************************************************//** Checks which fields have changed in a row and stores the new data to an update vector. @return DB_SUCCESS or err code */ static ib_err_t ib_calc_diff( /*=========*/ ib_cursor_t* cursor, /*!< in: current cursor */ upd_t* upd, /*!< in/out: update vector */ const ib_tuple_t*old_tuple, /*!< in: Old tuple in table */ const ib_tuple_t*new_tuple) /*!< in: New tuple to update */ { ulint i; ulint n_changed = 0; ib_err_t err = DB_SUCCESS; ulint n_fields = dtuple_get_n_fields(new_tuple->ptr); UT_DBG_ENTER_FUNC; ut_a(old_tuple->type == TPL_ROW); ut_a(new_tuple->type == TPL_ROW); ut_a(old_tuple->index->table == new_tuple->index->table); for (i = 0; i < n_fields; ++i) { ulint mtype; ulint prtype; upd_field_t* upd_field; dfield_t* new_dfield; dfield_t* old_dfield; new_dfield = dtuple_get_nth_field(new_tuple->ptr, i); old_dfield = dtuple_get_nth_field(old_tuple->ptr, i); mtype = dtype_get_mtype(dfield_get_type(old_dfield)); prtype = dtype_get_prtype(dfield_get_type(old_dfield)); /* Skip the system columns */ if (mtype == DATA_SYS) { continue; } else if ((prtype & DATA_NOT_NULL) && dfield_is_null(new_dfield)) { err = DB_DATA_MISMATCH; break; } if (dfield_get_len(new_dfield) != dfield_get_len(old_dfield) || (!dfield_is_null(old_dfield) && memcmp(dfield_get_data(new_dfield), dfield_get_data(old_dfield), dfield_get_len(old_dfield)) != 0)) { upd_field = &upd->fields[n_changed]; ib_update_col(cursor, upd_field, i, new_dfield); ++n_changed; } } if (err == DB_SUCCESS) { upd->info_bits = 0; upd->n_fields = n_changed; } return(err); } /**********************************************************************//** Run the update query and do error handling. @return DB_SUCCESS or error code */ UNIV_INLINE ib_err_t ib_update_row_with_lock_retry( /*==========================*/ que_thr_t* thr, /*!< in: Update query graph */ upd_node_t* node, /*!< in: Update node for the query */ trx_savept_t* savept) /*!< in: savepoint to rollback to in case of an error */ { trx_t* trx; ib_err_t err; ib_bool_t lock_wait; trx = thr_get_trx(thr); do { thr->run_node = node; thr->prev_node = node; row_upd_step(thr); err = trx->error_state; if (err != DB_SUCCESS) { que_thr_stop_client(thr); if (err != DB_RECORD_NOT_FOUND) { thr->lock_state = QUE_THR_LOCK_ROW; lock_wait = ib_handle_errors( &err, trx, thr, savept); thr->lock_state = QUE_THR_LOCK_NOLOCK; } else { lock_wait = FALSE; } } else { lock_wait = FALSE; } } while (lock_wait); return(err); } /*********************************************************************//** Does an update or delete of a row. @return DB_SUCCESS or err code */ UNIV_INLINE ib_err_t ib_execute_update_query_graph( /*==========================*/ ib_cursor_t* cursor, /*!< in: Cursor instance */ btr_pcur_t* pcur) /*!< in: Btree persistent cursor */ { ib_err_t err; que_thr_t* thr; upd_node_t* node; trx_savept_t savept; trx_t* trx = cursor->prebuilt->trx; dict_table_t* table = cursor->prebuilt->table; ib_qry_proc_t* q_proc = &cursor->q_proc; UT_DBG_ENTER_FUNC; /* The transaction must be running. */ ut_a(trx->conc_state != TRX_NOT_STARTED); node = q_proc->node.upd; /* This is a short term solution to fix the purge lag. */ ib_delay_dml_if_needed(); ut_a(dict_index_is_clust(pcur->btr_cur.index)); btr_pcur_copy_stored_position(node->pcur, pcur); ut_a(node->pcur->rel_pos == BTR_PCUR_ON); savept = trx_savept_take(trx); thr = que_fork_get_first_thr(q_proc->grph.upd); node->state = UPD_NODE_UPDATE_CLUSTERED; que_thr_move_to_run_state(thr); err = ib_update_row_with_lock_retry(thr, node, &savept); if (err == DB_SUCCESS) { que_thr_stop_for_client_no_error(thr, trx); if (node->is_delete) { if (table->stat_n_rows > 0) { table->stat_n_rows--; } srv_n_rows_deleted++; } else { srv_n_rows_updated++; } ib_update_statistics_if_needed(table); } else if (err == DB_RECORD_NOT_FOUND) { trx->error_state = DB_SUCCESS; } ib_wake_master_thread(); trx->op_info = ""; return(err); } /*****************************************************************//** Update a row in a table. @return DB_SUCCESS or err code */ ib_err_t ib_cursor_update_row( /*=================*/ ib_crsr_t ib_crsr, /*!< in: InnoDB cursor instance */ const ib_tpl_t ib_old_tpl, /*!< in: Old tuple in table */ const ib_tpl_t ib_new_tpl) /*!< in: New tuple to update */ { upd_t* upd; ib_err_t err; btr_pcur_t* pcur; ib_cursor_t* cursor = (ib_cursor_t*) ib_crsr; row_prebuilt_t* prebuilt = cursor->prebuilt; const ib_tuple_t*old_tuple = (const ib_tuple_t*) ib_old_tpl; const ib_tuple_t*new_tuple = (const ib_tuple_t*) ib_new_tpl; IB_CHECK_PANIC(); UT_DBG_ENTER_FUNC; if (dict_index_is_clust(prebuilt->index)) { pcur = cursor->prebuilt->pcur; } else if (prebuilt->need_to_access_clustered && cursor->prebuilt->clust_pcur != NULL) { pcur = cursor->prebuilt->clust_pcur; } else { return(DB_ERROR); } ut_a(old_tuple->type == TPL_ROW); ut_a(new_tuple->type == TPL_ROW); upd = ib_update_vector_create(cursor); err = ib_calc_diff(cursor, upd, old_tuple, new_tuple); if (err == DB_SUCCESS) { /* Note that this is not a delete. */ cursor->q_proc.node.upd->is_delete = FALSE; err = ib_execute_update_query_graph(cursor, pcur); } return(err); } /**********************************************************************//** Build the update query graph to delete a row from an index. @return DB_SUCCESS or err code */ static ib_err_t ib_delete_row( /*==========*/ ib_cursor_t* cursor, /*!< in: current cursor */ btr_pcur_t* pcur, /*!< in: Btree persistent cursor */ const rec_t* rec) /*!< in: record to delete */ { ulint i; upd_t* upd; ib_err_t err; ib_tuple_t* tuple; ib_tpl_t ib_tpl; ulint n_cols; upd_field_t* upd_field; ib_bool_t page_format; dict_table_t* table = cursor->prebuilt->table; dict_index_t* dict_index = dict_table_get_first_index(table); IB_CHECK_PANIC(); UT_DBG_ENTER_FUNC; n_cols = dict_index_get_n_ordering_defined_by_user(dict_index); ib_tpl = ib_key_tuple_new(dict_index, n_cols); if (!ib_tpl) { return(DB_OUT_OF_MEMORY); } tuple = (ib_tuple_t*) ib_tpl; upd = ib_update_vector_create(cursor); page_format = dict_table_is_comp(dict_index->table); ib_read_tuple(rec, page_format, tuple); upd->n_fields = ib_tuple_get_n_cols(ib_tpl); for (i = 0; i < upd->n_fields; ++i) { dfield_t* dfield; upd_field = &upd->fields[i]; dfield = dtuple_get_nth_field(tuple->ptr, i); dfield_copy_data(&upd_field->new_val, dfield); upd_field->exp = NULL; upd_field->orig_len = 0; upd->info_bits = 0; upd_field->field_no = dict_col_get_clust_pos( &table->cols[i], dict_index); } /* Note that this is a delete. */ cursor->q_proc.node.upd->is_delete = TRUE; err = ib_execute_update_query_graph(cursor, pcur); ib_tuple_delete(ib_tpl); return(err); } /*****************************************************************//** Delete a row in a table. @return DB_SUCCESS or err code */ ib_err_t ib_cursor_delete_row( /*=================*/ ib_crsr_t ib_crsr) /*!< in: InnoDB cursor instance */ { ib_err_t err; btr_pcur_t* pcur; dict_index_t* dict_index; ib_cursor_t* cursor = (ib_cursor_t*) ib_crsr; row_prebuilt_t* prebuilt = cursor->prebuilt; IB_CHECK_PANIC(); UT_DBG_ENTER_FUNC; dict_index = dict_table_get_first_index(prebuilt->index->table); /* Check whether this is a secondary index cursor */ if (dict_index != prebuilt->index) { if (prebuilt->need_to_access_clustered) { pcur = prebuilt->clust_pcur; } else { return(DB_ERROR); } } else { pcur = prebuilt->pcur; } if (ib_btr_cursor_is_positioned(pcur)) { const rec_t* rec; ib_bool_t page_format; page_format = dict_table_is_comp(dict_index->table); if (!row_sel_row_cache_is_empty(prebuilt)) { rec = row_sel_row_cache_get(prebuilt); ut_a(rec != NULL); } else { mtr_t mtr; mtr_start(&mtr); if (btr_pcur_restore_position( BTR_SEARCH_LEAF, pcur, &mtr)) { rec = btr_pcur_get_rec(pcur); } else { rec = NULL; } mtr_commit(&mtr); } if (rec && !rec_get_deleted_flag(rec, page_format)) { err = ib_delete_row(cursor, pcur, rec); } else{ err = DB_RECORD_NOT_FOUND; } } else { err = DB_RECORD_NOT_FOUND; } return(err); } /*****************************************************************//** Read current row. @return DB_SUCCESS or err code */ ib_err_t ib_cursor_read_row( /*===============*/ ib_crsr_t ib_crsr, /*!< in: InnoDB cursor instance */ ib_tpl_t ib_tpl) /*!< out: read cols into this tuple */ { ib_err_t err; ib_tuple_t* tuple = (ib_tuple_t*) ib_tpl; ib_cursor_t* cursor = (ib_cursor_t*) ib_crsr; IB_CHECK_PANIC(); UT_DBG_ENTER_FUNC; ut_a(cursor->prebuilt->trx->conc_state != TRX_NOT_STARTED); /* When searching with IB_EXACT_MATCH set, row_search_for_client() will not position the persistent cursor but will copy the record found into the row cache. It should be the only entry. */ if (!ib_cursor_is_positioned(ib_crsr) && row_sel_row_cache_is_empty(cursor->prebuilt)) { err = DB_RECORD_NOT_FOUND; } else if (!row_sel_row_cache_is_empty(cursor->prebuilt)) { const rec_t* rec; ib_bool_t page_format; page_format = dict_table_is_comp(tuple->index->table); rec = row_sel_row_cache_get(cursor->prebuilt); ut_a(rec != NULL); if (!rec_get_deleted_flag(rec, page_format)) { ib_read_tuple(rec, page_format, tuple); err = DB_SUCCESS; } else{ err = DB_RECORD_NOT_FOUND; } } else { mtr_t mtr; btr_pcur_t* pcur; row_prebuilt_t* prebuilt = cursor->prebuilt; if (prebuilt->need_to_access_clustered && tuple->type == TPL_ROW) { pcur = prebuilt->clust_pcur; } else { pcur = prebuilt->pcur; } if (pcur == NULL) { return(DB_ERROR); } mtr_start(&mtr); if (btr_pcur_restore_position(BTR_SEARCH_LEAF, pcur, &mtr)) { const rec_t* rec; ib_bool_t page_format; page_format = dict_table_is_comp(tuple->index->table); rec = btr_pcur_get_rec(pcur); if (!rec_get_deleted_flag(rec, page_format)) { ib_read_tuple(rec, page_format, tuple); err = DB_SUCCESS; } else{ err = DB_RECORD_NOT_FOUND; } } else { err = DB_RECORD_NOT_FOUND; } mtr_commit(&mtr); } return(err); } /*****************************************************************//** Move cursor to the prev user record in the table. @return DB_SUCCESS or err code */ ib_err_t ib_cursor_prev( /*===========*/ ib_crsr_t ib_crsr) /*!< in: InnoDB cursor instance */ { ib_err_t err; ib_cursor_t* cursor = (ib_cursor_t*) ib_crsr; row_prebuilt_t* prebuilt = cursor->prebuilt; IB_CHECK_PANIC(); UT_DBG_ENTER_FUNC; /* We want to move to the next record */ dtuple_set_n_fields(prebuilt->search_tuple, 0); row_sel_row_cache_next(prebuilt); err = row_search_for_client( srv_force_recovery, IB_CUR_L, prebuilt, ROW_SEL_DEFAULT, ROW_SEL_PREV); return(err); } /*****************************************************************//** Move cursor to the next user record in the table. @return DB_SUCCESS or err code */ ib_err_t ib_cursor_next( /*===========*/ ib_crsr_t ib_crsr) /*!< in: InnoDB cursor instance */ { ib_err_t err; ib_cursor_t* cursor = (ib_cursor_t*) ib_crsr; row_prebuilt_t* prebuilt = cursor->prebuilt; IB_CHECK_PANIC(); UT_DBG_ENTER_FUNC; /* We want to move to the next record */ dtuple_set_n_fields(prebuilt->search_tuple, 0); row_sel_row_cache_next(prebuilt); err = row_search_for_client( srv_force_recovery, IB_CUR_G, prebuilt, ROW_SEL_DEFAULT, ROW_SEL_NEXT); return(err); } /*****************************************************************//** Move cursor to the first record in the table. @return DB_SUCCESS or err code */ UNIV_INLINE ib_err_t ib_cursor_position( /*===============*/ ib_cursor_t* cursor, /*!< in: InnoDB cursor instance */ ib_srch_mode_t mode) /*!< in: Search mode */ { ib_err_t err; row_prebuilt_t* prebuilt = cursor->prebuilt; IB_CHECK_PANIC(); UT_DBG_ENTER_FUNC; /* We want to position at one of the ends, row_search_for_client() uses the search_tuple fields to work out what to do. */ dtuple_set_n_fields(prebuilt->search_tuple, 0); err = row_search_for_client( srv_force_recovery, mode, prebuilt, ROW_SEL_DEFAULT, ROW_SEL_MOVETO); return(err); } /*****************************************************************//** Move cursor to the first record in the table. @return DB_SUCCESS or err code */ ib_err_t ib_cursor_first( /*============*/ ib_crsr_t ib_crsr) /*!< in: InnoDB cursor instance */ { ib_cursor_t* cursor = (ib_cursor_t*) ib_crsr; IB_CHECK_PANIC(); return(ib_cursor_position(cursor, IB_CUR_G)); } /*****************************************************************//** Move cursor to the last record in the table. @return DB_SUCCESS or err code */ ib_err_t ib_cursor_last( /*===========*/ ib_crsr_t ib_crsr) /*!< in: InnoDB cursor instance */ { ib_cursor_t* cursor = (ib_cursor_t*) ib_crsr; IB_CHECK_PANIC(); return(ib_cursor_position(cursor, IB_CUR_L)); } /*****************************************************************//** Search for key. @return DB_SUCCESS or err code */ ib_err_t ib_cursor_moveto( /*=============*/ ib_crsr_t ib_crsr, /*!< in: InnoDB cursor instance */ ib_tpl_t ib_tpl, /*!< in: Key to search for */ ib_srch_mode_t ib_srch_mode, /*!< in: search mode */ int* result) /*!< out: -1, 0 or 1 depending on tuple eq or gt than current row */ { ulint i; ulint n_fields; ib_err_t err = DB_SUCCESS; ib_tuple_t* tuple = (ib_tuple_t*) ib_tpl; ib_cursor_t* cursor = (ib_cursor_t*) ib_crsr; row_prebuilt_t* prebuilt = cursor->prebuilt; dtuple_t* search_tuple = prebuilt->search_tuple; IB_CHECK_PANIC(); UT_DBG_ENTER_FUNC; ut_a(tuple->type == TPL_KEY); n_fields = dict_index_get_n_ordering_defined_by_user(prebuilt->index); dtuple_set_n_fields(search_tuple, n_fields); dtuple_set_n_fields_cmp(search_tuple, n_fields); /* Do a shallow copy */ for (i = 0; i < n_fields; ++i) { dfield_copy(dtuple_get_nth_field(search_tuple, i), dtuple_get_nth_field(tuple->ptr, i)); } ut_a(prebuilt->select_lock_type <= LOCK_NUM); err = row_search_for_client( srv_force_recovery, ib_srch_mode, prebuilt, (ib_match_t) cursor->match_mode, ROW_SEL_MOVETO); *result = prebuilt->result; return(err); } /*****************************************************************//** Attach the cursor to the transaction. */ void ib_cursor_attach_trx( /*=================*/ ib_crsr_t ib_crsr, /*!< in: cursor instance */ ib_trx_t ib_trx) /*!< in: transaction */ { ib_cursor_t* cursor = (ib_cursor_t*) ib_crsr; row_prebuilt_t* prebuilt = cursor->prebuilt; UT_DBG_ENTER_FUNC; ut_a(ib_trx != NULL); ut_a(prebuilt->trx == NULL); row_prebuilt_reset(prebuilt); row_prebuilt_update_trx(prebuilt, (trx_t*) ib_trx); /* Assign a read view if the transaction does not have it yet */ trx_assign_read_view(prebuilt->trx); ut_a(prebuilt->trx->conc_state != TRX_NOT_STARTED); ++prebuilt->trx->n_client_tables_in_use; } /*****************************************************************//** Set the client comparison function for BLOBs and client types. */ void ib_set_client_compare( /*==================*/ ib_client_cmp_t client_cmp_func)/*!< in: client col. compare callback */ { UT_DBG_ENTER_FUNC; ib_client_compare = client_cmp_func; } /*****************************************************************//** Set the cursor search mode. */ void ib_cursor_set_match_mode( /*=====================*/ ib_crsr_t ib_crsr, /*!< in: Cursor instance */ ib_match_mode_t match_mode) /*!< in: ib_cursor_moveto match mode */ { ib_cursor_t* cursor = (ib_cursor_t*) ib_crsr; UT_DBG_ENTER_FUNC; cursor->match_mode = match_mode; } /*****************************************************************//** Get the dfield instance for the column in the tuple. @return dfield instance in tuple */ UNIV_INLINE dfield_t* ib_col_get_dfield( /*==============*/ ib_tuple_t* tuple, /*!< in: tuple instance */ ulint col_no) /*!< in: col no. in tuple */ { dfield_t* dfield; UT_DBG_ENTER_FUNC; dfield = dtuple_get_nth_field(tuple->ptr, col_no); return(dfield); } /*****************************************************************//** Predicate to check whether a column type contains variable length data. @return DB_SUCCESS or error code */ UNIV_INLINE ib_err_t ib_col_is_capped( /*==============*/ const dtype_t* dtype) /* in: column type */ { return((dtype_get_mtype(dtype) == DATA_VARCHAR || dtype_get_mtype(dtype) == DATA_CHAR || dtype_get_mtype(dtype) == DATA_CLIENT || dtype_get_mtype(dtype) == DATA_VARCLIENT || dtype_get_mtype(dtype) == DATA_FIXBINARY || dtype_get_mtype(dtype) == DATA_BINARY) && dtype_get_len(dtype) > 0); } /*****************************************************************//** Set a column of the tuple. Make a copy using the tuple's heap. @return DB_SUCCESS or error code */ ib_err_t ib_col_set_value( /*=============*/ ib_tpl_t ib_tpl, /*!< in: tuple instance */ ib_ulint_t col_no, /*!< in: column index in tuple */ const void* src, /*!< in: data value */ ib_ulint_t len) /*!< in: data value len */ { const dtype_t* dtype; dfield_t* dfield; void* dst = NULL; ib_tuple_t* tuple = (ib_tuple_t*) ib_tpl; IB_CHECK_PANIC(); UT_DBG_ENTER_FUNC; #ifdef UNIV_DEBUG mem_heap_verify(tuple->heap); #endif dfield = ib_col_get_dfield(tuple, col_no); /* User wants to set the column to NULL. */ if (len == IB_SQL_NULL) { dfield_set_null(dfield); return(DB_SUCCESS); } dtype = dfield_get_type(dfield); /* Not allowed to update system columns. */ if (dtype_get_mtype(dtype) == DATA_SYS) { return(DB_DATA_MISMATCH); } dst = dfield_get_data(dfield); /* Since TEXT/CLOB also map to DATA_VARCHAR we need to make an exception. Perhaps we need to set the precise type and check for that. */ if (ib_col_is_capped(dtype)) { len = ut_min(len, dtype_get_len(dtype)); if (dst == NULL) { dst = mem_heap_alloc(tuple->heap, dtype_get_len(dtype)); ut_a(dst != NULL); } } else if (dst == NULL || len > dfield_get_len(dfield)) { dst = mem_heap_alloc(tuple->heap, len); } if (dst == NULL) { return(DB_OUT_OF_MEMORY); } switch (dtype_get_mtype(dtype)) { case DATA_INT: { if (dtype_get_len(dtype) == len) { ibool usign; usign = dtype_get_prtype(dtype) & DATA_UNSIGNED; mach_write_int_type(dst, src, len, usign); } else { return(DB_DATA_MISMATCH); } break; } case DATA_FLOAT: if (len == sizeof(float)) { mach_float_ptr_write(dst, src); } else { return(DB_DATA_MISMATCH); } break; case DATA_DOUBLE: if (len == sizeof(double)) { mach_double_ptr_write(dst, src); } else { return(DB_DATA_MISMATCH); } break; case DATA_SYS: ut_error; break; case DATA_CHAR: { ulint pad_char = ULINT_UNDEFINED; pad_char = dtype_get_pad_char( dtype_get_mtype(dtype), dtype_get_prtype(dtype)); ut_a(pad_char != ULINT_UNDEFINED); memset((byte*) dst + len, pad_char, dtype_get_len(dtype) - len); len = dtype_get_len(dtype); /* Fall through */ } case DATA_BLOB: case DATA_BINARY: case DATA_CLIENT: case DATA_DECIMAL: case DATA_VARCHAR: case DATA_VARCLIENT: case DATA_FIXBINARY: memcpy(dst, src, len); break; default: ut_error; } if (dst != dfield_get_data(dfield)) { dfield_set_data(dfield, dst, len); } else { dfield_set_len(dfield, len); } #ifdef UNIV_DEBUG mem_heap_verify(tuple->heap); #endif return(DB_SUCCESS); } /*****************************************************************//** Get the size of the data available in a column of the tuple. @return bytes avail or IB_SQL_NULL */ ib_ulint_t ib_col_get_len( /*===========*/ ib_tpl_t ib_tpl, /*!< in: tuple instance */ ib_ulint_t i) /*!< in: column index in tuple */ { const dfield_t* dfield; ulint data_len; ib_tuple_t* tuple = (ib_tuple_t*) ib_tpl; UT_DBG_ENTER_FUNC; dfield = ib_col_get_dfield(tuple, i); data_len = dfield_get_len(dfield); return(data_len == UNIV_SQL_NULL ? IB_SQL_NULL : data_len); } /*****************************************************************//** Copy a column value from the tuple. @return bytes copied or IB_SQL_NULL */ UNIV_INLINE ib_ulint_t ib_col_copy_value_low( /*==================*/ ib_tpl_t ib_tpl, /*!< in: tuple instance */ ib_ulint_t i, /*!< in: column index in tuple */ void* dst, /*!< out: copied data value */ ib_ulint_t len) /*!< in: max data value len to copy */ { const void* data; const dfield_t* dfield; ulint data_len; ib_tuple_t* tuple = (ib_tuple_t*) ib_tpl; UT_DBG_ENTER_FUNC; dfield = ib_col_get_dfield(tuple, i); data = dfield_get_data(dfield); data_len = dfield_get_len(dfield); if (data_len != UNIV_SQL_NULL) { const dtype_t* dtype = dfield_get_type(dfield); switch (dtype_get_mtype(dfield_get_type(dfield))) { case DATA_INT: { ibool usign; ut_a(data_len == len); usign = dtype_get_prtype(dtype) & DATA_UNSIGNED; mach_read_int_type(dst, data, data_len, usign); break; } case DATA_FLOAT: if (len == data_len) { float f; ut_a(data_len == sizeof(f)); f = mach_float_read(data); memcpy(dst, &f, sizeof(f)); } else { data_len = 0; } break; case DATA_DOUBLE: if (len == data_len) { double d; ut_a(data_len == sizeof(d)); d = mach_double_read(data); memcpy(dst, &d, sizeof(d)); } else { data_len = 0; } break; default: data_len = ut_min(data_len, len); memcpy(dst, data, data_len); } } else { data_len = IB_SQL_NULL; } return(data_len); } /*****************************************************************//** Copy a column value from the tuple. @return bytes copied or IB_SQL_NULL */ ib_ulint_t ib_col_copy_value( /*==============*/ ib_tpl_t ib_tpl, /*!< in: tuple instance */ ib_ulint_t i, /*!< in: column index in tuple */ void* dst, /*!< out: copied data value */ ib_ulint_t len) /*!< in: max data value len to copy */ { return(ib_col_copy_value_low(ib_tpl, i, dst, len)); } /*****************************************************************//** Get the InnoDB column attribute from the internal column precise type. @return precise type in api format */ UNIV_INLINE ib_col_attr_t ib_col_get_attr( /*============*/ ulint prtype) /*!< in: column definition */ { ib_col_attr_t attr = IB_COL_NONE; UT_DBG_ENTER_FUNC; if (prtype & DATA_UNSIGNED) { attr |= IB_COL_UNSIGNED; } if (prtype & DATA_NOT_NULL) { attr |= IB_COL_NOT_NULL; } if (prtype & DATA_CUSTOM_TYPE) { attr |= IB_COL_CUSTOM1; } if (prtype & (DATA_CUSTOM_TYPE << 1)) { attr |= IB_COL_CUSTOM2; } if (prtype & (DATA_CUSTOM_TYPE << 2)) { attr |= IB_COL_CUSTOM3; } return(attr); } /*****************************************************************//** Get a column type, length and attributes from the tuple. @return len of column data */ UNIV_INLINE ib_ulint_t ib_col_get_meta_low( /*================*/ ib_tpl_t ib_tpl, /*!< in: tuple instance */ ib_ulint_t i, /*!< in: column index in tuple */ ib_col_meta_t* ib_col_meta) /*!< out: column meta data */ { ib_u16_t prtype; const dfield_t* dfield; ulint data_len; ib_tuple_t* tuple = (ib_tuple_t*) ib_tpl; UT_DBG_ENTER_FUNC; dfield = ib_col_get_dfield(tuple, i); data_len = dfield_get_len(dfield); /* We assume 1-1 mapping between the ENUM and internal type codes. */ ib_col_meta->type = dtype_get_mtype(dfield_get_type(dfield)); ib_col_meta->type_len = dtype_get_len(dfield_get_type(dfield)); prtype = (ib_u16_t) dtype_get_prtype(dfield_get_type(dfield)); ib_col_meta->attr = ib_col_get_attr(prtype); ib_col_meta->client_type = prtype & DATA_CLIENT_TYPE_MASK; return(data_len); } /*************************************************************//** Read a signed int 8 bit column from an InnoDB tuple. */ UNIV_INLINE ib_err_t ib_tuple_check_int( /*===============*/ ib_tpl_t ib_tpl, /*!< in: InnoDB tuple */ ib_ulint_t i, /*!< in: column number */ ib_bool_t usign, /*!< in: true if unsigned */ ulint size) /*!< in: size of integer */ { ib_col_meta_t ib_col_meta; ib_col_get_meta_low(ib_tpl, i, &ib_col_meta); if (ib_col_meta.type != IB_INT) { return(DB_DATA_MISMATCH); } else if (ib_col_meta.type_len == IB_SQL_NULL) { return(DB_UNDERFLOW); } else if (ib_col_meta.type_len != size) { return(DB_DATA_MISMATCH); } else if ((ib_col_meta.attr & IB_COL_UNSIGNED) && !usign) { return(DB_DATA_MISMATCH); } return(DB_SUCCESS); } /*************************************************************//** Read a signed int 8 bit column from an InnoDB tuple. @return DB_SUCCESS or error */ ib_err_t ib_tuple_read_i8( /*=============*/ ib_tpl_t ib_tpl, /*!< in: InnoDB tuple */ ib_ulint_t i, /*!< in: column number */ ib_i8_t* ival) /*!< out: integer value */ { ib_err_t err; IB_CHECK_PANIC(); err = ib_tuple_check_int(ib_tpl, i, IB_FALSE, sizeof(*ival)); if (err == DB_SUCCESS) { ib_col_copy_value_low(ib_tpl, i, ival, sizeof(*ival)); } return(err); } /*************************************************************//** Read an unsigned int 8 bit column from an InnoDB tuple. @return DB_SUCCESS or error */ ib_err_t ib_tuple_read_u8( /*=============*/ ib_tpl_t ib_tpl, /*!< in: InnoDB tuple */ ib_ulint_t i, /*!< in: column number */ ib_u8_t* ival) /*!< out: integer value */ { ib_err_t err; IB_CHECK_PANIC(); err = ib_tuple_check_int(ib_tpl, i, IB_TRUE, sizeof(*ival)); if (err == DB_SUCCESS) { ib_col_copy_value_low(ib_tpl, i, ival, sizeof(*ival)); } return(err); } /*************************************************************//** Read a signed int 16 bit column from an InnoDB tuple. @return DB_SUCCESS or error */ ib_err_t ib_tuple_read_i16( /*==============*/ ib_tpl_t ib_tpl, /*!< in: InnoDB tuple */ ib_ulint_t i, /*!< in: column number */ ib_i16_t* ival) /*!< out: integer value */ { ib_err_t err; IB_CHECK_PANIC(); err = ib_tuple_check_int(ib_tpl, i, FALSE, sizeof(*ival)); if (err == DB_SUCCESS) { ib_col_copy_value_low(ib_tpl, i, ival, sizeof(*ival)); } return(err); } /*************************************************************//** Read an unsigned int 16 bit column from an InnoDB tuple. @return DB_SUCCESS or error */ ib_err_t ib_tuple_read_u16( /*==============*/ ib_tpl_t ib_tpl, /*!< in: InnoDB tuple */ ib_ulint_t i, /*!< in: column number */ ib_u16_t* ival) /*!< out: integer value */ { ib_err_t err; IB_CHECK_PANIC(); err = ib_tuple_check_int(ib_tpl, i, IB_TRUE, sizeof(*ival)); if (err == DB_SUCCESS) { ib_col_copy_value_low(ib_tpl, i, ival, sizeof(*ival)); } return(err); } /*************************************************************//** Read a signed int 32 bit column from an InnoDB tuple. @return DB_SUCCESS or error */ ib_err_t ib_tuple_read_i32( /*==============*/ ib_tpl_t ib_tpl, /*!< in: InnoDB tuple */ ib_ulint_t i, /*!< in: column number */ ib_i32_t* ival) /*!< out: integer value */ { ib_err_t err; IB_CHECK_PANIC(); err = ib_tuple_check_int(ib_tpl, i, FALSE, sizeof(*ival)); if (err == DB_SUCCESS) { ib_col_copy_value_low(ib_tpl, i, ival, sizeof(*ival)); } return(err); } /*************************************************************//** Read an unsigned int 32 bit column from an InnoDB tuple. @return DB_SUCCESS or error */ ib_err_t ib_tuple_read_u32( /*==============*/ ib_tpl_t ib_tpl, /*!< in: InnoDB tuple */ ib_ulint_t i, /*!< in: column number */ ib_u32_t* ival) /*!< out: integer value */ { ib_err_t err; IB_CHECK_PANIC(); err = ib_tuple_check_int(ib_tpl, i, IB_TRUE, sizeof(*ival)); if (err == DB_SUCCESS) { ib_col_copy_value_low(ib_tpl, i, ival, sizeof(*ival)); } return(err); } /*************************************************************//** Read a signed int 64 bit column from an InnoDB tuple. @return DB_SUCCESS or error */ ib_err_t ib_tuple_read_i64( /*==============*/ ib_tpl_t ib_tpl, /*!< in: InnoDB tuple */ ib_ulint_t i, /*!< in: column number */ ib_i64_t* ival) /*!< out: integer value */ { ib_err_t err; IB_CHECK_PANIC(); err = ib_tuple_check_int(ib_tpl, i, FALSE, sizeof(*ival)); if (err == DB_SUCCESS) { ib_col_copy_value_low(ib_tpl, i, ival, sizeof(*ival)); } return(err); } /*************************************************************//** Read an unsigned int 64 bit column from an InnoDB tuple. @return DB_SUCCESS or error */ ib_err_t ib_tuple_read_u64( /*==============*/ ib_tpl_t ib_tpl, /*!< in: InnoDB tuple */ ib_ulint_t i, /*!< in: column number */ ib_u64_t* ival) /*!< out: integer value */ { ib_err_t err; IB_CHECK_PANIC(); err = ib_tuple_check_int(ib_tpl, i, IB_TRUE, sizeof(*ival)); if (err == DB_SUCCESS) { ib_col_copy_value_low(ib_tpl, i, ival, sizeof(*ival)); } return(err); } /*****************************************************************//** Get a column value pointer from the tuple. @return NULL or pointer to buffer */ const void* ib_col_get_value( /*=============*/ ib_tpl_t ib_tpl, /*!< in: tuple instance */ ib_ulint_t i) /*!< in: column index in tuple */ { const void* data; const dfield_t* dfield; ulint data_len; ib_tuple_t* tuple = (ib_tuple_t*) ib_tpl; UT_DBG_ENTER_FUNC; dfield = ib_col_get_dfield(tuple, i); data = dfield_get_data(dfield); data_len = dfield_get_len(dfield); return(data_len != UNIV_SQL_NULL ? data : NULL); } /*****************************************************************//** Get a column type, length and attributes from the tuple. @return len of column data */ ib_ulint_t ib_col_get_meta( /*============*/ ib_tpl_t ib_tpl, /*!< in: tuple instance */ ib_ulint_t i, /*!< in: column index in tuple */ ib_col_meta_t* ib_col_meta) /*!< out: column meta data */ { return(ib_col_get_meta_low(ib_tpl, i, ib_col_meta)); } /*****************************************************************//** "Clear" or reset an InnoDB tuple. We free the heap and recreate the tuple. @return new tuple, or NULL */ ib_tpl_t ib_tuple_clear( /*============*/ ib_tpl_t ib_tpl) /*!< in,own: tuple (will be freed) */ { const dict_index_t* dict_index; ulint n_cols; ib_tuple_t* tuple = (ib_tuple_t*) ib_tpl; ib_tuple_type_t type = tuple->type; mem_heap_t* heap = tuple->heap; UT_DBG_ENTER_FUNC; dict_index = tuple->index; n_cols = dtuple_get_n_fields(tuple->ptr); mem_heap_empty(heap); if (type == TPL_ROW) { return(ib_row_tuple_new_low(dict_index, n_cols, heap)); } else { return(ib_key_tuple_new_low(dict_index, n_cols, heap)); } } /*****************************************************************//** Create a new cluster key search tuple and copy the contents of the secondary index key tuple columns that refer to the cluster index record to the cluster key. It does a deep copy of the column data. @return DB_SUCCESS or error code */ ib_err_t ib_tuple_get_cluster_key( /*=====================*/ ib_crsr_t ib_crsr, /*!< in: secondary index cursor */ ib_tpl_t* ib_dst_tpl, /*!< out,own: destination tuple */ const ib_tpl_t ib_src_tpl) /*!< in: source tuple */ { ulint i; ulint n_fields; ib_err_t err = DB_SUCCESS; ib_tuple_t* dst_tuple = NULL; ib_cursor_t* cursor = (ib_cursor_t*) ib_crsr; ib_tuple_t* src_tuple = (ib_tuple_t*) ib_src_tpl; dict_index_t* clust_index; IB_CHECK_PANIC(); clust_index = dict_table_get_first_index(cursor->prebuilt->table); /* We need to ensure that the src tuple belongs to the same table as the open cursor and that it's not a tuple for a cluster index. */ if (src_tuple->type != TPL_KEY) { return(DB_ERROR); } else if (src_tuple->index->table != cursor->prebuilt->table) { return(DB_DATA_MISMATCH); } else if (src_tuple->index == clust_index) { return(DB_ERROR); } /* Create the cluster index key search tuple. */ *ib_dst_tpl = ib_clust_search_tuple_create(ib_crsr); if (!*ib_dst_tpl) { return(DB_OUT_OF_MEMORY); } dst_tuple = (ib_tuple_t*) *ib_dst_tpl; ut_a(dst_tuple->index == clust_index); n_fields = dict_index_get_n_unique(dst_tuple->index); /* Do a deep copy of the data fields. */ for (i = 0; i < n_fields; i++) { ulint pos; dfield_t* src_field; dfield_t* dst_field; pos = dict_index_get_nth_field_pos( src_tuple->index, dst_tuple->index, i); ut_a(pos != ULINT_UNDEFINED); src_field = dtuple_get_nth_field(src_tuple->ptr, pos); dst_field = dtuple_get_nth_field(dst_tuple->ptr, i); if (!dfield_is_null(src_field)) { UNIV_MEM_ASSERT_RW(src_field->data, src_field->len); dst_field->data = mem_heap_dup( dst_tuple->heap, src_field->data, src_field->len); dst_field->len = src_field->len; } else { dfield_set_null(dst_field); } } return(err); } /*****************************************************************//** Copy the contents of source tuple to destination tuple. The tuples must be of the same type and belong to the same table/index. @return DB_SUCCESS or error code */ ib_err_t ib_tuple_copy( /*==========*/ ib_tpl_t ib_dst_tpl, /*!< in: destination tuple */ const ib_tpl_t ib_src_tpl) /*!< in: source tuple */ { ulint i; ulint n_fields; ib_err_t err = DB_SUCCESS; const ib_tuple_t*src_tuple = (const ib_tuple_t*) ib_src_tpl; ib_tuple_t* dst_tuple = (ib_tuple_t*) ib_dst_tpl; IB_CHECK_PANIC(); /* Make sure src and dst are not the same. */ ut_a(src_tuple != dst_tuple); /* Make sure they are the same type and refer to the same index. */ if (src_tuple->type != dst_tuple->type || src_tuple->index != dst_tuple->index) { return(DB_DATA_MISMATCH); } n_fields = dtuple_get_n_fields(src_tuple->ptr); ut_ad(n_fields == dtuple_get_n_fields(dst_tuple->ptr)); /* Do a deep copy of the data fields. */ for (i = 0; i < n_fields; ++i) { dfield_t* src_field; dfield_t* dst_field; src_field = dtuple_get_nth_field(src_tuple->ptr, i); dst_field = dtuple_get_nth_field(dst_tuple->ptr, i); if (!dfield_is_null(src_field)) { UNIV_MEM_ASSERT_RW(src_field->data, src_field->len); dst_field->data = mem_heap_dup( dst_tuple->heap, src_field->data, src_field->len); dst_field->len = src_field->len; } else { dfield_set_null(dst_field); } } return(err); } /*****************************************************************//** Create an InnoDB tuple used for index/table search. @return own: Tuple for current index */ ib_tpl_t ib_sec_search_tuple_create( /*=======================*/ ib_crsr_t ib_crsr) /*!< in: Cursor instance */ { ulint n_cols; ib_cursor_t* cursor = (ib_cursor_t*) ib_crsr; dict_index_t* dict_index = cursor->prebuilt->index; UT_DBG_ENTER_FUNC; n_cols = dict_index_get_n_unique_in_tree(dict_index); return(ib_key_tuple_new(dict_index, n_cols)); } /*****************************************************************//** Create an InnoDB tuple used for index/table search. @return own: Tuple for current index */ ib_tpl_t ib_sec_read_tuple_create( /*=====================*/ ib_crsr_t ib_crsr) /*!< in: Cursor instance */ { ulint n_cols; ib_cursor_t* cursor = (ib_cursor_t*) ib_crsr; dict_index_t* dict_index = cursor->prebuilt->index; UT_DBG_ENTER_FUNC; n_cols = dict_index_get_n_fields(dict_index); return(ib_row_tuple_new(dict_index, n_cols)); } /*****************************************************************//** Create an InnoDB tuple used for table key operations. @return own: Tuple for current table */ ib_tpl_t ib_clust_search_tuple_create( /*=========================*/ ib_crsr_t ib_crsr) /*!< in: Cursor instance */ { ulint n_cols; ib_cursor_t* cursor = (ib_cursor_t*) ib_crsr; dict_index_t* dict_index; dict_index = dict_table_get_first_index(cursor->prebuilt->table); UT_DBG_ENTER_FUNC; n_cols = dict_index_get_n_ordering_defined_by_user(dict_index); return(ib_key_tuple_new(dict_index, n_cols)); } /*****************************************************************//** Create an InnoDB tuple for table row operations. @return own: Tuple for current table */ ib_tpl_t ib_clust_read_tuple_create( /*=======================*/ ib_crsr_t ib_crsr) /*!< in: Cursor instance */ { ulint n_cols; ib_cursor_t* cursor = (ib_cursor_t*) ib_crsr; dict_index_t* dict_index; dict_index = dict_table_get_first_index(cursor->prebuilt->table); UT_DBG_ENTER_FUNC; n_cols = dict_table_get_n_cols(cursor->prebuilt->table); return(ib_row_tuple_new(dict_index, n_cols)); } /*****************************************************************//** Return the number of user columns in the tuple definition. @return number of user columns */ ib_ulint_t ib_tuple_get_n_user_cols( /*=====================*/ const ib_tpl_t ib_tpl) /*!< in: Tuple for current table */ { const ib_tuple_t* tuple = (const ib_tuple_t*) ib_tpl; UT_DBG_ENTER_FUNC; if (tuple->type == TPL_ROW) { return(dict_table_get_n_user_cols(tuple->index->table)); } return(dict_index_get_n_ordering_defined_by_user(tuple->index)); } /*****************************************************************//** Return the number of columns in the tuple definition. @return number of columns */ ib_ulint_t ib_tuple_get_n_cols( /*================*/ const ib_tpl_t ib_tpl) /*!< in: Tuple for table/index */ { const ib_tuple_t* tuple = (const ib_tuple_t*) ib_tpl; UT_DBG_ENTER_FUNC; return(dtuple_get_n_fields(tuple->ptr)); } /*****************************************************************//** Destroy an InnoDB tuple. */ void ib_tuple_delete( /*============*/ ib_tpl_t ib_tpl) /*!< in,own: Tuple instance to delete */ { ib_tuple_t* tuple = (ib_tuple_t*) ib_tpl; UT_DBG_ENTER_FUNC; mem_heap_free(tuple->heap); } /*****************************************************************//** Truncate a table. The cursor handle will be closed and set to NULL on success. @return DB_SUCCESS or error code */ ib_err_t ib_cursor_truncate( /*===============*/ ib_crsr_t* ib_crsr, /*!< in/out: cursor for table to truncate */ ib_id_t* table_id) /*!< out: new table id */ { ib_err_t err; ib_cursor_t* cursor = *(ib_cursor_t**) ib_crsr; row_prebuilt_t* prebuilt = cursor->prebuilt; IB_CHECK_PANIC(); UT_DBG_ENTER_FUNC; ut_a(ib_schema_lock_is_exclusive((ib_trx_t) prebuilt->trx)); *table_id = 0; err = ib_cursor_lock(*ib_crsr, IB_LOCK_X); if (err == DB_SUCCESS) { trx_t* trx; dict_table_t* table = prebuilt->table; /* We are going to free the cursor and the prebuilt. Store the transaction handle locally. */ trx = prebuilt->trx; err = ib_cursor_close(*ib_crsr); ut_a(err == DB_SUCCESS); *ib_crsr = NULL; /* This function currently commits the transaction on success. */ err = ddl_truncate_table(table, trx); if (err == DB_SUCCESS) { *table_id = ut_conv_dulint_to_longlong(table->id); } } return(err); } /*****************************************************************//** Truncate a table. @return DB_SUCCESS or error code */ ib_err_t ib_table_truncate( /*==============*/ const char* table_name, /*!< in: table name */ ib_id_t* table_id) /*!< out: new table id */ { ib_err_t err; dict_table_t* table; ib_err_t trunc_err; ib_trx_t ib_trx = NULL; ib_crsr_t ib_crsr = NULL; IB_CHECK_PANIC(); ib_trx = ib_trx_begin(IB_TRX_SERIALIZABLE); dict_mutex_enter(); table = dict_table_get_low(table_name); if (table != NULL && dict_table_get_first_index(table)) { dict_table_increment_handle_count(table, TRUE); err = ib_create_cursor(&ib_crsr, table, 0, (trx_t*) ib_trx); } else { err = DB_TABLE_NOT_FOUND; } dict_mutex_exit(); if (err == DB_SUCCESS) { err = ib_schema_lock_exclusive(ib_trx); } if (err == DB_SUCCESS) { trunc_err = ib_cursor_truncate(&ib_crsr, table_id); ut_a(err == DB_SUCCESS); } else { trunc_err = err; } if (ib_crsr != NULL) { err = ib_cursor_close(ib_crsr); ut_a(err == DB_SUCCESS); } if (trunc_err == DB_SUCCESS) { ut_a(ib_trx_state(ib_trx) == IB_TRX_NOT_STARTED); err = ib_schema_unlock(ib_trx); ut_a(err == DB_SUCCESS); err = ib_trx_release(ib_trx); ut_a(err == DB_SUCCESS); } else { err = ib_trx_rollback(ib_trx); ut_a(err == DB_SUCCESS); } return(trunc_err); } /*****************************************************************//** Get a table id. This function will acquire the dictionary mutex. @return DB_SUCCESS if found */ ib_err_t ib_table_get_id( /*============*/ const char* table_name, /*!< in: table to find */ ib_id_t* table_id) /*!< out: table id if found */ { ib_err_t err; IB_CHECK_PANIC(); UT_DBG_ENTER_FUNC; dict_mutex_enter(); err = ib_table_get_id_low(table_name, table_id); dict_mutex_exit(); return(err); } /*****************************************************************//** Get an index id. @return DB_SUCCESS if found */ ib_err_t ib_index_get_id( /*============*/ const char* table_name, /*!< in: find index for this table */ const char* index_name, /*!< in: index to find */ ib_id_t* index_id) /*!< out: index id if found */ { dict_table_t* table; char* normalized_name; ib_err_t err = DB_TABLE_NOT_FOUND; IB_CHECK_PANIC(); UT_DBG_ENTER_FUNC; *index_id = 0; normalized_name = mem_alloc(ut_strlen(table_name) + 1); ib_normalize_table_name(normalized_name, table_name); table = ib_lookup_table_by_name(normalized_name); mem_free(normalized_name); normalized_name = NULL; if (table != NULL) { dict_index_t* dict_index; dict_index = dict_table_get_index_on_name(table, index_name); if (dict_index != NULL) { /* We only support 32 bit table and index ids. Because we need to pack the table id into the index id. */ ut_a(ut_dulint_get_high(table->id) == 0); ut_a(ut_dulint_get_high(dict_index->id) == 0); *index_id = ut_dulint_get_low(table->id); *index_id <<= 32; *index_id |= ut_dulint_get_low(dict_index->id); err = DB_SUCCESS; } } return(err); } /*********************************************************************//** Create a database if it doesn't exist. @return IB_TRUE on success */ ib_bool_t ib_database_create( /*===============*/ const char* dbname) /*!< in: database name to create */ { const char* ptr; UT_DBG_ENTER_FUNC; for (ptr = dbname; *ptr; ++ptr) { if (*ptr == SRV_PATH_SEPARATOR) { return(IB_FALSE); } } /* Only necessary if file per table is set. */ if (srv_file_per_table) { return(fil_mkdir(dbname)); } return(IB_TRUE); } /*********************************************************************//** Drop a database if it exists. Drop all tables in the database too. @return DB_SUCCESS or error code */ ib_err_t ib_database_drop( /*=============*/ const char* dbname) /*!< in: database name to drop */ { ib_trx_t ib_trx; char* ptr = NULL; ib_err_t err = DB_SUCCESS; ulint len = ut_strlen(dbname); IB_CHECK_PANIC(); UT_DBG_ENTER_FUNC; if (len == 0) { return(DB_INVALID_INPUT); } ptr = (char*) mem_alloc(len + 2); memset(ptr, 0x0, len + 2); ut_strcpy(ptr, dbname); #ifdef __WIN__ ib_to_lower_case(ptr); #endif /* __WIN__ */ ib_trx = ib_trx_begin(IB_TRX_SERIALIZABLE); /* Drop all the tables in the database first. */ /* ddl_drop_database() expects a string that ends in '/'. */ if (ptr[len - 1] != '/') { ptr[len] = '/'; } err = ddl_drop_database(ptr, (trx_t*) ib_trx); /* Only necessary if file per table is set. */ if (err == DB_SUCCESS && srv_file_per_table) { fil_rmdir(ptr); } mem_free(ptr); if (err == DB_SUCCESS) { ib_err_t trx_err; trx_err = ib_trx_commit(ib_trx); ut_a(trx_err == DB_SUCCESS); } else { ib_err_t trx_err; trx_err = ib_trx_rollback(ib_trx); ut_a(trx_err == DB_SUCCESS); } return(err); } /*****************************************************************//** Check if cursor is positioned. @return IB_TRUE if positioned */ ib_bool_t ib_cursor_is_positioned( /*====================*/ const ib_crsr_t ib_crsr) /*!< in: InnoDB cursor instance */ { const ib_cursor_t* cursor = (const ib_cursor_t*) ib_crsr; const row_prebuilt_t* prebuilt = cursor->prebuilt; UT_DBG_ENTER_FUNC; return(ib_btr_cursor_is_positioned(prebuilt->pcur)); } /*****************************************************************//** Latches the data dictionary in shared mode. @return DB_SUCCESS or error code */ ib_err_t ib_schema_lock_shared( /*==================*/ ib_trx_t ib_trx) /*!< in/out: transaction */ { ib_err_t err = DB_SUCCESS; trx_t* trx = (trx_t*) ib_trx; IB_CHECK_PANIC(); if (trx->dict_operation_lock_mode == 0 || trx->dict_operation_lock_mode == RW_S_LATCH) { dict_freeze_data_dictionary((trx_t*) ib_trx); } return(err); } /*****************************************************************//** Latches the data dictionary in exclusive mode. @return DB_SUCCESS or error code */ ib_err_t ib_schema_lock_exclusive( /*======================*/ ib_trx_t ib_trx) /*!< in/out: transaction */ { ib_err_t err = DB_SUCCESS; trx_t* trx = (trx_t*) ib_trx; IB_CHECK_PANIC(); if (trx->dict_operation_lock_mode == 0 || trx->dict_operation_lock_mode == RW_X_LATCH) { dict_lock_data_dictionary((trx_t*) ib_trx); } else { err = DB_SCHEMA_NOT_LOCKED; } return(err); } /*****************************************************************//** Checks if the data dictionary is latched in exclusive mode. @return TRUE if exclusive latch */ ib_bool_t ib_schema_lock_is_exclusive( /*========================*/ const ib_trx_t ib_trx) /*!< in: transaction */ { const trx_t* trx = (const trx_t*) ib_trx; return(trx->dict_operation_lock_mode == RW_X_LATCH); } /*****************************************************************//** Checks if the data dictionary is latched in shared mode. @return TRUE if shared latch */ ib_bool_t ib_schema_lock_is_shared( /*=====================*/ const ib_trx_t ib_trx) /*!< in: transaction */ { const trx_t* trx = (const trx_t*) ib_trx; return(trx->dict_operation_lock_mode == RW_S_LATCH); } /*****************************************************************//** Unlocks the data dictionary. @return DB_SUCCESS or error code */ ib_err_t ib_schema_unlock( /*=============*/ ib_trx_t ib_trx) /*!< in/out: transaction */ { ib_err_t err = DB_SUCCESS; trx_t* trx = (trx_t*) ib_trx; IB_CHECK_PANIC(); if (trx->dict_operation_lock_mode == RW_X_LATCH) { dict_unlock_data_dictionary((trx_t*) ib_trx); } else if (trx->dict_operation_lock_mode == RW_S_LATCH) { dict_unfreeze_data_dictionary((trx_t*) ib_trx); } else { err = DB_SCHEMA_NOT_LOCKED; } return(err); } /*****************************************************************//** Set the Lock an InnoDB cursor/table. @return DB_SUCCESS or error code */ ib_err_t ib_cursor_lock( /*===========*/ ib_crsr_t ib_crsr, /*!< in/out: InnoDB cursor */ ib_lck_mode_t ib_lck_mode) /*!< in: InnoDB lock mode */ { ib_cursor_t* cursor = (ib_cursor_t*) ib_crsr; row_prebuilt_t* prebuilt = cursor->prebuilt; trx_t* trx = prebuilt->trx; dict_table_t* table = prebuilt->table; IB_CHECK_PANIC(); return(ib_trx_lock_table_with_retry( trx, table, (enum lock_mode) ib_lck_mode)); } /*****************************************************************//** Set the Lock an InnoDB table using the table id. @return DB_SUCCESS or error code */ ib_err_t ib_table_lock( /*==========*/ ib_trx_t ib_trx, /*!< in/out: transaction */ ib_id_t table_id, /*!< in: table id */ ib_lck_mode_t ib_lck_mode) /*!< in: InnoDB lock mode */ { ib_err_t err; que_thr_t* thr; mem_heap_t* heap; dict_table_t* table; ib_qry_proc_t q_proc; trx_t* trx = (trx_t*) ib_trx; IB_CHECK_PANIC(); UT_DBG_ENTER_FUNC; ut_a(trx->conc_state != TRX_NOT_STARTED); table = ib_open_table_by_id(table_id, FALSE); if (table == NULL) { return(DB_TABLE_NOT_FOUND); } ut_a(ib_lck_mode <= LOCK_NUM); heap = mem_heap_create(128); q_proc.node.sel = sel_node_create(heap); thr = pars_complete_graph_for_exec(q_proc.node.sel, trx, heap); q_proc.grph.sel = que_node_get_parent(thr); q_proc.grph.sel->state = QUE_FORK_ACTIVE; trx->op_info = "setting table lock"; ut_a(ib_lck_mode == IB_LOCK_IS || ib_lck_mode == IB_LOCK_IX); err = lock_table(0, table, (enum lock_mode) ib_lck_mode, thr); trx->error_state = err; dict_table_decrement_handle_count(table, FALSE); mem_heap_free(heap); return(err); } /*****************************************************************//** Unlock an InnoDB table. @return DB_SUCCESS or error code */ ib_err_t ib_cursor_unlock( /*=============*/ ib_crsr_t ib_crsr) /*!< in/out: InnoDB cursor */ { ib_err_t err; ib_cursor_t* cursor = (ib_cursor_t*) ib_crsr; row_prebuilt_t* prebuilt = cursor->prebuilt; IB_CHECK_PANIC(); UT_DBG_ENTER_FUNC; if (prebuilt->trx->client_n_tables_locked > 0) { --prebuilt->trx->client_n_tables_locked; err= DB_SUCCESS; } else { err = DB_ERROR; } return(err); } /*****************************************************************//** Set the Lock mode of the cursor. @return DB_SUCCESS or error code */ ib_err_t ib_cursor_set_lock_mode( /*====================*/ ib_crsr_t ib_crsr, /*!< in/out: InnoDB cursor */ ib_lck_mode_t ib_lck_mode) /*!< in: InnoDB lock mode */ { ib_err_t err = DB_SUCCESS; ib_cursor_t* cursor = (ib_cursor_t*) ib_crsr; row_prebuilt_t* prebuilt = cursor->prebuilt; IB_CHECK_PANIC(); UT_DBG_ENTER_FUNC; ut_a(ib_lck_mode <= LOCK_NUM); if (ib_lck_mode == IB_LOCK_X) { err = ib_cursor_lock(ib_crsr, IB_LOCK_IX); } else { err = ib_cursor_lock(ib_crsr, IB_LOCK_IS); } if (err == DB_SUCCESS) { prebuilt->select_lock_type = (enum lock_mode) ib_lck_mode; ut_a(prebuilt->trx->conc_state != TRX_NOT_STARTED); } return(err); } /*****************************************************************//** Set need to access clustered index record. */ void ib_cursor_set_cluster_access( /*=========================*/ ib_crsr_t ib_crsr) /*!< in/out: InnoDB cursor */ { ib_cursor_t* cursor = (ib_cursor_t*) ib_crsr; row_prebuilt_t* prebuilt = cursor->prebuilt; UT_DBG_ENTER_FUNC; prebuilt->need_to_access_clustered = TRUE; } /*****************************************************************//** Set to true if it's a simple select. */ void ib_cursor_set_simple_select( /*=========================*/ ib_crsr_t ib_crsr) /*!< in/out: InnoDB cursor */ { ib_cursor_t* cursor = (ib_cursor_t*) ib_crsr; row_prebuilt_t* prebuilt = cursor->prebuilt; UT_DBG_ENTER_FUNC; prebuilt->simple_select = TRUE; } /*******************************************************************//** Creates a named savepoint. The transaction must be started. If there is already a savepoint of the same name, this call erases that old savepoint and replaces it with a new. Savepoints are deleted in a transaction commit or rollback. */ void ib_savepoint_take( /*==============*/ ib_trx_t ib_trx, /*!< in: transaction */ const void* name, /*!< in: savepoint name */ ib_ulint_t name_len) /*!< in: length of name in bytes */ { trx_named_savept_t* savep; trx_t* trx = (trx_t*) ib_trx; ut_a(trx); ut_a(name != NULL); ut_a(name_len > 0); ut_a(trx->conc_state != TRX_NOT_STARTED); savep = UT_LIST_GET_FIRST(trx->trx_savepoints); /* Check if there is a savepoint with the same name already. */ while (savep != NULL) { if (name_len == savep->name_len && 0 == ut_memcmp(savep->name, name, name_len)) { break; } savep = UT_LIST_GET_NEXT(trx_savepoints, savep); } if (savep) { /* There is a savepoint with the same name: free that */ UT_LIST_REMOVE(trx_savepoints, trx->trx_savepoints, savep); mem_free(savep); } /* Create a new savepoint and add it as the last in the list */ savep = mem_alloc(sizeof(trx_named_savept_t) + name_len); savep->name = savep + 1; savep->savept = trx_savept_take(trx); savep->name_len = name_len; ut_memcpy(savep->name, name, name_len); UT_LIST_ADD_LAST(trx_savepoints, trx->trx_savepoints, savep); } /*******************************************************************//** Releases only the named savepoint. Savepoints which were set after this savepoint are left as is. @return if no savepoint of the name found then DB_NO_SAVEPOINT, otherwise DB_SUCCESS */ ib_err_t ib_savepoint_release( /*=================*/ ib_trx_t ib_trx, /*!< in: transaction handle */ const void* name, /*!< in: savepoint name */ ib_ulint_t name_len) /*!< in: length of name in bytes */ { trx_named_savept_t* savep; trx_t* trx = (trx_t*) ib_trx; IB_CHECK_PANIC(); savep = UT_LIST_GET_FIRST(trx->trx_savepoints); /* Search for the savepoint by name and free if found. */ while (savep != NULL) { if (name_len == savep->name_len && 0 == ut_memcmp(savep->name, name, name_len)) { UT_LIST_REMOVE(trx_savepoints, trx->trx_savepoints, savep); mem_free(savep); return(DB_SUCCESS); } savep = UT_LIST_GET_NEXT(trx_savepoints, savep); } return(DB_NO_SAVEPOINT); } /*******************************************************************//** Rolls back a transaction back to a named savepoint. Modifications after the savepoint are undone but InnoDB does NOT release the corresponding locks which are stored in memory. If a lock is 'implicit', that is, a new inserted row holds a lock where the lock information is carried by the trx id stored in the row, these locks are naturally released in the rollback. Savepoints which were set after this savepoint are deleted. If name equals NULL then all the savepoints are rolled back. @return if no savepoint of the name found then DB_NO_SAVEPOINT, otherwise DB_SUCCESS */ ib_err_t ib_savepoint_rollback( /*==================*/ ib_trx_t ib_trx, /*!< in: transaction handle */ const void* name, /*!< in: savepoint name can be NULL */ ib_ulint_t name_len) /*!< in: length of name in bytes */ { ib_err_t err; trx_named_savept_t* savep; trx_t* trx = (trx_t*) ib_trx; IB_CHECK_PANIC(); if (trx->conc_state == TRX_NOT_STARTED) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Error: transaction trying to rollback a " "savepoint "); ut_print_name(ib_stream, trx, FALSE, name); ib_logger(ib_stream, " though it is not started\n"); return(DB_ERROR); } savep = UT_LIST_GET_FIRST(trx->trx_savepoints); if (name != NULL) { while (savep != NULL) { if (savep->name_len == name_len && 0 == ut_memcmp(savep->name, name, name_len)) { /* Found */ break; } savep = UT_LIST_GET_NEXT(trx_savepoints, savep); } } if (savep == NULL) { return(DB_NO_SAVEPOINT); } /* We can now free all savepoints strictly later than this one */ trx_roll_savepoints_free(trx, savep); trx->op_info = "rollback to a savepoint"; err = trx_general_rollback(trx, TRUE, &savep->savept); /* Store the current undo_no of the transaction so that we know where to roll back if we have to roll back the next SQL statement: */ trx_mark_sql_stat_end(trx); trx->op_info = ""; return(err); } /*****************************************************************//** Convert from internal format to the the table definition table attributes */ static void ib_table_get_format( /*================*/ const dict_table_t* table, /*!< in: table definition */ ib_tbl_fmt_t* tbl_fmt,/*!< out: table format */ ulint* page_size)/*!< out: page size */ { UT_DBG_ENTER_FUNC; *page_size = 0; *tbl_fmt = IB_TBL_REDUNDANT; switch(table->flags) { case 0: break; case DICT_TF_COMPACT: /* The compact row format */ *tbl_fmt = IB_TBL_COMPACT; break; case DICT_TF_COMPACT | DICT_TF_FORMAT_ZIP << DICT_TF_FORMAT_SHIFT: *tbl_fmt = IB_TBL_DYNAMIC; break; #if DICT_TF_FORMAT_MAX > DICT_TF_FORMAT_ZIP # error "missing case labels for DICT_TF_FORMAT_ZIP .. DICT_TF_FORMAT_MAX" #endif default: if (table->flags & DICT_TF_ZSSIZE_MASK) { *tbl_fmt = IB_TBL_COMPRESSED; *page_size = ((PAGE_ZIP_MIN_SIZE >> 1) << ((table->flags & DICT_TF_ZSSIZE_MASK) >> DICT_TF_ZSSIZE_SHIFT)); } } } /*****************************************************************//** Call the visitor for each column in a table. @return return value from index_col */ static int ib_table_schema_visit_table_columns( /*================================*/ const dict_table_t* table, /*!< in: table to visit */ ib_schema_visitor_table_col_t table_col, /*!< in: table column visitor */ void* arg) /*!< in: argument to visitor */ { ulint i; for (i = 0; i < table->n_cols; ++i) { dict_col_t* col; const char* name; ib_col_attr_t attr; ulint col_no; int user_err; col = dict_table_get_nth_col(table, i); col_no = dict_col_get_no(col); name = dict_table_get_col_name(table, col_no); attr = ib_col_get_attr(col->prtype); user_err = table_col( arg, name, col->mtype, col->len, attr); if (user_err) { return(user_err); } } return(0); } /*****************************************************************//** Call the visitor for each column in an index. @return return value from index_col */ static int ib_table_schema_visit_index_columns( /*================================*/ const dict_index_t* dict_index, /*!< in: index to visit */ ib_schema_visitor_index_col_t index_col, /*!< in: index column visitor */ void* arg) /*!< in: argument to visitor */ { ulint i; ulint n_index_cols = dict_index->n_user_defined_cols; for (i = 0; i < n_index_cols; ++i) { const dict_field_t* dfield; int user_err; dfield = &dict_index->fields[i]; user_err = index_col(arg, dfield->name, dfield->prefix_len); if (user_err) { return(user_err); } } return(0); } /*****************************************************************//** Read a table's schema using the visitor pattern. It will make the following sequence of calls: visitor->table() visitor->table_col() for each user column visitor->index() for each user index visitor->index_col() for each column in user index It will stop if any of the above functions returns a non-zero value. The caller must have an exclusive lock on the InnoDB data dictionary @return DB_SUCCESS or DB_ERROR */ ib_err_t ib_table_schema_visit( /*==================*/ ib_trx_t ib_trx, /*!< in: transaction that owns the schema lock */ const char* name, /*!< in: table name to read */ const ib_schema_visitor_t* visitor,/*!< in: visitor functions to invoke on each definition */ void* arg) /*!< Argument passed to the visitor functions. */ { dict_index_t* dict_index; dict_table_t* table; ib_tbl_fmt_t tbl_fmt; ulint page_size; int n_indexes; int user_err = 0; char* normalized_name; IB_CHECK_PANIC(); UT_DBG_ENTER_FUNC; if (!ib_schema_lock_is_exclusive(ib_trx)) { return(DB_SCHEMA_NOT_LOCKED); } normalized_name = mem_alloc(ut_strlen(name) + 1); ib_normalize_table_name(normalized_name, name); table = ib_lookup_table_by_name(normalized_name); mem_free(normalized_name); normalized_name = NULL; if (table != NULL) { dict_table_increment_handle_count(table, TRUE); } else { return(DB_TABLE_NOT_FOUND); } ib_table_get_format(table, &tbl_fmt, &page_size); /* We need the count of user defined indexes only. */ n_indexes = UT_LIST_GET_LEN(table->indexes); /* The first index is always the cluster index. */ dict_index = dict_table_get_first_index(table); /* Only the clustered index can be auto generated. */ if (dict_index->n_user_defined_cols == 0) { --n_indexes; } if (visitor->version < IB_SCHEMA_VISITOR_TABLE) { goto func_exit; } else if (visitor->table) { user_err = visitor->table( arg, table->name, tbl_fmt, page_size, table->n_cols, n_indexes); if (user_err) { goto func_exit; } } if (visitor->version < IB_SCHEMA_VISITOR_TABLE_COL) { goto func_exit; } else if (visitor->table_col) { user_err = ib_table_schema_visit_table_columns( table, visitor->table_col, arg); if (user_err) { goto func_exit; } } if (!visitor->index) { goto func_exit; } else if (visitor->version < IB_SCHEMA_VISITOR_TABLE_AND_INDEX) { goto func_exit; } /* Traverse the user defined indexes. */ do { ulint n_index_cols; n_index_cols = dict_index->n_user_defined_cols; /* Ignore system generated indexes. */ if (n_index_cols > 0) { user_err = visitor->index( arg, dict_index->name, dict_index_is_unique(dict_index) != 0, dict_index_is_clust(dict_index) != 0, n_index_cols); if (user_err) { goto func_exit; } if (visitor->version >= IB_SCHEMA_VISITOR_TABLE_AND_INDEX_COL && visitor->index_col) { user_err = ib_table_schema_visit_index_columns( dict_index, visitor->index_col, arg); if (user_err) { break; } } } dict_index = UT_LIST_GET_NEXT(indexes, dict_index); } while (dict_index != NULL); func_exit: ut_a(ib_schema_lock_is_exclusive(ib_trx)); dict_table_decrement_handle_count(table, TRUE); return(user_err != 0 ? DB_ERROR : DB_SUCCESS); } /*****************************************************************//** List all the tables in the InnoDB's data dictionary. It will abort if visitor returns a non-zero value. It will call the function: visitor.tables(arg, const char* name, int name_len); The function will abort if visitor.tables() returns non-zero. */ ib_err_t ib_schema_tables_iterate( /*=====================*/ ib_trx_t ib_trx, /*!< in: transaction that owns the schema lock */ ib_schema_visitor_table_all_t visitor,/*!< in: visitor function */ void* arg) /*!< in: argument passed to the visitor function */ { ib_err_t err; dict_table_t* table; ib_crsr_t ib_crsr; ib_err_t crsr_err; ib_tpl_t ib_tpl = NULL; IB_CHECK_PANIC(); if (!ib_schema_lock_is_exclusive(ib_trx)) { return(DB_SCHEMA_NOT_LOCKED); } table = ib_lookup_table_by_name("SYS_TABLES"); if (table != NULL) { dict_table_increment_handle_count(table, TRUE); err = ib_create_cursor(&ib_crsr, table, 0, (trx_t*) ib_trx); } else { return(DB_TABLE_NOT_FOUND); } if (err == DB_SUCCESS) { err = ib_cursor_first(ib_crsr); } ib_tpl = ib_clust_read_tuple_create(ib_crsr); while (err == DB_SUCCESS) { const void* ptr; ib_col_meta_t ib_col_meta; err = ib_cursor_read_row(ib_crsr, ib_tpl); if (err == DB_SUCCESS) { ib_ulint_t len; ptr = ib_col_get_value(ib_tpl, 0); /* Can't have NULL columns. */ ut_a(ptr != NULL); len = ib_col_get_meta_low(ib_tpl, 0, &ib_col_meta); ut_a(len != UNIV_SQL_NULL); if (visitor(arg, (const char*) ptr, len)) { break; } err = ib_cursor_next(ib_crsr); } } ib_tuple_delete(ib_tpl); crsr_err = ib_cursor_close(ib_crsr); ut_a(crsr_err == DB_SUCCESS); if (err == DB_END_OF_INDEX) { err = DB_SUCCESS; } return(err); } /*************************************************************//** Convert and write an INT column value to an InnoDB tuple. @return DB_SUCCESS or error */ UNIV_INLINE ib_err_t ib_tuple_write_int( /*===============*/ ib_tpl_t ib_tpl, /*!< upd: tuple to write to */ ulint col_no, /*!< in: column number */ const void* value, /*!< in: integer value */ ulint value_len) /*!< in: sizeof value type */ { const dfield_t* dfield; ulint data_len; ulint type_len; ib_tuple_t* tuple = (ib_tuple_t*) ib_tpl; UT_DBG_ENTER_FUNC; ut_a(col_no < ib_tuple_get_n_cols(ib_tpl)); dfield = ib_col_get_dfield(tuple, col_no); data_len = dfield_get_len(dfield); type_len = dtype_get_len(dfield_get_type(dfield)); if (dtype_get_mtype(dfield_get_type(dfield)) != DATA_INT || value_len != data_len) { return(DB_DATA_MISMATCH); } return(ib_col_set_value(ib_tpl, col_no, value, type_len)); } /*****************************************************************//** Write an integer value to a column. Integers are stored in big-endian format and will need to be converted from the host format. @return DB_SUCESS or error */ ib_err_t ib_tuple_write_i8( /*==============*/ ib_tpl_t ib_tpl, /*!< upd: tuple to write to */ int col_no, /*!< in: column number */ ib_i8_t val) /*!< in: value to write */ { return(ib_col_set_value(ib_tpl, col_no, &val, sizeof(val))); } /*****************************************************************//** Write an integer value to a column. Integers are stored in big-endian format and will need to be converted from the host format. @return DB_SUCESS or error */ ib_err_t ib_tuple_write_i16( /*===============*/ ib_tpl_t ib_tpl, /*!< upd: tuple to write to */ int col_no, /*!< in: column number */ ib_i16_t val) /*!< in: value to write */ { return(ib_col_set_value(ib_tpl, col_no, &val, sizeof(val))); } /*****************************************************************//** Write an integer value to a column. Integers are stored in big-endian format and will need to be converted from the host format. @return DB_SUCCESS or error */ ib_err_t ib_tuple_write_i32( /*===============*/ ib_tpl_t ib_tpl, /*!< upd: tuple to write to */ int col_no, /*!< in: column number */ ib_i32_t val) /*!< in: value to write */ { return(ib_col_set_value(ib_tpl, col_no, &val, sizeof(val))); } /*****************************************************************//** Write an integer value to a column. Integers are stored in big-endian format and will need to be converted from the host format. @return DB_SUCCESS or error */ ib_err_t ib_tuple_write_i64( /*===============*/ ib_tpl_t ib_tpl, /*!< upd: tuple to write to */ int col_no, /*!< in: column number */ ib_i64_t val) /*!< in: value to write */ { return(ib_col_set_value(ib_tpl, col_no, &val, sizeof(val))); } /*****************************************************************//** Write an integer value to a column. Integers are stored in big-endian format and will need to be converted from the host format. @return DB_SUCCESS or error */ ib_err_t ib_tuple_write_u8( /*==============*/ ib_tpl_t ib_tpl, /*!< upd: tuple to write to */ int col_no, /*!< in: column number */ ib_u8_t val) /*!< in: value to write */ { return(ib_col_set_value(ib_tpl, col_no, &val, sizeof(val))); } /*****************************************************************//** Write an integer value to a column. Integers are stored in big-endian format and will need to be converted from the host format. @return DB_SUCCESS or error */ ib_err_t ib_tuple_write_u16( /*===============*/ ib_tpl_t ib_tpl, /*!< upd: tupe to write to */ int col_no, /*!< in: column number */ ib_u16_t val) /*!< in: value to write */ { return(ib_col_set_value(ib_tpl, col_no, &val, sizeof(val))); } /*****************************************************************//** Write an integer value to a column. Integers are stored in big-endian format and will need to be converted from the host format. @return DB_SUCCESS or error */ ib_err_t ib_tuple_write_u32( /*===============*/ ib_tpl_t ib_tpl, /*!< upd: tuple to write to */ int col_no, /*!< in: column number */ ib_u32_t val) /*!< in: value to write */ { return(ib_col_set_value(ib_tpl, col_no, &val, sizeof(val))); } /*****************************************************************//** Write an integer value to a column. Integers are stored in big-endian format and will need to be converted from the host format. @return DB_SUCCESS or error */ ib_err_t ib_tuple_write_u64( /*===============*/ ib_tpl_t ib_tpl, /*!< upd: tuple to write to */ int col_no, /*!< in: column number */ ib_u64_t val) /*!< in: value to write */ { return(ib_col_set_value(ib_tpl, col_no, &val, sizeof(val))); } /*****************************************************************//** Inform the cursor that it's the start of an SQL statement. */ void ib_cursor_stmt_begin( /*=================*/ ib_crsr_t ib_crsr) { ib_cursor_t* cursor = (ib_cursor_t*) ib_crsr; cursor->prebuilt->sql_stat_start = TRUE; } /*****************************************************************//** Write a double value to a column. @return DB_SUCCESS or error */ ib_err_t ib_tuple_write_double( /*==================*/ ib_tpl_t ib_tpl, /*!< upd: tuple to write to */ int col_no, /*!< in: column number */ double val) /*!< in: value to write */ { const dfield_t* dfield; ib_tuple_t* tuple = (ib_tuple_t*) ib_tpl; UT_DBG_ENTER_FUNC; dfield = ib_col_get_dfield(tuple, col_no); if (dtype_get_mtype(dfield_get_type(dfield)) == DATA_DOUBLE) { return(ib_col_set_value(ib_tpl, col_no, &val, sizeof(val))); } else { return(DB_DATA_MISMATCH); } } /*************************************************************//** Read a double column value from an InnoDB tuple. @return DB_SUCCESS or error */ ib_err_t ib_tuple_read_double( /*=================*/ ib_tpl_t ib_tpl, /*!< in: InnoDB tuple */ ib_ulint_t col_no, /*!< in: column number */ double* dval) /*!< out: double value */ { ib_err_t err; const dfield_t* dfield; ib_tuple_t* tuple = (ib_tuple_t*) ib_tpl; UT_DBG_ENTER_FUNC; dfield = ib_col_get_dfield(tuple, col_no); if (dtype_get_mtype(dfield_get_type(dfield)) == DATA_DOUBLE) { ib_col_copy_value_low(ib_tpl, col_no, dval, sizeof(*dval)); err = DB_SUCCESS; } else { err = DB_DATA_MISMATCH; } return(err); } /*****************************************************************//** Write a float value to a column. @return DB_SUCCESS or error */ ib_err_t ib_tuple_write_float( /*=================*/ ib_tpl_t ib_tpl, /*!< upd: tuple to write to */ int col_no, /*!< in: column number */ float val) /*!< in: value to write */ { const dfield_t* dfield; ib_tuple_t* tuple = (ib_tuple_t*) ib_tpl; UT_DBG_ENTER_FUNC; dfield = ib_col_get_dfield(tuple, col_no); if (dtype_get_mtype(dfield_get_type(dfield)) == DATA_FLOAT) { return(ib_col_set_value(ib_tpl, col_no, &val, sizeof(val))); } else { return(DB_DATA_MISMATCH); } } /*************************************************************//** Read a float value from an InnoDB tuple. @return DB_SUCCESS or error */ ib_err_t ib_tuple_read_float( /*================*/ ib_tpl_t ib_tpl, /*!< in: InnoDB tuple */ ib_ulint_t col_no, /*!< in: column number */ float* fval) /*!< out: float value */ { ib_err_t err; const dfield_t* dfield; ib_tuple_t* tuple = (ib_tuple_t*) ib_tpl; UT_DBG_ENTER_FUNC; dfield = ib_col_get_dfield(tuple, col_no); if (dtype_get_mtype(dfield_get_type(dfield)) == DATA_FLOAT) { ib_col_copy_value_low(ib_tpl, col_no, fval, sizeof(*fval)); err = DB_SUCCESS; } else { err = DB_DATA_MISMATCH; } return(err); } /*************************************************************//** Set the message logging function. */ void ib_logger_set( /*==========*/ ib_msg_log_t ib_msg_log, /*!< in: the logging function */ ib_msg_stream_t ib_msg_stream) /*!< in: the message stream, this is the first argument to the logging function */ { ib_logger = (ib_logger_t) ib_msg_log; ib_stream = (ib_stream_t) ib_msg_stream; } /*************************************************************//** Convert an error number to a human readable text message. The returned string is static and should not be freed or modified. @return string, describing the error */ const char* ib_strerror( /*========*/ ib_err_t num) /*!< in: error number */ { switch (num) { case DB_SUCCESS: return("Success"); case DB_ERROR: return("Generic error"); case DB_OUT_OF_MEMORY: return("Cannot allocate memory"); case DB_OUT_OF_FILE_SPACE: return("Out of disk space"); case DB_LOCK_WAIT: return("Lock wait"); case DB_DEADLOCK: return("Deadlock"); case DB_ROLLBACK: return("Rollback"); case DB_DUPLICATE_KEY: return("Duplicate key"); case DB_QUE_THR_SUSPENDED: return("The queue thread has been suspended"); case DB_MISSING_HISTORY: return("Required history data has been deleted"); case DB_CLUSTER_NOT_FOUND: return("Cluster not found"); case DB_TABLE_NOT_FOUND: return("Table not found"); case DB_MUST_GET_MORE_FILE_SPACE: return("More file space needed"); case DB_TABLE_IS_BEING_USED: return("Table is being used"); case DB_TOO_BIG_RECORD: return("Record too big"); case DB_LOCK_WAIT_TIMEOUT: return("Lock wait timeout"); case DB_NO_REFERENCED_ROW: return("Referenced key value not found"); case DB_ROW_IS_REFERENCED: return("Row is referenced"); case DB_CANNOT_ADD_CONSTRAINT: return("Cannot add constraint"); case DB_CORRUPTION: return("Data structure corruption"); case DB_COL_APPEARS_TWICE_IN_INDEX: return("Column appears twice in index"); case DB_CANNOT_DROP_CONSTRAINT: return("Cannot drop constraint"); case DB_NO_SAVEPOINT: return("No such savepoint"); case DB_TABLESPACE_ALREADY_EXISTS: return("Tablespace already exists"); case DB_TABLESPACE_DELETED: return("No such tablespace"); case DB_LOCK_TABLE_FULL: return("Lock structs have exhausted the buffer pool"); case DB_FOREIGN_DUPLICATE_KEY: return("Foreign key activated with duplicate keys"); case DB_TOO_MANY_CONCURRENT_TRXS: return("Too many concurrent transactions"); case DB_UNSUPPORTED: return("Unsupported"); case DB_PRIMARY_KEY_IS_NULL: return("Primary key is NULL"); case DB_FAIL: return("Failed, retry may succeed"); case DB_OVERFLOW: return("Overflow"); case DB_UNDERFLOW: return("Underflow"); case DB_STRONG_FAIL: return("Failed, retry will not succeed"); case DB_ZIP_OVERFLOW: return("Zip overflow"); case DB_RECORD_NOT_FOUND: return("Record not found"); case DB_END_OF_INDEX: return("End of index"); case DB_SCHEMA_ERROR: return("Error while validating a table or index schema"); case DB_DATA_MISMATCH: return("Type mismatch"); case DB_SCHEMA_NOT_LOCKED: return("Schema not locked"); case DB_NOT_FOUND: return("Not found"); case DB_READONLY: return("Readonly"); case DB_INVALID_INPUT: return("Invalid input"); case DB_FATAL: return("InnoDB fatal error"); case DB_INTERRUPTED: return("Operation interrupted"); /* do not add default: in order to produce a warning if new code is added to the enum but not added here */ } /* NOT REACHED */ return("Unknown error"); } extern ib_panic_function_t ib_panic; void ib_set_panic_handler(ib_panic_handler_t new_panic_handler) { ib_panic = new_panic_handler; } extern ib_trx_is_interrupted_handler_t ib_trx_is_interrupted; void ib_set_trx_is_interrupted_handler(ib_trx_is_interrupted_handler_t handler) { ib_trx_is_interrupted = handler; } ib_err_t ib_get_duplicate_key(ib_trx_t ib_trx, const char **table_name, const char **index_name) { trx_t* trx = (trx_t*) ib_trx; if (trx->error_info == NULL) return DB_ERROR; *table_name = trx->error_info->table_name; *index_name = trx->error_info->name; return DB_SUCCESS; } ib_err_t ib_get_table_statistics(ib_crsr_t ib_crsr, ib_table_stats_t *table_stats, size_t sizeof_ib_table_stats_t) { ib_cursor_t* cursor = (ib_cursor_t*) ib_crsr; dict_table_t* table = cursor->prebuilt->table; if (table->stat_initialized != TRUE) { dict_update_statistics(table); } table_stats->stat_n_rows= table->stat_n_rows; table_stats->stat_clustered_index_size= table->stat_clustered_index_size * UNIV_PAGE_SIZE; table_stats->stat_sum_of_other_index_sizes = table->stat_sum_of_other_index_sizes * UNIV_PAGE_SIZE; table_stats->stat_modified_counter = table->stat_modified_counter; return DB_SUCCESS; } ib_err_t ib_get_index_stat_n_diff_key_vals(ib_crsr_t ib_crsr, const char* index_name, ib_u64_t *ncols, ib_i64_t **n_diff) { ib_cursor_t* cursor = (ib_cursor_t*) ib_crsr; dict_table_t* table = cursor->prebuilt->table; dict_index_t* index; if (table->stat_initialized != TRUE) { dict_update_statistics(table); } index = dict_table_get_index_on_name(table, index_name); if (index == NULL) return DB_NOT_FOUND; *ncols = index->n_uniq; *n_diff = (ib_int64_t*) malloc(sizeof(ib_int64_t) * index->n_uniq); dict_index_stat_mutex_enter(index); memcpy(*n_diff, index->stat_n_diff_key_vals, sizeof(ib_int64_t) * index->n_uniq); dict_index_stat_mutex_exit(index); return DB_SUCCESS; } ib_err_t ib_update_table_statistics(ib_crsr_t crsr) { ib_cursor_t* cursor = (ib_cursor_t*) crsr; dict_table_t* table = cursor->prebuilt->table; dict_update_statistics(table); return DB_SUCCESS; } ib_err_t ib_error_inject(int error_to_inject) { if (error_to_inject == 1) { srv_panic(DB_CORRUPTION, "test panic message"); return DB_SUCCESS; } return DB_ERROR; } haildb-2.3.2/api/api0sql.c0000644000175000017500000001347511513177357016143 0ustar00pcrewspcrews00000000000000/*********************************************************************** Copyright (c) 2009 Innobase Oy. All rights reserved. Copyright (c) 2009 Oracle. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ************************************************************************/ #include "config.h" #include #include #include #include #ifdef HAVE_UNISTD_H #include #endif #include "dict0dict.h" #include "pars0pars.h" #include "que0que.h" #include "trx0roll.h" #include "api0api.h" static int api_sql_enter_func_enabled = 0; #define UT_DBG_ENTER_FUNC_ENABLED api_sql_enter_func_enabled /*********************************************************************//** Function to parse ib_exec_sql() and ib_exec_ddl_sql() args. @return own: info struct */ static pars_info_t* ib_exec_vsql( /*=========*/ int n_args, /*!< in: no. of args */ va_list ap) /*!< in: arg list */ { int i; pars_info_t* info; UT_DBG_ENTER_FUNC; info = pars_info_create(); for (i = 0; i < n_args; ++i) { ib_col_type_t type; type = va_arg(ap, ib_col_type_t); switch (type) { case IB_CHAR: case IB_VARCHAR: { const char* n; const char* v; char prefix; n = va_arg(ap, const char *); v = va_arg(ap, const char *); prefix = *n; ut_a(prefix == ':' || prefix == '$'); ++n; if (prefix == '$') { pars_info_add_id(info, n, v); } else { pars_info_add_str_literal(info, n, v); } break; } case IB_INT: { byte* p; /* dest buffer */ ulint l; /* length */ ulint s; /* TRUE if signed integer */ const char* n; /* literal name */ ulint prtype; l = va_arg(ap, ib_ulint_t); s = va_arg(ap, ib_ulint_t); n = va_arg(ap, const char *); prtype = s ? 0 : DATA_UNSIGNED; p = mem_heap_alloc(info->heap, l); switch (l) { case 1: { byte v; v = va_arg(ap, int); mach_write_int_type(p, (byte*)&v, l, s); break; } case 2: { ib_uint16_t v; v = va_arg(ap, int); mach_write_int_type(p, (byte*)&v, l, s); break; } case 4: { ib_uint32_t v; v = va_arg(ap, ib_uint32_t); mach_write_int_type(p, (byte*)&v, l, s); break; } case 8: { ib_uint64_t v; v = va_arg(ap, ib_uint64_t); mach_write_int_type(p, (byte*)&v, l, s); break; } default: ut_error; } pars_info_add_literal(info, n, p, l, DATA_INT, prtype); break; } case IB_SYS: { const char* n; pars_user_func_cb_t f; void* a; n = va_arg(ap, const char *); f = va_arg(ap, pars_user_func_cb_t); a = va_arg(ap, void*); pars_info_add_function(info, n, f, a); break; } default: /* FIXME: Do the other types too */ ut_error; } } return(info); } /*********************************************************************//** Execute arbitrary SQL using InnoDB's internal parser. The statement is executed in a new transaction. Table name parameters must be prefixed with a '$' symbol and variables with ':' @return DB_SUCCESS or error code */ ib_err_t ib_exec_sql( /*========*/ const char* sql, /*!< in: sql to execute */ ib_ulint_t n_args, /*!< in: no. of args */ ...) { va_list ap; trx_t* trx; ib_err_t err; pars_info_t* info; UT_DBG_ENTER_FUNC; va_start(ap, n_args); info = ib_exec_vsql(n_args, ap); va_end(ap); /* We use the private SQL parser of Innobase to generate the query graphs needed to execute the SQL statement. */ trx = trx_allocate_for_client(NULL); err = trx_start(trx, ULINT_UNDEFINED); ut_a(err == DB_SUCCESS); trx->op_info = "exec client sql"; dict_mutex_enter(); /* Note that we've already acquired the dictionary mutex. */ err = que_eval_sql(info, sql, FALSE, trx); ut_a(err == DB_SUCCESS); dict_mutex_exit(); if (err != DB_SUCCESS) { trx_rollback(trx, FALSE, NULL); } else { trx_commit(trx); } trx->op_info = ""; trx_free_for_client(trx); return(err); } /*********************************************************************//** Execute arbitrary SQL using InnoDB's internal parser. The statement is executed in a background transaction. It will lock the data dictionary lock for the duration of the query. @return DB_SUCCESS or error code */ ib_err_t ib_exec_ddl_sql( /*============*/ const char* sql, /*!< in: sql to execute */ ib_ulint_t n_args, /*!< in: no. of args */ ...) { va_list ap; trx_t* trx; ib_err_t err; pars_info_t* info; int started; UT_DBG_ENTER_FUNC; va_start(ap, n_args); info = ib_exec_vsql(n_args, ap); va_end(ap); /* We use the private SQL parser of Innobase to generate the query graphs needed to execute the SQL statement. */ trx = trx_allocate_for_background(); started = trx_start(trx, ULINT_UNDEFINED); ut_a(started); trx->op_info = "exec client ddl sql"; err = ib_schema_lock_exclusive((ib_trx_t) trx); ut_a(err == DB_SUCCESS); /* Note that we've already acquired the dictionary mutex by setting reserve_dict_mutex to FALSE. */ err = que_eval_sql(info, sql, FALSE, trx); ut_a(err == DB_SUCCESS); ib_schema_unlock((ib_trx_t) trx); if (err != DB_SUCCESS) { trx_rollback(trx, FALSE, NULL); } else { trx_commit(trx); } trx->op_info = ""; trx_free_for_background(trx); return(err); } haildb-2.3.2/api/api0ucode.c0000644000175000017500000001545211513177357016440 0ustar00pcrewspcrews00000000000000/*********************************************************************** Copyright (c) 2008 Innobase Oy. All rights reserved. Copyright (c) 2008 Oracle. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ************************************************************************/ #include "univ.i" #include "ut0mem.h" #include "api0ucode.h" #include #ifdef HAVE_STRINGS_H #include #endif /**********************************************************************//** @file api/api0ucode.c Determines the connection character set. @return connection character set */ UNIV_INTERN const charset_t* ib_ucode_get_connection_charset(void) /*========-========================*/ { return(NULL); } /**********************************************************************//** Determines the character set based on id. FIXME: If the id can't be found then what do we do, return some default ? @return character set or NULL */ UNIV_INTERN const charset_t* ib_ucode_get_charset( /*=================*/ ulint id) /*!< in: Charset-collation code */ { return(NULL); } /******************************************************************//** Get the variable length bounds of the given (multibyte) character set. */ UNIV_INTERN void ib_ucode_get_charset_width( /*=======================*/ const charset_t*cs, /*!< in: Charset */ ulint* mbminlen, /*!< out: min len of a char (in bytes) */ ulint* mbmaxlen) /*!< out: max len of a char (in bytes) */ { *mbminlen = *mbmaxlen = 0; if (cs) { //FIXME //*mbminlen = charset_get_minlen(cs); //*mbmaxlen = charset_get_maxlen(cs); } } /******************************************************************//** Compare two strings ignoring case. @return 0 if equal */ int ib_utf8_strcasecmp( /*================*/ const char* p1, /*!< in: string to compare */ const char* p2) /*!< in: string to compare */ { /* FIXME: Call the UTF-8 comparison function. */ /* FIXME: This should take cs as the parameter. */ return(strcasecmp(p1, p2)); } /******************************************************************//** Compare two strings ignoring case. @return 0 if equal */ int ib_utf8_strncasecmp( /*=================*/ const char* p1, /*!< in: string to compare */ const char* p2, /*!< in: string to compare */ ulint len) /*!< in: length of string */ { /* FIXME: Call the UTF-8 comparison function. */ /* FIXME: This should take cs as the parameter. */ /* FIXME: Which function? Note that this is locale-dependent. For example, there is a capital dotted i and a lower-case dotless I (U+0130 and U+0131, respectively). In many other locales, I=i but not in Turkish. */ return(strncasecmp(p1, p2, len)); } /******************************************************************//** Makes all characters in a NUL-terminated UTF-8 string lower case. */ UNIV_INTERN void ib_utf8_casedown( /*=============*/ char* a) /*!< in/out: str to put in lower case */ { /* FIXME: Call the UTF-8 tolower() equivalent. */ /* FIXME: Is this function really needed? The proper implementation is locale-dependent. In Turkish, the lower-case counterpart of the upper-case I (U+0049, one byte) is the dotless i (U+0131, two bytes in UTF-8). That cannot even be converted in place. */ while (*a) { *a = tolower(*a); ++a; } } /******************************************************************//** Converts an identifier to a table name. */ UNIV_INTERN void ib_utf8_convert_from_table_id( /*==========================*/ const charset_t*cs, /*!< in: the 'from' character set */ char* to, /*!< out: converted identifier */ const char* from, /*!< in: identifier to convert */ ulint len) /*!< in: length of 'to', in bytes; should be at least 5 * strlen(to) + 1 */ { /* FIXME: why 5*strlen(to)+1? That is a relic from the MySQL 5.1 filename safe encoding that encodes some chars in four-digit hexadecimal notation, such as @0023. Do we even need this function? Could the files be named by table id or something? */ /* FIXME: Call the UTF-8 equivalent */ strncpy(to, from, len); } /******************************************************************//** Converts an identifier to UTF-8. */ UNIV_INTERN void ib_utf8_convert_from_id( /*=====================*/ const charset_t*cs, /*!< in: the 'from' character set */ char* to, /*!< out: converted identifier */ const char* from, /*!< in: identifier to convert */ ulint len) /*!< in: length of 'to', in bytes; should be at least 3 * strlen(to) + 1 */ { /* FIXME: why 3*strlen(to)+1? I suppose that it comes from MySQL, where the connection charset can be 8-bit, such as the "latin1" (really Windows Code Page 1252). Converting that to UTF-8 can take 1..3 characters per byte. */ /* FIXME: Do we even need this function? Can't we just assume that the connection character encoding always is UTF-8? (We may still want to support different collations for UTF-8.) */ /* FIXME: Call the UTF-8 equivalent */ strncpy(to, from, len); } /**********************************************************************//** Test whether a UTF-8 character is a space or not. @return TRUE if isspace(c) */ UNIV_INTERN int ib_utf8_isspace( /*============*/ const charset_t*cs, /*!< in: charset */ char c) /*!< in: character to test */ { /* FIXME: Call the equivalent UTF-8 function. */ /* FIXME: Do we really need this function? This is needed by the InnoDB foreign key parser in MySQL, because U+00A0 is a space in the MySQL connection charset latin1 but not in utf8. */ return(isspace(c)); } /******************************************************************//** This function is used to find the storage length in bytes of the characters that will fit into prefix_len bytes. @return number of bytes required to copy the characters that will fit into prefix_len bytes. */ UNIV_INTERN ulint ib_ucode_get_storage_size( /*======================*/ const charset_t*cs, /*!< in: character set */ ulint prefix_len, /*!< in: prefix length in bytes */ ulint str_len, /*!< in: length of the string in bytes */ const char* str) /*!< in: character string */ { /* FIXME: Do we really need this function? Can't we assume that all strings are UTF-8? (We still may want to support different collations.) */ return(ut_min(prefix_len, str_len)); } haildb-2.3.2/haildb.h0000644000175000017500000023550211513177357015246 0ustar00pcrewspcrews00000000000000/*********************************************************************** Copyright (c) 2008, 2009 Innobase Oy. All rights reserved. Copyright (c) 2008, 2009 Oracle. All rights reserved. Copyright (c) 2010 Stewart Smith This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ************************************************************************/ /*!< @file haildb.h */ #ifndef HAILDB_H #define HAILDB_H #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ #if defined(BUILDING_HAILDB) # if defined(HAVE_VISIBILITY) # define HAILDB_API __attribute__ ((visibility("default"))) # define HAILDB_LOCAL __attribute__ ((visibility("hidden"))) # elif defined (__SUNPRO_C) && (__SUNPRO_C >= 0x550) # define HAILDB_API __global # define HAILDB_LOCAL __hidden # elif defined(_MSC_VER) # define HAILDB_API extern __declspec(dllexport) # define HAILDB_LOCAL # endif /* defined(HAVE_VISIBILITY) */ #else /* defined(BUILDING_LIBDRIZZLE) */ # if defined(_MSC_VER) # define HAILDB_API extern __declspec(dllimport) # define HAILDB_LOCAL # else # define HAILDB_API # define HAILDB_LOCAL # endif /* defined(_MSC_VER) */ #endif /* defined(BUILDING_HAILDB) */ /** \enum db_err InnoDB error codes. Most of the error codes are internal to the engine and will not be seen by user applications. The partial error codes reflect the sub-state of an operation within InnoDB. Some of the error codes are deprecated and are no longer used. */ enum db_err { DB_SUCCESS = 10, /*!< A successult result */ /* The following are error codes */ DB_ERROR, /*!< This is a generic error code. It is used to classify error conditions that can't be represented by other codes */ DB_INTERRUPTED, /*!< An operation was interrupted by a user. */ DB_OUT_OF_MEMORY, /*!< Operation caused an out of memory error. Within InnoDB core code this is normally a fatal error */ DB_OUT_OF_FILE_SPACE, /*!< The operating system returned an out of file space error when trying to do an IO operation. */ DB_LOCK_WAIT, /*!< A lock request by transaction resulted in a lock wait. The thread is suspended internally by InnoDB and is put on a lock wait queue. */ DB_DEADLOCK, /*!< A lock request by a transaction resulted in a deadlock. The transaction was rolled back */ DB_ROLLBACK, /*!< Not used */ DB_DUPLICATE_KEY, /*!< A record insert or update violates a unique contraint. */ DB_QUE_THR_SUSPENDED, /*!< A query thread should be in state suspended but is trying to acquire a lock. Currently this is treated as a hard error and a violation of an invariant. */ DB_MISSING_HISTORY, /*!< Required history data has been deleted due to lack of space in rollback segment */ DB_CLUSTER_NOT_FOUND = 30, /*!< This error is not used */ DB_TABLE_NOT_FOUND, /*!< The table could not be found */ DB_MUST_GET_MORE_FILE_SPACE, /*!< The database has to be stopped and restarted with more file space */ DB_TABLE_IS_BEING_USED, /*!< The user is trying to create a table in the InnoDB data dictionary but a table with that name already exists */ DB_TOO_BIG_RECORD, /*!< A record in an index would not fit on a compressed page, or it would become bigger than 1/2 free space in an uncompressed page frame */ DB_LOCK_WAIT_TIMEOUT, /*!< Lock wait lasted too long */ DB_NO_REFERENCED_ROW, /*!< Referenced key value not found for a foreign key in an insert or update of a row */ DB_ROW_IS_REFERENCED, /*!< Cannot delete or update a row because it contains a key value which is referenced */ DB_CANNOT_ADD_CONSTRAINT, /*!< Adding a foreign key constraint to a table failed */ DB_CORRUPTION, /*!< Data structure corruption noticed */ DB_COL_APPEARS_TWICE_IN_INDEX, /*!< InnoDB cannot handle an index where same column appears twice */ DB_CANNOT_DROP_CONSTRAINT, /*!< Dropping a foreign key constraint from a table failed */ DB_NO_SAVEPOINT, /*!< No savepoint exists with the given name */ DB_TABLESPACE_ALREADY_EXISTS, /*!< We cannot create a new single-table tablespace because a file of the same name already exists */ DB_TABLESPACE_DELETED, /*!< Tablespace does not exist or is being dropped right now */ DB_LOCK_TABLE_FULL, /*!< Lock structs have exhausted the buffer pool (for big transactions, InnoDB stores the lock structs in the buffer pool) */ DB_FOREIGN_DUPLICATE_KEY, /*!< Foreign key constraints activated but the operation would lead to a duplicate key in some table */ DB_TOO_MANY_CONCURRENT_TRXS, /*!< When InnoDB runs out of the preconfigured undo slots, this can only happen when there are too many concurrent transactions */ DB_UNSUPPORTED, /*!< When InnoDB sees any artefact or a feature that it can't recoginize or work with e.g., FT indexes created by a later version of the engine. */ DB_PRIMARY_KEY_IS_NULL, /*!< A column in the PRIMARY KEY was found to be NULL */ DB_FATAL, /*!< The application should clean up and quite ASAP. Fatal error, InnoDB cannot continue operation without risking database corruption. */ /* The following are partial failure codes */ DB_FAIL = 1000, /*!< Partial failure code. */ DB_OVERFLOW, /*!< If an update or insert of a record doesn't fit in a Btree page */ DB_UNDERFLOW, /*!< If an update or delete of a record causes a Btree page to be below a minimum threshold */ DB_STRONG_FAIL, /*!< Failure to insert a secondary index entry to the insert buffer */ DB_ZIP_OVERFLOW, /*!< Failure trying to compress a page */ DB_RECORD_NOT_FOUND = 1500, /*!< Record not found */ DB_END_OF_INDEX, /*!< A cursor operation or search operation scanned to the end of the index. */ /* The following are API only error codes. */ DB_SCHEMA_ERROR = 2000, /*!< Generic schema error */ DB_DATA_MISMATCH, /*!< Column update or read failed because the types mismatch */ DB_SCHEMA_NOT_LOCKED, /*!< If an API function expects the schema to be locked in exclusive mode and if it's not then that API function will return this error code */ DB_NOT_FOUND, /*!< Generic error code for "Not found" type of errors */ DB_READONLY, /*!< Generic error code for "Readonly" type of errors */ DB_INVALID_INPUT, /*!< Generic error code for "Invalid input" type of errors */ }; #include #ifdef _MSC_VER #define strncasecmp _strnicmp #define strcasecmp _stricmp #endif /** \def UNIV_NO_IGNORE Some HailDB methods will produce warnings if the result of them is not checked in client code. This uses a GCC compiler extension. */ #if defined(__GNUC__) && (__GNUC__ > 2) && ! defined(__INTEL_COMPILER) #define UNIV_NO_IGNORE __attribute__ ((warn_unused_result)) #else #define UNIV_NO_IGNORE #endif /* __GNUC__ && __GNUC__ > 2 && !__INTEL_COMPILER */ /* See comment about ib_bool_t as to why the two macros are unsigned long. */ /** The boolean value of "true" used internally within InnoDB */ #define IB_TRUE 0x1UL /** The boolean value of "false" used internally within InnoDB */ #define IB_FALSE 0x0UL /* Basic types used by the InnoDB API. */ /** All InnoDB error codes are represented by ib_err_t. See \ref db_err for a complete list of possible error codes. */ typedef enum db_err ib_err_t; /** Representation of a byte within InnoDB */ typedef unsigned char ib_byte_t; /** Representation of an unsigned long int within InnoDB */ typedef unsigned long int ib_ulint_t; /** Representation of a void* within InnoDB */ typedef void* ib_opaque_t; /* Ideally we would like to have this as ib_byte_t, but we need to make it the same as the InnoDB internal ibool. */ /** Representation of a "boolean" type within InnoDB */ typedef ib_ulint_t ib_bool_t; /** A character set pointer */ typedef ib_opaque_t ib_charset_t; /* We assume C99 support except when using VisualStudio. */ #if !defined(_MSC_VER) #include #endif /* _MSC_VER */ /* Integer types used by the API. Microsft VS defines its own types and we use the Microsoft types when building with Visual Studio. */ #if defined(_MSC_VER) /** A signed 8 bit integral type. */ typedef __int8 ib_i8_t; #else /** A signed 8 bit integral type. */ typedef int8_t ib_i8_t; #endif #if defined(_MSC_VER) /** An unsigned 8 bit integral type. */ typedef unsigned __int8 ib_u8_t; #else /** An unsigned 8 bit integral type. */ typedef uint8_t ib_u8_t; #endif #if defined(_MSC_VER) /** A signed 16 bit integral type. */ typedef __int16 ib_i16_t; #else /** A signed 16 bit integral type. */ typedef int16_t ib_i16_t; #endif #if defined(_MSC_VER) /** An unsigned 16 bit integral type. */ typedef unsigned __int16 ib_u16_t; #else /** An unsigned 16 bit integral type. */ typedef uint16_t ib_u16_t; #endif #if defined(_MSC_VER) /** A signed 32 bit integral type. */ typedef __int32 ib_i32_t; #else /** A signed 32 bit integral type. */ typedef int32_t ib_i32_t; #endif #if defined(_MSC_VER) /** An unsigned 32 bit integral type. */ typedef unsigned __int32 ib_u32_t; #else /** An unsigned 32 bit integral type. */ typedef uint32_t ib_u32_t; #endif #if defined(_MSC_VER) /** A signed 64 bit integral type. */ typedef __int64 ib_i64_t; #else /** A signed 64 bit integral type. */ typedef int64_t ib_i64_t; #endif #if defined(_MSC_VER) /** An unsigned 64 bit integral type. */ typedef unsigned __int64 ib_u64_t; #else /** An unsigned 64 bit integral type. */ typedef uint64_t ib_u64_t; #endif /** The integral type that represents internal table and index ids. */ typedef ib_u64_t ib_id_t; /** @enum ib_cfg_type_t Possible types for a configuration variable. */ typedef enum { IB_CFG_IBOOL, /*!< The configuration parameter is of type ibool */ /* XXX Can we avoid having different types for ulint and ulong? - On Win64 "unsigned long" is 32 bits - ulong is always defined as "unsigned long" - On Win64 ulint is defined as 64 bit integer => On Win64 ulint != ulong. If we typecast all ulong and ulint variables to the smaller type ulong, then we will cut the range of the ulint variables. This is not a problem for most ulint variables because their max allowed values do not exceed 2^32-1 (e.g. log_groups is ulint but its max allowed value is 10). BUT buffer_pool_size and log_file_size allow up to 2^64-1. */ IB_CFG_ULINT, /*!< The configuration parameter is of type ulint */ IB_CFG_ULONG, /*!< The configuration parameter is of type ulong */ IB_CFG_TEXT, /*!< The configuration parameter is of type char* */ IB_CFG_CB /*!< The configuration parameter is a callback parameter */ } ib_cfg_type_t; /** @enum ib_col_type_t column types that are supported. */ typedef enum { IB_VARCHAR = 1, /*!< Character varying length. The column is not padded. */ IB_CHAR = 2, /*!< Fixed length character string. The column is padded to the right. */ IB_BINARY = 3, /*!< Fixed length binary, similar to IB_CHAR but the column is not padded to the right. */ IB_VARBINARY = 4, /*!< Variable length binary */ IB_BLOB = 5, /*!< Binary large object, or a TEXT type */ IB_INT = 6, /*!< Integer: can be any size from 1 - 8 bytes. If the size is 1, 2, 4 and 8 bytes then you can use the typed read and write functions. For other sizes you will need to use the ib_col_get_value() function and do the conversion yourself. */ IB_SYS = 8, /*!< System column, this column can be one of DATA_TRX_ID, DATA_ROLL_PTR or DATA_ROW_ID. */ IB_FLOAT = 9, /*!< C (float) floating point value. */ IB_DOUBLE = 10, /*!> C (double) floating point value. */ IB_DECIMAL = 11, /*!< Decimal stored as an ASCII string */ IB_VARCHAR_ANYCHARSET = 12, /*!< Any charset, varying length */ IB_CHAR_ANYCHARSET = 13 /*!< Any charset, fixed length */ } ib_col_type_t; /** @enum ib_tbl_fmt_t InnoDB table format types */ typedef enum { IB_TBL_REDUNDANT, /*!< Redundant row format, the column type and length is stored in the row.*/ IB_TBL_COMPACT, /*!< Compact row format, the column type is not stored in the row. The length is stored in the row but the storage format uses a compact format to store the length of the column data and record data storage format also uses less storage. */ IB_TBL_DYNAMIC, /*!< Compact row format. BLOB prefixes are not stored in the clustered index */ IB_TBL_COMPRESSED /*!< Similar to dynamic format but with pages compressed */ } ib_tbl_fmt_t; /** @enum ib_col_attr_t InnoDB column attributes */ typedef enum { IB_COL_NONE = 0, /*!< No special attributes. */ IB_COL_NOT_NULL = 1, /*!< Column data can't be NULL. */ IB_COL_UNSIGNED = 2, /*!< Column is IB_INT and unsigned. */ IB_COL_NOT_USED = 4, /*!< Future use, reserved. */ IB_COL_CUSTOM1 = 8, /*!< Custom precision type, this is a bit that is ignored by InnoDB and so can be set and queried by users. */ IB_COL_CUSTOM2 = 16, /*!< Custom precision type, this is a bit that is ignored by InnoDB and so can be set and queried by users. */ IB_COL_CUSTOM3 = 32 /*!< Custom precision type, this is a bit that is ignored by InnoDB and so can be set and queried by users. */ } ib_col_attr_t; /* Note: must match lock0types.h */ /** @enum ib_lck_mode_t InnoDB lock modes. */ typedef enum { IB_LOCK_IS = 0, /*!< Intention shared, an intention lock should be used to lock tables */ IB_LOCK_IX, /*!< Intention exclusive, an intention lock should be used to lock tables */ IB_LOCK_S, /*!< Shared locks should be used to lock rows */ IB_LOCK_X, /*!< Exclusive locks should be used to lock rows*/ IB_LOCK_NOT_USED, /*!< Future use, reserved */ IB_LOCK_NONE, /*!< This is used internally to note consistent read */ IB_LOCK_NUM = IB_LOCK_NONE /*!< number of lock modes */ } ib_lck_mode_t; /** @enum ib_srch_mode_t InnoDB cursor search modes for ib_cursor_moveto(). Note: Values must match those found in page0cur.h */ typedef enum { IB_CUR_G = 1, /*!< If search key is not found then position the cursor on the row that is greater than the search key */ IB_CUR_GE = 2, /*!< If the search key not found then position the cursor on the row that is greater than or equal to the search key */ IB_CUR_L = 3, /*!< If search key is not found then position the cursor on the row that is less than the search key */ IB_CUR_LE = 4 /*!< If search key is not found then position the cursor on the row that is less than or equal to the search key */ } ib_srch_mode_t; /** @enum ib_match_mode_t Various match modes used by ib_cursor_moveto() */ typedef enum { IB_CLOSEST_MATCH, /*!< Closest match possible */ IB_EXACT_MATCH, /*!< Search using a complete key value */ IB_EXACT_PREFIX /*!< Search using a key prefix which must match to rows: the prefix may contain an incomplete field (the last field in prefix may be just a prefix of a fixed length column) */ } ib_match_mode_t; /** @struct ib_col_meta_t InnoDB column meta data. */ typedef struct { ib_col_type_t type; /*!< Type of the column */ ib_col_attr_t attr; /*!< Column attributes */ ib_u32_t type_len; /*!< Length of type */ ib_u16_t client_type; /*!< 16 bits of data relevant only to the client. InnoDB doesn't care */ ib_charset_t* charset; /*!< Column charset */ } ib_col_meta_t; /* Note: Must be in sync with trx0trx.h */ /** @enum ib_trx_state_t The transaction state can be queried using the ib_trx_state() function. The InnoDB deadlock monitor can roll back a transaction and users should be prepared for this, especially where there is high contention. The way to determine the state of the transaction is to query it's state and check. */ typedef enum { IB_TRX_NOT_STARTED, /*!< Has not started yet, the transaction has not ben started yet.*/ IB_TRX_ACTIVE, /*!< The transaction is currently active and needs to be either committed or rolled back. */ IB_TRX_COMMITTED_IN_MEMORY, /*!< Not committed to disk yet */ IB_TRX_PREPARED /*!< Support for 2PC/XA */ } ib_trx_state_t; /* Note: Must be in sync with trx0trx.h */ /** @enum ib_trx_level_t Transaction isolation levels */ typedef enum { IB_TRX_READ_UNCOMMITTED = 0, /*!< Dirty read: non-locking SELECTs are performed so that we do not look at a possible earlier version of a record; thus they are not 'consistent' reads under this isolation level; otherwise like level 2 */ IB_TRX_READ_COMMITTED = 1, /*!< Somewhat Oracle-like isolation, except that in range UPDATE and DELETE we must block phantom rows with next-key locks; SELECT ... FOR UPDATE and ... LOCK IN SHARE MODE only lock the index records, NOT the gaps before them, and thus allow free inserting; each consistent read reads its own snapshot */ IB_TRX_REPEATABLE_READ = 2, /*!< All consistent reads in the same trx read the same snapshot; full next-key locking used in locking reads to block insertions into gaps */ IB_TRX_SERIALIZABLE = 3 /*!< All plain SELECTs are converted to LOCK IN SHARE MODE reads */ } ib_trx_level_t; /** @enum ib_shutdown_t When ib_shutdown() is called InnoDB may take a long time to shutdown because of background tasks e.g., purging deleted records. The following flags allow the user to control the shutdown behavior. */ typedef enum { IB_SHUTDOWN_NORMAL, /*!< Normal shutdown, do insert buffer merge and purge before complete shutdown. */ IB_SHUTDOWN_NO_IBUFMERGE_PURGE, /*!< Do not do a purge and index buffer merge at shutdown. */ IB_SHUTDOWN_NO_BUFPOOL_FLUSH /*!< Same as NO_IBUFMERGE_PURGE and in addition do not even flush the buffer pool to data files. No committed transactions are lost */ } ib_shutdown_t; /** Generical InnoDB callback prototype. */ typedef void (*ib_cb_t)(void); /** The first argument to the InnoDB message logging function. By default it's set to stderr. You should treat ib_msg_stream_t as a void*, since it will probably change in the future. */ typedef FILE* ib_msg_stream_t; /** All log messages are written to this function.It should have the same behavior as fprintf(3). */ typedef int (*ib_msg_log_t)(ib_msg_stream_t, const char*, ...); /* Note: This is to make it easy for API users to have type checking for arguments to our functions. Making it ib_opaque_t by itself will result in pointer decay resulting in subverting of the compiler's type checking. */ /** InnoDB tuple handle. This handle can refer to either a cluster index tuple or a secondary index tuple. There are two types of tuples for each type of index, making a total of four types of tuple handles. There is a tuple for reading the entire row contents and another for searching on the index key. */ typedef struct ib_tpl_struct* ib_tpl_t; /** InnoDB transaction handle, all database operations need to be covered by transactions. This handle represents a transaction. The handle can be created with ib_trx_begin(), you commit your changes with ib_trx_commit() and undo your changes using ib_trx_rollback(). If the InnoDB deadlock monitor rolls back the transaction then you need to free the transaction using the function ib_trx_release(). You can query the state of an InnoDB transaction by calling ib_trx_state(). */ typedef struct ib_trx_struct* ib_trx_t; /** InnoDB cursor handle */ typedef struct ib_crsr_struct* ib_crsr_t; /** InnoDB table schema handle */ typedef struct ib_tbl_sch_struct* ib_tbl_sch_t; /** InnoDB index schema handle */ typedef struct ib_idx_sch_struct* ib_idx_sch_t; /** Currently, this is also the number of callback functions in the struct. */ typedef enum { IB_SCHEMA_VISITOR_TABLE = 1, IB_SCHEMA_VISITOR_TABLE_COL = 2, IB_SCHEMA_VISITOR_TABLE_AND_INDEX = 3, IB_SCHEMA_VISITOR_TABLE_AND_INDEX_COL = 4 } ib_schema_visitor_version_t; /** Visit all tables in the InnoDB schem. */ typedef int (*ib_schema_visitor_table_all_t) ( /*!< return 0 on success, nonzero on failure (abort traversal) */ void* arg, /*!< User callback arg */ const char* name, /*!< Table name */ int name_len); /*!< Length of name in bytes */ /** Table visitor */ typedef int (*ib_schema_visitor_table_t) ( /*!< return 0 on success, nonzero on failure (abort traversal) */ void* arg, /*!< User callback arg */ const char* name, /*!< Table name */ ib_tbl_fmt_t tbl_fmt, /*!< Table type */ ib_ulint_t page_size, /*!< Table page size */ int n_cols, /*!< No. of cols defined */ int n_indexes); /*!< No. of indexes defined */ /** Table column visitor */ typedef int (*ib_schema_visitor_table_col_t) ( /*!< return 0 on success, nonzero on failure (abort traversal) */ void* arg, /*!< User callback arg */ const char* name, /*!< Column name */ ib_col_type_t col_type, /*!< Column type */ ib_ulint_t len, /*!< Column len */ ib_col_attr_t attr); /*!< Column attributes */ /** Index visitor */ typedef int (*ib_schema_visitor_index_t) ( /*!< return 0 on success, nonzero on failure (abort traversal) */ void* arg, /*!< User callback arg */ const char* name, /*!< Index name */ ib_bool_t clustered, /*!< True if clustered */ ib_bool_t unique, /*!< True if unique */ int n_cols); /*!< No. of cols defined */ /** Index column visitor */ typedef int (*ib_schema_visitor_index_col_t) ( /*!< return 0 on success, nonzero on failure (abort traversal) */ void* arg, /*!< User callback arg */ const char* name, /*!< Column name */ ib_ulint_t prefix_len); /*!< Prefix length */ /** Callback functions to traverse the schema of a table. */ typedef struct { ib_schema_visitor_version_t version; /*!< Visitor version */ ib_schema_visitor_table_t table; /*!< For travesing table info */ ib_schema_visitor_table_col_t table_col; /*!< For travesing table column info */ ib_schema_visitor_index_t index; /*!< For travesing index info */ ib_schema_visitor_index_col_t index_col; /*!< For travesing index column info */ } ib_schema_visitor_t; /*************************************************************//** This function is used to compare two data fields for which the data type is such that we must use the client code to compare them. */ typedef int (*ib_client_cmp_t)( /*!< out: 1, 0, -1, if a is greater, equal, less than b, respectively */ const ib_col_meta_t* col_meta, /*!< in: column meta data */ const ib_byte_t*p1, /*!< in: key */ ib_ulint_t p1_len, /*!< in: key length */ const ib_byte_t*p2, /*!< in: key */ ib_ulint_t p2_len); /*!< in: key length */ /* This should be the same as univ.i */ /** Represents SQL_NULL length */ #define IB_SQL_NULL 0xFFFFFFFF /** The number of system columns in a row. */ #define IB_N_SYS_COLS 3 /** The maximum length of a text column. */ #define MAX_TEXT_LEN 4096 /* MySQL uses 3 byte UTF-8 encoding. */ /** The maximum length of a column name in a table schema. */ #define IB_MAX_COL_NAME_LEN (64 * 3) /** The maximum length of a table name (plus database name). */ #define IB_MAX_TABLE_NAME_LEN (64 * 3) /*! @def ib_tbl_sch_add_blob_col(s, n) Add a BLOB column to a table schema. @param s is the the schema handle @param n is the column name */ #define ib_tbl_sch_add_blob_col(s, n) \ ib_table_schema_add_col(s, n, IB_BLOB, IB_COL_NONE, 0, 0) /*! @def ib_tbl_sch_add_text_col(s, n) Add a BLOB column to a table schema. @param s is the the schema handle @param n is the column name Add a TEXT column to a table schema. */ #define ib_tbl_sch_add_text_col(s, n) \ ib_table_schema_add_col(s, n, IB_VARCHAR, IB_COL_NONE, 0, MAX_TEXT_LEN) /*! @def ib_tbl_sch_add_varchar_col(s, n, l) Add a VARCHAR column to a table schema. @param s is the schema handle @param n is the column name @param l the max length of the VARCHAR column @return DB_SUCCESS or error code */ #define ib_tbl_sch_add_varchar_col(s, n, l) \ ib_table_schema_add_col(s, n, IB_VARCHAR, IB_COL_NONE, 0, l) /*! @def ib_tbl_sch_add_u32_col(s, n) Add an UNSIGNED INT column to a table schema. @param s is the schema handle @param n is the column name @return DB_SUCCESS or error code */ #define ib_tbl_sch_add_u32_col(s, n) \ ib_table_schema_add_col(s, n, IB_INT, IB_COL_UNSIGNED, 0, 4) /*! @def ib_tbl_sch_add_u64_col(s, n) Add an UNSIGNED BIGINT column to a table schema. @param s is the schema handle @param n is the column name @return DB_SUCCESS or error code */ #define ib_tbl_sch_add_u64_col(s, n) \ ib_table_schema_add_col(s, n, IB_INT, IB_COL_UNSIGNED, 0, 8) /*! @def ib_tbl_sch_add_u64_notnull_col(s, n) Add an UNSIGNED BIGINT NOT NULL column to a table schema. @param s is the schema handle @param n is the column name @return DB_SUCCESS or error code */ #define ib_tbl_sch_add_u64_notnull_col(s, n) \ ib_table_schema_add_col(s, n, IB_INT, \ IB_COL_NOT_NULL | IB_COL_UNSIGNED,0,\ 8) /*! @def ib_cfg_set_int(name, value) Set an int configuration variable. @ingroup config @param name is the config variable name @param value is the integer value of the variable @return DB_SUCCESS or error code */ #define ib_cfg_set_int(name, value) ib_cfg_set(name, value) /*! @def ib_cfg_set_text(name, value) Set a text configuration variable. @ingroup config @param name is the config variable name @param value is the char* value of the variable @return DB_SUCCESS or error code */ #define ib_cfg_set_text(name, value) ib_cfg_set(name, value) /*! @def ib_cfg_set_bool_on(name) Set a boolean configuration variable to IB_TRUE. @ingroup config @param name is the config variable name @return DB_SUCCESS or error code */ #define ib_cfg_set_bool_on(name) ib_cfg_set(name, IB_TRUE) /*! @def ib_cfg_set_bool_off(name) Set a boolean configuration variable to IB_FALSE. @ingroup config @param name is the config variable name @return DB_SUCCESS or error code */ #define ib_cfg_set_bool_off(name) ib_cfg_set(name, IB_FALSE) /*! @def ib_cfg_set_callback(name, value) Set a generic ib_cb_t callback function. @ingroup config @param name is the config variable name @param value is a pointer to a callback function @return DB_SUCCESS or error code */ #define ib_cfg_set_callback(name, value) ib_cfg_set(name, value) /** Callback function to compare InnoDB key columns in an index. */ extern ib_client_cmp_t ib_client_compare; /** Define the Doxygen groups: @defgroup init Startup/Shutdown functions @defgroup cursor Cursor functions @defgroup trx Transaction functions @defgroup config Configuration functions @defgroup ddl DDL functions @defgroup misc Miscellaneous functions @defgroup tuple Tuple functions @defgroup sql SQL functions @defgroup dml DML functions @defgroup debug Debug and Testing functions */ /*****************************************************************//** Return the API version number, the version number format is: | 16 bits future use | 16 bits current | 16 bits revision | 16 bits age | - If the library source code has changed at all since the last release, then revision will be incremented (`c:r:a' becomes `c:r+1:a'). - If any interfaces have been added, removed, or changed since the last update, current will be incremented, and revision will be set to 0. - If any interfaces have been added (but not changed or removed) since the last release, then age will be incremented. - If any interfaces have been changed or removed since the last release, then age will be set to 0. @ingroup misc @return API version number */ HAILDB_API ib_u64_t ib_api_version(void) UNIV_NO_IGNORE; /*=================*/ /*****************************************************************//** Initialize the InnoDB engine. This must be called prior to calling any other InnoDB API function. You can call only the ib_cfg_*() functions between calls to ib_init() and ib_startup(). No other HailDB functions should be called. @ingroup init @return DB_SUCCESS or error code */ HAILDB_API ib_err_t ib_init(void) UNIV_NO_IGNORE; /*=========*/ /*****************************************************************//** Startup the InnoDB engine. If this function is called on a non-existent database then based on the default or user specified configuration settings it will create all the necessary files. If the database was shutdown cleanly but the user deleted the REDO log files then it will recreate the REDO log files. @ingroup init @param format is the max file format name that the engine supports. Currently this is either Antelope or Barracuda although more may be added in the future without API changes. @return DB_SUCCESS or error code @see DB_SUCCESS */ HAILDB_API ib_err_t ib_startup( /*=======*/ const char* format) UNIV_NO_IGNORE; /*****************************************************************//** Shutdown the InnoDB engine. Call this function when they are no active transactions. It will close all files and release all memory on successful completion. All internal variables will be reset to their default values. @ingroup init @param flag is the shutdown flag @return DB_SUCCESS or error code */ HAILDB_API ib_err_t ib_shutdown( /*========*/ ib_shutdown_t flag) UNIV_NO_IGNORE; /*****************************************************************//** Start a transaction that's been rolled back. This special function exists for the case when InnoDB's deadlock detector has rolledack a transaction. While the transaction has been rolled back the handle is still valid and can be reused by calling this function. If you don't want to reuse the transaction handle then you can free the handle by calling ib_trx_release(). @ingroup trx @param ib_trx is the transaction to restart @param ib_trx_level is the transaction isolation level @return innobase txn handle */ HAILDB_API ib_err_t ib_trx_start( /*=========*/ ib_trx_t ib_trx, ib_trx_level_t ib_trx_level) UNIV_NO_IGNORE; /*****************************************************************//** Begin a transaction. This will allocate a new transaction handle and put the transaction in the active state. @ingroup trx @param ib_trx_level is the transaction isolation level @return innobase txn handle */ HAILDB_API ib_trx_t ib_trx_begin( /*=========*/ ib_trx_level_t ib_trx_level) UNIV_NO_IGNORE; /*****************************************************************//** Set client data for a transaction. This is passed back to the client in the trx_is_interrupted callback. HailDB will only ever pass this around, it will never dereference it. @ingroup trx @param ib_trx is the transaction to set the client data for @param client_data is client program's data about this transaction */ HAILDB_API void ib_trx_set_client_data( /*=========*/ ib_trx_t ib_trx, /*!< in: transaction */ void* client_data); /*****************************************************************//** Query the transaction's state. This function can be used to check for the state of the transaction in case it has been rolled back by the InnoDB deadlock detector. Note that when a transaction is selected as a victim for rollback, InnoDB will always return an appropriate error code indicating this. @see DB_DEADLOCK, @see DB_LOCK_TABLE_FULL and @see DB_LOCK_WAIT_TIMEOUT @ingroup trx @param ib_trx is the transaction handle @return transaction state */ HAILDB_API ib_trx_state_t ib_trx_state( /*=========*/ ib_trx_t ib_trx) UNIV_NO_IGNORE; /*****************************************************************//** Release the resources of the transaction. If the transaction was selected as a victim by InnoDB and rolled back then use this function to free the transaction handle. @ingroup trx @param ib_trx is the transaction handle @return DB_SUCCESS or err code */ HAILDB_API ib_err_t ib_trx_release( /*===========*/ ib_trx_t ib_trx) UNIV_NO_IGNORE; /*****************************************************************//** Commit a transaction. This function will release the schema latches too. It will also free the transaction handle. @ingroup trx @param ib_trx is thr transaction handle @return DB_SUCCESS or err code */ HAILDB_API ib_err_t ib_trx_commit( /*==========*/ ib_trx_t ib_trx) UNIV_NO_IGNORE; /*****************************************************************//** Rollback a transaction. This function will release the schema latches too. It will also free the transaction handle. @ingroup trx @param ib_trx is the transaction handle @return DB_SUCCESS or err code */ HAILDB_API ib_err_t ib_trx_rollback( /*============*/ ib_trx_t ib_trx) UNIV_NO_IGNORE; /*****************************************************************//** Add columns to a table schema. Tables are created in InnoDB by first creating a table schema which is identified by a handle. Then you add the column definitions to the table schema. @ingroup ddl @param ib_tbl_sch is the table schema instance @param name is the name of the column to add @param ib_col_type is the type of the column @param ib_col_attr are the attributes of the column, including constraints @param client_type is any 16 bit number relevant only to the client @param len is the maximum length of the column @return DB_SUCCESS or err code */ HAILDB_API ib_err_t ib_table_schema_add_col( /*====================*/ ib_tbl_sch_t ib_tbl_sch, const char* name, ib_col_type_t ib_col_type, ib_col_attr_t ib_col_attr, ib_u16_t client_type, ib_ulint_t len) UNIV_NO_IGNORE; /*****************************************************************//** Create and add an index key definition to a table schema. The index schema is owned by the table schema instance and will be freed when the table schema instance is freed. @ingroup ddl @param[in,out] ib_tbl_sch is the schema instance @param name name of the key definition to create @param[out] ib_idx_sch is the key definition schema instance @return DB_SUCCESS or err code */ HAILDB_API ib_err_t ib_table_schema_add_index( /*======================*/ ib_tbl_sch_t ib_tbl_sch, const char* name, ib_idx_sch_t* ib_idx_sch) UNIV_NO_IGNORE; /*****************************************************************//** Destroy a schema. The handle is freed by this function. @ingroup ddl @param ib_tbl_sch is the table schema to delte*/ HAILDB_API void ib_table_schema_delete( /*===================*/ ib_tbl_sch_t ib_tbl_sch); /*****************************************************************//** Create a table schema. @ingroup ddl @param name is the table name for which to create the schema @param[out] ib_tbl_sch is the schema instance that is created @param ib_tbl_fmt is the format of the table to be created @param page_size is the page size for the table or 0 for default @return DB_SUCCESS or err code */ HAILDB_API ib_err_t ib_table_schema_create( /*===================*/ const char* name, ib_tbl_sch_t* ib_tbl_sch, ib_tbl_fmt_t ib_tbl_fmt, ib_ulint_t page_size) UNIV_NO_IGNORE; /*****************************************************************//** Add columns to an index schema definition. @ingroup ddl @param[in,out] ib_idx_sch is the index schema instance @param name is the name of the column to add to the index schema @param prefix_len is the prefix length of the index or 0 if no prefix @return DB_SUCCESS or err code */ HAILDB_API ib_err_t ib_index_schema_add_col( /*====================*/ ib_idx_sch_t ib_idx_sch, const char* name, ib_ulint_t prefix_len) UNIV_NO_IGNORE; /*****************************************************************//** Create an index schema instance. @ingroup ddl @param ib_usr_trx is the current user transaction @param name is the name of the index to create @param table_name is the name of the table the index belongs to @param[out] ib_idx_sch is the newly created index schema instance @return DB_SUCCESS or err code */ HAILDB_API ib_err_t ib_index_schema_create( /*===================*/ ib_trx_t ib_usr_trx, const char* name, const char* table_name, ib_idx_sch_t* ib_idx_sch) UNIV_NO_IGNORE; /*****************************************************************//** Set index as clustered index. Implies UNIQUE. @ingroup ddl @param[in,out] ib_idx_sch is the index schema to update @return DB_SUCCESS or err code */ HAILDB_API ib_err_t ib_index_schema_set_clustered( /*==========================*/ ib_idx_sch_t ib_idx_sch) UNIV_NO_IGNORE; /*****************************************************************//** Set to true if it's a simple select. @ingroup sql @param[in, out] ib_crsr is the cursor to update */ HAILDB_API void ib_cursor_set_simple_select( /*========================*/ ib_crsr_t ib_crsr); /*****************************************************************//** Set index as a unique index. @ingroup ddl @param[in,out] ib_idx_sch is the index schema to update @return DB_SUCCESS or err code */ HAILDB_API ib_err_t ib_index_schema_set_unique( /*=======================*/ ib_idx_sch_t ib_idx_sch) UNIV_NO_IGNORE; /*****************************************************************//** Destroy an index schema. @ingroup ddl @param ib_idx_sch is the index schema to delete */ HAILDB_API void ib_index_schema_delete( /*===================*/ ib_idx_sch_t ib_idx_sch); /*****************************************************************//** Create a table in the InnoDB data dictionary using the schema definition. If the table exists in the database then this function will return DB_TABLE_IS_BEING_USED and id will contain that table's id. @ingroup ddl @param[in,out] ib_trx the current user transaction @param ib_tbl_sch the the schema for the table to create @param[out] id table id that was created @return DB_SUCCESS or err code */ HAILDB_API ib_err_t ib_table_create( /*============*/ ib_trx_t ib_trx, const ib_tbl_sch_t ib_tbl_sch, ib_id_t* id) UNIV_NO_IGNORE; /*****************************************************************//** Rename a table. Ensure that you have acquired the schema lock in exclusive mode. @ingroup ddl @param[in,out] ib_trx is the current user transaction @param old_name the current name of the table @param new_name the new name for the table @return DB_SUCCESS or err code */ HAILDB_API ib_err_t ib_table_rename( /*============*/ ib_trx_t ib_trx, const char* old_name, const char* new_name) UNIV_NO_IGNORE; /*****************************************************************//** Create a secondary index. The index id encodes the table id in the high 4 bytes and the index id in the lower 4 bytes. @ingroup ddl @param[in,out] ib_idx_sch the schema for the index @param[out] index_id is the new index id that was created @return DB_SUCCESS or err code */ HAILDB_API ib_err_t ib_index_create( /*============*/ ib_idx_sch_t ib_idx_sch, ib_id_t* index_id) UNIV_NO_IGNORE; /*****************************************************************//** Drop a table. Ensure that you have acquired the schema lock in exclusive mode. @ingroup ddl @param trx is the covering transaction. @param name is the name of the table to drop @return DB_SUCCESS or err code */ HAILDB_API ib_err_t ib_table_drop( /*==========*/ ib_trx_t trx, const char* name) UNIV_NO_IGNORE; /*****************************************************************//** Drop a secondary index. Ensure that you have acquired the schema lock in exclusive mode. @ingroup ddl @param trx is the covering transaction. @param index_id is the id of the index to drop @return DB_SUCCESS or err code */ HAILDB_API ib_err_t ib_index_drop( /*==========*/ ib_trx_t trx, ib_id_t index_id) UNIV_NO_IGNORE; /*****************************************************************//** Open an InnoDB table and return a cursor handle to it. @ingroup cursor @param table_id is the id of the table to open @param ib_trx is the current transaction handle, can be NULL @param[out] ib_crsr is the new cursor @return DB_SUCCESS or err code */ HAILDB_API ib_err_t ib_cursor_open_table_using_id( /*==========================*/ ib_id_t table_id, ib_trx_t ib_trx, ib_crsr_t* ib_crsr) UNIV_NO_IGNORE; /*****************************************************************//** Open an InnoDB index and return a cursor handle to it. @ingroup cursor @param index_id is the id of the index to open @param ib_trx is the current transaction handlem can be NULL @param[out] ib_crsr is the new cursor @return DB_SUCCESS or err code */ HAILDB_API ib_err_t ib_cursor_open_index_using_id( /*==========================*/ ib_id_t index_id, ib_trx_t ib_trx, ib_crsr_t* ib_crsr) UNIV_NO_IGNORE; /*****************************************************************//** Open an InnoDB secondary index cursor and return a cursor handle to it. @ingroup cursor @param ib_open_crsr is an open cursor @param index_name is the name of the index @param[out] ib_crsr is the new cursor @return DB_SUCCESS or err code */ HAILDB_API ib_err_t ib_cursor_open_index_using_name( /*============================*/ ib_crsr_t ib_open_crsr, const char* index_name, ib_crsr_t* ib_crsr) UNIV_NO_IGNORE; /*****************************************************************//** Open an InnoDB table by name and return a cursor handle to it. @ingroup cursor @param name is the table name to open @param ib_trx is the current transactionm, can be NULL @param ib_crsr is the new cursor @return DB_SUCCESS or err code */ HAILDB_API ib_err_t ib_cursor_open_table( /*=================*/ const char* name, ib_trx_t ib_trx, ib_crsr_t* ib_crsr) UNIV_NO_IGNORE; /*****************************************************************//** Reset the cursor. @ingroup cursor @param ib_crsr is an open cursor @return DB_SUCCESS or err code */ HAILDB_API ib_err_t ib_cursor_reset( /*============*/ ib_crsr_t ib_crsr) UNIV_NO_IGNORE; /*****************************************************************//** Close an InnoDB table and free the cursor. @ingroup cursor @param ib_crsr is an open cursor @return DB_SUCCESS or err code */ HAILDB_API ib_err_t ib_cursor_close( /*============*/ ib_crsr_t ib_crsr) UNIV_NO_IGNORE; /*****************************************************************//** Insert a row to a table. @ingroup dml @param ib_crsr is an open cursor @param ib_tpl is the tuple to insert @return DB_SUCCESS or err code */ HAILDB_API ib_err_t ib_cursor_insert_row( /*=================*/ ib_crsr_t ib_crsr, const ib_tpl_t ib_tpl) UNIV_NO_IGNORE; /*****************************************************************//** Update a row in a table. @ingroup dml @param ib_crsr is the cursor instance @param ib_old_tpl is the old tuple in the table @param ib_new_tpl is the new tuple with the updated values @return DB_SUCCESS or err code */ HAILDB_API ib_err_t ib_cursor_update_row( /*=================*/ ib_crsr_t ib_crsr, const ib_tpl_t ib_old_tpl, const ib_tpl_t ib_new_tpl) UNIV_NO_IGNORE; /*****************************************************************//** Delete a row in a table. @ingroup dml @param ib_crsr is the cursor instance @return DB_SUCCESS or err code */ HAILDB_API ib_err_t ib_cursor_delete_row( /*=================*/ ib_crsr_t ib_crsr) UNIV_NO_IGNORE; /*****************************************************************//** Read current row. @ingroup dml @param ib_crsr is the cursor instance @param[out] ib_tpl is the tuple to read the column values @return DB_SUCCESS or err code */ HAILDB_API ib_err_t ib_cursor_read_row( /*===============*/ ib_crsr_t ib_crsr, ib_tpl_t ib_tpl) UNIV_NO_IGNORE; /*****************************************************************//** Move cursor to the prev user record in the table. @ingroup cursor @param ib_crsr is the cursor instance @return DB_SUCCESS or err code */ HAILDB_API ib_err_t ib_cursor_prev( /*===========*/ ib_crsr_t ib_crsr) UNIV_NO_IGNORE; /*****************************************************************//** Move cursor to the next user record in the table. @ingroup cursor @param ib_crsr is the cursor instance @return DB_SUCCESS or err code */ HAILDB_API ib_err_t ib_cursor_next( /*===========*/ ib_crsr_t ib_crsr) UNIV_NO_IGNORE; /*****************************************************************//** Move cursor to the first record in the table. @ingroup cursor @param ib_crsr is the cursor instance @return DB_SUCCESS or err code */ HAILDB_API ib_err_t ib_cursor_first( /*============*/ ib_crsr_t ib_crsr) UNIV_NO_IGNORE; /*****************************************************************//** Move cursor to the last record in the table. @ingroup cursor @param ib_crsr is the cursor instance @return DB_SUCCESS or err code */ HAILDB_API ib_err_t ib_cursor_last( /*===========*/ ib_crsr_t ib_crsr) UNIV_NO_IGNORE; /*****************************************************************//** Search for key. @ingroup cursor @param ib_crsr is an open cursor instance @param ib_tpl is a key to search for @param ib_srch_mode is the search mode @param[out] result is -1, 0 or 1 depending on tuple eq or gt than the current row @return DB_SUCCESS or err code */ HAILDB_API ib_err_t ib_cursor_moveto( /*=============*/ ib_crsr_t ib_crsr, ib_tpl_t ib_tpl, ib_srch_mode_t ib_srch_mode, int* result) UNIV_NO_IGNORE; /*****************************************************************//** Attach the cursor to the transaction. The cursor must not already be attached to another transaction. @ingroup cursor @param ib_crsr is the cursor instance @param ib_trx is the transaction to attach to the cursor */ HAILDB_API void ib_cursor_attach_trx( /*=================*/ ib_crsr_t ib_crsr, ib_trx_t ib_trx); /*****************************************************************//** Set the client comparison function for BLOBs and client types. @ingroup misc @param client_cmp_func is the index key compare callback function */ HAILDB_API void ib_set_client_compare( /*==================*/ ib_client_cmp_t client_cmp_func); /*****************************************************************//** Set the match mode for ib_cursor_move(). @ingroup cursor @param ib_crsr is the cursor instance @param match_mode is the match mode to set */ HAILDB_API void ib_cursor_set_match_mode( /*=====================*/ ib_crsr_t ib_crsr, ib_match_mode_t match_mode); /*****************************************************************//** Set a column of the tuple. Make a copy using the tuple's heap. @ingroup dml @param ib_tpl is the tuple instance @param col_no is the column index in the tuple @param src is the data value to set @param len is the data value (src) length in bytes @return DB_SUCCESS or error code */ HAILDB_API ib_err_t ib_col_set_value( /*=============*/ ib_tpl_t ib_tpl, ib_ulint_t col_no, const void* src, ib_ulint_t len) UNIV_NO_IGNORE; /*****************************************************************//** Get the size of the data available in the column the tuple. @ingroup dml @param ib_tpl is the tuple instance @param i is the index (ordinal position) of the column within the tuple @return bytes avail or IB_SQL_NULL */ HAILDB_API ib_ulint_t ib_col_get_len( /*===========*/ ib_tpl_t ib_tpl, ib_ulint_t i) UNIV_NO_IGNORE; /*****************************************************************//** Copy a column value from the tuple. @ingroup dml @param ib_tpl is the tuple instance @param i is the index (ordinal position) of the column within the tuple @param[out] dst is where the data will be copied @param len is the maximum number of bytes that can be copied to dst @return bytes copied or IB_SQL_NULL */ HAILDB_API ib_ulint_t ib_col_copy_value( /*==============*/ ib_tpl_t ib_tpl, ib_ulint_t i, void* dst, ib_ulint_t len); /*************************************************************//** Read a signed int 8 bit column from an InnoDB tuple. @ingroup dml @param ib_tpl is the tuple instance @param i is the index (ordinal position) of the column within the tuple @param[out] ival is the integer value @return DB_SUCCESS or error */ HAILDB_API ib_err_t ib_tuple_read_i8( /*=============*/ ib_tpl_t ib_tpl, ib_ulint_t i, ib_i8_t* ival) UNIV_NO_IGNORE; /*************************************************************//** Read an unsigned int 8 bit column from an InnoDB tuple. @ingroup dml @param ib_tpl is the tuple instance @param i is the index (ordinal position) of the column within the tuple @param[out] ival is the integer value @return DB_SUCCESS or error */ HAILDB_API ib_err_t ib_tuple_read_u8( /*=============*/ ib_tpl_t ib_tpl, ib_ulint_t i, ib_u8_t* ival) UNIV_NO_IGNORE; /*************************************************************//** Read a signed int 16 bit column from an InnoDB tuple. @ingroup dml @param ib_tpl is the tuple instance @param i is the index (ordinal position) of the column within the tuple @param[out] ival is the integer value @return DB_SUCCESS or error */ HAILDB_API ib_err_t ib_tuple_read_i16( /*==============*/ ib_tpl_t ib_tpl, ib_ulint_t i, ib_i16_t* ival) UNIV_NO_IGNORE; /*************************************************************//** Read an unsigned int 16 bit column from an InnoDB tuple. @ingroup dml @param ib_tpl is the tuple instance @param i is the index (ordinal position) of the column within the tuple @param[out] ival is the integer value @return DB_SUCCESS or error */ HAILDB_API ib_err_t ib_tuple_read_u16( /*==============*/ ib_tpl_t ib_tpl, ib_ulint_t i, ib_u16_t* ival) UNIV_NO_IGNORE; /*************************************************************//** Read a signed int 32 bit column from an InnoDB tuple. @ingroup dml @param ib_tpl is the tuple instance @param i is the index (ordinal position) of the column within the tuple @param[out] ival is the integer value @return DB_SUCCESS or error */ HAILDB_API ib_err_t ib_tuple_read_i32( /*==============*/ ib_tpl_t ib_tpl, ib_ulint_t i, ib_i32_t* ival) UNIV_NO_IGNORE; /*************************************************************//** Read an unsigned int 32 bit column from an InnoDB tuple. @ingroup dml @param ib_tpl is the tuple instance @param i is the index (ordinal position) of the column within the tuple @param[out] ival is the integer value @return DB_SUCCESS or error */ HAILDB_API ib_err_t ib_tuple_read_u32( /*==============*/ ib_tpl_t ib_tpl, ib_ulint_t i, ib_u32_t* ival) UNIV_NO_IGNORE; /*************************************************************//** Read a signed int 64 bit column from an InnoDB tuple. @ingroup dml @param ib_tpl is the tuple instance @param i is the index (ordinal position) of the column within the tuple @param[out] ival is the integer value @return DB_SUCCESS or error */ HAILDB_API ib_err_t ib_tuple_read_i64( /*==============*/ ib_tpl_t ib_tpl, ib_ulint_t i, ib_i64_t* ival) UNIV_NO_IGNORE; /*************************************************************//** Read an unsigned int 64 bit column from an InnoDB tuple. @ingroup dml @param ib_tpl is the tuple instance @param i is the index (ordinal position) of the column within the tuple @param[out] ival is the integer value @return DB_SUCCESS or error */ HAILDB_API ib_err_t ib_tuple_read_u64( /*==============*/ ib_tpl_t ib_tpl, ib_ulint_t i, ib_u64_t* ival) UNIV_NO_IGNORE; /*****************************************************************//** Get a column value pointer from the tuple. @ingroup dml @param ib_tpl is the tuple instance @param i is the index (ordinal position) of the column within the tuple @return NULL or pointer to buffer */ HAILDB_API const void* ib_col_get_value( /*=============*/ ib_tpl_t ib_tpl, ib_ulint_t i) UNIV_NO_IGNORE; /*****************************************************************//** Get a column type, length and attributes from the tuple. @ingroup dml @param ib_tpl is the tuple instance @param i is the index (ordinal position) of the column within the tuple @param[out] ib_col_meta the column meta data @return len of column data */ HAILDB_API ib_ulint_t ib_col_get_meta( /*============*/ ib_tpl_t ib_tpl, ib_ulint_t i, ib_col_meta_t* ib_col_meta); /*****************************************************************//** "Clear" or reset an InnoDB tuple. We free the heap and recreate the tuple. @ingroup tuple @param ib_tpl is the tuple to be freed @return new tuple, or NULL */ HAILDB_API ib_tpl_t ib_tuple_clear( /*============*/ ib_tpl_t ib_tpl) UNIV_NO_IGNORE; /*****************************************************************//** Create a new cluster key search tuple and copy the contents of the secondary index key tuple columns that refer to the cluster index record to the cluster key. It does a deep copy of the column data. @ingroup tuple @param ib_crsr is a cursor opened on a secondary index @param[out] ib_dst_tpl is the tuple where the key data will be copied @param ib_src_tpl is the source secondary index tuple to copy from @return DB_SUCCESS or error code */ HAILDB_API ib_err_t ib_tuple_get_cluster_key( /*=====================*/ ib_crsr_t ib_crsr, ib_tpl_t* ib_dst_tpl, const ib_tpl_t ib_src_tpl) UNIV_NO_IGNORE; /*****************************************************************//** Copy the contents of source tuple to destination tuple. The tuples must be of the same type and belong to the same table/index. @ingroup tuple @param ib_dst_tpl is the destination tuple @param ib_src_tpl is the source tuple @return DB_SUCCESS or error code */ HAILDB_API ib_err_t ib_tuple_copy( /*==========*/ ib_tpl_t ib_dst_tpl, const ib_tpl_t ib_src_tpl) UNIV_NO_IGNORE; /*****************************************************************//** Create an InnoDB tuple used for index/table search. @ingroup tuple @param ib_crsr is the cursor instance @return tuple for current index */ HAILDB_API ib_tpl_t ib_sec_search_tuple_create( /*=======================*/ ib_crsr_t ib_crsr) UNIV_NO_IGNORE; /*****************************************************************//** Create an InnoDB tuple used for index/table search. @ingroup tuple @param ib_crsr is the cursor instance @return tuple for current index */ HAILDB_API ib_tpl_t ib_sec_read_tuple_create( /*=====================*/ ib_crsr_t ib_crsr) UNIV_NO_IGNORE; /*****************************************************************//** Create an InnoDB tuple used for table key operations. @ingroup tuple @param ib_crsr is the cursor instance @return tuple for current table */ HAILDB_API ib_tpl_t ib_clust_search_tuple_create( /*=========================*/ ib_crsr_t ib_crsr) UNIV_NO_IGNORE; /*****************************************************************//** Create an InnoDB tuple for table row operations. @ingroup tuple @param ib_crsr is the cursor instance @return tTuple for current table */ HAILDB_API ib_tpl_t ib_clust_read_tuple_create( /*=======================*/ ib_crsr_t ib_crsr) UNIV_NO_IGNORE; /*****************************************************************//** Return the number of user columns in the tuple definition. @ingroup tuple @param ib_tpl is a tuple @return number of user columns */ HAILDB_API ib_ulint_t ib_tuple_get_n_user_cols( /*=====================*/ const ib_tpl_t ib_tpl) UNIV_NO_IGNORE; /*****************************************************************//** Return the number of columns in the tuple definition. @ingroup tuple @param ib_tpl is a tuple @return number of columns */ HAILDB_API ib_ulint_t ib_tuple_get_n_cols( /*================*/ const ib_tpl_t ib_tpl) UNIV_NO_IGNORE; /*****************************************************************//** Destroy an InnoDB tuple. @ingroup tuple @param ib_tpl is the tuple instance to delete */ HAILDB_API void ib_tuple_delete( /*============*/ ib_tpl_t ib_tpl); /*****************************************************************//** Truncate a table. The cursor handle will be closed and set to NULL on success. @ingroup ddl @param[out] ib_crsr is the cursor for table to truncate @param[out] table_id is the new table id @return DB_SUCCESS or error code */ HAILDB_API ib_err_t ib_cursor_truncate( /*===============*/ ib_crsr_t* ib_crsr, ib_id_t* table_id) UNIV_NO_IGNORE; /*****************************************************************//** Truncate a table. @ingroup ddl @param table_name is the name of the table to truncate @param[out] table_id is the new table id @return DB_SUCCESS or error code */ HAILDB_API ib_err_t ib_table_truncate( /*==============*/ const char* table_name, ib_id_t* table_id) UNIV_NO_IGNORE; /*****************************************************************//** Get a table id. @ingroup ddl @param table_name is the name of the table to lookup @param[out] table_id is the new table id if found @return DB_SUCCESS if found */ HAILDB_API ib_err_t ib_table_get_id( /*============*/ const char* table_name, ib_id_t* table_id) UNIV_NO_IGNORE; /*****************************************************************//** Get an index id. @ingroup ddl @param table_name is the name of the table that contains the index @param index_name is the name of the index to lookup @param[out] index_id contains the index id if found @return DB_SUCCESS if found */ HAILDB_API ib_err_t ib_index_get_id( /*============*/ const char* table_name, const char* index_name, ib_id_t* index_id) UNIV_NO_IGNORE; /*********************************************************************//** Create a database if it doesn't exist. @ingroup ddl @param dbname is the name of the database to create @return IB_TRUE on success */ HAILDB_API ib_bool_t ib_database_create( /*===============*/ const char* dbname) UNIV_NO_IGNORE; /*********************************************************************//** Drop a database if it exists. This function will also drop all tables within the database. @ingroup ddl @param dbname is the name of the database to drop @return DB_SUCCESS or error code */ HAILDB_API ib_err_t ib_database_drop( /*=============*/ const char* dbname) UNIV_NO_IGNORE; /*****************************************************************//** Check if cursor is positioned. @ingroup cursor @param ib_crsr is the cursor instance to check @return IB_TRUE if positioned */ HAILDB_API ib_bool_t ib_cursor_is_positioned( /*====================*/ const ib_crsr_t ib_crsr) UNIV_NO_IGNORE; /*****************************************************************//** Latches the data dictionary in shared mode. @ingroup ddl @param ib_trx is the transaction instance @return DB_SUCCESS or error code */ HAILDB_API ib_err_t ib_schema_lock_shared( /*==================*/ ib_trx_t ib_trx) UNIV_NO_IGNORE; /*****************************************************************//** Latches the data dictionary in exclusive mode. @ingroup ddl @param ib_trx is the transaction instance @return DB_SUCCESS or error code */ HAILDB_API ib_err_t ib_schema_lock_exclusive( /*======================*/ ib_trx_t ib_trx) UNIV_NO_IGNORE; /*****************************************************************//** Checks if the data dictionary is latched in exclusive mode by a user transaction. @ingroup ddl @param ib_trx is a transaction instance @return TRUE if exclusive latch */ HAILDB_API ib_bool_t ib_schema_lock_is_exclusive( /*========================*/ const ib_trx_t ib_trx) UNIV_NO_IGNORE; /*****************************************************************//** Checks if the data dictionary is latched in shared mode. @param ib_trx is a transaction instance @return TRUE if shared latch */ HAILDB_API ib_bool_t ib_schema_lock_is_shared( /*=====================*/ const ib_trx_t ib_trx) UNIV_NO_IGNORE; /*****************************************************************//** Unlocks the data dictionary. @ingroup ddl @param ib_trx is a transaction instance @return DB_SUCCESS or error code */ HAILDB_API ib_err_t ib_schema_unlock( /*=============*/ ib_trx_t ib_trx); /*****************************************************************//** Lock an InnoDB cursor/table. @ingroup trx @param ib_crsr is the cursor instance @param ib_lck_mode is the lock mode @return DB_SUCCESS or error code */ HAILDB_API ib_err_t ib_cursor_lock( /*===========*/ ib_crsr_t ib_crsr, ib_lck_mode_t ib_lck_mode) UNIV_NO_IGNORE; /*****************************************************************//** Set the Lock an InnoDB table using the table id. @ingroup trx @param ib_trx is a transaction instance @param table_id is the table to lock @param ib_lck_mode is the lock mode @return DB_SUCCESS or error code */ HAILDB_API ib_err_t ib_table_lock( /*===========*/ ib_trx_t ib_trx, ib_id_t table_id, ib_lck_mode_t ib_lck_mode) UNIV_NO_IGNORE; /*****************************************************************//** Set the Lock mode of the cursor. @ingroup trx @param ib_crsr is the cursor instance for which we want to set the lock mode @param ib_lck_mode is the lock mode @return DB_SUCCESS or error code */ HAILDB_API ib_err_t ib_cursor_set_lock_mode( /*====================*/ ib_crsr_t ib_crsr, ib_lck_mode_t ib_lck_mode) UNIV_NO_IGNORE; /*****************************************************************//** Set need to access clustered index record flag. @ingroup dml @param ib_crsr is the cursor instance for which we want to set the flag */ HAILDB_API void ib_cursor_set_cluster_access( /*=========================*/ ib_crsr_t ib_crsr); /*****************************************************************//** Read a table's schema using the visitor pattern. It will make the following sequence of calls: visitor->table() visitor->table_col() for each user column visitor->index() for each user index visitor->index_col() for each column in user index It will stop if any of the above functions returns a non-zero value. The caller must have an exclusive lock on the InnoDB data dictionary @ingroup ddl @param ib_trx transaction that owns the schema lock @param name is the table name to read @param visitor visitor functions to invoke on each definition @param arg is the argument passed to the visitor functions. @return DB_SUCCESS or DB_ERROR */ HAILDB_API ib_err_t ib_table_schema_visit( /*==================*/ ib_trx_t ib_trx, const char* name, const ib_schema_visitor_t* visitor, void* arg) UNIV_NO_IGNORE; /*****************************************************************//** List all the tables in the InnoDB's data dictionary. It will abort if visitor returns a non-zero value. It will call the function: visitor.tables(arg, const char* name, int name_len); The function will abort if visitor.tables() returns non-zero. @ingroup ddl @param ib_trx is the transaction that owns the schema lock @param visitor is the visitor function @param arg argument passed to the visitor function @return DB_SUCCESS if successful */ HAILDB_API ib_err_t ib_schema_tables_iterate( /*=====================*/ ib_trx_t ib_trx, ib_schema_visitor_table_all_t visitor, void* arg) UNIV_NO_IGNORE; /*********************************************************************//** Get the type of a configuration variable. Returns DB_SUCCESS if the variable with name "name" was found and "type" was set. @ingroup config @param name is the variable name to look up @param[out] type is the type of the variable name if found @return DB_SUCCESS if successful */ HAILDB_API ib_err_t ib_cfg_var_get_type( /*================*/ const char* name, ib_cfg_type_t* type) UNIV_NO_IGNORE; /*********************************************************************//** Set a configuration variable. The second argument's type depends on the type of the variable with the given "name". Returns DB_SUCCESS if the variable with name "name" was found and if its value was set. @ingroup config @param name is the config variable name whose value is to be set @return DB_SUCCESS if set */ HAILDB_API ib_err_t ib_cfg_set( /*=======*/ const char* name, ...) UNIV_NO_IGNORE; /*********************************************************************//** Get the value of a configuration variable. The type of the returned value depends on the type of the configuration variable. DB_SUCCESS is returned if the variable with name "name" was found and "value" was set. @ingroup config @param name is the variable name whose value needs to be accessed @param[out] value is the value of the variable if found @return DB_SUCCESS if retrieved successfully */ HAILDB_API ib_err_t ib_cfg_get( /*=======*/ const char* name, void* value) UNIV_NO_IGNORE; /*********************************************************************//** Get a list of the names of all configuration variables. The caller is responsible for free(3)ing the returned array of strings when it is not needed anymore and for not modifying the individual strings. @ingroup config @param[out] names pointer to array of strings @param[out] names_num number of strings returned @return DB_SUCCESS or error code */ HAILDB_API ib_err_t ib_cfg_get_all( /*===========*/ const char*** names, ib_u32_t* names_num) UNIV_NO_IGNORE; /*******************************************************************//** Creates a named savepoint. The transaction must be started. If there is already a savepoint of the same name, this call erases that old savepoint and replaces it with a new. Savepoints are deleted in a transaction commit or rollback. @ingroup trx @param ib_trx is the transaction instance @param name is the name of the savepoint @param name_len is the length of name in bytes */ HAILDB_API void ib_savepoint_take( /*==============*/ ib_trx_t ib_trx, const void* name, ib_ulint_t name_len); /*******************************************************************//** Releases only the named savepoint. Savepoints which were set after this savepoint are left as is. @ingroup trx @param ib_trx is the active transaction @param name is the name of the savepoint @param name_len is the length of name in bytes @return if no savepoint of the name found then DB_NO_SAVEPOINT, otherwise DB_SUCCESS */ HAILDB_API ib_err_t ib_savepoint_release( /*=================*/ ib_trx_t ib_trx, const void* name, ib_ulint_t name_len) UNIV_NO_IGNORE; /*******************************************************************//** Rolls back a transaction back to a named savepoint. Modifications after the savepoint are undone but InnoDB does NOT release the corresponding locks which are stored in memory. If a lock is 'implicit', that is, a new inserted row holds a lock where the lock information is carried by the trx id stored in the row, these locks are naturally released in the rollback. Savepoints which were set after this savepoint are deleted. If name equals NULL then all the savepoints are rolled back. @ingroup trx @param ib_trx is the active transaction @param name is the savepoint name can be NULL @param name_len is the length of name in bytes @return if no savepoint of the name found then DB_NO_SAVEPOINT, otherwise DB_SUCCESS */ HAILDB_API ib_err_t ib_savepoint_rollback( /*==================*/ ib_trx_t ib_trx, const void* name, ib_ulint_t name_len) UNIV_NO_IGNORE; /*****************************************************************//** Write an integer value to a column. Integers are stored in big-endian format and will need to be converted from the host format. @ingroup dml @param[in,out] ib_tpl is the tuple to write to @param col_no is the column number to update @param val is the value to write @return DB_SUCESS or error */ HAILDB_API ib_err_t ib_tuple_write_i8( /*==============*/ ib_tpl_t ib_tpl, int col_no, ib_i8_t val) UNIV_NO_IGNORE; /*****************************************************************//** Write an integer value to a column. Integers are stored in big-endian format and will need to be converted from the host format. @ingroup dml @param[in,out] ib_tpl is the tuple to write to @param col_no is the column number to update @param val is the value to write @return DB_SUCESS or error */ HAILDB_API ib_err_t ib_tuple_write_i16( /*=================*/ ib_tpl_t ib_tpl, int col_no, ib_i16_t val) UNIV_NO_IGNORE; /*****************************************************************//** Write an integer value to a column. Integers are stored in big-endian format and will need to be converted from the host format. @ingroup dml @param[in,out] ib_tpl is the tuple to write to @param col_no is the column number to update @param val is the value to write @return DB_SUCESS or error */ HAILDB_API ib_err_t ib_tuple_write_i32( /*===============*/ ib_tpl_t ib_tpl, int col_no, ib_i32_t val) UNIV_NO_IGNORE; /*****************************************************************//** Write an integer value to a column. Integers are stored in big-endian format and will need to be converted from the host format. @ingroup dml @param[in,out] ib_tpl is the tuple to write to @param col_no is the column number to update @param val is the value to write @return DB_SUCESS or error */ HAILDB_API ib_err_t ib_tuple_write_i64( /*===============*/ ib_tpl_t ib_tpl, int col_no, ib_i64_t val) UNIV_NO_IGNORE; /*****************************************************************//** Write an integer value to a column. Integers are stored in big-endian format and will need to be converted from the host format. @ingroup dml @param[in,out] ib_tpl is the tuple to write to @param col_no is the column number to update @param val is the value to write @return DB_SUCESS or error */ HAILDB_API ib_err_t ib_tuple_write_u8( /*==============*/ ib_tpl_t ib_tpl, int col_no, ib_u8_t val) UNIV_NO_IGNORE; /*****************************************************************//** Write an integer value to a column. Integers are stored in big-endian format and will need to be converted from the host format. @ingroup dml @param[in,out] ib_tpl is the tuple to write to @param col_no is the column number to update @param val is the value to write @return DB_SUCESS or error */ HAILDB_API ib_err_t ib_tuple_write_u16( /*===============*/ ib_tpl_t ib_tpl, int col_no, ib_u16_t val) UNIV_NO_IGNORE; /*****************************************************************//** Write an integer value to a column. Integers are stored in big-endian format and will need to be converted from the host format. @ingroup dml @param[in,out] ib_tpl is the tuple to write to @param col_no is the column number to update @param val is the value to write @return DB_SUCESS or error */ HAILDB_API ib_err_t ib_tuple_write_u32( /*=================*/ ib_tpl_t ib_tpl, int col_no, ib_u32_t val) UNIV_NO_IGNORE; /*****************************************************************//** Write an integer value to a column. Integers are stored in big-endian format and will need to be converted from the host format. @ingroup dml @param[in,out] ib_tpl is the tuple to write to @param col_no is the column number to update @param val is the value to write @return DB_SUCESS or error */ HAILDB_API ib_err_t ib_tuple_write_u64( /*===============*/ ib_tpl_t ib_tpl, int col_no, ib_u64_t val) UNIV_NO_IGNORE; /*****************************************************************//** Inform the cursor that it's the start of an SQL statement. @ingroup cursor @param ib_crsr is the cursor instance */ HAILDB_API void ib_cursor_stmt_begin( /*=================*/ ib_crsr_t ib_crsr); /*****************************************************************//** Write a double value to a column. @ingroup dml @param[in,out] ib_tpl is the tuple to write to @param col_no is the column number to update @param val is the value to write @return DB_SUCCESS or error */ HAILDB_API ib_err_t ib_tuple_write_double( /*==================*/ ib_tpl_t ib_tpl, int col_no, double val) UNIV_NO_IGNORE; /*************************************************************//** Read a double column value from an InnoDB tuple. @ingroup dml @param ib_tpl is the tuple to read from @param col_no is the column number to read @param[out] dval is where the value is copied @return DB_SUCCESS or error */ HAILDB_API ib_err_t ib_tuple_read_double( /*=================*/ ib_tpl_t ib_tpl, ib_ulint_t col_no, double* dval) UNIV_NO_IGNORE; /*****************************************************************//** Write a float value to a column. @ingroup dml @param[in,out] ib_tpl is the tuple to write to @param col_no is the column number to update @param val is the value to write @return DB_SUCCESS or error */ HAILDB_API ib_err_t ib_tuple_write_float( /*=================*/ ib_tpl_t ib_tpl, int col_no, float val) UNIV_NO_IGNORE; /*************************************************************//** Read a float value from an InnoDB tuple. @ingroup dml @param ib_tpl is the tuple to read from @param col_no is the column number to read @param[out] fval is where the value is copied @return DB_SUCCESS or error */ HAILDB_API ib_err_t ib_tuple_read_float( /*================*/ ib_tpl_t ib_tpl, ib_ulint_t col_no, float* fval) UNIV_NO_IGNORE; /*************************************************************//** Set the message logging function. @ingroup misc @param ib_msg_log is the message logging function @param ib_msg_stream is the message stream, this is the first argument to the loggingfunction */ HAILDB_API void ib_logger_set( /*==========*/ ib_msg_log_t ib_msg_log, ib_msg_stream_t ib_msg_stream); /*************************************************************//** Convert an error number to a human readable text message. The returned string is static and should not be freed or modified. @ingroup misc @param db_errno is the error number @return string, describing the error */ HAILDB_API const char* ib_strerror( /*========*/ ib_err_t db_errno) UNIV_NO_IGNORE; /*******************************************************************//** Get the value of an INT status variable. @ingroup misc @param name is the status variable name @param[out] dst is where the output value is copied if name is found @return DB_SUCCESS if found and type is INT, DB_DATA_MISMATCH if found but type is not INT, DB_NOT_FOUND otherwise. */ HAILDB_API ib_err_t ib_status_get_i64( /*==============*/ const char* name, ib_i64_t* dst) UNIV_NO_IGNORE; /*********************************************************************//** Get a list of the names of all status variables. The caller is responsible for free(3)ing the returned array of strings when it is not needed anymore and for not modifying the individual strings. @ingroup config @param[out] names pointer to array of strings @param[out] names_num number of strings returned @return DB_SUCCESS or error code */ HAILDB_API ib_err_t ib_status_get_all( /*===========*/ const char*** names, ib_u32_t* names_num) UNIV_NO_IGNORE; /** Type of callback in the event of HailDB panicing. Your callback should call exit() rather soon, as continuing after a panic will lead to errors returned from every API function. We have also not fully tested every possible outcome from not immediately calling exit(). */ typedef void (*ib_panic_handler_t)(void*, int, char*, ...); /** Set panic handler. HailDB will "panic" upon finding certain forms of corruption. By setting a panic handler, you can implement your own notification to the end user of this corruption (e.g. popping up a dialog box). @ingroup config @param new_panic_handler your panic handler */ HAILDB_API void ib_set_panic_handler(ib_panic_handler_t new_panic_handler); /** Callback for checking if a transaction has been interrupted. This callback lets you implement the MySQL KILL command kind of functionality. A transaction may block in the thread it's running in (for example, while acquiring row locks or doing IO) but other threads may do something that causes ib_trx_is_interrupted_handler_t to return true. */ typedef int (*ib_trx_is_interrupted_handler_t)(void*); /** Set trx_is_interrupted_handler. You may specificy a callback that HailDB will check during certain wait situations to see if it should abort the operation or not. This lets you implement MySQL/Drizzle KILL command style functionality. @ingroup config @param handler the trx_is_interrupted callback */ HAILDB_API void ib_set_trx_is_interrupted_handler(ib_trx_is_interrupted_handler_t handler); /** Get which key caused a duplicate key error. In the event of \ref DB_DUPLICATE_KEY error, you can call this function immediately after to get the name of the table and index that caused the error. @ingroup trx @param ib_trx Transaction where error occured @param table_name pointer to be set to table_name. Valid until next ib_ function call. If you would like to keep it, make a copy. @param index_name pointer to be set to the index name. Valid until next ib_ function call. If you would like to keep it, make a copy. */ HAILDB_API ib_err_t ib_get_duplicate_key(ib_trx_t ib_trx, const char **table_name, const char **index_name); /** @struct ib_table_stats_t InnoDB Table and index statistics. */ typedef struct { ib_i64_t stat_n_rows; /*!< approximate number of rows in the table; we periodically calculate new estimates */ ib_u64_t stat_clustered_index_size; /*!< approximate clustered index size in bytes. */ ib_u64_t stat_sum_of_other_index_sizes; /*!< other indexes in bytes */ ib_u64_t stat_modified_counter; /*!< when a row is inserted, updated, or deleted, we add 1 to this number; we calculate new estimates for the stat_... values for the table and the indexes at an interval of 2 GB or when about 1 / 16 of table has been modified; also when an estimate operation is called for; the counter is reset to zero at statistics calculation; this counter is not protected by any latch, because this is only used for heuristics */ } ib_table_stats_t; /** Get table statistics. This function will fill out the provided \ref ib_table_stats_t with statistics about the table on the currently opened \ref ib_crsr_t @ingroup misc @param ib_crsr A Cursor that is opened to a table @param table_stats a \ref ib_table_stats_t to be filled out by HailDB @param sizeof_ib_table_stats_t sizeof(ib_table_stats_t). This allows for ABI compatible changes to the size of ib_table_stats_t. @returns \ref DB_SUCCESS or error */ HAILDB_API ib_err_t ib_get_table_statistics(ib_crsr_t ib_crsr, ib_table_stats_t *table_stats, size_t sizeof_ib_table_stats_t); /** Get statistics on number of different key values per index part This function returns the approximate different key values for this index. They are periodically recalculated. @ingroup misc @param ib_crsr A Cursor that is opened to a table @param index_name name of the index @param ncols returns the number of elements in n_diff @param n_diff An array allocated with malloc() (user needs to free()) containing the statistics @returns \ref DB_SUCCESS or error. \ref DB_NOT_FOUND if index is not found */ HAILDB_API ib_err_t ib_get_index_stat_n_diff_key_vals(ib_crsr_t ib_crsr, const char* index_name, ib_u64_t *ncols, ib_i64_t **n_diff); /** Force an update of table and index statistics This function forces an update to the table and index statistics for the table crsr is opened on. @ingroup misc @param crsr A Cursor that is opened to a table @returns \ref DB_SUCCESS or error. */ HAILDB_API ib_err_t ib_update_table_statistics(ib_crsr_t crsr); /** Inject an error into HailDB This function will simulate an error condition inside HailDB. You should not rely on this function. It is for HailDB test suite use only, parts may only be compiled into debug libraries and this function can quite legitimately just return DB_ERROR and cause Voldemort to pay you a visit. @ingroup debug @param error_to_inject The error inject code to insert. */ HAILDB_API ib_err_t ib_error_inject(int error_to_inject); #ifdef __cplusplus } /* extern "C" */ #endif /* __cplusplus */ #endif /* HAILDB_H */ haildb-2.3.2/lock/0000755000175000017500000000000011513177437014572 5ustar00pcrewspcrews00000000000000haildb-2.3.2/lock/lock0lock.c0000644000175000017500000046311211513177357016627 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2010, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file lock/lock0lock.c The transaction lock system Created 5/7/1996 Heikki Tuuri *******************************************************/ #define LOCK_MODULE_IMPLEMENTATION #include "lock0lock.h" #include "lock0priv.h" #ifdef UNIV_NONINL #include "lock0lock.ic" #include "lock0priv.ic" #endif #include "api0ucode.h" #include "usr0sess.h" #include "trx0purge.h" #include "dict0mem.h" #include "trx0sys.h" /* Restricts the length of search we will do in the waits-for graph of transactions */ #define LOCK_MAX_N_STEPS_IN_DEADLOCK_CHECK 1000000 /* Restricts the recursion depth of the search we will do in the waits-for graph of transactions */ #define LOCK_MAX_DEPTH_IN_DEADLOCK_CHECK 200 /* When releasing transaction locks, this specifies how often we release the kernel mutex for a moment to give also others access to it */ #define LOCK_RELEASE_KERNEL_INTERVAL 1000 /* Safety margin when creating a new record lock: this many extra records can be inserted to the page without need to create a lock with a bigger bitmap */ #define LOCK_PAGE_BITMAP_MARGIN 64 /* An explicit record lock affects both the record and the gap before it. An implicit x-lock does not affect the gap, it only locks the index record from read or update. If a transaction has modified or inserted an index record, then it owns an implicit x-lock on the record. On a secondary index record, a transaction has an implicit x-lock also if it has modified the clustered index record, the max trx id of the page where the secondary index record resides is >= trx id of the transaction (or database recovery is running), and there are no explicit non-gap lock requests on the secondary index record. This complicated definition for a secondary index comes from the implementation: we want to be able to determine if a secondary index record has an implicit x-lock, just by looking at the present clustered index record, not at the historical versions of the record. The complicated definition can be explained to the user so that there is nondeterminism in the access path when a query is answered: we may, or may not, access the clustered index record and thus may, or may not, bump into an x-lock set there. Different transaction can have conflicting locks set on the gap at the same time. The locks on the gap are purely inhibitive: an insert cannot be made, or a select cursor may have to wait if a different transaction has a conflicting lock on the gap. An x-lock on the gap does not give the right to insert into the gap. An explicit lock can be placed on a user record or the supremum record of a page. The locks on the supremum record are always thought to be of the gap type, though the gap bit is not set. When we perform an update of a record where the size of the record changes, we may temporarily store its explicit locks on the infimum record of the page, though the infimum otherwise never carries locks. A waiting record lock can also be of the gap type. A waiting lock request can be granted when there is no conflicting mode lock request by another transaction ahead of it in the explicit lock queue. In version 4.0.5 we added yet another explicit lock type: LOCK_REC_NOT_GAP. It only locks the record it is placed on, not the gap before the record. This lock type is necessary to emulate an Oracle-like READ COMMITTED isolation level. ------------------------------------------------------------------------- RULE 1: If there is an implicit x-lock on a record, and there are non-gap ------- lock requests waiting in the queue, then the transaction holding the implicit x-lock also has an explicit non-gap record x-lock. Therefore, as locks are released, we can grant locks to waiting lock requests purely by looking at the explicit lock requests in the queue. RULE 3: Different transactions cannot have conflicting granted non-gap locks ------- on a record at the same time. However, they can have conflicting granted gap locks. RULE 4: If a there is a waiting lock request in a queue, no lock request, ------- gap or not, can be inserted ahead of it in the queue. In record deletes and page splits new gap type locks can be created by the database manager for a transaction, and without rule 4, the waits-for graph of transactions might become cyclic without the database noticing it, as the deadlock check is only performed when a transaction itself requests a lock! ------------------------------------------------------------------------- An insert is allowed to a gap if there are no explicit lock requests by other transactions on the next record. It does not matter if these lock requests are granted or waiting, gap bit set or not, with the exception that a gap type request set by another transaction to wait for its turn to do an insert is ignored. On the other hand, an implicit x-lock by another transaction does not prevent an insert, which allows for more concurrency when using an Oracle-style sequence number generator for the primary key with many transactions doing inserts concurrently. A modify of a record is allowed if the transaction has an x-lock on the record, or if other transactions do not have any non-gap lock requests on the record. A read of a single user record with a cursor is allowed if the transaction has a non-gap explicit, or an implicit lock on the record, or if the other transactions have no x-lock requests on the record. At a page supremum a read is always allowed. In summary, an implicit lock is seen as a granted x-lock only on the record, not on the gap. An explicit lock with no gap bit set is a lock both on the record and the gap. If the gap bit is set, the lock is only on the gap. Different transaction cannot own conflicting locks on the record at the same time, but they may own conflicting locks on the gap. Granted locks on a record give an access right to the record, but gap type locks just inhibit operations. NOTE: Finding out if some transaction has an implicit x-lock on a secondary index record can be cumbersome. We may have to look at previous versions of the corresponding clustered index record to find out if a delete marked secondary index record was delete marked by an active transaction, not by a committed one. FACT A: If a transaction has inserted a row, it can delete it any time without need to wait for locks. PROOF: The transaction has an implicit x-lock on every index record inserted for the row, and can thus modify each record without the need to wait. Q.E.D. FACT B: If a transaction has read some result set with a cursor, it can read it again, and retrieves the same result set, if it has not modified the result set in the meantime. Hence, there is no phantom problem. If the biggest record, in the alphabetical order, touched by the cursor is removed, a lock wait may occur, otherwise not. PROOF: When a read cursor proceeds, it sets an s-lock on each user record it passes, and a gap type s-lock on each page supremum. The cursor must wait until it has these locks granted. Then no other transaction can have a granted x-lock on any of the user records, and therefore cannot modify the user records. Neither can any other transaction insert into the gaps which were passed over by the cursor. Page splits and merges, and removal of obsolete versions of records do not affect this, because when a user record or a page supremum is removed, the next record inherits its locks as gap type locks, and therefore blocks inserts to the same gap. Also, if a page supremum is inserted, it inherits its locks from the successor record. When the cursor is positioned again at the start of the result set, the records it will touch on its course are either records it touched during the last pass or new inserted page supremums. It can immediately access all these records, and when it arrives at the biggest record, it notices that the result set is complete. If the biggest record was removed, lock wait can occur because the next record only inherits a gap type lock, and a wait may be needed. Q.E.D. */ /* If an index record should be changed or a new inserted, we must check the lock on the record or the next. When a read cursor starts reading, we will set a record level s-lock on each record it passes, except on the initial record on which the cursor is positioned before we start to fetch records. Our index tree search has the convention that the B-tree cursor is positioned BEFORE the first possibly matching record in the search. Optimizations are possible here: if the record is searched on an equality condition to a unique key, we could actually set a special lock on the record, a lock which would not prevent any insert before this record. In the next key locking an x-lock set on a record also prevents inserts just before that record. There are special infimum and supremum records on each page. A supremum record can be locked by a read cursor. This records cannot be updated but the lock prevents insert of a user record to the end of the page. Next key locks will prevent the phantom problem where new rows could appear to SELECT result sets after the select operation has been performed. Prevention of phantoms ensures the serilizability of transactions. What should we check if an insert of a new record is wanted? Only the lock on the next record on the same page, because also the supremum record can carry a lock. An s-lock prevents insertion, but what about an x-lock? If it was set by a searched update, then there is implicitly an s-lock, too, and the insert should be prevented. What if our transaction owns an x-lock to the next record, but there is a waiting s-lock request on the next record? If this s-lock was placed by a read cursor moving in the ascending order in the index, we cannot do the insert immediately, because when we finally commit our transaction, the read cursor should see also the new inserted record. So we should move the read cursor backward from the next record for it to pass over the new inserted record. This move backward may be too cumbersome to implement. If we in this situation just enqueue a second x-lock request for our transaction on the next record, then the deadlock mechanism notices a deadlock between our transaction and the s-lock request transaction. This seems to be an ok solution. We could have the convention that granted explicit record locks, lock the corresponding records from changing, and also lock the gaps before them from inserting. A waiting explicit lock request locks the gap before from inserting. Implicit record x-locks, which we derive from the transaction id in the clustered index record, only lock the record itself from modification, not the gap before it from inserting. How should we store update locks? If the search is done by a unique key, we could just modify the record trx id. Otherwise, we could put a record x-lock on the record. If the update changes ordering fields of the clustered index record, the inserted new record needs no record lock in lock table, the trx id is enough. The same holds for a secondary index record. Searched delete is similar to update. PROBLEM: What about waiting lock requests? If a transaction is waiting to make an update to a record which another modified, how does the other transaction know to send the end-lock-wait signal to the waiting transaction? If we have the convention that a transaction may wait for just one lock at a time, how do we preserve it if lock wait ends? PROBLEM: Checking the trx id label of a secondary index record. In the case of a modification, not an insert, is this necessary? A secondary index record is modified only by setting or resetting its deleted flag. A secondary index record contains fields to uniquely determine the corresponding clustered index record. A secondary index record is therefore only modified if we also modify the clustered index record, and the trx id checking is done on the clustered index record, before we come to modify the secondary index record. So, in the case of delete marking or unmarking a secondary index record, we do not have to care about trx ids, only the locks in the lock table must be checked. In the case of a select from a secondary index, the trx id is relevant, and in this case we may have to search the clustered index record. PROBLEM: How to update record locks when page is split or merged, or -------------------------------------------------------------------- a record is deleted or updated? If the size of fields in a record changes, we perform the update by a delete followed by an insert. How can we retain the locks set or waiting on the record? Because a record lock is indexed in the bitmap by the heap number of the record, when we remove the record from the record list, it is possible still to keep the lock bits. If the page is reorganized, we could make a table of old and new heap numbers, and permute the bitmaps in the locks accordingly. We can add to the table a row telling where the updated record ended. If the update does not require a reorganization of the page, we can simply move the lock bits for the updated record to the position determined by its new heap number (we may have to allocate a new lock, if we run out of the bitmap in the old one). A more complicated case is the one where the reinsertion of the updated record is done pessimistically, because the structure of the tree may change. PROBLEM: If a supremum record is removed in a page merge, or a record --------------------------------------------------------------------- removed in a purge, what to do to the waiting lock requests? In a split to the right, we just move the lock requests to the new supremum. If a record is removed, we could move the waiting lock request to its inheritor, the next record in the index. But, the next record may already have lock requests on its own queue. A new deadlock check should be made then. Maybe it is easier just to release the waiting transactions. They can then enqueue new lock requests on appropriate records. PROBLEM: When a record is inserted, what locks should it inherit from the ------------------------------------------------------------------------- upper neighbor? An insert of a new supremum record in a page split is always possible, but an insert of a new user record requires that the upper neighbor does not have any lock requests by other transactions, granted or waiting, in its lock queue. Solution: We can copy the locks as gap type locks, so that also the waiting locks are transformed to granted gap type locks on the inserted record. */ /* LOCK COMPATIBILITY MATRIX * IS IX S X AI * IS + + + - + * IX + + - - + * S + - + - - * X - - - - - * AI + + - - - * * Note that for rows, InnoDB only acquires S or X locks. * For tables, InnoDB normally acquires IS or IX locks. * S or X table locks are only acquired for LOCK TABLES. * Auto-increment (AI) locks are needed because of * statement-level MySQL binlog. * See also lock_mode_compatible(). */ #define LK(a,b) (1 << ((a) * LOCK_NUM + (b))) #define LKS(a,b) LK(a,b) | LK(b,a) /* Define the lock compatibility matrix in a ulint. The first line below defines the diagonal entries. The following lines define the compatibility for LOCK_IX, LOCK_S, and LOCK_AUTO_INC using LKS(), since the matrix is symmetric. */ #define LOCK_MODE_COMPATIBILITY 0 \ | LK(LOCK_IS, LOCK_IS) | LK(LOCK_IX, LOCK_IX) | LK(LOCK_S, LOCK_S) \ | LKS(LOCK_IX, LOCK_IS) | LKS(LOCK_IS, LOCK_AUTO_INC) \ | LKS(LOCK_S, LOCK_IS) \ | LKS(LOCK_AUTO_INC, LOCK_IS) | LKS(LOCK_AUTO_INC, LOCK_IX) /* STRONGER-OR-EQUAL RELATION (mode1=row, mode2=column) * IS IX S X AI * IS + - - - - * IX + + - - - * S + - + - - * X + + + + + * AI - - - - + * See lock_mode_stronger_or_eq(). */ /* Define the stronger-or-equal lock relation in a ulint. This relation contains all pairs LK(mode1, mode2) where mode1 is stronger than or equal to mode2. */ #define LOCK_MODE_STRONGER_OR_EQ 0 \ | LK(LOCK_IS, LOCK_IS) \ | LK(LOCK_IX, LOCK_IS) | LK(LOCK_IX, LOCK_IX) \ | LK(LOCK_S, LOCK_IS) | LK(LOCK_S, LOCK_S) \ | LK(LOCK_AUTO_INC, LOCK_AUTO_INC) \ | LK(LOCK_X, LOCK_IS) | LK(LOCK_X, LOCK_IX) | LK(LOCK_X, LOCK_S) \ | LK(LOCK_X, LOCK_AUTO_INC) | LK(LOCK_X, LOCK_X) #ifdef UNIV_DEBUG UNIV_INTERN ibool lock_print_waits = FALSE; /*********************************************************************//** Validates the lock system. @return TRUE if ok */ static ibool lock_validate(void); /*===============*/ /*********************************************************************//** Validates the record lock queues on a page. @return TRUE if ok */ static ibool lock_rec_validate_page( /*===================*/ ulint space, /*!< in: space id */ ulint zip_size,/*!< in: compressed page size in bytes or 0 for uncompressed pages */ ulint page_no);/*!< in: page number */ #endif /* UNIV_DEBUG */ /* The lock system */ UNIV_INTERN lock_sys_t* lock_sys = NULL; /* We store info on the latest deadlock error to this buffer. InnoDB Monitor will then fetch it and print */ UNIV_INTERN ibool lock_deadlock_found = FALSE; UNIV_INTERN ib_stream_t lock_latest_err_stream; /* Flags for recursive deadlock search */ #define LOCK_VICTIM_IS_START 1 #define LOCK_VICTIM_IS_OTHER 2 #define LOCK_EXCEED_MAX_DEPTH 3 /********************************************************************//** Checks if a lock request results in a deadlock. @return TRUE if a deadlock was detected and we chose trx as a victim; FALSE if no deadlock, or there was a deadlock, but we chose other transaction(s) as victim(s) */ static ibool lock_deadlock_occurs( /*=================*/ lock_t* lock, /*!< in: lock the transaction is requesting */ trx_t* trx); /*!< in: transaction */ /********************************************************************//** Looks recursively for a deadlock. @return 0 if no deadlock found, LOCK_VICTIM_IS_START if there was a deadlock and we chose 'start' as the victim, LOCK_VICTIM_IS_OTHER if a deadlock was found and we chose some other trx as a victim: we must do the search again in this last case because there may be another deadlock! LOCK_EXCEED_MAX_DEPTH if the lock search exceeds max steps or max depth. */ static ulint lock_deadlock_recursive( /*====================*/ trx_t* start, /*!< in: recursion starting point */ trx_t* trx, /*!< in: a transaction waiting for a lock */ lock_t* wait_lock, /*!< in: lock that is waiting to be granted */ ulint* cost, /*!< in/out: number of calculation steps thus far: if this exceeds LOCK_MAX_N_STEPS_... we return LOCK_EXCEED_MAX_DEPTH */ ulint depth); /*!< in: recursion depth: if this exceeds LOCK_MAX_DEPTH_IN_DEADLOCK_CHECK, we return LOCK_EXCEED_MAX_DEPTH */ /*********************************************************************//** Reset the lock variables. */ UNIV_INTERN void lock_var_init(void) /*===============*/ { #ifdef UNIV_DEBUG lock_print_waits = FALSE; #endif /* UNIV_DEBUG */ lock_sys = NULL; lock_deadlock_found = FALSE; lock_latest_err_stream = NULL; } /************************************************************************* Gets the nth bit of a record lock. @return TRUE if bit set also if i == ULINT_UNDEFINED return FALSE*/ UNIV_INLINE ibool lock_rec_get_nth_bit( /*=================*/ const lock_t* lock, /*!< in: record lock */ ulint i) /*!< in: index of the bit */ { ulint byte_index; ulint bit_index; ut_ad(lock); ut_ad(lock_get_type_low(lock) == LOCK_REC); if (i >= lock->un_member.rec_lock.n_bits) { return(FALSE); } byte_index = i / 8; bit_index = i % 8; return(1 & ((const byte*) &lock[1])[byte_index] >> bit_index); } /*************************************************************************/ #define lock_mutex_enter_kernel() mutex_enter(&kernel_mutex) #define lock_mutex_exit_kernel() mutex_exit(&kernel_mutex) /*********************************************************************//** Checks that a transaction id is sensible, i.e., not in the future. @return TRUE if ok */ UNIV_INTERN ibool lock_check_trx_id_sanity( /*=====================*/ trx_id_t trx_id, /*!< in: trx id */ const rec_t* rec, /*!< in: user record */ dict_index_t* index, /*!< in: index */ const ulint* offsets, /*!< in: rec_get_offsets(rec, index) */ ibool has_kernel_mutex)/*!< in: TRUE if the caller owns the kernel mutex */ { ibool is_ok = TRUE; ut_ad(rec_offs_validate(rec, index, offsets)); if (!has_kernel_mutex) { mutex_enter(&kernel_mutex); } /* A sanity check: the trx_id in rec must be smaller than the global trx id counter */ if (ut_dulint_cmp(trx_id, trx_sys->max_trx_id) >= 0) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Error: transaction id associated" " with record\n"); rec_print_new(ib_stream, rec, offsets); ib_logger(ib_stream, "InnoDB: in "); dict_index_name_print(ib_stream, NULL, index); ib_logger(ib_stream, "\n" "InnoDB: is " TRX_ID_FMT " which is higher than the" " global trx id counter " TRX_ID_FMT "!\n" "InnoDB: The table is corrupt. You have to do" " dump + drop + reimport.\n", TRX_ID_PREP_PRINTF(trx_id), TRX_ID_PREP_PRINTF(trx_sys->max_trx_id)); is_ok = FALSE; } if (!has_kernel_mutex) { mutex_exit(&kernel_mutex); } return(is_ok); } /*********************************************************************//** Checks that a record is seen in a consistent read. @return TRUE if sees, or FALSE if an earlier version of the record should be retrieved */ UNIV_INTERN ibool lock_clust_rec_cons_read_sees( /*==========================*/ const rec_t* rec, /*!< in: user record which should be read or passed over by a read cursor */ dict_index_t* index, /*!< in: clustered index */ const ulint* offsets,/*!< in: rec_get_offsets(rec, index) */ read_view_t* view) /*!< in: consistent read view */ { trx_id_t trx_id; ut_ad(dict_index_is_clust(index)); ut_ad(page_rec_is_user_rec(rec)); ut_ad(rec_offs_validate(rec, index, offsets)); /* NOTE that we call this function while holding the search system latch. To obey the latching order we must NOT reserve the kernel mutex here! */ trx_id = row_get_rec_trx_id(rec, index, offsets); return(read_view_sees_trx_id(view, trx_id)); } /*********************************************************************//** Checks that a non-clustered index record is seen in a consistent read. NOTE that a non-clustered index page contains so little information on its modifications that also in the case FALSE, the present version of rec may be the right, but we must check this from the clustered index record. @return TRUE if certainly sees, or FALSE if an earlier version of the clustered index record might be needed */ UNIV_INTERN ulint lock_sec_rec_cons_read_sees( /*========================*/ const rec_t* rec, /*!< in: user record which should be read or passed over by a read cursor */ const read_view_t* view) /*!< in: consistent read view */ { trx_id_t max_trx_id; ut_ad(page_rec_is_user_rec(rec)); /* NOTE that we might call this function while holding the search system latch. To obey the latching order we must NOT reserve the kernel mutex here! */ if (recv_recovery_is_on()) { return(FALSE); } max_trx_id = page_get_max_trx_id(page_align(rec)); ut_ad(!ut_dulint_is_zero(max_trx_id)); return(ut_dulint_cmp(max_trx_id, view->up_limit_id) < 0); } /*********************************************************************//** Creates the lock system at database start. */ UNIV_INTERN void lock_sys_create( /*============*/ ulint n_cells) /*!< in: number of slots in lock hash table */ { lock_sys = mem_alloc(sizeof(lock_sys_t)); lock_sys->rec_hash = hash_create(n_cells); /* hash_create_mutexes(lock_sys->rec_hash, 2, SYNC_REC_LOCK); */ lock_latest_err_stream = os_file_create_tmpfile(); ut_a(lock_latest_err_stream); } /*********************************************************************//** Closes the lock system at database shutdown. */ UNIV_INTERN void lock_sys_close(void) /*================*/ { /* This can happen if we decide to abort during the startup phase. */ if (lock_sys == NULL) { return; } /* hash_free_mutexes(lock_sys->rec_hash); */ hash_table_free(lock_sys->rec_hash); lock_sys->rec_hash = NULL; if (lock_latest_err_stream != NULL) { fclose(lock_latest_err_stream); lock_latest_err_stream = NULL; } mem_free(lock_sys); lock_sys = NULL; } /************************************************************************* Gets the size of a lock struct. @return size in bytes */ UNIV_INTERN ulint lock_get_size(void) /*===============*/ { return((ulint)sizeof(lock_t)); } /*********************************************************************//** Gets the mode of a lock. @return mode */ UNIV_INLINE enum lock_mode lock_get_mode( /*==========*/ const lock_t* lock) /*!< in: lock */ { ut_ad(lock); return(lock->type_mode & LOCK_MODE_MASK); } /*********************************************************************//** Gets the wait flag of a lock. @return TRUE if waiting */ UNIV_INLINE ibool lock_get_wait( /*==========*/ const lock_t* lock) /*!< in: lock */ { ut_ad(lock); if (UNIV_UNLIKELY(lock->type_mode & LOCK_WAIT)) { return(TRUE); } return(FALSE); } /*********************************************************************//** Gets the source table of an ALTER TABLE transaction. The table must be covered by an IX or IS table lock. @return the source table of transaction, if it is covered by an IX or IS table lock; dest if there is no source table, and NULL if the transaction is locking more than two tables or an inconsistency is found */ UNIV_INTERN dict_table_t* lock_get_src_table( /*===============*/ trx_t* trx, /*!< in: transaction */ dict_table_t* dest, /*!< in: destination of ALTER TABLE */ enum lock_mode* mode) /*!< out: lock mode of the source table */ { dict_table_t* src; lock_t* lock; src = NULL; *mode = LOCK_NONE; for (lock = UT_LIST_GET_FIRST(trx->trx_locks); lock; lock = UT_LIST_GET_NEXT(trx_locks, lock)) { lock_table_t* tab_lock; enum lock_mode lock_mode; if (!(lock_get_type_low(lock) & LOCK_TABLE)) { /* We are only interested in table locks. */ continue; } tab_lock = &lock->un_member.tab_lock; if (dest == tab_lock->table) { /* We are not interested in the destination table. */ continue; } else if (!src) { /* This presumably is the source table. */ src = tab_lock->table; if (UT_LIST_GET_LEN(src->locks) != 1 || UT_LIST_GET_FIRST(src->locks) != lock) { /* We only support the case when there is only one lock on this table. */ return(NULL); } } else if (src != tab_lock->table) { /* The transaction is locking more than two tables (src and dest): abort */ return(NULL); } /* Check that the source table is locked by LOCK_IX or LOCK_IS. */ lock_mode = lock_get_mode(lock); if (lock_mode == LOCK_IX || lock_mode == LOCK_IS) { if (*mode != LOCK_NONE && *mode != lock_mode) { /* There are multiple locks on src. */ return(NULL); } *mode = lock_mode; } } if (!src) { /* No source table lock found: flag the situation to caller */ src = dest; } return(src); } /*********************************************************************//** Determine if the given table is exclusively "owned" by the given transaction, i.e., transaction holds LOCK_IX and possibly LOCK_AUTO_INC on the table. @return TRUE if table is only locked by trx, with LOCK_IX, and possibly LOCK_AUTO_INC */ UNIV_INTERN ibool lock_is_table_exclusive( /*====================*/ dict_table_t* table, /*!< in: table */ trx_t* trx) /*!< in: transaction */ { const lock_t* lock; ibool ok = FALSE; ut_ad(table); ut_ad(trx); lock_mutex_enter_kernel(); for (lock = UT_LIST_GET_FIRST(table->locks); lock; lock = UT_LIST_GET_NEXT(locks, &lock->un_member.tab_lock)) { if (lock->trx != trx) { /* A lock on the table is held by some other transaction. */ goto not_ok; } if (!(lock_get_type_low(lock) & LOCK_TABLE)) { /* We are interested in table locks only. */ continue; } switch (lock_get_mode(lock)) { case LOCK_IX: ok = TRUE; break; case LOCK_AUTO_INC: /* It is allowed for trx to hold an auto_increment lock. */ break; default: not_ok: /* Other table locks than LOCK_IX are not allowed. */ ok = FALSE; goto func_exit; } } func_exit: lock_mutex_exit_kernel(); return(ok); } /*********************************************************************//** Sets the wait flag of a lock and the back pointer in trx to lock. */ UNIV_INLINE void lock_set_lock_and_trx_wait( /*=======================*/ lock_t* lock, /*!< in: lock */ trx_t* trx) /*!< in: trx */ { ut_ad(lock); ut_ad(trx->wait_lock == NULL); trx->wait_lock = lock; lock->type_mode |= LOCK_WAIT; } /**********************************************************************//** The back pointer to a waiting lock request in the transaction is set to NULL and the wait bit in lock type_mode is reset. */ UNIV_INLINE void lock_reset_lock_and_trx_wait( /*=========================*/ lock_t* lock) /*!< in: record lock */ { ut_ad((lock->trx)->wait_lock == lock); ut_ad(lock_get_wait(lock)); /* Reset the back pointer in trx to this waiting lock request */ (lock->trx)->wait_lock = NULL; lock->type_mode &= ~LOCK_WAIT; } /*********************************************************************//** Gets the gap flag of a record lock. @return TRUE if gap flag set */ UNIV_INLINE ibool lock_rec_get_gap( /*=============*/ const lock_t* lock) /*!< in: record lock */ { ut_ad(lock); ut_ad(lock_get_type_low(lock) == LOCK_REC); if (lock->type_mode & LOCK_GAP) { return(TRUE); } return(FALSE); } /*********************************************************************//** Gets the LOCK_REC_NOT_GAP flag of a record lock. @return TRUE if LOCK_REC_NOT_GAP flag set */ UNIV_INLINE ibool lock_rec_get_rec_not_gap( /*=====================*/ const lock_t* lock) /*!< in: record lock */ { ut_ad(lock); ut_ad(lock_get_type_low(lock) == LOCK_REC); if (lock->type_mode & LOCK_REC_NOT_GAP) { return(TRUE); } return(FALSE); } /*********************************************************************//** Gets the waiting insert flag of a record lock. @return TRUE if gap flag set */ UNIV_INLINE ibool lock_rec_get_insert_intention( /*==========================*/ const lock_t* lock) /*!< in: record lock */ { ut_ad(lock); ut_ad(lock_get_type_low(lock) == LOCK_REC); if (lock->type_mode & LOCK_INSERT_INTENTION) { return(TRUE); } return(FALSE); } /*********************************************************************//** Calculates if lock mode 1 is stronger or equal to lock mode 2. @return nonzero if mode1 stronger or equal to mode2 */ UNIV_INLINE ulint lock_mode_stronger_or_eq( /*=====================*/ enum lock_mode mode1, /*!< in: lock mode */ enum lock_mode mode2) /*!< in: lock mode */ { ut_ad(mode1 == LOCK_X || mode1 == LOCK_S || mode1 == LOCK_IX || mode1 == LOCK_IS || mode1 == LOCK_AUTO_INC); ut_ad(mode2 == LOCK_X || mode2 == LOCK_S || mode2 == LOCK_IX || mode2 == LOCK_IS || mode2 == LOCK_AUTO_INC); return((LOCK_MODE_STRONGER_OR_EQ) & LK(mode1, mode2)); } /*********************************************************************//** Calculates if lock mode 1 is compatible with lock mode 2. @return nonzero if mode1 compatible with mode2 */ UNIV_INLINE ulint lock_mode_compatible( /*=================*/ enum lock_mode mode1, /*!< in: lock mode */ enum lock_mode mode2) /*!< in: lock mode */ { ut_ad(mode1 == LOCK_X || mode1 == LOCK_S || mode1 == LOCK_IX || mode1 == LOCK_IS || mode1 == LOCK_AUTO_INC); ut_ad(mode2 == LOCK_X || mode2 == LOCK_S || mode2 == LOCK_IX || mode2 == LOCK_IS || mode2 == LOCK_AUTO_INC); return((LOCK_MODE_COMPATIBILITY) & LK(mode1, mode2)); } /*********************************************************************//** Checks if a lock request for a new lock has to wait for request lock2. @return TRUE if new lock has to wait for lock2 to be removed */ UNIV_INLINE ibool lock_rec_has_to_wait( /*=================*/ const trx_t* trx, /*!< in: trx of new lock */ ulint type_mode,/*!< in: precise mode of the new lock to set: LOCK_S or LOCK_X, possibly ORed to LOCK_GAP or LOCK_REC_NOT_GAP, LOCK_INSERT_INTENTION */ const lock_t* lock2, /*!< in: another record lock; NOTE that it is assumed that this has a lock bit set on the same record as in the new lock we are setting */ ibool lock_is_on_supremum) /*!< in: TRUE if we are setting the lock on the 'supremum' record of an index page: we know then that the lock request is really for a 'gap' type lock */ { ut_ad(trx && lock2); ut_ad(lock_get_type_low(lock2) == LOCK_REC); if (trx != lock2->trx && !lock_mode_compatible(LOCK_MODE_MASK & type_mode, lock_get_mode(lock2))) { /* We have somewhat complex rules when gap type record locks cause waits */ if ((lock_is_on_supremum || (type_mode & LOCK_GAP)) && !(type_mode & LOCK_INSERT_INTENTION)) { /* Gap type locks without LOCK_INSERT_INTENTION flag do not need to wait for anything. This is because different users can have conflicting lock types on gaps. */ return(FALSE); } if (!(type_mode & LOCK_INSERT_INTENTION) && lock_rec_get_gap(lock2)) { /* Record lock (LOCK_ORDINARY or LOCK_REC_NOT_GAP does not need to wait for a gap type lock */ return(FALSE); } if ((type_mode & LOCK_GAP) && lock_rec_get_rec_not_gap(lock2)) { /* Lock on gap does not need to wait for a LOCK_REC_NOT_GAP type lock */ return(FALSE); } if (lock_rec_get_insert_intention(lock2)) { /* No lock request needs to wait for an insert intention lock to be removed. This is ok since our rules allow conflicting locks on gaps. This eliminates a spurious deadlock caused by a next-key lock waiting for an insert intention lock; when the insert intention lock was granted, the insert deadlocked on the waiting next-key lock. Also, insert intention locks do not disturb each other. */ return(FALSE); } return(TRUE); } return(FALSE); } /*********************************************************************//** Checks if a lock request lock1 has to wait for request lock2. @return TRUE if lock1 has to wait for lock2 to be removed */ UNIV_INTERN ibool lock_has_to_wait( /*=============*/ const lock_t* lock1, /*!< in: waiting lock */ const lock_t* lock2) /*!< in: another lock; NOTE that it is assumed that this has a lock bit set on the same record as in lock1 if the locks are record locks */ { ut_ad(lock1 && lock2); if (lock1->trx != lock2->trx && !lock_mode_compatible(lock_get_mode(lock1), lock_get_mode(lock2))) { if (lock_get_type_low(lock1) == LOCK_REC) { ut_ad(lock_get_type_low(lock2) == LOCK_REC); /* If this lock request is for a supremum record then the second bit on the lock bitmap is set */ return(lock_rec_has_to_wait(lock1->trx, lock1->type_mode, lock2, lock_rec_get_nth_bit( lock1, 1))); } return(TRUE); } return(FALSE); } /*============== RECORD LOCK BASIC FUNCTIONS ============================*/ /*********************************************************************//** Gets the number of bits in a record lock bitmap. @return number of bits */ UNIV_INLINE ulint lock_rec_get_n_bits( /*================*/ const lock_t* lock) /*!< in: record lock */ { return(lock->un_member.rec_lock.n_bits); } /**********************************************************************//** Sets the nth bit of a record lock to TRUE. */ UNIV_INLINE void lock_rec_set_nth_bit( /*=================*/ lock_t* lock, /*!< in: record lock */ ulint i) /*!< in: index of the bit */ { ulint byte_index; ulint bit_index; ut_ad(lock); ut_ad(lock_get_type_low(lock) == LOCK_REC); ut_ad(i < lock->un_member.rec_lock.n_bits); byte_index = i / 8; bit_index = i % 8; ((byte*) &lock[1])[byte_index] |= 1 << bit_index; } /**********************************************************************//** Looks for a set bit in a record lock bitmap. Returns ULINT_UNDEFINED, if none found. @return bit index == heap number of the record, or ULINT_UNDEFINED if none found */ UNIV_INTERN ulint lock_rec_find_set_bit( /*==================*/ const lock_t* lock) /*!< in: record lock with at least one bit set */ { ulint i; for (i = 0; i < lock_rec_get_n_bits(lock); i++) { if (lock_rec_get_nth_bit(lock, i)) { return(i); } } return(ULINT_UNDEFINED); } /**********************************************************************//** Resets the nth bit of a record lock. */ UNIV_INLINE void lock_rec_reset_nth_bit( /*===================*/ lock_t* lock, /*!< in: record lock */ ulint i) /*!< in: index of the bit which must be set to TRUE when this function is called */ { ulint byte_index; ulint bit_index; ut_ad(lock); ut_ad(lock_get_type_low(lock) == LOCK_REC); ut_ad(i < lock->un_member.rec_lock.n_bits); byte_index = i / 8; bit_index = i % 8; ((byte*) &lock[1])[byte_index] &= ~(1 << bit_index); } /*********************************************************************//** Gets the first or next record lock on a page. @return next lock, NULL if none exists */ UNIV_INLINE lock_t* lock_rec_get_next_on_page( /*======================*/ lock_t* lock) /*!< in: a record lock */ { ulint space; ulint page_no; ut_ad(mutex_own(&kernel_mutex)); ut_ad(lock_get_type_low(lock) == LOCK_REC); space = lock->un_member.rec_lock.space; page_no = lock->un_member.rec_lock.page_no; for (;;) { lock = HASH_GET_NEXT(hash, lock); if (!lock) { break; } if ((lock->un_member.rec_lock.space == space) && (lock->un_member.rec_lock.page_no == page_no)) { break; } } return(lock); } /*********************************************************************//** Gets the first record lock on a page, where the page is identified by its file address. @return first lock, NULL if none exists */ UNIV_INLINE lock_t* lock_rec_get_first_on_page_addr( /*============================*/ ulint space, /*!< in: space */ ulint page_no)/*!< in: page number */ { lock_t* lock; ut_ad(mutex_own(&kernel_mutex)); lock = HASH_GET_FIRST(lock_sys->rec_hash, lock_rec_hash(space, page_no)); while (lock) { if ((lock->un_member.rec_lock.space == space) && (lock->un_member.rec_lock.page_no == page_no)) { break; } lock = HASH_GET_NEXT(hash, lock); } return(lock); } /*********************************************************************//** Returns TRUE if there are explicit record locks on a page. @return TRUE if there are explicit record locks on the page */ UNIV_INTERN ibool lock_rec_expl_exist_on_page( /*========================*/ ulint space, /*!< in: space id */ ulint page_no)/*!< in: page number */ { ibool ret; mutex_enter(&kernel_mutex); if (lock_rec_get_first_on_page_addr(space, page_no)) { ret = TRUE; } else { ret = FALSE; } mutex_exit(&kernel_mutex); return(ret); } /*********************************************************************//** Gets the first record lock on a page, where the page is identified by a pointer to it. @return first lock, NULL if none exists */ UNIV_INLINE lock_t* lock_rec_get_first_on_page( /*=======================*/ const buf_block_t* block) /*!< in: buffer block */ { ulint hash; lock_t* lock; ulint space = buf_block_get_space(block); ulint page_no = buf_block_get_page_no(block); ut_ad(mutex_own(&kernel_mutex)); hash = buf_block_get_lock_hash_val(block); lock = HASH_GET_FIRST(lock_sys->rec_hash, hash); while (lock) { if ((lock->un_member.rec_lock.space == space) && (lock->un_member.rec_lock.page_no == page_no)) { break; } lock = HASH_GET_NEXT(hash, lock); } return(lock); } /*********************************************************************//** Gets the next explicit lock request on a record. @return next lock, NULL if none exists or if heap_no == ULINT_UNDEFINED */ UNIV_INLINE lock_t* lock_rec_get_next( /*==============*/ ulint heap_no,/*!< in: heap number of the record */ lock_t* lock) /*!< in: lock */ { ut_ad(mutex_own(&kernel_mutex)); do { ut_ad(lock_get_type_low(lock) == LOCK_REC); lock = lock_rec_get_next_on_page(lock); } while (lock && !lock_rec_get_nth_bit(lock, heap_no)); return(lock); } /*********************************************************************//** Gets the first explicit lock request on a record. @return first lock, NULL if none exists */ UNIV_INLINE lock_t* lock_rec_get_first( /*===============*/ const buf_block_t* block, /*!< in: block containing the record */ ulint heap_no)/*!< in: heap number of the record */ { lock_t* lock; ut_ad(mutex_own(&kernel_mutex)); for (lock = lock_rec_get_first_on_page(block); lock; lock = lock_rec_get_next_on_page(lock)) { if (lock_rec_get_nth_bit(lock, heap_no)) { break; } } return(lock); } /*********************************************************************//** Resets the record lock bitmap to zero. NOTE: does not touch the wait_lock pointer in the transaction! This function is used in lock object creation and resetting. */ static void lock_rec_bitmap_reset( /*==================*/ lock_t* lock) /*!< in: record lock */ { ulint n_bytes; ut_ad(lock_get_type_low(lock) == LOCK_REC); /* Reset to zero the bitmap which resides immediately after the lock struct */ n_bytes = lock_rec_get_n_bits(lock) / 8; ut_ad((lock_rec_get_n_bits(lock) % 8) == 0); memset(&lock[1], 0, n_bytes); } /*********************************************************************//** Copies a record lock to heap. @return copy of lock */ static lock_t* lock_rec_copy( /*==========*/ const lock_t* lock, /*!< in: record lock */ mem_heap_t* heap) /*!< in: memory heap */ { ulint size; ut_ad(lock_get_type_low(lock) == LOCK_REC); size = sizeof(lock_t) + lock_rec_get_n_bits(lock) / 8; return(mem_heap_dup(heap, lock, size)); } /*********************************************************************//** Gets the previous record lock set on a record. @return previous lock on the same record, NULL if none exists */ UNIV_INTERN const lock_t* lock_rec_get_prev( /*==============*/ const lock_t* in_lock,/*!< in: record lock */ ulint heap_no)/*!< in: heap number of the record */ { lock_t* lock; ulint space; ulint page_no; lock_t* found_lock = NULL; ut_ad(mutex_own(&kernel_mutex)); ut_ad(lock_get_type_low(in_lock) == LOCK_REC); space = in_lock->un_member.rec_lock.space; page_no = in_lock->un_member.rec_lock.page_no; lock = lock_rec_get_first_on_page_addr(space, page_no); for (;;) { ut_ad(lock); if (lock == in_lock) { return(found_lock); } if (lock_rec_get_nth_bit(lock, heap_no)) { found_lock = lock; } lock = lock_rec_get_next_on_page(lock); } } /*============= FUNCTIONS FOR ANALYZING TABLE LOCK QUEUE ================*/ /*********************************************************************//** Checks if a transaction has the specified table lock, or stronger. @return lock or NULL */ UNIV_INLINE lock_t* lock_table_has( /*===========*/ trx_t* trx, /*!< in: transaction */ dict_table_t* table, /*!< in: table */ enum lock_mode mode) /*!< in: lock mode */ { lock_t* lock; ut_ad(mutex_own(&kernel_mutex)); /* Look for stronger locks the same trx already has on the table */ lock = UT_LIST_GET_LAST(table->locks); while (lock != NULL) { if (lock->trx == trx && lock_mode_stronger_or_eq(lock_get_mode(lock), mode)) { /* The same trx already has locked the table in a mode stronger or equal to the mode given */ ut_ad(!lock_get_wait(lock)); return(lock); } lock = UT_LIST_GET_PREV(un_member.tab_lock.locks, lock); } return(NULL); } /*============= FUNCTIONS FOR ANALYZING RECORD LOCK QUEUE ================*/ /*********************************************************************//** Checks if a transaction has a GRANTED explicit lock on rec stronger or equal to precise_mode. @return lock or NULL */ UNIV_INLINE lock_t* lock_rec_has_expl( /*==============*/ ulint precise_mode,/*!< in: LOCK_S or LOCK_X possibly ORed to LOCK_GAP or LOCK_REC_NOT_GAP, for a supremum record we regard this always a gap type request */ const buf_block_t* block, /*!< in: buffer block containing the record */ ulint heap_no,/*!< in: heap number of the record */ trx_t* trx) /*!< in: transaction */ { lock_t* lock; ut_ad(mutex_own(&kernel_mutex)); ut_ad((precise_mode & LOCK_MODE_MASK) == LOCK_S || (precise_mode & LOCK_MODE_MASK) == LOCK_X); ut_ad(!(precise_mode & LOCK_INSERT_INTENTION)); lock = lock_rec_get_first(block, heap_no); while (lock) { if (lock->trx == trx && lock_mode_stronger_or_eq(lock_get_mode(lock), precise_mode & LOCK_MODE_MASK) && !lock_get_wait(lock) && (!lock_rec_get_rec_not_gap(lock) || (precise_mode & LOCK_REC_NOT_GAP) || heap_no == PAGE_HEAP_NO_SUPREMUM) && (!lock_rec_get_gap(lock) || (precise_mode & LOCK_GAP) || heap_no == PAGE_HEAP_NO_SUPREMUM) && (!lock_rec_get_insert_intention(lock))) { return(lock); } lock = lock_rec_get_next(heap_no, lock); } return(NULL); } #ifdef UNIV_DEBUG /*********************************************************************//** Checks if some other transaction has a lock request in the queue. @return lock or NULL */ static lock_t* lock_rec_other_has_expl_req( /*========================*/ enum lock_mode mode, /*!< in: LOCK_S or LOCK_X */ ulint gap, /*!< in: LOCK_GAP if also gap locks are taken into account, or 0 if not */ ulint wait, /*!< in: LOCK_WAIT if also waiting locks are taken into account, or 0 if not */ const buf_block_t* block, /*!< in: buffer block containing the record */ ulint heap_no,/*!< in: heap number of the record */ const trx_t* trx) /*!< in: transaction, or NULL if requests by all transactions are taken into account */ { lock_t* lock; ut_ad(mutex_own(&kernel_mutex)); ut_ad(mode == LOCK_X || mode == LOCK_S); ut_ad(gap == 0 || gap == LOCK_GAP); ut_ad(wait == 0 || wait == LOCK_WAIT); lock = lock_rec_get_first(block, heap_no); while (lock) { if (lock->trx != trx && (gap || !(lock_rec_get_gap(lock) || heap_no == PAGE_HEAP_NO_SUPREMUM)) && (wait || !lock_get_wait(lock)) && lock_mode_stronger_or_eq(lock_get_mode(lock), mode)) { return(lock); } lock = lock_rec_get_next(heap_no, lock); } return(NULL); } #endif /* UNIV_DEBUG */ /*********************************************************************//** Checks if some other transaction has a conflicting explicit lock request in the queue, so that we have to wait. @return lock or NULL */ static lock_t* lock_rec_other_has_conflicting( /*===========================*/ enum lock_mode mode, /*!< in: LOCK_S or LOCK_X, possibly ORed to LOCK_GAP or LOC_REC_NOT_GAP, LOCK_INSERT_INTENTION */ const buf_block_t* block, /*!< in: buffer block containing the record */ ulint heap_no,/*!< in: heap number of the record */ trx_t* trx) /*!< in: our transaction */ { lock_t* lock; ut_ad(mutex_own(&kernel_mutex)); lock = lock_rec_get_first(block, heap_no); if (UNIV_LIKELY_NULL(lock)) { if (UNIV_UNLIKELY(heap_no == PAGE_HEAP_NO_SUPREMUM)) { do { if (lock_rec_has_to_wait(trx, mode, lock, TRUE)) { return(lock); } lock = lock_rec_get_next(heap_no, lock); } while (lock); } else { do { if (lock_rec_has_to_wait(trx, mode, lock, FALSE)) { return(lock); } lock = lock_rec_get_next(heap_no, lock); } while (lock); } } return(NULL); } /*********************************************************************//** Looks for a suitable type record lock struct by the same trx on the same page. This can be used to save space when a new record lock should be set on a page: no new struct is needed, if a suitable old is found. @return lock or NULL */ UNIV_INLINE lock_t* lock_rec_find_similar_on_page( /*==========================*/ ulint type_mode, /*!< in: lock type_mode field */ ulint heap_no, /*!< in: heap number of the record */ lock_t* lock, /*!< in: lock_rec_get_first_on_page() */ const trx_t* trx) /*!< in: transaction */ { ut_ad(mutex_own(&kernel_mutex)); while (lock != NULL) { if (lock->trx == trx && lock->type_mode == type_mode && lock_rec_get_n_bits(lock) > heap_no) { return(lock); } lock = lock_rec_get_next_on_page(lock); } return(NULL); } /*********************************************************************//** Checks if some transaction has an implicit x-lock on a record in a secondary index. @return transaction which has the x-lock, or NULL */ static trx_t* lock_sec_rec_some_has_impl_off_kernel( /*==================================*/ const rec_t* rec, /*!< in: user record */ dict_index_t* index, /*!< in: secondary index */ const ulint* offsets)/*!< in: rec_get_offsets(rec, index) */ { const page_t* page = page_align(rec); ut_ad(mutex_own(&kernel_mutex)); ut_ad(!dict_index_is_clust(index)); ut_ad(page_rec_is_user_rec(rec)); ut_ad(rec_offs_validate(rec, index, offsets)); /* Some transaction may have an implicit x-lock on the record only if the max trx id for the page >= min trx id for the trx list, or database recovery is running. We do not write the changes of a page max trx id to the log, and therefore during recovery, this value for a page may be incorrect. */ if (!(ut_dulint_cmp(page_get_max_trx_id(page), trx_list_get_min_trx_id()) >= 0) && !recv_recovery_is_on()) { return(NULL); } /* Ok, in this case it is possible that some transaction has an implicit x-lock. We have to look in the clustered index. */ if (!lock_check_trx_id_sanity(page_get_max_trx_id(page), rec, index, offsets, TRUE)) { buf_page_print(page, 0); /* The page is corrupt: try to avoid a crash by returning NULL */ return(NULL); } return(row_vers_impl_x_locked_off_kernel(rec, index, offsets)); } /*********************************************************************//** Return approximate number or record locks (bits set in the bitmap) for this transaction. Since delete-marked records may be removed, the record count will not be precise. */ UNIV_INTERN ulint lock_number_of_rows_locked( /*=======================*/ trx_t* trx) /*!< in: transaction */ { lock_t* lock; ulint n_records = 0; ulint n_bits; ulint n_bit; lock = UT_LIST_GET_FIRST(trx->trx_locks); while (lock) { if (lock_get_type_low(lock) == LOCK_REC) { n_bits = lock_rec_get_n_bits(lock); for (n_bit = 0; n_bit < n_bits; n_bit++) { if (lock_rec_get_nth_bit(lock, n_bit)) { n_records++; } } } lock = UT_LIST_GET_NEXT(trx_locks, lock); } return (n_records); } /*============== RECORD LOCK CREATION AND QUEUE MANAGEMENT =============*/ /*********************************************************************//** Creates a new record lock and inserts it to the lock queue. Does NOT check for deadlocks or lock compatibility! @return created lock */ static lock_t* lock_rec_create_low( /*================*/ ulint type_mode, /*!< in: lock mode and wait flag, type is ignored and replaced by LOCK_REC */ ulint space, /*!< in: space id */ ulint page_no, /*!< in: page number */ ulint heap_no, /*!< in: heap number of the record */ ulint n_bits, /*!< in: no. of bits in lock bitmap */ dict_index_t* index, /*!< in: index of record */ trx_t* trx) /*!< in: transaction */ { lock_t* lock; ulint n_bytes; ut_ad(mutex_own(&kernel_mutex)); /* If rec is the supremum record, then we reset the gap and LOCK_REC_NOT_GAP bits, as all locks on the supremum are automatically of the gap type */ if (UNIV_UNLIKELY(heap_no == PAGE_HEAP_NO_SUPREMUM)) { ut_ad(!(type_mode & LOCK_REC_NOT_GAP)); type_mode = type_mode & ~(LOCK_GAP | LOCK_REC_NOT_GAP); } /* Make lock bitmap bigger by a safety margin */ n_bytes = 1 + (n_bits + LOCK_PAGE_BITMAP_MARGIN) / 8; lock = mem_heap_alloc(trx->lock_heap, sizeof(lock_t) + n_bytes); UT_LIST_ADD_LAST(trx_locks, trx->trx_locks, lock); lock->trx = trx; lock->type_mode = (type_mode & ~LOCK_TYPE_MASK) | LOCK_REC; lock->index = index; lock->un_member.rec_lock.space = space; lock->un_member.rec_lock.page_no = page_no; lock->un_member.rec_lock.n_bits = n_bytes * 8; /* Reset to zero the bitmap which resides immediately after the lock struct */ lock_rec_bitmap_reset(lock); /* Set the bit corresponding to rec */ lock_rec_set_nth_bit(lock, heap_no); HASH_INSERT(lock_t, hash, lock_sys->rec_hash, lock_rec_fold(space, page_no), lock); if (UNIV_UNLIKELY(type_mode & LOCK_WAIT)) { lock_set_lock_and_trx_wait(lock, trx); } return(lock); } /*********************************************************************//** Creates a new record lock and inserts it to the lock queue. @return created lock */ static lock_t* lock_rec_create( /*============*/ ulint type_mode,/*!< in: lock mode and wait flag, type is ignored and replaced by LOCK_REC */ const buf_block_t* block, /*!< in: buffer block containing the record */ ulint heap_no,/*!< in: heap number of the record */ dict_index_t* index, /*!< in: index of record */ trx_t* trx) /*!< in: transaction */ { const page_t* page; ulint space; ulint n_bits; ulint page_no; ut_ad(mutex_own(&kernel_mutex)); space = buf_block_get_space(block); page_no = buf_block_get_page_no(block); page = block->frame; ut_ad(!!page_is_comp(page) == dict_table_is_comp(index->table)); n_bits = page_dir_get_n_heap(page); return(lock_rec_create_low( type_mode, space, page_no, heap_no, n_bits, index, trx)); } /*********************************************************************//** Enqueues a waiting request for a lock which cannot be granted immediately. Checks for deadlocks. @return DB_LOCK_WAIT, DB_DEADLOCK, or DB_QUE_THR_SUSPENDED, or DB_SUCCESS; DB_SUCCESS means that there was a deadlock, but another transaction was chosen as a victim, and we got the lock immediately: no need to wait then */ static ulint lock_rec_enqueue_waiting( /*=====================*/ ulint type_mode,/*!< in: lock mode this transaction is requesting: LOCK_S or LOCK_X, possibly ORed with LOCK_GAP or LOCK_REC_NOT_GAP, ORed with LOCK_INSERT_INTENTION if this waiting lock request is set when performing an insert of an index record */ const buf_block_t* block, /*!< in: buffer block containing the record */ ulint heap_no,/*!< in: heap number of the record */ dict_index_t* index, /*!< in: index of record */ que_thr_t* thr) /*!< in: query thread */ { lock_t* lock; trx_t* trx; ut_ad(mutex_own(&kernel_mutex)); /* Test if there already is some other reason to suspend thread: we do not enqueue a lock request if the query thread should be stopped anyway */ if (UNIV_UNLIKELY(que_thr_stop(thr))) { ut_error; return(DB_QUE_THR_SUSPENDED); } trx = thr_get_trx(thr); switch (trx_get_dict_operation(trx)) { case TRX_DICT_OP_NONE: break; case TRX_DICT_OP_TABLE: case TRX_DICT_OP_INDEX: ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Error: a record lock wait happens" " in a dictionary operation!\n" "InnoDB: "); dict_index_name_print(ib_stream, trx, index); ib_logger(ib_stream, ".\n" "InnoDB: Submit a detailed bug report " "check the InnoDB website for details"); } /* Enqueue the lock request that will wait to be granted */ lock = lock_rec_create(type_mode | LOCK_WAIT, block, heap_no, index, trx); /* Check if a deadlock occurs: if yes, remove the lock request and return an error code */ if (UNIV_UNLIKELY(lock_deadlock_occurs(lock, trx))) { lock_reset_lock_and_trx_wait(lock); lock_rec_reset_nth_bit(lock, heap_no); return(DB_DEADLOCK); } /* If there was a deadlock but we chose another transaction as a victim, it is possible that we already have the lock now granted! */ if (trx->wait_lock == NULL) { return(DB_SUCCESS); } trx->que_state = TRX_QUE_LOCK_WAIT; trx->was_chosen_as_deadlock_victim = FALSE; trx->wait_started = time(NULL); ut_a(que_thr_stop(thr)); #ifdef UNIV_DEBUG if (lock_print_waits) { ib_logger(ib_stream, "Lock wait for trx %lu in index ", (ulong) ut_dulint_get_low(trx->id)); ut_print_name(ib_stream, trx, FALSE, index->name); } #endif /* UNIV_DEBUG */ return(DB_LOCK_WAIT); } /*********************************************************************//** Adds a record lock request in the record queue. The request is normally added as the last in the queue, but if there are no waiting lock requests on the record, and the request to be added is not a waiting request, we can reuse a suitable record lock object already existing on the same page, just setting the appropriate bit in its bitmap. This is a low-level function which does NOT check for deadlocks or lock compatibility! @return lock where the bit was set */ static lock_t* lock_rec_add_to_queue( /*==================*/ ulint type_mode,/*!< in: lock mode, wait, gap etc. flags; type is ignored and replaced by LOCK_REC */ const buf_block_t* block, /*!< in: buffer block containing the record */ ulint heap_no,/*!< in: heap number of the record */ dict_index_t* index, /*!< in: index of record */ trx_t* trx) /*!< in: transaction */ { lock_t* lock; ut_ad(mutex_own(&kernel_mutex)); #ifdef UNIV_DEBUG switch (type_mode & LOCK_MODE_MASK) { case LOCK_X: case LOCK_S: break; default: ut_error; } if (!(type_mode & (LOCK_WAIT | LOCK_GAP))) { enum lock_mode mode = (type_mode & LOCK_MODE_MASK) == LOCK_S ? LOCK_X : LOCK_S; lock_t* other_lock = lock_rec_other_has_expl_req(mode, 0, LOCK_WAIT, block, heap_no, trx); ut_a(!other_lock); } #endif /* UNIV_DEBUG */ type_mode |= LOCK_REC; /* If rec is the supremum record, then we can reset the gap bit, as all locks on the supremum are automatically of the gap type, and we try to avoid unnecessary memory consumption of a new record lock struct for a gap type lock */ if (UNIV_UNLIKELY(heap_no == PAGE_HEAP_NO_SUPREMUM)) { ut_ad(!(type_mode & LOCK_REC_NOT_GAP)); /* There should never be LOCK_REC_NOT_GAP on a supremum record, but let us play safe */ type_mode = type_mode & ~(LOCK_GAP | LOCK_REC_NOT_GAP); } /* Look for a waiting lock request on the same record or on a gap */ lock = lock_rec_get_first_on_page(block); while (lock != NULL) { if (lock_get_wait(lock) && (lock_rec_get_nth_bit(lock, heap_no))) { goto somebody_waits; } lock = lock_rec_get_next_on_page(lock); } if (UNIV_LIKELY(!(type_mode & LOCK_WAIT))) { /* Look for a similar record lock on the same page: if one is found and there are no waiting lock requests, we can just set the bit */ lock = lock_rec_find_similar_on_page( type_mode, heap_no, lock_rec_get_first_on_page(block), trx); if (lock) { lock_rec_set_nth_bit(lock, heap_no); return(lock); } } somebody_waits: return(lock_rec_create(type_mode, block, heap_no, index, trx)); } /*********************************************************************//** This is a fast routine for locking a record in the most common cases: there are no explicit locks on the page, or there is just one lock, owned by this transaction, and of the right type_mode. This is a low-level function which does NOT look at implicit locks! Checks lock compatibility within explicit locks. This function sets a normal next-key lock, or in the case of a page supremum record, a gap type lock. @return TRUE if locking succeeded */ UNIV_INLINE ibool lock_rec_lock_fast( /*===============*/ ibool impl, /*!< in: if TRUE, no lock is set if no wait is necessary: we assume that the caller will set an implicit lock */ ulint mode, /*!< in: lock mode: LOCK_X or LOCK_S possibly ORed to either LOCK_GAP or LOCK_REC_NOT_GAP */ const buf_block_t* block, /*!< in: buffer block containing the record */ ulint heap_no,/*!< in: heap number of record */ dict_index_t* index, /*!< in: index of record */ que_thr_t* thr) /*!< in: query thread */ { lock_t* lock; trx_t* trx; ut_ad(mutex_own(&kernel_mutex)); ut_ad((LOCK_MODE_MASK & mode) != LOCK_S || lock_table_has(thr_get_trx(thr), index->table, LOCK_IS)); ut_ad((LOCK_MODE_MASK & mode) != LOCK_X || lock_table_has(thr_get_trx(thr), index->table, LOCK_IX)); ut_ad((LOCK_MODE_MASK & mode) == LOCK_S || (LOCK_MODE_MASK & mode) == LOCK_X); ut_ad(mode - (LOCK_MODE_MASK & mode) == LOCK_GAP || mode - (LOCK_MODE_MASK & mode) == 0 || mode - (LOCK_MODE_MASK & mode) == LOCK_REC_NOT_GAP); lock = lock_rec_get_first_on_page(block); trx = thr_get_trx(thr); if (lock == NULL) { if (!impl) { lock_rec_create(mode, block, heap_no, index, trx); } return(TRUE); } if (lock_rec_get_next_on_page(lock)) { return(FALSE); } if (lock->trx != trx || lock->type_mode != (mode | LOCK_REC) || lock_rec_get_n_bits(lock) <= heap_no) { return(FALSE); } if (!impl) { /* If the nth bit of the record lock is already set then we do not set a new lock bit, otherwise we do set */ if (!lock_rec_get_nth_bit(lock, heap_no)) { lock_rec_set_nth_bit(lock, heap_no); } } return(TRUE); } /*********************************************************************//** This is the general, and slower, routine for locking a record. This is a low-level function which does NOT look at implicit locks! Checks lock compatibility within explicit locks. This function sets a normal next-key lock, or in the case of a page supremum record, a gap type lock. @return DB_SUCCESS, DB_LOCK_WAIT, or error code */ static ulint lock_rec_lock_slow( /*===============*/ ibool impl, /*!< in: if TRUE, no lock is set if no wait is necessary: we assume that the caller will set an implicit lock */ ulint mode, /*!< in: lock mode: LOCK_X or LOCK_S possibly ORed to either LOCK_GAP or LOCK_REC_NOT_GAP */ const buf_block_t* block, /*!< in: buffer block containing the record */ ulint heap_no,/*!< in: heap number of record */ dict_index_t* index, /*!< in: index of record */ que_thr_t* thr) /*!< in: query thread */ { trx_t* trx; ulint err; ut_ad(mutex_own(&kernel_mutex)); ut_ad((LOCK_MODE_MASK & mode) != LOCK_S || lock_table_has(thr_get_trx(thr), index->table, LOCK_IS)); ut_ad((LOCK_MODE_MASK & mode) != LOCK_X || lock_table_has(thr_get_trx(thr), index->table, LOCK_IX)); ut_ad((LOCK_MODE_MASK & mode) == LOCK_S || (LOCK_MODE_MASK & mode) == LOCK_X); ut_ad(mode - (LOCK_MODE_MASK & mode) == LOCK_GAP || mode - (LOCK_MODE_MASK & mode) == 0 || mode - (LOCK_MODE_MASK & mode) == LOCK_REC_NOT_GAP); trx = thr_get_trx(thr); if (lock_rec_has_expl(mode, block, heap_no, trx)) { /* The trx already has a strong enough lock on rec: do nothing */ err = DB_SUCCESS; } else if (lock_rec_other_has_conflicting(mode, block, heap_no, trx)) { /* If another transaction has a non-gap conflicting request in the queue, as this transaction does not have a lock strong enough already granted on the record, we have to wait. */ err = lock_rec_enqueue_waiting(mode, block, heap_no, index, thr); } else { if (!impl) { /* Set the requested lock on the record */ lock_rec_add_to_queue(LOCK_REC | mode, block, heap_no, index, trx); } err = DB_SUCCESS; } return(err); } /*********************************************************************//** Tries to lock the specified record in the mode requested. If not immediately possible, enqueues a waiting lock request. This is a low-level function which does NOT look at implicit locks! Checks lock compatibility within explicit locks. This function sets a normal next-key lock, or in the case of a page supremum record, a gap type lock. @return DB_SUCCESS, DB_LOCK_WAIT, or error code */ static ulint lock_rec_lock( /*==========*/ ibool impl, /*!< in: if TRUE, no lock is set if no wait is necessary: we assume that the caller will set an implicit lock */ ulint mode, /*!< in: lock mode: LOCK_X or LOCK_S possibly ORed to either LOCK_GAP or LOCK_REC_NOT_GAP */ const buf_block_t* block, /*!< in: buffer block containing the record */ ulint heap_no,/*!< in: heap number of record */ dict_index_t* index, /*!< in: index of record */ que_thr_t* thr) /*!< in: query thread */ { ulint err; ut_ad(mutex_own(&kernel_mutex)); ut_ad((LOCK_MODE_MASK & mode) != LOCK_S || lock_table_has(thr_get_trx(thr), index->table, LOCK_IS)); ut_ad((LOCK_MODE_MASK & mode) != LOCK_X || lock_table_has(thr_get_trx(thr), index->table, LOCK_IX)); ut_ad((LOCK_MODE_MASK & mode) == LOCK_S || (LOCK_MODE_MASK & mode) == LOCK_X); ut_ad(mode - (LOCK_MODE_MASK & mode) == LOCK_GAP || mode - (LOCK_MODE_MASK & mode) == LOCK_REC_NOT_GAP || mode - (LOCK_MODE_MASK & mode) == 0); if (lock_rec_lock_fast(impl, mode, block, heap_no, index, thr)) { /* We try a simplified and faster subroutine for the most common cases */ err = DB_SUCCESS; } else { err = lock_rec_lock_slow(impl, mode, block, heap_no, index, thr); } return(err); } /*********************************************************************//** Checks if a waiting record lock request still has to wait in a queue. @return TRUE if still has to wait */ static ibool lock_rec_has_to_wait_in_queue( /*==========================*/ lock_t* wait_lock) /*!< in: waiting record lock */ { lock_t* lock; ulint space; ulint page_no; ulint heap_no; ut_ad(mutex_own(&kernel_mutex)); ut_ad(lock_get_wait(wait_lock)); ut_ad(lock_get_type_low(wait_lock) == LOCK_REC); space = wait_lock->un_member.rec_lock.space; page_no = wait_lock->un_member.rec_lock.page_no; heap_no = lock_rec_find_set_bit(wait_lock); lock = lock_rec_get_first_on_page_addr(space, page_no); while (lock != wait_lock) { if (lock_rec_get_nth_bit(lock, heap_no) && lock_has_to_wait(wait_lock, lock)) { return(TRUE); } lock = lock_rec_get_next_on_page(lock); } return(FALSE); } /*************************************************************//** Grants a lock to a waiting lock request and releases the waiting transaction. */ static void lock_grant( /*=======*/ lock_t* lock) /*!< in/out: waiting lock request */ { ut_ad(mutex_own(&kernel_mutex)); lock_reset_lock_and_trx_wait(lock); #ifdef UNIV_DEBUG if (lock_print_waits) { ib_logger(ib_stream, "Lock wait for trx %lu ends\n", (ulong) ut_dulint_get_low(lock->trx->id)); } #endif /* UNIV_DEBUG */ /* If we are resolving a deadlock by choosing another transaction as a victim, then our original transaction may not be in the TRX_QUE_LOCK_WAIT state, and there is no need to end the lock wait for it */ if (lock->trx->que_state == TRX_QUE_LOCK_WAIT) { trx_end_lock_wait(lock->trx); } } /*************************************************************//** Cancels a waiting record lock request and releases the waiting transaction that requested it. NOTE: does NOT check if waiting lock requests behind this one can now be granted! */ static void lock_rec_cancel( /*============*/ lock_t* lock) /*!< in: waiting record lock request */ { ut_ad(mutex_own(&kernel_mutex)); ut_ad(lock_get_type_low(lock) == LOCK_REC); /* Reset the bit (there can be only one set bit) in the lock bitmap */ lock_rec_reset_nth_bit(lock, lock_rec_find_set_bit(lock)); /* Reset the wait flag and the back pointer to lock in trx */ lock_reset_lock_and_trx_wait(lock); /* The following function releases the trx from lock wait */ trx_end_lock_wait(lock->trx); } /*************************************************************//** Removes a record lock request, waiting or granted, from the queue and grants locks to other transactions in the queue if they now are entitled to a lock. NOTE: all record locks contained in in_lock are removed. */ static void lock_rec_dequeue_from_page( /*=======================*/ lock_t* in_lock)/*!< in: record lock object: all record locks which are contained in this lock object are removed; transactions waiting behind will get their lock requests granted, if they are now qualified to it */ { ulint space; ulint page_no; lock_t* lock; trx_t* trx; ut_ad(mutex_own(&kernel_mutex)); ut_ad(lock_get_type_low(in_lock) == LOCK_REC); trx = in_lock->trx; space = in_lock->un_member.rec_lock.space; page_no = in_lock->un_member.rec_lock.page_no; HASH_DELETE(lock_t, hash, lock_sys->rec_hash, lock_rec_fold(space, page_no), in_lock); UT_LIST_REMOVE(trx_locks, trx->trx_locks, in_lock); /* Check if waiting locks in the queue can now be granted: grant locks if there are no conflicting locks ahead. */ lock = lock_rec_get_first_on_page_addr(space, page_no); while (lock != NULL) { if (lock_get_wait(lock) && !lock_rec_has_to_wait_in_queue(lock)) { /* Grant the lock */ lock_grant(lock); } lock = lock_rec_get_next_on_page(lock); } } /*************************************************************//** Removes a record lock request, waiting or granted, from the queue. */ static void lock_rec_discard( /*=============*/ lock_t* in_lock)/*!< in: record lock object: all record locks which are contained in this lock object are removed */ { ulint space; ulint page_no; trx_t* trx; ut_ad(mutex_own(&kernel_mutex)); ut_ad(lock_get_type_low(in_lock) == LOCK_REC); trx = in_lock->trx; space = in_lock->un_member.rec_lock.space; page_no = in_lock->un_member.rec_lock.page_no; HASH_DELETE(lock_t, hash, lock_sys->rec_hash, lock_rec_fold(space, page_no), in_lock); UT_LIST_REMOVE(trx_locks, trx->trx_locks, in_lock); } /*************************************************************//** Removes record lock objects set on an index page which is discarded. This function does not move locks, or check for waiting locks, therefore the lock bitmaps must already be reset when this function is called. */ static void lock_rec_free_all_from_discard_page( /*================================*/ const buf_block_t* block) /*!< in: page to be discarded */ { ulint space; ulint page_no; lock_t* lock; lock_t* next_lock; ut_ad(mutex_own(&kernel_mutex)); space = buf_block_get_space(block); page_no = buf_block_get_page_no(block); lock = lock_rec_get_first_on_page_addr(space, page_no); while (lock != NULL) { ut_ad(lock_rec_find_set_bit(lock) == ULINT_UNDEFINED); ut_ad(!lock_get_wait(lock)); next_lock = lock_rec_get_next_on_page(lock); lock_rec_discard(lock); lock = next_lock; } } /*============= RECORD LOCK MOVING AND INHERITING ===================*/ /*************************************************************//** Resets the lock bits for a single record. Releases transactions waiting for lock requests here. */ static void lock_rec_reset_and_release_wait( /*============================*/ const buf_block_t* block, /*!< in: buffer block containing the record */ ulint heap_no)/*!< in: heap number of record */ { lock_t* lock; ut_ad(mutex_own(&kernel_mutex)); lock = lock_rec_get_first(block, heap_no); while (lock != NULL) { if (lock_get_wait(lock)) { lock_rec_cancel(lock); } else { lock_rec_reset_nth_bit(lock, heap_no); } lock = lock_rec_get_next(heap_no, lock); } } /*************************************************************//** Makes a record to inherit the locks (except LOCK_INSERT_INTENTION type) of another record as gap type locks, but does not reset the lock bits of the other record. Also waiting lock requests on rec are inherited as GRANTED gap locks. */ static void lock_rec_inherit_to_gap( /*====================*/ const buf_block_t* heir_block, /*!< in: block containing the record which inherits */ const buf_block_t* block, /*!< in: block containing the record from which inherited; does NOT reset the locks on this record */ ulint heir_heap_no, /*!< in: heap_no of the inheriting record */ ulint heap_no) /*!< in: heap_no of the donating record */ { lock_t* lock; ut_ad(mutex_own(&kernel_mutex)); lock = lock_rec_get_first(block, heap_no); /* If session is using READ COMMITTED isolation level, we do not want locks set by an UPDATE or a DELETE to be inherited as gap type locks. But we DO want S-locks set by a consistency constraint to be inherited also then. */ while (lock != NULL) { if (!lock_rec_get_insert_intention(lock) && lock->trx->isolation_level != TRX_ISO_READ_COMMITTED && lock_get_mode(lock) == LOCK_X) { lock_rec_add_to_queue(LOCK_REC | LOCK_GAP | lock_get_mode(lock), heir_block, heir_heap_no, lock->index, lock->trx); } lock = lock_rec_get_next(heap_no, lock); } } /*************************************************************//** Makes a record to inherit the gap locks (except LOCK_INSERT_INTENTION type) of another record as gap type locks, but does not reset the lock bits of the other record. Also waiting lock requests are inherited as GRANTED gap locks. */ static void lock_rec_inherit_to_gap_if_gap_lock( /*================================*/ const buf_block_t* block, /*!< in: buffer block */ ulint heir_heap_no, /*!< in: heap_no of record which inherits */ ulint heap_no) /*!< in: heap_no of record from which inherited; does NOT reset the locks on this record */ { lock_t* lock; ut_ad(mutex_own(&kernel_mutex)); lock = lock_rec_get_first(block, heap_no); while (lock != NULL) { if (!lock_rec_get_insert_intention(lock) && (heap_no == PAGE_HEAP_NO_SUPREMUM || !lock_rec_get_rec_not_gap(lock))) { lock_rec_add_to_queue(LOCK_REC | LOCK_GAP | lock_get_mode(lock), block, heir_heap_no, lock->index, lock->trx); } lock = lock_rec_get_next(heap_no, lock); } } /*************************************************************//** Moves the locks of a record to another record and resets the lock bits of the donating record. */ static void lock_rec_move( /*==========*/ const buf_block_t* receiver, /*!< in: buffer block containing the receiving record */ const buf_block_t* donator, /*!< in: buffer block containing the donating record */ ulint receiver_heap_no,/*!< in: heap_no of the record which gets the locks; there must be no lock requests on it! */ ulint donator_heap_no)/*!< in: heap_no of the record which gives the locks */ { lock_t* lock; ut_ad(mutex_own(&kernel_mutex)); lock = lock_rec_get_first(donator, donator_heap_no); ut_ad(lock_rec_get_first(receiver, receiver_heap_no) == NULL); while (lock != NULL) { const ulint type_mode = lock->type_mode; lock_rec_reset_nth_bit(lock, donator_heap_no); if (UNIV_UNLIKELY(type_mode & LOCK_WAIT)) { lock_reset_lock_and_trx_wait(lock); } /* Note that we FIRST reset the bit, and then set the lock: the function works also if donator == receiver */ lock_rec_add_to_queue(type_mode, receiver, receiver_heap_no, lock->index, lock->trx); lock = lock_rec_get_next(donator_heap_no, lock); } ut_ad(lock_rec_get_first(donator, donator_heap_no) == NULL); } /*************************************************************//** Updates the lock table when we have reorganized a page. NOTE: we copy also the locks set on the infimum of the page; the infimum may carry locks if an update of a record is occurring on the page, and its locks were temporarily stored on the infimum. */ UNIV_INTERN void lock_move_reorganize_page( /*======================*/ const buf_block_t* block, /*!< in: old index page, now reorganized */ const buf_block_t* oblock) /*!< in: copy of the old, not reorganized page */ { lock_t* lock; UT_LIST_BASE_NODE_T(lock_t) old_locks; mem_heap_t* heap = NULL; ulint comp; lock_mutex_enter_kernel(); lock = lock_rec_get_first_on_page(block); if (lock == NULL) { lock_mutex_exit_kernel(); return; } heap = mem_heap_create(256); /* Copy first all the locks on the page to heap and reset the bitmaps in the original locks; chain the copies of the locks using the trx_locks field in them. */ UT_LIST_INIT(old_locks); do { /* Make a copy of the lock */ lock_t* old_lock = lock_rec_copy(lock, heap); UT_LIST_ADD_LAST(trx_locks, old_locks, old_lock); /* Reset bitmap of lock */ lock_rec_bitmap_reset(lock); if (lock_get_wait(lock)) { lock_reset_lock_and_trx_wait(lock); } lock = lock_rec_get_next_on_page(lock); } while (lock != NULL); comp = page_is_comp(block->frame); ut_ad(comp == page_is_comp(oblock->frame)); for (lock = UT_LIST_GET_FIRST(old_locks); lock; lock = UT_LIST_GET_NEXT(trx_locks, lock)) { /* NOTE: we copy also the locks set on the infimum and supremum of the page; the infimum may carry locks if an update of a record is occurring on the page, and its locks were temporarily stored on the infimum */ page_cur_t cur1; page_cur_t cur2; page_cur_set_before_first(block, &cur1); page_cur_set_before_first(oblock, &cur2); /* Set locks according to old locks */ for (;;) { ulint old_heap_no; ulint new_heap_no; ut_ad(comp || !memcmp(page_cur_get_rec(&cur1), page_cur_get_rec(&cur2), rec_get_data_size_old( page_cur_get_rec( &cur2)))); if (UNIV_LIKELY(comp)) { old_heap_no = rec_get_heap_no_new( page_cur_get_rec(&cur2)); new_heap_no = rec_get_heap_no_new( page_cur_get_rec(&cur1)); } else { old_heap_no = rec_get_heap_no_old( page_cur_get_rec(&cur2)); new_heap_no = rec_get_heap_no_old( page_cur_get_rec(&cur1)); } if (lock_rec_get_nth_bit(lock, old_heap_no)) { /* Clear the bit in old_lock. */ ut_d(lock_rec_reset_nth_bit(lock, old_heap_no)); /* NOTE that the old lock bitmap could be too small for the new heap number! */ lock_rec_add_to_queue(lock->type_mode, block, new_heap_no, lock->index, lock->trx); /* if (new_heap_no == PAGE_HEAP_NO_SUPREMUM && lock_get_wait(lock)) { ib_logger(ib_stream, "---\n--\n!!!Lock reorg: supr type %lu\n", lock->type_mode); } */ } if (UNIV_UNLIKELY (new_heap_no == PAGE_HEAP_NO_SUPREMUM)) { ut_ad(old_heap_no == PAGE_HEAP_NO_SUPREMUM); break; } page_cur_move_to_next(&cur1); page_cur_move_to_next(&cur2); } #ifdef UNIV_DEBUG { ulint i = lock_rec_find_set_bit(lock); /* Check that all locks were moved. */ if (UNIV_UNLIKELY(i != ULINT_UNDEFINED)) { ib_logger(ib_stream, "lock_move_reorganize_page():" " %lu not moved in %p\n", (ulong) i, (void*) lock); ut_error; } } #endif /* UNIV_DEBUG */ } lock_mutex_exit_kernel(); mem_heap_free(heap); #ifdef UNIV_DEBUG_LOCK_VALIDATE ut_ad(lock_rec_validate_page(buf_block_get_space(block), buf_block_get_zip_size(block), buf_block_get_page_no(block))); #endif } /*************************************************************//** Moves the explicit locks on user records to another page if a record list end is moved to another page. */ UNIV_INTERN void lock_move_rec_list_end( /*===================*/ const buf_block_t* new_block, /*!< in: index page to move to */ const buf_block_t* block, /*!< in: index page */ const rec_t* rec) /*!< in: record on page: this is the first record moved */ { lock_t* lock; const ulint comp = page_rec_is_comp(rec); lock_mutex_enter_kernel(); /* Note: when we move locks from record to record, waiting locks and possible granted gap type locks behind them are enqueued in the original order, because new elements are inserted to a hash table to the end of the hash chain, and lock_rec_add_to_queue does not reuse locks if there are waiters in the queue. */ for (lock = lock_rec_get_first_on_page(block); lock; lock = lock_rec_get_next_on_page(lock)) { page_cur_t cur1; page_cur_t cur2; const ulint type_mode = lock->type_mode; page_cur_position(rec, block, &cur1); if (page_cur_is_before_first(&cur1)) { page_cur_move_to_next(&cur1); } page_cur_set_before_first(new_block, &cur2); page_cur_move_to_next(&cur2); /* Copy lock requests on user records to new page and reset the lock bits on the old */ while (!page_cur_is_after_last(&cur1)) { ulint heap_no; if (comp) { heap_no = rec_get_heap_no_new( page_cur_get_rec(&cur1)); } else { heap_no = rec_get_heap_no_old( page_cur_get_rec(&cur1)); ut_ad(!memcmp(page_cur_get_rec(&cur1), page_cur_get_rec(&cur2), rec_get_data_size_old( page_cur_get_rec(&cur2)))); } if (lock_rec_get_nth_bit(lock, heap_no)) { lock_rec_reset_nth_bit(lock, heap_no); if (UNIV_UNLIKELY(type_mode & LOCK_WAIT)) { lock_reset_lock_and_trx_wait(lock); } if (comp) { heap_no = rec_get_heap_no_new( page_cur_get_rec(&cur2)); } else { heap_no = rec_get_heap_no_old( page_cur_get_rec(&cur2)); } lock_rec_add_to_queue(type_mode, new_block, heap_no, lock->index, lock->trx); } page_cur_move_to_next(&cur1); page_cur_move_to_next(&cur2); } } lock_mutex_exit_kernel(); #ifdef UNIV_DEBUG_LOCK_VALIDATE ut_ad(lock_rec_validate_page(buf_block_get_space(block), buf_block_get_zip_size(block), buf_block_get_page_no(block))); ut_ad(lock_rec_validate_page(buf_block_get_space(new_block), buf_block_get_zip_size(block), buf_block_get_page_no(new_block))); #endif } /*************************************************************//** Moves the explicit locks on user records to another page if a record list start is moved to another page. */ UNIV_INTERN void lock_move_rec_list_start( /*=====================*/ const buf_block_t* new_block, /*!< in: index page to move to */ const buf_block_t* block, /*!< in: index page */ const rec_t* rec, /*!< in: record on page: this is the first record NOT copied */ const rec_t* old_end) /*!< in: old previous-to-last record on new_page before the records were copied */ { lock_t* lock; const ulint comp = page_rec_is_comp(rec); ut_ad(block->frame == page_align(rec)); ut_ad(new_block->frame == page_align(old_end)); lock_mutex_enter_kernel(); for (lock = lock_rec_get_first_on_page(block); lock; lock = lock_rec_get_next_on_page(lock)) { page_cur_t cur1; page_cur_t cur2; const ulint type_mode = lock->type_mode; page_cur_set_before_first(block, &cur1); page_cur_move_to_next(&cur1); page_cur_position(old_end, new_block, &cur2); page_cur_move_to_next(&cur2); /* Copy lock requests on user records to new page and reset the lock bits on the old */ while (page_cur_get_rec(&cur1) != rec) { ulint heap_no; if (comp) { heap_no = rec_get_heap_no_new( page_cur_get_rec(&cur1)); } else { heap_no = rec_get_heap_no_old( page_cur_get_rec(&cur1)); ut_ad(!memcmp(page_cur_get_rec(&cur1), page_cur_get_rec(&cur2), rec_get_data_size_old( page_cur_get_rec( &cur2)))); } if (lock_rec_get_nth_bit(lock, heap_no)) { lock_rec_reset_nth_bit(lock, heap_no); if (UNIV_UNLIKELY(type_mode & LOCK_WAIT)) { lock_reset_lock_and_trx_wait(lock); } if (comp) { heap_no = rec_get_heap_no_new( page_cur_get_rec(&cur2)); } else { heap_no = rec_get_heap_no_old( page_cur_get_rec(&cur2)); } lock_rec_add_to_queue(type_mode, new_block, heap_no, lock->index, lock->trx); } page_cur_move_to_next(&cur1); page_cur_move_to_next(&cur2); } #ifdef UNIV_DEBUG if (page_rec_is_supremum(rec)) { ulint i; for (i = PAGE_HEAP_NO_USER_LOW; i < lock_rec_get_n_bits(lock); i++) { if (UNIV_UNLIKELY (lock_rec_get_nth_bit(lock, i))) { ib_logger(ib_stream, "lock_move_rec_list_start():" " %lu not moved in %p\n", (ulong) i, (void*) lock); ut_error; } } } #endif /* UNIV_DEBUG */ } lock_mutex_exit_kernel(); #ifdef UNIV_DEBUG_LOCK_VALIDATE ut_ad(lock_rec_validate_page(buf_block_get_space(block), buf_block_get_zip_size(block), buf_block_get_page_no(block))); #endif } /*************************************************************//** Updates the lock table when a page is split to the right. */ UNIV_INTERN void lock_update_split_right( /*====================*/ const buf_block_t* right_block, /*!< in: right page */ const buf_block_t* left_block) /*!< in: left page */ { ulint heap_no = lock_get_min_heap_no(right_block); lock_mutex_enter_kernel(); /* Move the locks on the supremum of the left page to the supremum of the right page */ lock_rec_move(right_block, left_block, PAGE_HEAP_NO_SUPREMUM, PAGE_HEAP_NO_SUPREMUM); /* Inherit the locks to the supremum of left page from the successor of the infimum on right page */ lock_rec_inherit_to_gap(left_block, right_block, PAGE_HEAP_NO_SUPREMUM, heap_no); lock_mutex_exit_kernel(); } /*************************************************************//** Updates the lock table when a page is merged to the right. */ UNIV_INTERN void lock_update_merge_right( /*====================*/ const buf_block_t* right_block, /*!< in: right page to which merged */ const rec_t* orig_succ, /*!< in: original successor of infimum on the right page before merge */ const buf_block_t* left_block) /*!< in: merged index page which will be discarded */ { lock_mutex_enter_kernel(); /* Inherit the locks from the supremum of the left page to the original successor of infimum on the right page, to which the left page was merged */ lock_rec_inherit_to_gap(right_block, left_block, page_rec_get_heap_no(orig_succ), PAGE_HEAP_NO_SUPREMUM); /* Reset the locks on the supremum of the left page, releasing waiting transactions */ lock_rec_reset_and_release_wait(left_block, PAGE_HEAP_NO_SUPREMUM); lock_rec_free_all_from_discard_page(left_block); lock_mutex_exit_kernel(); } /*************************************************************//** Updates the lock table when the root page is copied to another in btr_root_raise_and_insert. Note that we leave lock structs on the root page, even though they do not make sense on other than leaf pages: the reason is that in a pessimistic update the infimum record of the root page will act as a dummy carrier of the locks of the record to be updated. */ UNIV_INTERN void lock_update_root_raise( /*===================*/ const buf_block_t* block, /*!< in: index page to which copied */ const buf_block_t* root) /*!< in: root page */ { lock_mutex_enter_kernel(); /* Move the locks on the supremum of the root to the supremum of block */ lock_rec_move(block, root, PAGE_HEAP_NO_SUPREMUM, PAGE_HEAP_NO_SUPREMUM); lock_mutex_exit_kernel(); } /*************************************************************//** Updates the lock table when a page is copied to another and the original page is removed from the chain of leaf pages, except if page is the root! */ UNIV_INTERN void lock_update_copy_and_discard( /*=========================*/ const buf_block_t* new_block, /*!< in: index page to which copied */ const buf_block_t* block) /*!< in: index page; NOT the root! */ { lock_mutex_enter_kernel(); /* Move the locks on the supremum of the old page to the supremum of new_page */ lock_rec_move(new_block, block, PAGE_HEAP_NO_SUPREMUM, PAGE_HEAP_NO_SUPREMUM); lock_rec_free_all_from_discard_page(block); lock_mutex_exit_kernel(); } /*************************************************************//** Updates the lock table when a page is split to the left. */ UNIV_INTERN void lock_update_split_left( /*===================*/ const buf_block_t* right_block, /*!< in: right page */ const buf_block_t* left_block) /*!< in: left page */ { ulint heap_no = lock_get_min_heap_no(right_block); lock_mutex_enter_kernel(); /* Inherit the locks to the supremum of the left page from the successor of the infimum on the right page */ lock_rec_inherit_to_gap(left_block, right_block, PAGE_HEAP_NO_SUPREMUM, heap_no); lock_mutex_exit_kernel(); } /*************************************************************//** Updates the lock table when a page is merged to the left. */ UNIV_INTERN void lock_update_merge_left( /*===================*/ const buf_block_t* left_block, /*!< in: left page to which merged */ const rec_t* orig_pred, /*!< in: original predecessor of supremum on the left page before merge */ const buf_block_t* right_block) /*!< in: merged index page which will be discarded */ { const rec_t* left_next_rec; ut_ad(left_block->frame == page_align(orig_pred)); lock_mutex_enter_kernel(); left_next_rec = page_rec_get_next_const(orig_pred); if (!page_rec_is_supremum(left_next_rec)) { /* Inherit the locks on the supremum of the left page to the first record which was moved from the right page */ lock_rec_inherit_to_gap(left_block, left_block, page_rec_get_heap_no(left_next_rec), PAGE_HEAP_NO_SUPREMUM); /* Reset the locks on the supremum of the left page, releasing waiting transactions */ lock_rec_reset_and_release_wait(left_block, PAGE_HEAP_NO_SUPREMUM); } /* Move the locks from the supremum of right page to the supremum of the left page */ lock_rec_move(left_block, right_block, PAGE_HEAP_NO_SUPREMUM, PAGE_HEAP_NO_SUPREMUM); lock_rec_free_all_from_discard_page(right_block); lock_mutex_exit_kernel(); } /*************************************************************//** Resets the original locks on heir and replaces them with gap type locks inherited from rec. */ UNIV_INTERN void lock_rec_reset_and_inherit_gap_locks( /*=================================*/ const buf_block_t* heir_block, /*!< in: block containing the record which inherits */ const buf_block_t* block, /*!< in: block containing the record from which inherited; does NOT reset the locks on this record */ ulint heir_heap_no, /*!< in: heap_no of the inheriting record */ ulint heap_no) /*!< in: heap_no of the donating record */ { mutex_enter(&kernel_mutex); lock_rec_reset_and_release_wait(heir_block, heir_heap_no); lock_rec_inherit_to_gap(heir_block, block, heir_heap_no, heap_no); mutex_exit(&kernel_mutex); } /*************************************************************//** Updates the lock table when a page is discarded. */ UNIV_INTERN void lock_update_discard( /*================*/ const buf_block_t* heir_block, /*!< in: index page which will inherit the locks */ ulint heir_heap_no, /*!< in: heap_no of the record which will inherit the locks */ const buf_block_t* block) /*!< in: index page which will be discarded */ { const page_t* page = block->frame; const rec_t* rec; ulint heap_no; lock_mutex_enter_kernel(); if (!lock_rec_get_first_on_page(block)) { /* No locks exist on page, nothing to do */ lock_mutex_exit_kernel(); return; } /* Inherit all the locks on the page to the record and reset all the locks on the page */ if (page_is_comp(page)) { rec = page + PAGE_NEW_INFIMUM; do { heap_no = rec_get_heap_no_new(rec); lock_rec_inherit_to_gap(heir_block, block, heir_heap_no, heap_no); lock_rec_reset_and_release_wait(block, heap_no); rec = page + rec_get_next_offs(rec, TRUE); } while (heap_no != PAGE_HEAP_NO_SUPREMUM); } else { rec = page + PAGE_OLD_INFIMUM; do { heap_no = rec_get_heap_no_old(rec); lock_rec_inherit_to_gap(heir_block, block, heir_heap_no, heap_no); lock_rec_reset_and_release_wait(block, heap_no); rec = page + rec_get_next_offs(rec, FALSE); } while (heap_no != PAGE_HEAP_NO_SUPREMUM); } lock_rec_free_all_from_discard_page(block); lock_mutex_exit_kernel(); } /*************************************************************//** Updates the lock table when a new user record is inserted. */ UNIV_INTERN void lock_update_insert( /*===============*/ const buf_block_t* block, /*!< in: buffer block containing rec */ const rec_t* rec) /*!< in: the inserted record */ { ulint receiver_heap_no; ulint donator_heap_no; ut_ad(block->frame == page_align(rec)); /* Inherit the gap-locking locks for rec, in gap mode, from the next record */ if (page_rec_is_comp(rec)) { receiver_heap_no = rec_get_heap_no_new(rec); donator_heap_no = rec_get_heap_no_new( page_rec_get_next_low(rec, TRUE)); } else { receiver_heap_no = rec_get_heap_no_old(rec); donator_heap_no = rec_get_heap_no_old( page_rec_get_next_low(rec, FALSE)); } lock_mutex_enter_kernel(); lock_rec_inherit_to_gap_if_gap_lock(block, receiver_heap_no, donator_heap_no); lock_mutex_exit_kernel(); } /*************************************************************//** Updates the lock table when a record is removed. */ UNIV_INTERN void lock_update_delete( /*===============*/ const buf_block_t* block, /*!< in: buffer block containing rec */ const rec_t* rec) /*!< in: the record to be removed */ { const page_t* page = block->frame; ulint heap_no; ulint next_heap_no; ut_ad(page == page_align(rec)); if (page_is_comp(page)) { heap_no = rec_get_heap_no_new(rec); next_heap_no = rec_get_heap_no_new(page + rec_get_next_offs(rec, TRUE)); } else { heap_no = rec_get_heap_no_old(rec); next_heap_no = rec_get_heap_no_old(page + rec_get_next_offs(rec, FALSE)); } lock_mutex_enter_kernel(); /* Let the next record inherit the locks from rec, in gap mode */ lock_rec_inherit_to_gap(block, block, next_heap_no, heap_no); /* Reset the lock bits on rec and release waiting transactions */ lock_rec_reset_and_release_wait(block, heap_no); lock_mutex_exit_kernel(); } /*********************************************************************//** Stores on the page infimum record the explicit locks of another record. This function is used to store the lock state of a record when it is updated and the size of the record changes in the update. The record is moved in such an update, perhaps to another page. The infimum record acts as a dummy carrier record, taking care of lock releases while the actual record is being moved. */ UNIV_INTERN void lock_rec_store_on_page_infimum( /*===========================*/ const buf_block_t* block, /*!< in: buffer block containing rec */ const rec_t* rec) /*!< in: record whose lock state is stored on the infimum record of the same page; lock bits are reset on the record */ { ulint heap_no = page_rec_get_heap_no(rec); ut_ad(block->frame == page_align(rec)); lock_mutex_enter_kernel(); lock_rec_move(block, block, PAGE_HEAP_NO_INFIMUM, heap_no); lock_mutex_exit_kernel(); } /*********************************************************************//** Restores the state of explicit lock requests on a single record, where the state was stored on the infimum of the page. */ UNIV_INTERN void lock_rec_restore_from_page_infimum( /*===============================*/ const buf_block_t* block, /*!< in: buffer block containing rec */ const rec_t* rec, /*!< in: record whose lock state is restored */ const buf_block_t* donator)/*!< in: page (rec is not necessarily on this page) whose infimum stored the lock state; lock bits are reset on the infimum */ { ulint heap_no = page_rec_get_heap_no(rec); lock_mutex_enter_kernel(); lock_rec_move(block, donator, heap_no, PAGE_HEAP_NO_INFIMUM); lock_mutex_exit_kernel(); } /*=========== DEADLOCK CHECKING ======================================*/ /********************************************************************//** Checks if a lock request results in a deadlock. @return TRUE if a deadlock was detected and we chose trx as a victim; FALSE if no deadlock, or there was a deadlock, but we chose other transaction(s) as victim(s) */ static ibool lock_deadlock_occurs( /*=================*/ lock_t* lock, /*!< in: lock the transaction is requesting */ trx_t* trx) /*!< in: transaction */ { trx_t* mark_trx; ulint ret; ulint cost = 0; ut_ad(trx); ut_ad(lock); ut_ad(mutex_own(&kernel_mutex)); retry: /* We check that adding this trx to the waits-for graph does not produce a cycle. First mark all active transactions with 0: */ mark_trx = UT_LIST_GET_FIRST(trx_sys->trx_list); while (mark_trx) { mark_trx->deadlock_mark = 0; mark_trx = UT_LIST_GET_NEXT(trx_list, mark_trx); } ret = lock_deadlock_recursive(trx, trx, lock, &cost, 0); switch (ret) { case LOCK_VICTIM_IS_OTHER: /* We chose some other trx as a victim: retry if there still is a deadlock */ goto retry; case LOCK_EXCEED_MAX_DEPTH: /* If the lock search exceeds the max step or the max depth, the current trx will be the victim. Print its information. */ ut_print_timestamp(ib_stream); ib_logger(ib_stream, "TOO DEEP OR LONG SEARCH IN THE LOCK TABLE" " WAITS-FOR GRAPH, WE WILL ROLL BACK" " FOLLOWING TRANSACTION \n"); ib_logger(ib_stream, "\n*** TRANSACTION:\n"); trx_print(ib_stream, trx, 3000); ib_logger(ib_stream, "*** WAITING FOR THIS LOCK TO BE GRANTED:\n"); if (lock_get_type(lock) == LOCK_REC) { lock_rec_print(ib_stream, lock); } else { lock_table_print(ib_stream, lock); } break; case LOCK_VICTIM_IS_START: ib_logger(ib_stream, "*** WE ROLL BACK TRANSACTION (2)\n"); break; default: /* No deadlock detected*/ return(FALSE); } lock_deadlock_found = TRUE; return(TRUE); } /********************************************************************//** Looks recursively for a deadlock. @return 0 if no deadlock found, LOCK_VICTIM_IS_START if there was a deadlock and we chose 'start' as the victim, LOCK_VICTIM_IS_OTHER if a deadlock was found and we chose some other trx as a victim: we must do the search again in this last case because there may be another deadlock! LOCK_EXCEED_MAX_DEPTH if the lock search exceeds max steps or max depth. */ static ulint lock_deadlock_recursive( /*====================*/ trx_t* start, /*!< in: recursion starting point */ trx_t* trx, /*!< in: a transaction waiting for a lock */ lock_t* wait_lock, /*!< in: lock that is waiting to be granted */ ulint* cost, /*!< in/out: number of calculation steps thus far: if this exceeds LOCK_MAX_N_STEPS_... we return LOCK_EXCEED_MAX_DEPTH */ ulint depth) /*!< in: recursion depth: if this exceeds LOCK_MAX_DEPTH_IN_DEADLOCK_CHECK, we return LOCK_EXCEED_MAX_DEPTH */ { ulint ret; lock_t* lock; trx_t* lock_trx; ulint heap_no = ULINT_UNDEFINED; ut_a(trx); ut_a(start); ut_a(wait_lock); ut_ad(mutex_own(&kernel_mutex)); if (trx->deadlock_mark == 1) { /* We have already exhaustively searched the subtree starting from this trx */ return(0); } *cost = *cost + 1; if (lock_get_type_low(wait_lock) == LOCK_REC) { ulint space; ulint page_no; heap_no = lock_rec_find_set_bit(wait_lock); ut_a(heap_no != ULINT_UNDEFINED); space = wait_lock->un_member.rec_lock.space; page_no = wait_lock->un_member.rec_lock.page_no; lock = lock_rec_get_first_on_page_addr(space, page_no); /* Position the iterator on the first matching record lock. */ while (lock != NULL && lock != wait_lock && !lock_rec_get_nth_bit(lock, heap_no)) { lock = lock_rec_get_next_on_page(lock); } if (lock == wait_lock) { lock = NULL; } ut_ad(lock == NULL || lock_rec_get_nth_bit(lock, heap_no)); } else { lock = wait_lock; } /* Look at the locks ahead of wait_lock in the lock queue */ for (;;) { /* Get previous table lock. */ if (heap_no == ULINT_UNDEFINED) { lock = UT_LIST_GET_PREV( un_member.tab_lock.locks, lock); } if (lock == NULL) { /* We can mark this subtree as searched */ trx->deadlock_mark = 1; return(FALSE); } if (lock_has_to_wait(wait_lock, lock)) { ibool too_far = depth > LOCK_MAX_DEPTH_IN_DEADLOCK_CHECK || *cost > LOCK_MAX_N_STEPS_IN_DEADLOCK_CHECK; lock_trx = lock->trx; if (lock_trx == start) { /* We came back to the recursion starting point: a deadlock detected; or we have searched the waits-for graph too long */ ib_stream_t ib_stream; ib_stream = lock_latest_err_stream; ut_print_timestamp(ib_stream); ib_logger(ib_stream, "\n*** (1) TRANSACTION:\n"); trx_print(ib_stream, wait_lock->trx, 3000); ib_logger(ib_stream, "*** (1) WAITING FOR THIS LOCK" " TO BE GRANTED:\n"); if (lock_get_type_low(wait_lock) == LOCK_REC) { lock_rec_print(ib_stream, wait_lock); } else { lock_table_print(ib_stream, wait_lock); } ib_logger(ib_stream, "*** (2) TRANSACTION:\n"); trx_print(ib_stream, lock->trx, 3000); ib_logger(ib_stream, "*** (2) HOLDS THE LOCK(S):\n"); if (lock_get_type_low(lock) == LOCK_REC) { lock_rec_print(ib_stream, lock); } else { lock_table_print(ib_stream, lock); } ib_logger(ib_stream, "*** (2) WAITING FOR THIS LOCK" " TO BE GRANTED:\n"); if (lock_get_type_low(start->wait_lock) == LOCK_REC) { lock_rec_print( ib_stream, start->wait_lock); } else { lock_table_print( ib_stream, start->wait_lock); } #ifdef UNIV_DEBUG if (lock_print_waits) { ib_logger(ib_stream, "Deadlock detected\n"); } #endif /* UNIV_DEBUG */ if (trx_weight_cmp(wait_lock->trx, start) >= 0) { /* Our recursion starting point transaction is 'smaller', let us choose 'start' as the victim and roll back it */ return(LOCK_VICTIM_IS_START); } lock_deadlock_found = TRUE; /* Let us choose the transaction of wait_lock as a victim to try to avoid deadlocking our recursion starting point transaction */ ib_logger(ib_stream, "*** WE ROLL BACK TRANSACTION (1)\n"); wait_lock->trx->was_chosen_as_deadlock_victim = TRUE; lock_cancel_waiting_and_release(wait_lock); /* Since trx and wait_lock are no longer in the waits-for graph, we can return FALSE; note that our selective algorithm can choose several transactions as victims, but still we may end up rolling back also the recursion starting point transaction! */ return(LOCK_VICTIM_IS_OTHER); } if (too_far) { #ifdef UNIV_DEBUG if (lock_print_waits) { ib_logger(ib_stream, "Deadlock search exceeds" " max steps or depth.\n"); } #endif /* UNIV_DEBUG */ /* The information about transaction/lock to be rolled back is available in the top level. Do not print anything here. */ return(LOCK_EXCEED_MAX_DEPTH); } if (lock_trx->que_state == TRX_QUE_LOCK_WAIT) { /* Another trx ahead has requested lock in an incompatible mode, and is itself waiting for a lock */ ret = lock_deadlock_recursive( start, lock_trx, lock_trx->wait_lock, cost, depth + 1); if (ret != 0) { return(ret); } } } /* Get the next record lock to check. */ if (heap_no != ULINT_UNDEFINED) { ut_a(lock != NULL); do { lock = lock_rec_get_next_on_page(lock); } while (lock != NULL && lock != wait_lock && !lock_rec_get_nth_bit(lock, heap_no)); if (lock == wait_lock) { lock = NULL; } } }/* end of the 'for (;;)'-loop */ } /*========================= TABLE LOCKS ==============================*/ /*********************************************************************//** Creates a table lock object and adds it as the last in the lock queue of the table. Does NOT check for deadlocks or lock compatibility. @return own: new lock object */ UNIV_INLINE lock_t* lock_table_create( /*==============*/ dict_table_t* table, /*!< in: database table in dictionary cache */ ulint type_mode,/*!< in: lock mode possibly ORed with LOCK_WAIT */ trx_t* trx) /*!< in: trx */ { lock_t* lock; ut_ad(table && trx); ut_ad(mutex_own(&kernel_mutex)); lock = mem_heap_alloc(trx->lock_heap, sizeof(lock_t)); UT_LIST_ADD_LAST(trx_locks, trx->trx_locks, lock); lock->type_mode = type_mode | LOCK_TABLE; lock->trx = trx; lock->un_member.tab_lock.table = table; UT_LIST_ADD_LAST(un_member.tab_lock.locks, table->locks, lock); if (UNIV_UNLIKELY(type_mode & LOCK_WAIT)) { lock_set_lock_and_trx_wait(lock, trx); } return(lock); } /*************************************************************//** Removes a table lock request from the queue and the trx list of locks; this is a low-level function which does NOT check if waiting requests can now be granted. */ UNIV_INLINE void lock_table_remove_low( /*==================*/ lock_t* lock) /*!< in: table lock */ { trx_t* trx; dict_table_t* table; ut_ad(mutex_own(&kernel_mutex)); trx = lock->trx; table = lock->un_member.tab_lock.table; UT_LIST_REMOVE(trx_locks, trx->trx_locks, lock); UT_LIST_REMOVE(un_member.tab_lock.locks, table->locks, lock); } /*********************************************************************//** Enqueues a waiting request for a table lock which cannot be granted immediately. Checks for deadlocks. @return DB_LOCK_WAIT, DB_DEADLOCK, or DB_QUE_THR_SUSPENDED, or DB_SUCCESS; DB_SUCCESS means that there was a deadlock, but another transaction was chosen as a victim, and we got the lock immediately: no need to wait then */ static ulint lock_table_enqueue_waiting( /*=======================*/ ulint mode, /*!< in: lock mode this transaction is requesting */ dict_table_t* table, /*!< in: table */ que_thr_t* thr) /*!< in: query thread */ { lock_t* lock; trx_t* trx; ut_ad(mutex_own(&kernel_mutex)); /* Test if there already is some other reason to suspend thread: we do not enqueue a lock request if the query thread should be stopped anyway */ if (que_thr_stop(thr)) { ut_error; return(DB_QUE_THR_SUSPENDED); } trx = thr_get_trx(thr); switch (trx_get_dict_operation(trx)) { case TRX_DICT_OP_NONE: break; case TRX_DICT_OP_TABLE: case TRX_DICT_OP_INDEX: ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Error: a table lock wait happens" " in a dictionary operation!\n" "InnoDB: Table name "); ut_print_name(ib_stream, trx, TRUE, table->name); ib_logger(ib_stream, ".\n" "InnoDB: Submit a detailed bug report, " "check the InnoDB website for details"); } /* Enqueue the lock request that will wait to be granted */ lock = lock_table_create(table, mode | LOCK_WAIT, trx); /* Check if a deadlock occurs: if yes, remove the lock request and return an error code */ if (lock_deadlock_occurs(lock, trx)) { /* The order here is important, we don't want to lose the state of the lock before calling remove. */ lock_table_remove_low(lock); lock_reset_lock_and_trx_wait(lock); return(DB_DEADLOCK); } if (trx->wait_lock == NULL) { /* Deadlock resolution chose another transaction as a victim, and we accidentally got our lock granted! */ return(DB_SUCCESS); } trx->que_state = TRX_QUE_LOCK_WAIT; trx->was_chosen_as_deadlock_victim = FALSE; trx->wait_started = time(NULL); ut_a(que_thr_stop(thr)); return(DB_LOCK_WAIT); } /*********************************************************************//** Checks if other transactions have an incompatible mode lock request in the lock queue. @return lock or NULL */ UNIV_INLINE lock_t* lock_table_other_has_incompatible( /*==============================*/ trx_t* trx, /*!< in: transaction, or NULL if all transactions should be included */ ulint wait, /*!< in: LOCK_WAIT if also waiting locks are taken into account, or 0 if not */ dict_table_t* table, /*!< in: table */ enum lock_mode mode) /*!< in: lock mode */ { lock_t* lock; ut_ad(mutex_own(&kernel_mutex)); lock = UT_LIST_GET_LAST(table->locks); while (lock != NULL) { if ((lock->trx != trx) && (!lock_mode_compatible(lock_get_mode(lock), mode)) && (wait || !(lock_get_wait(lock)))) { return(lock); } lock = UT_LIST_GET_PREV(un_member.tab_lock.locks, lock); } return(NULL); } /*********************************************************************//** Locks the specified database table in the mode given. If the lock cannot be granted immediately, the query thread is put to wait. @return DB_SUCCESS, DB_LOCK_WAIT, DB_DEADLOCK, or DB_QUE_THR_SUSPENDED */ UNIV_INTERN ulint lock_table( /*=======*/ ulint flags, /*!< in: if BTR_NO_LOCKING_FLAG bit is set, does nothing */ dict_table_t* table, /*!< in: database table in dictionary cache */ enum lock_mode mode, /*!< in: lock mode */ que_thr_t* thr) /*!< in: query thread */ { trx_t* trx; ulint err; ut_ad(table && thr); if (flags & BTR_NO_LOCKING_FLAG) { return(DB_SUCCESS); } ut_a(flags == 0); trx = thr_get_trx(thr); lock_mutex_enter_kernel(); /* Look for stronger locks the same trx already has on the table */ if (lock_table_has(trx, table, mode)) { lock_mutex_exit_kernel(); return(DB_SUCCESS); } /* We have to check if the new lock is compatible with any locks other transactions have in the table lock queue. */ if (lock_table_other_has_incompatible(trx, LOCK_WAIT, table, mode)) { /* Another trx has a request on the table in an incompatible mode: this trx may have to wait */ err = lock_table_enqueue_waiting(mode | flags, table, thr); lock_mutex_exit_kernel(); return(err); } lock_table_create(table, mode | flags, trx); ut_a(!flags || mode == LOCK_S || mode == LOCK_X); lock_mutex_exit_kernel(); return(DB_SUCCESS); } /************************************************************************* Checks if a waiting table lock request still has to wait in a queue. @return TRUE if still has to wait */ static ibool lock_table_has_to_wait_in_queue( /*============================*/ lock_t* wait_lock) /*!< in: waiting table lock */ { dict_table_t* table; lock_t* lock; ut_ad(lock_get_wait(wait_lock)); table = wait_lock->un_member.tab_lock.table; lock = UT_LIST_GET_FIRST(table->locks); while (lock != wait_lock) { if (lock_has_to_wait(wait_lock, lock)) { return(TRUE); } lock = UT_LIST_GET_NEXT(un_member.tab_lock.locks, lock); } return(FALSE); } /*************************************************************//** Removes a table lock request, waiting or granted, from the queue and grants locks to other transactions in the queue, if they now are entitled to a lock. */ static void lock_table_dequeue( /*===============*/ lock_t* in_lock)/*!< in: table lock object; transactions waiting behind will get their lock requests granted, if they are now qualified to it */ { lock_t* lock; ut_ad(mutex_own(&kernel_mutex)); ut_a(lock_get_type_low(in_lock) == LOCK_TABLE); lock = UT_LIST_GET_NEXT(un_member.tab_lock.locks, in_lock); lock_table_remove_low(in_lock); /* Check if waiting locks in the queue can now be granted: grant locks if there are no conflicting locks ahead. */ while (lock != NULL) { if (lock_get_wait(lock) && !lock_table_has_to_wait_in_queue(lock)) { /* Grant the lock */ lock_grant(lock); } lock = UT_LIST_GET_NEXT(un_member.tab_lock.locks, lock); } } /*=========================== LOCK RELEASE ==============================*/ /*************************************************************//** Removes a granted record lock of a transaction from the queue and grants locks to other transactions waiting in the queue if they now are entitled to a lock. */ UNIV_INTERN void lock_rec_unlock( /*============*/ trx_t* trx, /*!< in: transaction that has set a record lock */ const buf_block_t* block, /*!< in: buffer block containing rec */ const rec_t* rec, /*!< in: record */ enum lock_mode lock_mode)/*!< in: LOCK_S or LOCK_X */ { lock_t* lock; lock_t* release_lock = NULL; ulint heap_no; ut_ad(trx && rec); ut_ad(block->frame == page_align(rec)); heap_no = page_rec_get_heap_no(rec); mutex_enter(&kernel_mutex); lock = lock_rec_get_first(block, heap_no); /* Find the last lock with the same lock_mode and transaction from the record. */ while (lock != NULL) { if (lock->trx == trx && lock_get_mode(lock) == lock_mode) { release_lock = lock; ut_a(!lock_get_wait(lock)); } lock = lock_rec_get_next(heap_no, lock); } /* If a record lock is found, release the record lock */ if (UNIV_LIKELY(release_lock != NULL)) { lock_rec_reset_nth_bit(release_lock, heap_no); } else { mutex_exit(&kernel_mutex); ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Error: unlock row could not" " find a %lu mode lock on the record\n", (ulong) lock_mode); return; } /* Check if we can now grant waiting lock requests */ lock = lock_rec_get_first(block, heap_no); while (lock != NULL) { if (lock_get_wait(lock) && !lock_rec_has_to_wait_in_queue(lock)) { /* Grant the lock */ lock_grant(lock); } lock = lock_rec_get_next(heap_no, lock); } mutex_exit(&kernel_mutex); } /************************************************************************* Releases transaction locks, and releases possible other transactions waiting because of these locks. */ UNIV_INTERN void lock_release_off_kernel( /*====================*/ trx_t* trx) /*!< in: transaction */ { ulint count; lock_t* lock; ut_ad(mutex_own(&kernel_mutex)); lock = UT_LIST_GET_LAST(trx->trx_locks); count = 0; while (lock != NULL) { count++; if (lock_get_type_low(lock) == LOCK_REC) { lock_rec_dequeue_from_page(lock); } else { ut_ad(lock_get_type_low(lock) & LOCK_TABLE); lock_table_dequeue(lock); } if (count == LOCK_RELEASE_KERNEL_INTERVAL) { /* Release the kernel mutex for a while, so that we do not monopolize it */ lock_mutex_exit_kernel(); lock_mutex_enter_kernel(); count = 0; } lock = UT_LIST_GET_LAST(trx->trx_locks); } mem_heap_empty(trx->lock_heap); } /*********************************************************************//** Cancels a waiting lock request and releases possible other transactions waiting behind it. */ UNIV_INTERN void lock_cancel_waiting_and_release( /*============================*/ lock_t* lock) /*!< in: waiting lock request */ { ut_ad(mutex_own(&kernel_mutex)); if (lock_get_type_low(lock) == LOCK_REC) { lock_rec_dequeue_from_page(lock); } else { ut_ad(lock_get_type_low(lock) & LOCK_TABLE); lock_table_dequeue(lock); } /* Reset the wait flag and the back pointer to lock in trx */ lock_reset_lock_and_trx_wait(lock); /* The following function releases the trx from lock wait */ trx_end_lock_wait(lock->trx); } /* True if a lock mode is S or X */ #define IS_LOCK_S_OR_X(lock) \ (lock_get_mode(lock) == LOCK_S \ || lock_get_mode(lock) == LOCK_X) /*********************************************************************//** Removes locks of a transaction on a table to be dropped. If remove_also_table_sx_locks is TRUE then table-level S and X locks are also removed in addition to other table-level and record-level locks. No lock, that is going to be removed, is allowed to be a wait lock. */ static void lock_remove_all_on_table_for_trx( /*=============================*/ dict_table_t* table, /*!< in: table to be dropped */ trx_t* trx, /*!< in: a transaction */ ibool remove_also_table_sx_locks)/*!< in: also removes table S and X locks */ { lock_t* lock; lock_t* prev_lock; ut_ad(mutex_own(&kernel_mutex)); lock = UT_LIST_GET_LAST(trx->trx_locks); while (lock != NULL) { prev_lock = UT_LIST_GET_PREV(trx_locks, lock); if (lock_get_type_low(lock) == LOCK_REC && lock->index->table == table) { ut_a(!lock_get_wait(lock)); lock_rec_discard(lock); } else if (lock_get_type_low(lock) & LOCK_TABLE && lock->un_member.tab_lock.table == table && (remove_also_table_sx_locks || !IS_LOCK_S_OR_X(lock))) { ut_a(!lock_get_wait(lock)); lock_table_remove_low(lock); } lock = prev_lock; } } /*********************************************************************//** Removes locks on a table to be dropped or truncated. If remove_also_table_sx_locks is TRUE then table-level S and X locks are also removed in addition to other table-level and record-level locks. No lock, that is going to be removed, is allowed to be a wait lock. */ UNIV_INTERN void lock_remove_all_on_table( /*=====================*/ dict_table_t* table, /*!< in: table to be dropped or truncated */ ibool remove_also_table_sx_locks)/*!< in: also removes table S and X locks */ { lock_t* lock; lock_t* prev_lock; mutex_enter(&kernel_mutex); lock = UT_LIST_GET_FIRST(table->locks); while (lock != NULL) { prev_lock = UT_LIST_GET_PREV(un_member.tab_lock.locks, lock); /* If we should remove all locks (remove_also_table_sx_locks is TRUE), or if the lock is not table-level S or X lock, then check we are not going to remove a wait lock. */ if (remove_also_table_sx_locks || !(lock_get_type(lock) == LOCK_TABLE && IS_LOCK_S_OR_X(lock))) { // HACK: For testing if (lock_get_wait(lock)) { if (remove_also_table_sx_locks) { ut_error; } else { goto next; } } } lock_remove_all_on_table_for_trx(table, lock->trx, remove_also_table_sx_locks); if (prev_lock == NULL) { if (lock == UT_LIST_GET_FIRST(table->locks)) { /* lock was not removed, pick its successor */ lock = UT_LIST_GET_NEXT( un_member.tab_lock.locks, lock); } else { /* lock was removed, pick the first one */ lock = UT_LIST_GET_FIRST(table->locks); } } else if (UT_LIST_GET_NEXT(un_member.tab_lock.locks, prev_lock) != lock) { /* If lock was removed by lock_remove_all_on_table_for_trx() then pick the successor of prev_lock ... */ lock = UT_LIST_GET_NEXT( un_member.tab_lock.locks, prev_lock); } else { next: /* ... otherwise pick the successor of lock. */ lock = UT_LIST_GET_NEXT( un_member.tab_lock.locks, lock); } } mutex_exit(&kernel_mutex); } /*===================== VALIDATION AND DEBUGGING ====================*/ /*********************************************************************//** Prints info of a table lock. */ UNIV_INTERN void lock_table_print( /*=============*/ ib_stream_t ib_stream, /*!< in: stream where to print */ const lock_t* lock) /*!< in: table type lock */ { ut_ad(mutex_own(&kernel_mutex)); ut_a(lock_get_type_low(lock) == LOCK_TABLE); ib_logger(ib_stream, "TABLE LOCK table "); ut_print_name(ib_stream, lock->trx, TRUE, lock->un_member.tab_lock.table->name); ib_logger(ib_stream, " trx id " TRX_ID_FMT, TRX_ID_PREP_PRINTF(lock->trx->id)); if (lock_get_mode(lock) == LOCK_S) { ib_logger(ib_stream, " lock mode S"); } else if (lock_get_mode(lock) == LOCK_X) { ib_logger(ib_stream, " lock mode X"); } else if (lock_get_mode(lock) == LOCK_IS) { ib_logger(ib_stream, " lock mode IS"); } else if (lock_get_mode(lock) == LOCK_IX) { ib_logger(ib_stream, " lock mode IX"); } else if (lock_get_mode(lock) == LOCK_AUTO_INC) { ib_logger(ib_stream, " lock mode AUTO-INC"); } else { ib_logger(ib_stream, " unknown lock mode %lu", (ulong) lock_get_mode(lock)); } if (lock_get_wait(lock)) { ib_logger(ib_stream, " waiting"); } ib_logger(ib_stream, "\n"); } /*********************************************************************//** Prints info of a record lock. */ UNIV_INTERN void lock_rec_print( /*===========*/ ib_stream_t ib_stream, /*!< in: file where to print */ const lock_t* lock) /*!< in: record type lock */ { const buf_block_t* block; ulint space; ulint page_no; ulint i; mtr_t mtr; mem_heap_t* heap = NULL; ulint offsets_[REC_OFFS_NORMAL_SIZE]; ulint* offsets = offsets_; rec_offs_init(offsets_); ut_ad(mutex_own(&kernel_mutex)); ut_a(lock_get_type_low(lock) == LOCK_REC); space = lock->un_member.rec_lock.space; page_no = lock->un_member.rec_lock.page_no; ib_logger(ib_stream, "RECORD LOCKS space id %lu page no %lu n bits %lu ", (ulong) space, (ulong) page_no, (ulong) lock_rec_get_n_bits(lock)); dict_index_name_print(ib_stream, lock->trx, lock->index); ib_logger(ib_stream, " trx id " TRX_ID_FMT, TRX_ID_PREP_PRINTF(lock->trx->id)); if (lock_get_mode(lock) == LOCK_S) { ib_logger(ib_stream, " lock mode S"); } else if (lock_get_mode(lock) == LOCK_X) { ib_logger(ib_stream, " lock_mode X"); } else { ut_error; } if (lock_rec_get_gap(lock)) { ib_logger(ib_stream, " locks gap before rec"); } if (lock_rec_get_rec_not_gap(lock)) { ib_logger(ib_stream, " locks rec but not gap"); } if (lock_rec_get_insert_intention(lock)) { ib_logger(ib_stream, " insert intention"); } if (lock_get_wait(lock)) { ib_logger(ib_stream, " waiting"); } mtr_start(&mtr); ib_logger(ib_stream, "\n"); block = buf_page_try_get(space, page_no, &mtr); if (block) { for (i = 0; i < lock_rec_get_n_bits(lock); i++) { if (lock_rec_get_nth_bit(lock, i)) { const rec_t* rec = page_find_rec_with_heap_no( buf_block_get_frame(block), i); offsets = rec_get_offsets( rec, lock->index, offsets, ULINT_UNDEFINED, &heap); ib_logger(ib_stream, "Record lock, heap no %lu ", (ulong) i); rec_print_new(ib_stream, rec, offsets); ib_logger(ib_stream, "\n"); } } } else { for (i = 0; i < lock_rec_get_n_bits(lock); i++) { ib_logger(ib_stream, "Record lock, heap no %lu\n", (ulong) i); } } mtr_commit(&mtr); if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } } #ifdef UNIV_DEBUG /* Print the number of lock structs from lock_print_info_summary() only in non-production builds for performance reasons. */ #define PRINT_NUM_OF_LOCK_STRUCTS #endif /* UNIV_DEBUG */ #ifdef PRINT_NUM_OF_LOCK_STRUCTS /*********************************************************************//** Calculates the number of record lock structs in the record lock hash table. @return number of record locks */ static ulint lock_get_n_rec_locks(void) /*======================*/ { lock_t* lock; ulint n_locks = 0; ulint i; ut_ad(mutex_own(&kernel_mutex)); for (i = 0; i < hash_get_n_cells(lock_sys->rec_hash); i++) { lock = HASH_GET_FIRST(lock_sys->rec_hash, i); while (lock) { n_locks++; lock = HASH_GET_NEXT(hash, lock); } } return(n_locks); } #endif /* PRINT_NUM_OF_LOCK_STRUCTS */ /*********************************************************************//** Prints info of locks for all transactions. @return FALSE if not able to obtain kernel mutex and exits without printing info */ UNIV_INTERN ibool lock_print_info_summary( /*====================*/ ib_stream_t ib_stream, /*!< in: stream where to print */ ibool nowait) /*!< in: whether to wait for the kernel mutex */ { /* if nowait is FALSE, wait on the kernel mutex, otherwise return immediately if fail to obtain the mutex. */ if (!nowait) { lock_mutex_enter_kernel(); } else if (mutex_enter_nowait(&kernel_mutex)) { ib_logger(ib_stream, "FAIL TO OBTAIN KERNEL MUTEX, " "SKIP LOCK INFO PRINTING\n"); return(FALSE); } if (lock_deadlock_found) { ib_logger(ib_stream, "------------------------\n" "LATEST DETECTED DEADLOCK\n" "------------------------\n"); } ib_logger(ib_stream, "------------\n" "TRANSACTIONS\n" "------------\n"); ib_logger(ib_stream, "Trx id counter " TRX_ID_FMT "\n", TRX_ID_PREP_PRINTF(trx_sys->max_trx_id)); ib_logger(ib_stream, "Purge done for trx's n:o < " TRX_ID_FMT " undo n:o < " TRX_ID_FMT "\n", TRX_ID_PREP_PRINTF(purge_sys->purge_trx_no), TRX_ID_PREP_PRINTF(purge_sys->purge_undo_no)); ib_logger(ib_stream, "History list length %lu\n", (ulong) trx_sys->rseg_history_len); #ifdef PRINT_NUM_OF_LOCK_STRUCTS ib_logger(ib_stream, "Total number of lock structs in row lock hash table %lu\n", (ulong) lock_get_n_rec_locks()); #endif /* PRINT_NUM_OF_LOCK_STRUCTS */ return(TRUE); } /*********************************************************************//** Prints info of locks for each transaction. */ UNIV_INTERN void lock_print_info_all_transactions( /*=============================*/ ib_stream_t ib_stream) /*!< in: stream where to print */ { lock_t* lock; ibool load_page_first = TRUE; ulint nth_trx = 0; ulint nth_lock = 0; ulint i; mtr_t mtr; trx_t* trx; ib_logger(ib_stream, "LIST OF TRANSACTIONS FOR EACH SESSION:\n"); /* First print info on non-active transactions */ trx = UT_LIST_GET_FIRST(trx_sys->client_trx_list); while (trx) { if (trx->conc_state == TRX_NOT_STARTED) { ib_logger(ib_stream, "---"); trx_print(ib_stream, trx, 600); } trx = UT_LIST_GET_NEXT(client_trx_list, trx); } loop: trx = UT_LIST_GET_FIRST(trx_sys->trx_list); i = 0; /* Since we temporarily release the kernel mutex when reading a database page in below, variable trx may be obsolete now and we must loop through the trx list to get probably the same trx, or some other trx. */ while (trx && (i < nth_trx)) { trx = UT_LIST_GET_NEXT(trx_list, trx); i++; } if (trx == NULL) { lock_mutex_exit_kernel(); ut_ad(lock_validate()); return; } if (nth_lock == 0) { ib_logger(ib_stream, "---"); trx_print(ib_stream, trx, 600); if (trx->read_view) { ib_logger(ib_stream, "Trx read view will not see trx with" " id >= " TRX_ID_FMT ", sees < " TRX_ID_FMT "\n", TRX_ID_PREP_PRINTF( trx->read_view->low_limit_id), TRX_ID_PREP_PRINTF( trx->read_view->up_limit_id)); } if (trx->que_state == TRX_QUE_LOCK_WAIT) { ib_logger(ib_stream, "------- TRX HAS BEEN WAITING %lu SEC" " FOR THIS LOCK TO BE GRANTED:\n", (ulong) difftime(time(NULL), trx->wait_started)); if (lock_get_type_low(trx->wait_lock) == LOCK_REC) { lock_rec_print(ib_stream, trx->wait_lock); } else { lock_table_print(ib_stream, trx->wait_lock); } ib_logger(ib_stream, "------------------\n"); } } if (!srv_print_innodb_lock_monitor) { nth_trx++; goto loop; } i = 0; /* Look at the note about the trx loop above why we loop here: lock may be an obsolete pointer now. */ lock = UT_LIST_GET_FIRST(trx->trx_locks); while (lock && (i < nth_lock)) { lock = UT_LIST_GET_NEXT(trx_locks, lock); i++; } if (lock == NULL) { nth_trx++; nth_lock = 0; goto loop; } if (lock_get_type_low(lock) == LOCK_REC) { if (load_page_first) { ulint space = lock->un_member.rec_lock.space; ulint zip_size= fil_space_get_zip_size(space); ulint page_no = lock->un_member.rec_lock.page_no; if (UNIV_UNLIKELY(zip_size == ULINT_UNDEFINED)) { /* It is a single table tablespace and the .ibd file is missing (TRUNCATE TABLE probably stole the locks): just print the lock without attempting to load the page in the buffer pool. */ ib_logger(ib_stream, "RECORD LOCKS on" " non-existing space %lu\n", (ulong) space); goto print_rec; } lock_mutex_exit_kernel(); mtr_start(&mtr); buf_page_get_with_no_latch(space, zip_size, page_no, &mtr); mtr_commit(&mtr); load_page_first = FALSE; lock_mutex_enter_kernel(); goto loop; } print_rec: lock_rec_print(ib_stream, lock); } else { ut_ad(lock_get_type_low(lock) & LOCK_TABLE); lock_table_print(ib_stream, lock); } load_page_first = TRUE; nth_lock++; if (nth_lock >= 10) { ib_logger(ib_stream, "10 LOCKS PRINTED FOR THIS TRX:" " SUPPRESSING FURTHER PRINTS\n"); nth_trx++; nth_lock = 0; goto loop; } goto loop; } #ifdef UNIV_DEBUG /*********************************************************************//** Validates the lock queue on a table. @return TRUE if ok */ static ibool lock_table_queue_validate( /*======================*/ dict_table_t* table) /*!< in: table */ { lock_t* lock; ut_ad(mutex_own(&kernel_mutex)); lock = UT_LIST_GET_FIRST(table->locks); while (lock) { ut_a(((lock->trx)->conc_state == TRX_ACTIVE) || ((lock->trx)->conc_state == TRX_PREPARED) || ((lock->trx)->conc_state == TRX_COMMITTED_IN_MEMORY)); if (!lock_get_wait(lock)) { ut_a(!lock_table_other_has_incompatible( lock->trx, 0, table, lock_get_mode(lock))); } else { ut_a(lock_table_has_to_wait_in_queue(lock)); } lock = UT_LIST_GET_NEXT(un_member.tab_lock.locks, lock); } return(TRUE); } /*********************************************************************//** Validates the lock queue on a single record. @return TRUE if ok */ static ibool lock_rec_queue_validate( /*====================*/ const buf_block_t* block, /*!< in: buffer block containing rec */ const rec_t* rec, /*!< in: record to look at */ dict_index_t* index, /*!< in: index, or NULL if not known */ const ulint* offsets)/*!< in: rec_get_offsets(rec, index) */ { trx_t* impl_trx; lock_t* lock; ulint heap_no; ut_a(rec); ut_a(block->frame == page_align(rec)); ut_ad(rec_offs_validate(rec, index, offsets)); ut_ad(!page_rec_is_comp(rec) == !rec_offs_comp(offsets)); heap_no = page_rec_get_heap_no(rec); lock_mutex_enter_kernel(); if (!page_rec_is_user_rec(rec)) { lock = lock_rec_get_first(block, heap_no); while (lock) { switch(lock->trx->conc_state) { case TRX_ACTIVE: case TRX_PREPARED: case TRX_COMMITTED_IN_MEMORY: break; default: ut_error; } ut_a(trx_in_trx_list(lock->trx)); if (lock_get_wait(lock)) { ut_a(lock_rec_has_to_wait_in_queue(lock)); } if (index) { ut_a(lock->index == index); } lock = lock_rec_get_next(heap_no, lock); } lock_mutex_exit_kernel(); return(TRUE); } if (!index); else if (dict_index_is_clust(index)) { impl_trx = lock_clust_rec_some_has_impl(rec, index, offsets); if (impl_trx && lock_rec_other_has_expl_req(LOCK_S, 0, LOCK_WAIT, block, heap_no, impl_trx)) { ut_a(lock_rec_has_expl(LOCK_X | LOCK_REC_NOT_GAP, block, heap_no, impl_trx)); } #if 0 } else { /* The kernel mutex may get released temporarily in the next function call: we have to release lock table mutex to obey the latching order */ /* If this thread is holding the file space latch (fil_space_t::latch), the following check WILL break latching order and may cause a deadlock of threads. */ /* NOTE: This is a bogus check that would fail in the following case: Our transaction is updating a row. After it has updated the clustered index record, it goes to a secondary index record and finds someone else holding an explicit S- or X-lock on that secondary index record, presumably from a locking read. Our transaction cannot update the secondary index immediately, but places a waiting X-lock request on the secondary index record. There is nothing illegal in this. The assertion is simply too strong. */ /* From the locking point of view, each secondary index is a separate table. A lock that is held on secondary index rec does not give any rights to modify or read the clustered index rec. Therefore, we can think of the sec index as a separate 'table' from the clust index 'table'. Conversely, a transaction that has acquired a lock on and modified a clustered index record may need to wait for a lock on the corresponding record in a secondary index. */ impl_trx = lock_sec_rec_some_has_impl_off_kernel( rec, index, offsets); if (impl_trx && lock_rec_other_has_expl_req(LOCK_S, 0, LOCK_WAIT, block, heap_no, impl_trx)) { ut_a(lock_rec_has_expl(LOCK_X | LOCK_REC_NOT_GAP, block, heap_no, impl_trx)); } #endif } lock = lock_rec_get_first(block, heap_no); while (lock) { ut_a(lock->trx->conc_state == TRX_ACTIVE || lock->trx->conc_state == TRX_PREPARED || lock->trx->conc_state == TRX_COMMITTED_IN_MEMORY); ut_a(trx_in_trx_list(lock->trx)); if (index) { ut_a(lock->index == index); } if (!lock_rec_get_gap(lock) && !lock_get_wait(lock)) { enum lock_mode mode; if (lock_get_mode(lock) == LOCK_S) { mode = LOCK_X; } else { mode = LOCK_S; } ut_a(!lock_rec_other_has_expl_req( mode, 0, 0, block, heap_no, lock->trx)); } else if (lock_get_wait(lock) && !lock_rec_get_gap(lock)) { ut_a(lock_rec_has_to_wait_in_queue(lock)); } lock = lock_rec_get_next(heap_no, lock); } lock_mutex_exit_kernel(); return(TRUE); } /*********************************************************************//** Validates the record lock queues on a page. @return TRUE if ok */ static ibool lock_rec_validate_page( /*===================*/ ulint space, /*!< in: space id */ ulint zip_size,/*!< in: compressed page size in bytes or 0 for uncompressed pages */ ulint page_no)/*!< in: page number */ { dict_index_t* index; buf_block_t* block; const page_t* page; lock_t* lock; const rec_t* rec; ulint nth_lock = 0; ulint nth_bit = 0; ulint i; mtr_t mtr; mem_heap_t* heap = NULL; ulint offsets_[REC_OFFS_NORMAL_SIZE]; ulint* offsets = offsets_; rec_offs_init(offsets_); ut_ad(!mutex_own(&kernel_mutex)); mtr_start(&mtr); ut_ad(zip_size != ULINT_UNDEFINED); block = buf_page_get(space, zip_size, page_no, RW_X_LATCH, &mtr); buf_block_dbg_add_level(block, SYNC_NO_ORDER_CHECK); page = block->frame; lock_mutex_enter_kernel(); loop: lock = lock_rec_get_first_on_page_addr(space, page_no); if (!lock) { goto function_exit; } for (i = 0; i < nth_lock; i++) { lock = lock_rec_get_next_on_page(lock); if (!lock) { goto function_exit; } } ut_a(trx_in_trx_list(lock->trx)); ut_a(lock->trx->conc_state == TRX_ACTIVE || lock->trx->conc_state == TRX_PREPARED || lock->trx->conc_state == TRX_COMMITTED_IN_MEMORY); # ifdef UNIV_SYNC_DEBUG /* Only validate the record queues when this thread is not holding a space->latch. Deadlocks are possible due to latching order violation when UNIV_DEBUG is defined while UNIV_SYNC_DEBUG is not. */ if (!sync_thread_levels_contains(SYNC_FSP)) # endif /* UNIV_SYNC_DEBUG */ for (i = nth_bit; i < lock_rec_get_n_bits(lock); i++) { if (i == 1 || lock_rec_get_nth_bit(lock, i)) { index = lock->index; rec = page_find_rec_with_heap_no(page, i); ut_a(rec); offsets = rec_get_offsets(rec, index, offsets, ULINT_UNDEFINED, &heap); ib_logger(ib_stream, "Validating %lu %lu\n", (ulong) space, (ulong) page_no); lock_mutex_exit_kernel(); /* If this thread is holding the file space latch (fil_space_t::latch), the following check WILL break the latching order and may cause a deadlock of threads. */ lock_rec_queue_validate(block, rec, index, offsets); lock_mutex_enter_kernel(); nth_bit = i + 1; goto loop; } } nth_bit = 0; nth_lock++; goto loop; function_exit: lock_mutex_exit_kernel(); mtr_commit(&mtr); if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } return(TRUE); } /*********************************************************************//** Validates the lock system. @return TRUE if ok */ static ibool lock_validate(void) /*===============*/ { lock_t* lock; trx_t* trx; dulint limit; ulint space; ulint page_no; ulint i; lock_mutex_enter_kernel(); trx = UT_LIST_GET_FIRST(trx_sys->trx_list); while (trx) { lock = UT_LIST_GET_FIRST(trx->trx_locks); while (lock) { if (lock_get_type_low(lock) & LOCK_TABLE) { lock_table_queue_validate( lock->un_member.tab_lock.table); } lock = UT_LIST_GET_NEXT(trx_locks, lock); } trx = UT_LIST_GET_NEXT(trx_list, trx); } for (i = 0; i < hash_get_n_cells(lock_sys->rec_hash); i++) { limit = ut_dulint_zero; for (;;) { lock = HASH_GET_FIRST(lock_sys->rec_hash, i); while (lock) { ut_a(trx_in_trx_list(lock->trx)); space = lock->un_member.rec_lock.space; page_no = lock->un_member.rec_lock.page_no; if (ut_dulint_cmp( ut_dulint_create(space, page_no), limit) >= 0) { break; } lock = HASH_GET_NEXT(hash, lock); } if (!lock) { break; } lock_mutex_exit_kernel(); lock_rec_validate_page(space, fil_space_get_zip_size(space), page_no); lock_mutex_enter_kernel(); limit = ut_dulint_create(space, page_no + 1); } } lock_mutex_exit_kernel(); return(TRUE); } #endif /* UNIV_DEBUG */ /*============ RECORD LOCK CHECKS FOR ROW OPERATIONS ====================*/ /*********************************************************************//** Checks if locks of other transactions prevent an immediate insert of a record. If they do, first tests if the query thread should anyway be suspended for some reason; if not, then puts the transaction and the query thread to the lock wait state and inserts a waiting request for a gap x-lock to the lock queue. @return DB_SUCCESS, DB_LOCK_WAIT, DB_DEADLOCK, or DB_QUE_THR_SUSPENDED */ UNIV_INTERN ulint lock_rec_insert_check_and_lock( /*===========================*/ ulint flags, /*!< in: if BTR_NO_LOCKING_FLAG bit is set, does nothing */ const rec_t* rec, /*!< in: record after which to insert */ buf_block_t* block, /*!< in/out: buffer block of rec */ dict_index_t* index, /*!< in: index */ que_thr_t* thr, /*!< in: query thread */ mtr_t* mtr, /*!< in/out: mini-transaction */ ibool* inherit)/*!< out: set to TRUE if the new inserted record maybe should inherit LOCK_GAP type locks from the successor record */ { const rec_t* next_rec; trx_t* trx; lock_t* lock; ulint err; ulint next_rec_heap_no; ut_ad(block->frame == page_align(rec)); if (flags & BTR_NO_LOCKING_FLAG) { return(DB_SUCCESS); } trx = thr_get_trx(thr); next_rec = page_rec_get_next_const(rec); next_rec_heap_no = page_rec_get_heap_no(next_rec); lock_mutex_enter_kernel(); /* When inserting a record into an index, the table must be at least IX-locked or we must be building an index, in which case the table must be at least S-locked. */ ut_ad(lock_table_has(trx, index->table, LOCK_IX) || (*index->name == TEMP_INDEX_PREFIX && lock_table_has(trx, index->table, LOCK_S))); lock = lock_rec_get_first(block, next_rec_heap_no); if (UNIV_LIKELY(lock == NULL)) { /* We optimize CPU time usage in the simplest case */ lock_mutex_exit_kernel(); if (!dict_index_is_clust(index)) { /* Update the page max trx id field */ page_update_max_trx_id(block, buf_block_get_page_zip(block), trx->id, mtr); } *inherit = FALSE; return(DB_SUCCESS); } *inherit = TRUE; /* If another transaction has an explicit lock request which locks the gap, waiting or granted, on the successor, the insert has to wait. An exception is the case where the lock by the another transaction is a gap type lock which it placed to wait for its turn to insert. We do not consider that kind of a lock conflicting with our insert. This eliminates an unnecessary deadlock which resulted when 2 transactions had to wait for their insert. Both had waiting gap type lock requests on the successor, which produced an unnecessary deadlock. */ if (lock_rec_other_has_conflicting( LOCK_X | LOCK_GAP | LOCK_INSERT_INTENTION, block, next_rec_heap_no, trx)) { /* Note that we may get DB_SUCCESS also here! */ err = lock_rec_enqueue_waiting(LOCK_X | LOCK_GAP | LOCK_INSERT_INTENTION, block, next_rec_heap_no, index, thr); } else { err = DB_SUCCESS; } lock_mutex_exit_kernel(); if ((err == DB_SUCCESS) && !dict_index_is_clust(index)) { /* Update the page max trx id field */ page_update_max_trx_id(block, buf_block_get_page_zip(block), trx->id, mtr); } #ifdef UNIV_DEBUG { mem_heap_t* heap = NULL; ulint offsets_[REC_OFFS_NORMAL_SIZE]; const ulint* offsets; rec_offs_init(offsets_); offsets = rec_get_offsets(next_rec, index, offsets_, ULINT_UNDEFINED, &heap); ut_ad(lock_rec_queue_validate(block, next_rec, index, offsets)); if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } } #endif /* UNIV_DEBUG */ return(err); } /*********************************************************************//** If a transaction has an implicit x-lock on a record, but no explicit x-lock set on the record, sets one for it. NOTE that in the case of a secondary index, the kernel mutex may get temporarily released. */ static void lock_rec_convert_impl_to_expl( /*==========================*/ const buf_block_t* block, /*!< in: buffer block of rec */ const rec_t* rec, /*!< in: user record on page */ dict_index_t* index, /*!< in: index of record */ const ulint* offsets)/*!< in: rec_get_offsets(rec, index) */ { trx_t* impl_trx; ut_ad(mutex_own(&kernel_mutex)); ut_ad(page_rec_is_user_rec(rec)); ut_ad(rec_offs_validate(rec, index, offsets)); ut_ad(!page_rec_is_comp(rec) == !rec_offs_comp(offsets)); if (dict_index_is_clust(index)) { impl_trx = lock_clust_rec_some_has_impl(rec, index, offsets); } else { impl_trx = lock_sec_rec_some_has_impl_off_kernel( rec, index, offsets); } if (impl_trx) { ulint heap_no = page_rec_get_heap_no(rec); /* If the transaction has no explicit x-lock set on the record, set one for it */ if (!lock_rec_has_expl(LOCK_X | LOCK_REC_NOT_GAP, block, heap_no, impl_trx)) { lock_rec_add_to_queue( LOCK_REC | LOCK_X | LOCK_REC_NOT_GAP, block, heap_no, index, impl_trx); } } } /*********************************************************************//** Checks if locks of other transactions prevent an immediate modify (update, delete mark, or delete unmark) of a clustered index record. If they do, first tests if the query thread should anyway be suspended for some reason; if not, then puts the transaction and the query thread to the lock wait state and inserts a waiting request for a record x-lock to the lock queue. @return DB_SUCCESS, DB_LOCK_WAIT, DB_DEADLOCK, or DB_QUE_THR_SUSPENDED */ UNIV_INTERN ulint lock_clust_rec_modify_check_and_lock( /*=================================*/ ulint flags, /*!< in: if BTR_NO_LOCKING_FLAG bit is set, does nothing */ const buf_block_t* block, /*!< in: buffer block of rec */ const rec_t* rec, /*!< in: record which should be modified */ dict_index_t* index, /*!< in: clustered index */ const ulint* offsets,/*!< in: rec_get_offsets(rec, index) */ que_thr_t* thr) /*!< in: query thread */ { ulint err; ulint heap_no; ut_ad(rec_offs_validate(rec, index, offsets)); ut_ad(dict_index_is_clust(index)); ut_ad(block->frame == page_align(rec)); if (flags & BTR_NO_LOCKING_FLAG) { return(DB_SUCCESS); } heap_no = rec_offs_comp(offsets) ? rec_get_heap_no_new(rec) : rec_get_heap_no_old(rec); lock_mutex_enter_kernel(); ut_ad(lock_table_has(thr_get_trx(thr), index->table, LOCK_IX)); /* If a transaction has no explicit x-lock set on the record, set one for it */ lock_rec_convert_impl_to_expl(block, rec, index, offsets); err = lock_rec_lock(TRUE, LOCK_X | LOCK_REC_NOT_GAP, block, heap_no, index, thr); lock_mutex_exit_kernel(); ut_ad(lock_rec_queue_validate(block, rec, index, offsets)); return(err); } /*********************************************************************//** Checks if locks of other transactions prevent an immediate modify (delete mark or delete unmark) of a secondary index record. @return DB_SUCCESS, DB_LOCK_WAIT, DB_DEADLOCK, or DB_QUE_THR_SUSPENDED */ UNIV_INTERN ulint lock_sec_rec_modify_check_and_lock( /*===============================*/ ulint flags, /*!< in: if BTR_NO_LOCKING_FLAG bit is set, does nothing */ buf_block_t* block, /*!< in/out: buffer block of rec */ const rec_t* rec, /*!< in: record which should be modified; NOTE: as this is a secondary index, we always have to modify the clustered index record first: see the comment below */ dict_index_t* index, /*!< in: secondary index */ que_thr_t* thr, /*!< in: query thread */ mtr_t* mtr) /*!< in/out: mini-transaction */ { ulint err; ulint heap_no; ut_ad(!dict_index_is_clust(index)); ut_ad(block->frame == page_align(rec)); if (flags & BTR_NO_LOCKING_FLAG) { return(DB_SUCCESS); } heap_no = page_rec_get_heap_no(rec); /* Another transaction cannot have an implicit lock on the record, because when we come here, we already have modified the clustered index record, and this would not have been possible if another active transaction had modified this secondary index record. */ lock_mutex_enter_kernel(); ut_ad(lock_table_has(thr_get_trx(thr), index->table, LOCK_IX)); err = lock_rec_lock(TRUE, LOCK_X | LOCK_REC_NOT_GAP, block, heap_no, index, thr); lock_mutex_exit_kernel(); #ifdef UNIV_DEBUG { mem_heap_t* heap = NULL; ulint offsets_[REC_OFFS_NORMAL_SIZE]; const ulint* offsets; rec_offs_init(offsets_); offsets = rec_get_offsets(rec, index, offsets_, ULINT_UNDEFINED, &heap); ut_ad(lock_rec_queue_validate(block, rec, index, offsets)); if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } } #endif /* UNIV_DEBUG */ if (err == DB_SUCCESS) { /* Update the page max trx id field */ page_update_max_trx_id(block, buf_block_get_page_zip(block), thr_get_trx(thr)->id, mtr); } return(err); } /*********************************************************************//** Like the counterpart for a clustered index below, but now we read a secondary index record. @return DB_SUCCESS, DB_LOCK_WAIT, DB_DEADLOCK, or DB_QUE_THR_SUSPENDED */ UNIV_INTERN ulint lock_sec_rec_read_check_and_lock( /*=============================*/ ulint flags, /*!< in: if BTR_NO_LOCKING_FLAG bit is set, does nothing */ const buf_block_t* block, /*!< in: buffer block of rec */ const rec_t* rec, /*!< in: user record or page supremum record which should be read or passed over by a read cursor */ dict_index_t* index, /*!< in: secondary index */ const ulint* offsets,/*!< in: rec_get_offsets(rec, index) */ enum lock_mode mode, /*!< in: mode of the lock which the read cursor should set on records: LOCK_S or LOCK_X; the latter is possible in SELECT FOR UPDATE */ ulint gap_mode,/*!< in: LOCK_ORDINARY, LOCK_GAP, or LOCK_REC_NOT_GAP */ que_thr_t* thr) /*!< in: query thread */ { ulint err; ulint heap_no; ut_ad(!dict_index_is_clust(index)); ut_ad(block->frame == page_align(rec)); ut_ad(page_rec_is_user_rec(rec) || page_rec_is_supremum(rec)); ut_ad(rec_offs_validate(rec, index, offsets)); ut_ad(mode == LOCK_X || mode == LOCK_S); if (flags & BTR_NO_LOCKING_FLAG) { return(DB_SUCCESS); } heap_no = page_rec_get_heap_no(rec); lock_mutex_enter_kernel(); ut_ad(mode != LOCK_X || lock_table_has(thr_get_trx(thr), index->table, LOCK_IX)); ut_ad(mode != LOCK_S || lock_table_has(thr_get_trx(thr), index->table, LOCK_IS)); /* Some transaction may have an implicit x-lock on the record only if the max trx id for the page >= min trx id for the trx list or a database recovery is running. */ if (((ut_dulint_cmp(page_get_max_trx_id(block->frame), trx_list_get_min_trx_id()) >= 0) || recv_recovery_is_on()) && !page_rec_is_supremum(rec)) { lock_rec_convert_impl_to_expl(block, rec, index, offsets); } err = lock_rec_lock(FALSE, mode | gap_mode, block, heap_no, index, thr); lock_mutex_exit_kernel(); ut_ad(lock_rec_queue_validate(block, rec, index, offsets)); return(err); } /*********************************************************************//** Checks if locks of other transactions prevent an immediate read, or passing over by a read cursor, of a clustered index record. If they do, first tests if the query thread should anyway be suspended for some reason; if not, then puts the transaction and the query thread to the lock wait state and inserts a waiting request for a record lock to the lock queue. Sets the requested mode lock on the record. @return DB_SUCCESS, DB_LOCK_WAIT, DB_DEADLOCK, or DB_QUE_THR_SUSPENDED */ UNIV_INTERN ulint lock_clust_rec_read_check_and_lock( /*===============================*/ ulint flags, /*!< in: if BTR_NO_LOCKING_FLAG bit is set, does nothing */ const buf_block_t* block, /*!< in: buffer block of rec */ const rec_t* rec, /*!< in: user record or page supremum record which should be read or passed over by a read cursor */ dict_index_t* index, /*!< in: clustered index */ const ulint* offsets,/*!< in: rec_get_offsets(rec, index) */ enum lock_mode mode, /*!< in: mode of the lock which the read cursor should set on records: LOCK_S or LOCK_X; the latter is possible in SELECT FOR UPDATE */ ulint gap_mode,/*!< in: LOCK_ORDINARY, LOCK_GAP, or LOCK_REC_NOT_GAP */ que_thr_t* thr) /*!< in: query thread */ { ulint err; ulint heap_no; ut_ad(dict_index_is_clust(index)); ut_ad(block->frame == page_align(rec)); ut_ad(page_rec_is_user_rec(rec) || page_rec_is_supremum(rec)); ut_ad(gap_mode == LOCK_ORDINARY || gap_mode == LOCK_GAP || gap_mode == LOCK_REC_NOT_GAP); ut_ad(rec_offs_validate(rec, index, offsets)); if (flags & BTR_NO_LOCKING_FLAG) { return(DB_SUCCESS); } heap_no = page_rec_get_heap_no(rec); lock_mutex_enter_kernel(); ut_ad(mode != LOCK_X || lock_table_has(thr_get_trx(thr), index->table, LOCK_IX)); ut_ad(mode != LOCK_S || lock_table_has(thr_get_trx(thr), index->table, LOCK_IS)); if (UNIV_LIKELY(heap_no != PAGE_HEAP_NO_SUPREMUM)) { lock_rec_convert_impl_to_expl(block, rec, index, offsets); } err = lock_rec_lock(FALSE, mode | gap_mode, block, heap_no, index, thr); lock_mutex_exit_kernel(); ut_ad(lock_rec_queue_validate(block, rec, index, offsets)); return(err); } /*********************************************************************//** Checks if locks of other transactions prevent an immediate read, or passing over by a read cursor, of a clustered index record. If they do, first tests if the query thread should anyway be suspended for some reason; if not, then puts the transaction and the query thread to the lock wait state and inserts a waiting request for a record lock to the lock queue. Sets the requested mode lock on the record. This is an alternative version of lock_clust_rec_read_check_and_lock() that does not require the parameter "offsets". @return DB_SUCCESS, DB_LOCK_WAIT, DB_DEADLOCK, or DB_QUE_THR_SUSPENDED */ UNIV_INTERN ulint lock_clust_rec_read_check_and_lock_alt( /*===================================*/ ulint flags, /*!< in: if BTR_NO_LOCKING_FLAG bit is set, does nothing */ const buf_block_t* block, /*!< in: buffer block of rec */ const rec_t* rec, /*!< in: user record or page supremum record which should be read or passed over by a read cursor */ dict_index_t* index, /*!< in: clustered index */ enum lock_mode mode, /*!< in: mode of the lock which the read cursor should set on records: LOCK_S or LOCK_X; the latter is possible in SELECT FOR UPDATE */ ulint gap_mode,/*!< in: LOCK_ORDINARY, LOCK_GAP, or LOCK_REC_NOT_GAP */ que_thr_t* thr) /*!< in: query thread */ { mem_heap_t* tmp_heap = NULL; ulint offsets_[REC_OFFS_NORMAL_SIZE]; ulint* offsets = offsets_; ulint ret; rec_offs_init(offsets_); offsets = rec_get_offsets(rec, index, offsets, ULINT_UNDEFINED, &tmp_heap); ret = lock_clust_rec_read_check_and_lock(flags, block, rec, index, offsets, mode, gap_mode, thr); if (tmp_heap) { mem_heap_free(tmp_heap); } return(ret); } /*******************************************************************//** Gets the type of a lock. Non-inline version for using outside of the lock module. @return LOCK_TABLE or LOCK_REC */ UNIV_INTERN ulint lock_get_type( /*==========*/ const lock_t* lock) /*!< in: lock */ { return(lock_get_type_low(lock)); } /*******************************************************************//** Gets the id of the transaction owning a lock. @return transaction id */ UNIV_INTERN ib_uint64_t lock_get_trx_id( /*============*/ const lock_t* lock) /*!< in: lock */ { return(trx_get_id(lock->trx)); } /*******************************************************************//** Gets the mode of a lock in a human readable string. The string should not be free()'d or modified. @return lock mode */ UNIV_INTERN const char* lock_get_mode_str( /*==============*/ const lock_t* lock) /*!< in: lock */ { ibool is_gap_lock; is_gap_lock = lock_get_type_low(lock) == LOCK_REC && lock_rec_get_gap(lock); switch (lock_get_mode(lock)) { case LOCK_S: if (is_gap_lock) { return("S,GAP"); } else { return("S"); } case LOCK_X: if (is_gap_lock) { return("X,GAP"); } else { return("X"); } case LOCK_IS: if (is_gap_lock) { return("IS,GAP"); } else { return("IS"); } case LOCK_IX: if (is_gap_lock) { return("IX,GAP"); } else { return("IX"); } case LOCK_AUTO_INC: return("AUTO_INC"); default: return("UNKNOWN"); } } /*******************************************************************//** Gets the type of a lock in a human readable string. The string should not be free()'d or modified. @return lock type */ UNIV_INTERN const char* lock_get_type_str( /*==============*/ const lock_t* lock) /*!< in: lock */ { switch (lock_get_type_low(lock)) { case LOCK_REC: return("RECORD"); case LOCK_TABLE: return("TABLE"); default: return("UNKNOWN"); } } /*******************************************************************//** Gets the table on which the lock is. @return table */ UNIV_INLINE dict_table_t* lock_get_table( /*===========*/ const lock_t* lock) /*!< in: lock */ { switch (lock_get_type_low(lock)) { case LOCK_REC: return(lock->index->table); case LOCK_TABLE: return(lock->un_member.tab_lock.table); default: ut_error; return(NULL); } } /*******************************************************************//** Gets the id of the table on which the lock is. @return id of the table */ UNIV_INTERN ib_uint64_t lock_get_table_id( /*==============*/ const lock_t* lock) /*!< in: lock */ { dict_table_t* table; table = lock_get_table(lock); return((ib_uint64_t)ut_conv_dulint_to_longlong(table->id)); } /*******************************************************************//** Gets the name of the table on which the lock is. The string should not be free()'d or modified. @return name of the table */ UNIV_INTERN const char* lock_get_table_name( /*================*/ const lock_t* lock) /*!< in: lock */ { dict_table_t* table; table = lock_get_table(lock); return(table->name); } /*******************************************************************//** For a record lock, gets the index on which the lock is. @return index */ UNIV_INTERN const dict_index_t* lock_rec_get_index( /*===============*/ const lock_t* lock) /*!< in: lock */ { ut_a(lock_get_type_low(lock) == LOCK_REC); return(lock->index); } /*******************************************************************//** For a record lock, gets the name of the index on which the lock is. The string should not be free()'d or modified. @return name of the index */ UNIV_INTERN const char* lock_rec_get_index_name( /*====================*/ const lock_t* lock) /*!< in: lock */ { ut_a(lock_get_type_low(lock) == LOCK_REC); return(lock->index->name); } /*******************************************************************//** For a record lock, gets the tablespace number on which the lock is. @return tablespace number */ UNIV_INTERN ulint lock_rec_get_space_id( /*==================*/ const lock_t* lock) /*!< in: lock */ { ut_a(lock_get_type_low(lock) == LOCK_REC); return(lock->un_member.rec_lock.space); } /*******************************************************************//** For a record lock, gets the page number on which the lock is. @return page number */ UNIV_INTERN ulint lock_rec_get_page_no( /*=================*/ const lock_t* lock) /*!< in: lock */ { ut_a(lock_get_type_low(lock) == LOCK_REC); return(lock->un_member.rec_lock.page_no); } haildb-2.3.2/lock/lock0iter.c0000644000175000017500000000621611513177357016640 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 2007, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file lock/lock0iter.c Lock queue iterator. Can iterate over table and record lock queues. Created July 16, 2007 Vasil Dimov *******************************************************/ #define LOCK_MODULE_IMPLEMENTATION #include "univ.i" #include "lock0iter.h" #include "lock0lock.h" #include "lock0priv.h" #include "ut0dbg.h" #include "ut0lst.h" /*******************************************************************//** Initialize lock queue iterator so that it starts to iterate from "lock". bit_no specifies the record number within the heap where the record is stored. It can be undefined (ULINT_UNDEFINED) in two cases: 1. If the lock is a table lock, thus we have a table lock queue; 2. If the lock is a record lock and it is a wait lock. In this case bit_no is calculated in this function by using lock_rec_find_set_bit(). There is exactly one bit set in the bitmap of a wait lock. */ UNIV_INTERN void lock_queue_iterator_reset( /*======================*/ lock_queue_iterator_t* iter, /*!< out: iterator */ const lock_t* lock, /*!< in: lock to start from */ ulint bit_no) /*!< in: record number in the heap */ { iter->current_lock = lock; if (bit_no != ULINT_UNDEFINED) { iter->bit_no = bit_no; } else { switch (lock_get_type_low(lock)) { case LOCK_TABLE: iter->bit_no = ULINT_UNDEFINED; break; case LOCK_REC: iter->bit_no = lock_rec_find_set_bit(lock); ut_a(iter->bit_no != ULINT_UNDEFINED); break; default: ut_error; } } } /*******************************************************************//** Gets the previous lock in the lock queue, returns NULL if there are no more locks (i.e. the current lock is the first one). The iterator is receded (if not-NULL is returned). @return previous lock or NULL */ UNIV_INTERN const lock_t* lock_queue_iterator_get_prev( /*=========================*/ lock_queue_iterator_t* iter) /*!< in/out: iterator */ { const lock_t* prev_lock; switch (lock_get_type_low(iter->current_lock)) { case LOCK_REC: prev_lock = lock_rec_get_prev( iter->current_lock, iter->bit_no); break; case LOCK_TABLE: prev_lock = UT_LIST_GET_PREV( un_member.tab_lock.locks, iter->current_lock); break; default: ut_error; } if (prev_lock != NULL) { iter->current_lock = prev_lock; } return(prev_lock); } haildb-2.3.2/fut/0000755000175000017500000000000011513177437014440 5ustar00pcrewspcrews00000000000000haildb-2.3.2/fut/fut0lst.c0000644000175000017500000003562111513177357016215 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /******************************************************************//** @file fut/fut0lst.c File-based list utilities Created 11/28/1995 Heikki Tuuri ***********************************************************************/ #include "fut0lst.h" #ifdef UNIV_NONINL #include "fut0lst.ic" #endif #include "buf0buf.h" #include "page0page.h" /********************************************************************//** Adds a node to an empty list. */ static void flst_add_to_empty( /*==============*/ flst_base_node_t* base, /*!< in: pointer to base node of empty list */ flst_node_t* node, /*!< in: node to add */ mtr_t* mtr) /*!< in: mini-transaction handle */ { ulint space; fil_addr_t node_addr; ulint len; ut_ad(mtr && base && node); ut_ad(base != node); ut_ad(mtr_memo_contains_page(mtr, base, MTR_MEMO_PAGE_X_FIX)); ut_ad(mtr_memo_contains_page(mtr, node, MTR_MEMO_PAGE_X_FIX)); len = flst_get_len(base, mtr); ut_a(len == 0); buf_ptr_get_fsp_addr(node, &space, &node_addr); /* Update first and last fields of base node */ flst_write_addr(base + FLST_FIRST, node_addr, mtr); flst_write_addr(base + FLST_LAST, node_addr, mtr); /* Set prev and next fields of node to add */ flst_write_addr(node + FLST_PREV, fil_addr_null, mtr); flst_write_addr(node + FLST_NEXT, fil_addr_null, mtr); /* Update len of base node */ mlog_write_ulint(base + FLST_LEN, len + 1, MLOG_4BYTES, mtr); } /********************************************************************//** Adds a node as the last node in a list. */ UNIV_INTERN void flst_add_last( /*==========*/ flst_base_node_t* base, /*!< in: pointer to base node of list */ flst_node_t* node, /*!< in: node to add */ mtr_t* mtr) /*!< in: mini-transaction handle */ { ulint space; fil_addr_t node_addr; ulint len; fil_addr_t last_addr; flst_node_t* last_node; ut_ad(mtr && base && node); ut_ad(base != node); ut_ad(mtr_memo_contains_page(mtr, base, MTR_MEMO_PAGE_X_FIX)); ut_ad(mtr_memo_contains_page(mtr, node, MTR_MEMO_PAGE_X_FIX)); len = flst_get_len(base, mtr); last_addr = flst_get_last(base, mtr); buf_ptr_get_fsp_addr(node, &space, &node_addr); /* If the list is not empty, call flst_insert_after */ if (len != 0) { if (last_addr.page == node_addr.page) { last_node = page_align(node) + last_addr.boffset; } else { ulint zip_size = fil_space_get_zip_size(space); last_node = fut_get_ptr(space, zip_size, last_addr, RW_X_LATCH, mtr); } flst_insert_after(base, last_node, node, mtr); } else { /* else call flst_add_to_empty */ flst_add_to_empty(base, node, mtr); } } /********************************************************************//** Adds a node as the first node in a list. */ UNIV_INTERN void flst_add_first( /*===========*/ flst_base_node_t* base, /*!< in: pointer to base node of list */ flst_node_t* node, /*!< in: node to add */ mtr_t* mtr) /*!< in: mini-transaction handle */ { ulint space; fil_addr_t node_addr; ulint len; fil_addr_t first_addr; flst_node_t* first_node; ut_ad(mtr && base && node); ut_ad(base != node); ut_ad(mtr_memo_contains_page(mtr, base, MTR_MEMO_PAGE_X_FIX)); ut_ad(mtr_memo_contains_page(mtr, node, MTR_MEMO_PAGE_X_FIX)); len = flst_get_len(base, mtr); first_addr = flst_get_first(base, mtr); buf_ptr_get_fsp_addr(node, &space, &node_addr); /* If the list is not empty, call flst_insert_before */ if (len != 0) { if (first_addr.page == node_addr.page) { first_node = page_align(node) + first_addr.boffset; } else { ulint zip_size = fil_space_get_zip_size(space); first_node = fut_get_ptr(space, zip_size, first_addr, RW_X_LATCH, mtr); } flst_insert_before(base, node, first_node, mtr); } else { /* else call flst_add_to_empty */ flst_add_to_empty(base, node, mtr); } } /********************************************************************//** Inserts a node after another in a list. */ UNIV_INTERN void flst_insert_after( /*==============*/ flst_base_node_t* base, /*!< in: pointer to base node of list */ flst_node_t* node1, /*!< in: node to insert after */ flst_node_t* node2, /*!< in: node to add */ mtr_t* mtr) /*!< in: mini-transaction handle */ { ulint space; fil_addr_t node1_addr; fil_addr_t node2_addr; flst_node_t* node3; fil_addr_t node3_addr; ulint len; ut_ad(mtr && node1 && node2 && base); ut_ad(base != node1); ut_ad(base != node2); ut_ad(node2 != node1); ut_ad(mtr_memo_contains_page(mtr, base, MTR_MEMO_PAGE_X_FIX)); ut_ad(mtr_memo_contains_page(mtr, node1, MTR_MEMO_PAGE_X_FIX)); ut_ad(mtr_memo_contains_page(mtr, node2, MTR_MEMO_PAGE_X_FIX)); buf_ptr_get_fsp_addr(node1, &space, &node1_addr); buf_ptr_get_fsp_addr(node2, &space, &node2_addr); node3_addr = flst_get_next_addr(node1, mtr); /* Set prev and next fields of node2 */ flst_write_addr(node2 + FLST_PREV, node1_addr, mtr); flst_write_addr(node2 + FLST_NEXT, node3_addr, mtr); if (!fil_addr_is_null(node3_addr)) { /* Update prev field of node3 */ ulint zip_size = fil_space_get_zip_size(space); node3 = fut_get_ptr(space, zip_size, node3_addr, RW_X_LATCH, mtr); flst_write_addr(node3 + FLST_PREV, node2_addr, mtr); } else { /* node1 was last in list: update last field in base */ flst_write_addr(base + FLST_LAST, node2_addr, mtr); } /* Set next field of node1 */ flst_write_addr(node1 + FLST_NEXT, node2_addr, mtr); /* Update len of base node */ len = flst_get_len(base, mtr); mlog_write_ulint(base + FLST_LEN, len + 1, MLOG_4BYTES, mtr); } /********************************************************************//** Inserts a node before another in a list. */ UNIV_INTERN void flst_insert_before( /*===============*/ flst_base_node_t* base, /*!< in: pointer to base node of list */ flst_node_t* node2, /*!< in: node to insert */ flst_node_t* node3, /*!< in: node to insert before */ mtr_t* mtr) /*!< in: mini-transaction handle */ { ulint space; flst_node_t* node1; fil_addr_t node1_addr; fil_addr_t node2_addr; fil_addr_t node3_addr; ulint len; ut_ad(mtr && node2 && node3 && base); ut_ad(base != node2); ut_ad(base != node3); ut_ad(node2 != node3); ut_ad(mtr_memo_contains_page(mtr, base, MTR_MEMO_PAGE_X_FIX)); ut_ad(mtr_memo_contains_page(mtr, node2, MTR_MEMO_PAGE_X_FIX)); ut_ad(mtr_memo_contains_page(mtr, node3, MTR_MEMO_PAGE_X_FIX)); buf_ptr_get_fsp_addr(node2, &space, &node2_addr); buf_ptr_get_fsp_addr(node3, &space, &node3_addr); node1_addr = flst_get_prev_addr(node3, mtr); /* Set prev and next fields of node2 */ flst_write_addr(node2 + FLST_PREV, node1_addr, mtr); flst_write_addr(node2 + FLST_NEXT, node3_addr, mtr); if (!fil_addr_is_null(node1_addr)) { ulint zip_size = fil_space_get_zip_size(space); /* Update next field of node1 */ node1 = fut_get_ptr(space, zip_size, node1_addr, RW_X_LATCH, mtr); flst_write_addr(node1 + FLST_NEXT, node2_addr, mtr); } else { /* node3 was first in list: update first field in base */ flst_write_addr(base + FLST_FIRST, node2_addr, mtr); } /* Set prev field of node3 */ flst_write_addr(node3 + FLST_PREV, node2_addr, mtr); /* Update len of base node */ len = flst_get_len(base, mtr); mlog_write_ulint(base + FLST_LEN, len + 1, MLOG_4BYTES, mtr); } /********************************************************************//** Removes a node. */ UNIV_INTERN void flst_remove( /*========*/ flst_base_node_t* base, /*!< in: pointer to base node of list */ flst_node_t* node2, /*!< in: node to remove */ mtr_t* mtr) /*!< in: mini-transaction handle */ { ulint space; ulint zip_size; flst_node_t* node1; fil_addr_t node1_addr; fil_addr_t node2_addr; flst_node_t* node3; fil_addr_t node3_addr; ulint len; ut_ad(mtr && node2 && base); ut_ad(mtr_memo_contains_page(mtr, base, MTR_MEMO_PAGE_X_FIX)); ut_ad(mtr_memo_contains_page(mtr, node2, MTR_MEMO_PAGE_X_FIX)); buf_ptr_get_fsp_addr(node2, &space, &node2_addr); zip_size = fil_space_get_zip_size(space); node1_addr = flst_get_prev_addr(node2, mtr); node3_addr = flst_get_next_addr(node2, mtr); if (!fil_addr_is_null(node1_addr)) { /* Update next field of node1 */ if (node1_addr.page == node2_addr.page) { node1 = page_align(node2) + node1_addr.boffset; } else { node1 = fut_get_ptr(space, zip_size, node1_addr, RW_X_LATCH, mtr); } ut_ad(node1 != node2); flst_write_addr(node1 + FLST_NEXT, node3_addr, mtr); } else { /* node2 was first in list: update first field in base */ flst_write_addr(base + FLST_FIRST, node3_addr, mtr); } if (!fil_addr_is_null(node3_addr)) { /* Update prev field of node3 */ if (node3_addr.page == node2_addr.page) { node3 = page_align(node2) + node3_addr.boffset; } else { node3 = fut_get_ptr(space, zip_size, node3_addr, RW_X_LATCH, mtr); } ut_ad(node2 != node3); flst_write_addr(node3 + FLST_PREV, node1_addr, mtr); } else { /* node2 was last in list: update last field in base */ flst_write_addr(base + FLST_LAST, node1_addr, mtr); } /* Update len of base node */ len = flst_get_len(base, mtr); ut_ad(len > 0); mlog_write_ulint(base + FLST_LEN, len - 1, MLOG_4BYTES, mtr); } /********************************************************************//** Cuts off the tail of the list, including the node given. The number of nodes which will be removed must be provided by the caller, as this function does not measure the length of the tail. */ UNIV_INTERN void flst_cut_end( /*=========*/ flst_base_node_t* base, /*!< in: pointer to base node of list */ flst_node_t* node2, /*!< in: first node to remove */ ulint n_nodes,/*!< in: number of nodes to remove, must be >= 1 */ mtr_t* mtr) /*!< in: mini-transaction handle */ { ulint space; flst_node_t* node1; fil_addr_t node1_addr; fil_addr_t node2_addr; ulint len; ut_ad(mtr && node2 && base); ut_ad(mtr_memo_contains_page(mtr, base, MTR_MEMO_PAGE_X_FIX)); ut_ad(mtr_memo_contains_page(mtr, node2, MTR_MEMO_PAGE_X_FIX)); ut_ad(n_nodes > 0); buf_ptr_get_fsp_addr(node2, &space, &node2_addr); node1_addr = flst_get_prev_addr(node2, mtr); if (!fil_addr_is_null(node1_addr)) { /* Update next field of node1 */ if (node1_addr.page == node2_addr.page) { node1 = page_align(node2) + node1_addr.boffset; } else { node1 = fut_get_ptr(space, fil_space_get_zip_size(space), node1_addr, RW_X_LATCH, mtr); } flst_write_addr(node1 + FLST_NEXT, fil_addr_null, mtr); } else { /* node2 was first in list: update the field in base */ flst_write_addr(base + FLST_FIRST, fil_addr_null, mtr); } flst_write_addr(base + FLST_LAST, node1_addr, mtr); /* Update len of base node */ len = flst_get_len(base, mtr); ut_ad(len >= n_nodes); mlog_write_ulint(base + FLST_LEN, len - n_nodes, MLOG_4BYTES, mtr); } /********************************************************************//** Cuts off the tail of the list, not including the given node. The number of nodes which will be removed must be provided by the caller, as this function does not measure the length of the tail. */ UNIV_INTERN void flst_truncate_end( /*==============*/ flst_base_node_t* base, /*!< in: pointer to base node of list */ flst_node_t* node2, /*!< in: first node not to remove */ ulint n_nodes,/*!< in: number of nodes to remove */ mtr_t* mtr) /*!< in: mini-transaction handle */ { fil_addr_t node2_addr; ulint len; ulint space; ut_ad(mtr && node2 && base); ut_ad(mtr_memo_contains_page(mtr, base, MTR_MEMO_PAGE_X_FIX)); ut_ad(mtr_memo_contains_page(mtr, node2, MTR_MEMO_PAGE_X_FIX)); if (n_nodes == 0) { ut_ad(fil_addr_is_null(flst_get_next_addr(node2, mtr))); return; } buf_ptr_get_fsp_addr(node2, &space, &node2_addr); /* Update next field of node2 */ flst_write_addr(node2 + FLST_NEXT, fil_addr_null, mtr); flst_write_addr(base + FLST_LAST, node2_addr, mtr); /* Update len of base node */ len = flst_get_len(base, mtr); ut_ad(len >= n_nodes); mlog_write_ulint(base + FLST_LEN, len - n_nodes, MLOG_4BYTES, mtr); } /********************************************************************//** Validates a file-based list. @return TRUE if ok */ UNIV_INTERN ibool flst_validate( /*==========*/ const flst_base_node_t* base, /*!< in: pointer to base node of list */ mtr_t* mtr1) /*!< in: mtr */ { ulint space; ulint zip_size; const flst_node_t* node; fil_addr_t node_addr; fil_addr_t base_addr; ulint len; ulint i; mtr_t mtr2; ut_ad(base); ut_ad(mtr_memo_contains_page(mtr1, base, MTR_MEMO_PAGE_X_FIX)); /* We use two mini-transaction handles: the first is used to lock the base node, and prevent other threads from modifying the list. The second is used to traverse the list. We cannot run the second mtr without committing it at times, because if the list is long, then the x-locked pages could fill the buffer resulting in a deadlock. */ /* Find out the space id */ buf_ptr_get_fsp_addr(base, &space, &base_addr); zip_size = fil_space_get_zip_size(space); len = flst_get_len(base, mtr1); node_addr = flst_get_first(base, mtr1); for (i = 0; i < len; i++) { mtr_start(&mtr2); node = fut_get_ptr(space, zip_size, node_addr, RW_X_LATCH, &mtr2); node_addr = flst_get_next_addr(node, &mtr2); mtr_commit(&mtr2); /* Commit mtr2 each round to prevent buffer becoming full */ } ut_a(fil_addr_is_null(node_addr)); node_addr = flst_get_last(base, mtr1); for (i = 0; i < len; i++) { mtr_start(&mtr2); node = fut_get_ptr(space, zip_size, node_addr, RW_X_LATCH, &mtr2); node_addr = flst_get_prev_addr(node, &mtr2); mtr_commit(&mtr2); /* Commit mtr2 each round to prevent buffer becoming full */ } ut_a(fil_addr_is_null(node_addr)); return(TRUE); } /********************************************************************//** Prints info of a file-based list. */ UNIV_INTERN void flst_print( /*=======*/ const flst_base_node_t* base, /*!< in: pointer to base node of list */ mtr_t* mtr) /*!< in: mtr */ { const buf_frame_t* frame; ulint len; ut_ad(base && mtr); ut_ad(mtr_memo_contains_page(mtr, base, MTR_MEMO_PAGE_X_FIX)); frame = page_align((byte*) base); len = flst_get_len(base, mtr); ib_logger(ib_stream, "FILE-BASED LIST:\n" "Base node in space %lu page %lu byte offset %lu; len %lu\n", (ulong) page_get_space_id(frame), (ulong) page_get_page_no(frame), (ulong) page_offset(base), (ulong) len); } haildb-2.3.2/fut/fut0fut.c0000644000175000017500000000216411513177357016205 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /******************************************************************//** @file fut/fut0fut.c File-based utilities Created 12/13/1995 Heikki Tuuri ***********************************************************************/ #include "fut0fut.h" #ifdef UNIV_NONINL #include "fut0fut.ic" #endif haildb-2.3.2/README0000644000175000017500000000241011513177357014520 0ustar00pcrewspcrews00000000000000This is the source of HailDB 2.3.0 ================================== Requirements =============================================== Zlib >= 1.2.x Need the development header files and library Unix =============================================== Instructions for compiling and installing: ------------------------------------------ 1. ./configure If you want to disable support for compressed tables you can run configure with the following command line argument: ./configure --disable-compression By default, support for compressed tables is enabled. If you want to disable support for shared libraries you can run configure with the following command line argument: ./configure --disable-shared By default, it will build both shared and static libraries The configure script attempts to select the optimal latching implementation for your configuration, if not explicitly set on the configure command line. If you need to force a particular selection you can use the following command line argument to select: --with-atomic-ops= one of [gcc_builtins|solaris|innodb] 2. make 3. make install 4. Enjoy! Windows ======= HailDB does not currently build on Windows. Although there is no real reason why support could not be continued. haildb-2.3.2/fsp/0000755000175000017500000000000011513177437014432 5ustar00pcrewspcrews00000000000000haildb-2.3.2/fsp/fsp0fsp.c0000644000175000017500000036042711513177357016174 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2010, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /******************************************************************//** @file fsp/fsp0fsp.c File space management Created 11/29/1995 Heikki Tuuri ***********************************************************************/ #include "fsp0fsp.h" #ifdef UNIV_NONINL #include "fsp0fsp.ic" #endif #include "buf0buf.h" #include "fil0fil.h" #include "mtr0log.h" #include "ut0byte.h" #include "page0page.h" #include "page0zip.h" #ifdef UNIV_HOTBACKUP # include "fut0lst.h" #else /* UNIV_HOTBACKUP */ # include "sync0sync.h" # include "fut0fut.h" # include "srv0srv.h" # include "ibuf0ibuf.h" # include "btr0btr.h" # include "btr0sea.h" # include "dict0boot.h" # include "log0log.h" #endif /* UNIV_HOTBACKUP */ #include "dict0mem.h" #define FSP_HEADER_OFFSET FIL_PAGE_DATA /* Offset of the space header within a file page */ /* The data structures in files are defined just as byte strings in C */ typedef byte fsp_header_t; typedef byte xdes_t; /* SPACE HEADER ============ File space header data structure: this data structure is contained in the first page of a space. The space for this header is reserved in every extent descriptor page, but used only in the first. */ /*-------------------------------------*/ #define FSP_SPACE_ID 0 /* space id */ #define FSP_NOT_USED 4 /* this field contained a value up to which we know that the modifications in the database have been flushed to the file space; not used now */ #define FSP_SIZE 8 /* Current size of the space in pages */ #define FSP_FREE_LIMIT 12 /* Minimum page number for which the free list has not been initialized: the pages >= this limit are, by definition, free; note that in a single-table tablespace where size < 64 pages, this number is 64, i.e., we have initialized the space about the first extent, but have not physically allocted those pages to the file */ #define FSP_SPACE_FLAGS 16 /* table->flags & ~DICT_TF_COMPACT */ #define FSP_FRAG_N_USED 20 /* number of used pages in the FSP_FREE_FRAG list */ #define FSP_FREE 24 /* list of free extents */ #define FSP_FREE_FRAG (24 + FLST_BASE_NODE_SIZE) /* list of partially free extents not belonging to any segment */ #define FSP_FULL_FRAG (24 + 2 * FLST_BASE_NODE_SIZE) /* list of full extents not belonging to any segment */ #define FSP_SEG_ID (24 + 3 * FLST_BASE_NODE_SIZE) /* 8 bytes which give the first unused segment id */ #define FSP_SEG_INODES_FULL (32 + 3 * FLST_BASE_NODE_SIZE) /* list of pages containing segment headers, where all the segment inode slots are reserved */ #define FSP_SEG_INODES_FREE (32 + 4 * FLST_BASE_NODE_SIZE) /* list of pages containing segment headers, where not all the segment header slots are reserved */ /*-------------------------------------*/ /* File space header size */ #define FSP_HEADER_SIZE (32 + 5 * FLST_BASE_NODE_SIZE) #define FSP_FREE_ADD 4 /* this many free extents are added to the free list from above FSP_FREE_LIMIT at a time */ /* FILE SEGMENT INODE ================== Segment inode which is created for each segment in a tablespace. NOTE: in purge we assume that a segment having only one currently used page can be freed in a few steps, so that the freeing cannot fill the file buffer with bufferfixed file pages. */ typedef byte fseg_inode_t; #define FSEG_INODE_PAGE_NODE FSEG_PAGE_DATA /* the list node for linking segment inode pages */ #define FSEG_ARR_OFFSET (FSEG_PAGE_DATA + FLST_NODE_SIZE) /*-------------------------------------*/ #define FSEG_ID 0 /* 8 bytes of segment id: if this is ut_dulint_zero, it means that the header is unused */ #define FSEG_NOT_FULL_N_USED 8 /* number of used segment pages in the FSEG_NOT_FULL list */ #define FSEG_FREE 12 /* list of free extents of this segment */ #define FSEG_NOT_FULL (12 + FLST_BASE_NODE_SIZE) /* list of partially free extents */ #define FSEG_FULL (12 + 2 * FLST_BASE_NODE_SIZE) /* list of full extents */ #define FSEG_MAGIC_N (12 + 3 * FLST_BASE_NODE_SIZE) /* magic number used in debugging */ #define FSEG_FRAG_ARR (16 + 3 * FLST_BASE_NODE_SIZE) /* array of individual pages belonging to this segment in fsp fragment extent lists */ #define FSEG_FRAG_ARR_N_SLOTS (FSP_EXTENT_SIZE / 2) /* number of slots in the array for the fragment pages */ #define FSEG_FRAG_SLOT_SIZE 4 /* a fragment page slot contains its page number within space, FIL_NULL means that the slot is not in use */ /*-------------------------------------*/ #define FSEG_INODE_SIZE \ (16 + 3 * FLST_BASE_NODE_SIZE \ + FSEG_FRAG_ARR_N_SLOTS * FSEG_FRAG_SLOT_SIZE) #define FSP_SEG_INODES_PER_PAGE(zip_size) \ (((zip_size ? zip_size : UNIV_PAGE_SIZE) \ - FSEG_ARR_OFFSET - 10) / FSEG_INODE_SIZE) /* Number of segment inodes which fit on a single page */ #define FSEG_MAGIC_N_VALUE 97937874 #define FSEG_FILLFACTOR 8 /* If this value is x, then if the number of unused but reserved pages in a segment is less than reserved pages * 1/x, and there are at least FSEG_FRAG_LIMIT used pages, then we allow a new empty extent to be added to the segment in fseg_alloc_free_page. Otherwise, we use unused pages of the segment. */ #define FSEG_FRAG_LIMIT FSEG_FRAG_ARR_N_SLOTS /* If the segment has >= this many used pages, it may be expanded by allocating extents to the segment; until that only individual fragment pages are allocated from the space */ #define FSEG_FREE_LIST_LIMIT 40 /* If the reserved size of a segment is at least this many extents, we allow extents to be put to the free list of the extent: at most FSEG_FREE_LIST_MAX_LEN many */ #define FSEG_FREE_LIST_MAX_LEN 4 /* EXTENT DESCRIPTOR ================= File extent descriptor data structure: contains bits to tell which pages in the extent are free and which contain old tuple version to clean. */ /*-------------------------------------*/ #define XDES_ID 0 /* The identifier of the segment to which this extent belongs */ #define XDES_FLST_NODE 8 /* The list node data structure for the descriptors */ #define XDES_STATE (FLST_NODE_SIZE + 8) /* contains state information of the extent */ #define XDES_BITMAP (FLST_NODE_SIZE + 12) /* Descriptor bitmap of the pages in the extent */ /*-------------------------------------*/ #define XDES_BITS_PER_PAGE 2 /* How many bits are there per page */ #define XDES_FREE_BIT 0 /* Index of the bit which tells if the page is free */ #define XDES_CLEAN_BIT 1 /* NOTE: currently not used! Index of the bit which tells if there are old versions of tuples on the page */ /* States of a descriptor */ #define XDES_FREE 1 /* extent is in free list of space */ #define XDES_FREE_FRAG 2 /* extent is in free fragment list of space */ #define XDES_FULL_FRAG 3 /* extent is in full fragment list of space */ #define XDES_FSEG 4 /* extent belongs to a segment */ /* File extent data structure size in bytes. */ #define XDES_SIZE \ (XDES_BITMAP + UT_BITS_IN_BYTES(FSP_EXTENT_SIZE * XDES_BITS_PER_PAGE)) /* Offset of the descriptor array on a descriptor page */ #define XDES_ARR_OFFSET (FSP_HEADER_OFFSET + FSP_HEADER_SIZE) #ifndef UNIV_HOTBACKUP /* Flag to indicate if we have printed the tablespace full error. */ static ibool fsp_tbs_full_error_printed = FALSE; /**********************************************************************//** Returns an extent to the free list of a space. */ static void fsp_free_extent( /*============*/ ulint space, /*!< in: space id */ ulint zip_size,/*!< in: compressed page size in bytes or 0 for uncompressed pages */ ulint page, /*!< in: page offset in the extent */ mtr_t* mtr); /*!< in: mtr */ /**********************************************************************//** Frees an extent of a segment to the space free list. */ static void fseg_free_extent( /*=============*/ fseg_inode_t* seg_inode, /*!< in: segment inode */ ulint space, /*!< in: space id */ ulint zip_size,/*!< in: compressed page size in bytes or 0 for uncompressed pages */ ulint page, /*!< in: page offset in the extent */ mtr_t* mtr); /*!< in: mtr handle */ /**********************************************************************//** Calculates the number of pages reserved by a segment, and how many pages are currently used. @return number of reserved pages */ static ulint fseg_n_reserved_pages_low( /*======================*/ fseg_inode_t* header, /*!< in: segment inode */ ulint* used, /*!< out: number of pages used (not more than reserved) */ mtr_t* mtr); /*!< in: mtr handle */ /********************************************************************//** Marks a page used. The page must reside within the extents of the given segment. */ static void fseg_mark_page_used( /*================*/ fseg_inode_t* seg_inode,/*!< in: segment inode */ ulint space, /*!< in: space id */ ulint zip_size,/*!< in: compressed page size in bytes or 0 for uncompressed pages */ ulint page, /*!< in: page offset */ mtr_t* mtr); /*!< in: mtr */ /**********************************************************************//** Returns the first extent descriptor for a segment. We think of the extent lists of the segment catenated in the order FSEG_FULL -> FSEG_NOT_FULL -> FSEG_FREE. @return the first extent descriptor, or NULL if none */ static xdes_t* fseg_get_first_extent( /*==================*/ fseg_inode_t* inode, /*!< in: segment inode */ ulint space, /*!< in: space id */ ulint zip_size,/*!< in: compressed page size in bytes or 0 for uncompressed pages */ mtr_t* mtr); /*!< in: mtr */ /**********************************************************************//** Puts new extents to the free list if there are free extents above the free limit. If an extent happens to contain an extent descriptor page, the extent is put to the FSP_FREE_FRAG list with the page marked as used. */ static void fsp_fill_free_list( /*===============*/ ibool init_space, /*!< in: TRUE if this is a single-table tablespace and we are only initing the tablespace's first extent descriptor page and ibuf bitmap page; then we do not allocate more extents */ ulint space, /*!< in: space */ fsp_header_t* header, /*!< in: space header */ mtr_t* mtr); /*!< in: mtr */ /**********************************************************************//** Allocates a single free page from a segment. This function implements the intelligent allocation strategy which tries to minimize file space fragmentation. @return the allocated page number, FIL_NULL if no page could be allocated */ static ulint fseg_alloc_free_page_low( /*=====================*/ ulint space, /*!< in: space */ ulint zip_size,/*!< in: compressed page size in bytes or 0 for uncompressed pages */ fseg_inode_t* seg_inode, /*!< in: segment inode */ ulint hint, /*!< in: hint of which page would be desirable */ byte direction, /*!< in: if the new page is needed because of an index page split, and records are inserted there in order, into which direction they go alphabetically: FSP_DOWN, FSP_UP, FSP_NO_DIR */ mtr_t* mtr); /*!< in: mtr handle */ #endif /* !UNIV_HOTBACKUP */ /**********************************************************************//** Reads the file space size stored in the header page. @return tablespace size stored in the space header */ UNIV_INTERN ulint fsp_get_size_low( /*=============*/ page_t* page) /*!< in: header page (page 0 in the tablespace) */ { return(mach_read_from_4(page + FSP_HEADER_OFFSET + FSP_SIZE)); } #ifndef UNIV_HOTBACKUP /**********************************************************************//** Gets a pointer to the space header and x-locks its page. @return pointer to the space header, page x-locked */ UNIV_INLINE fsp_header_t* fsp_get_space_header( /*=================*/ ulint id, /*!< in: space id */ ulint zip_size,/*!< in: compressed page size in bytes or 0 for uncompressed pages */ mtr_t* mtr) /*!< in: mtr */ { buf_block_t* block; fsp_header_t* header; ut_ad(ut_is_2pow(zip_size)); ut_ad(zip_size <= UNIV_PAGE_SIZE); ut_ad(!zip_size || zip_size >= PAGE_ZIP_MIN_SIZE); ut_ad(id || !zip_size); block = buf_page_get(id, zip_size, 0, RW_X_LATCH, mtr); header = FSP_HEADER_OFFSET + buf_block_get_frame(block); buf_block_dbg_add_level(block, SYNC_FSP_PAGE); ut_ad(id == mach_read_from_4(FSP_SPACE_ID + header)); ut_ad(zip_size == dict_table_flags_to_zip_size( mach_read_from_4(FSP_SPACE_FLAGS + header))); return(header); } /**********************************************************************//** Gets a descriptor bit of a page. @return TRUE if free */ UNIV_INLINE ibool xdes_get_bit( /*=========*/ const xdes_t* descr, /*!< in: descriptor */ ulint bit, /*!< in: XDES_FREE_BIT or XDES_CLEAN_BIT */ ulint offset, /*!< in: page offset within extent: 0 ... FSP_EXTENT_SIZE - 1 */ mtr_t* mtr) /*!< in: mtr */ { ulint index; ulint byte_index; ulint bit_index; ut_ad(mtr_memo_contains_page(mtr, descr, MTR_MEMO_PAGE_X_FIX)); ut_ad((bit == XDES_FREE_BIT) || (bit == XDES_CLEAN_BIT)); ut_ad(offset < FSP_EXTENT_SIZE); index = bit + XDES_BITS_PER_PAGE * offset; byte_index = index / 8; bit_index = index % 8; return(ut_bit_get_nth(mtr_read_ulint(descr + XDES_BITMAP + byte_index, MLOG_1BYTE, mtr), bit_index)); } /**********************************************************************//** Sets a descriptor bit of a page. */ UNIV_INLINE void xdes_set_bit( /*=========*/ xdes_t* descr, /*!< in: descriptor */ ulint bit, /*!< in: XDES_FREE_BIT or XDES_CLEAN_BIT */ ulint offset, /*!< in: page offset within extent: 0 ... FSP_EXTENT_SIZE - 1 */ ibool val, /*!< in: bit value */ mtr_t* mtr) /*!< in: mtr */ { ulint index; ulint byte_index; ulint bit_index; ulint descr_byte; ut_ad(mtr_memo_contains_page(mtr, descr, MTR_MEMO_PAGE_X_FIX)); ut_ad((bit == XDES_FREE_BIT) || (bit == XDES_CLEAN_BIT)); ut_ad(offset < FSP_EXTENT_SIZE); index = bit + XDES_BITS_PER_PAGE * offset; byte_index = index / 8; bit_index = index % 8; descr_byte = mtr_read_ulint(descr + XDES_BITMAP + byte_index, MLOG_1BYTE, mtr); descr_byte = ut_bit_set_nth(descr_byte, bit_index, val); mlog_write_ulint(descr + XDES_BITMAP + byte_index, descr_byte, MLOG_1BYTE, mtr); } /**********************************************************************//** Looks for a descriptor bit having the desired value. Starts from hint and scans upward; at the end of the extent the search is wrapped to the start of the extent. @return bit index of the bit, ULINT_UNDEFINED if not found */ UNIV_INLINE ulint xdes_find_bit( /*==========*/ xdes_t* descr, /*!< in: descriptor */ ulint bit, /*!< in: XDES_FREE_BIT or XDES_CLEAN_BIT */ ibool val, /*!< in: desired bit value */ ulint hint, /*!< in: hint of which bit position would be desirable */ mtr_t* mtr) /*!< in: mtr */ { ulint i; ut_ad(descr && mtr); ut_ad(val <= TRUE); ut_ad(hint < FSP_EXTENT_SIZE); ut_ad(mtr_memo_contains_page(mtr, descr, MTR_MEMO_PAGE_X_FIX)); for (i = hint; i < FSP_EXTENT_SIZE; i++) { if (val == xdes_get_bit(descr, bit, i, mtr)) { return(i); } } for (i = 0; i < hint; i++) { if (val == xdes_get_bit(descr, bit, i, mtr)) { return(i); } } return(ULINT_UNDEFINED); } /**********************************************************************//** Looks for a descriptor bit having the desired value. Scans the extent in a direction opposite to xdes_find_bit. @return bit index of the bit, ULINT_UNDEFINED if not found */ UNIV_INLINE ulint xdes_find_bit_downward( /*===================*/ xdes_t* descr, /*!< in: descriptor */ ulint bit, /*!< in: XDES_FREE_BIT or XDES_CLEAN_BIT */ ibool val, /*!< in: desired bit value */ ulint hint, /*!< in: hint of which bit position would be desirable */ mtr_t* mtr) /*!< in: mtr */ { ulint i; ut_ad(descr && mtr); ut_ad(val <= TRUE); ut_ad(hint < FSP_EXTENT_SIZE); ut_ad(mtr_memo_contains_page(mtr, descr, MTR_MEMO_PAGE_X_FIX)); for (i = hint + 1; i > 0; i--) { if (val == xdes_get_bit(descr, bit, i - 1, mtr)) { return(i - 1); } } for (i = FSP_EXTENT_SIZE - 1; i > hint; i--) { if (val == xdes_get_bit(descr, bit, i, mtr)) { return(i); } } return(ULINT_UNDEFINED); } /**********************************************************************//** Returns the number of used pages in a descriptor. @return number of pages used */ UNIV_INLINE ulint xdes_get_n_used( /*============*/ const xdes_t* descr, /*!< in: descriptor */ mtr_t* mtr) /*!< in: mtr */ { ulint i; ulint count = 0; ut_ad(descr && mtr); ut_ad(mtr_memo_contains_page(mtr, descr, MTR_MEMO_PAGE_X_FIX)); for (i = 0; i < FSP_EXTENT_SIZE; i++) { if (FALSE == xdes_get_bit(descr, XDES_FREE_BIT, i, mtr)) { count++; } } return(count); } /**********************************************************************//** Returns true if extent contains no used pages. @return TRUE if totally free */ UNIV_INLINE ibool xdes_is_free( /*=========*/ const xdes_t* descr, /*!< in: descriptor */ mtr_t* mtr) /*!< in: mtr */ { if (0 == xdes_get_n_used(descr, mtr)) { return(TRUE); } return(FALSE); } /**********************************************************************//** Returns true if extent contains no free pages. @return TRUE if full */ UNIV_INLINE ibool xdes_is_full( /*=========*/ const xdes_t* descr, /*!< in: descriptor */ mtr_t* mtr) /*!< in: mtr */ { if (FSP_EXTENT_SIZE == xdes_get_n_used(descr, mtr)) { return(TRUE); } return(FALSE); } /**********************************************************************//** Sets the state of an xdes. */ UNIV_INLINE void xdes_set_state( /*===========*/ xdes_t* descr, /*!< in/out: descriptor */ ulint state, /*!< in: state to set */ mtr_t* mtr) /*!< in: mtr handle */ { ut_ad(descr && mtr); ut_ad(state >= XDES_FREE); ut_ad(state <= XDES_FSEG); ut_ad(mtr_memo_contains_page(mtr, descr, MTR_MEMO_PAGE_X_FIX)); mlog_write_ulint(descr + XDES_STATE, state, MLOG_4BYTES, mtr); } /**********************************************************************//** Gets the state of an xdes. @return state */ UNIV_INLINE ulint xdes_get_state( /*===========*/ const xdes_t* descr, /*!< in: descriptor */ mtr_t* mtr) /*!< in: mtr handle */ { ulint state; ut_ad(descr && mtr); ut_ad(mtr_memo_contains_page(mtr, descr, MTR_MEMO_PAGE_X_FIX)); state = mtr_read_ulint(descr + XDES_STATE, MLOG_4BYTES, mtr); ut_ad(state - 1 < XDES_FSEG); return(state); } /**********************************************************************//** Inits an extent descriptor to the free and clean state. */ UNIV_INLINE void xdes_init( /*======*/ xdes_t* descr, /*!< in: descriptor */ mtr_t* mtr) /*!< in: mtr */ { ulint i; ut_ad(descr && mtr); ut_ad(mtr_memo_contains_page(mtr, descr, MTR_MEMO_PAGE_X_FIX)); ut_ad((XDES_SIZE - XDES_BITMAP) % 4 == 0); for (i = XDES_BITMAP; i < XDES_SIZE; i += 4) { mlog_write_ulint(descr + i, 0xFFFFFFFFUL, MLOG_4BYTES, mtr); } xdes_set_state(descr, XDES_FREE, mtr); } /********************************************************************//** Calculates the page where the descriptor of a page resides. @return descriptor page offset */ UNIV_INLINE ulint xdes_calc_descriptor_page( /*======================*/ ulint zip_size, /*!< in: compressed page size in bytes; 0 for uncompressed pages */ ulint offset) /*!< in: page offset */ { #ifndef DOXYGEN /* Doxygen gets confused of these */ # if UNIV_PAGE_SIZE <= XDES_ARR_OFFSET \ + (UNIV_PAGE_SIZE / FSP_EXTENT_SIZE) * XDES_SIZE # error # endif # if PAGE_ZIP_MIN_SIZE <= XDES_ARR_OFFSET \ + (PAGE_ZIP_MIN_SIZE / FSP_EXTENT_SIZE) * XDES_SIZE # error # endif #endif /* !DOXYGEN */ ut_ad(ut_is_2pow(zip_size)); if (!zip_size) { return(ut_2pow_round(offset, UNIV_PAGE_SIZE)); } else { ut_ad(zip_size > XDES_ARR_OFFSET + (zip_size / FSP_EXTENT_SIZE) * XDES_SIZE); return(ut_2pow_round(offset, zip_size)); } } /********************************************************************//** Calculates the descriptor index within a descriptor page. @return descriptor index */ UNIV_INLINE ulint xdes_calc_descriptor_index( /*=======================*/ ulint zip_size, /*!< in: compressed page size in bytes; 0 for uncompressed pages */ ulint offset) /*!< in: page offset */ { ut_ad(ut_is_2pow(zip_size)); if (!zip_size) { return(ut_2pow_remainder(offset, UNIV_PAGE_SIZE) / FSP_EXTENT_SIZE); } else { return(ut_2pow_remainder(offset, zip_size) / FSP_EXTENT_SIZE); } } /********************************************************************//** Gets pointer to a the extent descriptor of a page. The page where the extent descriptor resides is x-locked. If the page offset is equal to the free limit of the space, adds new extents from above the free limit to the space free list, if not free limit == space size. This adding is necessary to make the descriptor defined, as they are uninitialized above the free limit. @return pointer to the extent descriptor, NULL if the page does not exist in the space or if the offset exceeds the free limit */ UNIV_INLINE xdes_t* xdes_get_descriptor_with_space_hdr( /*===============================*/ fsp_header_t* sp_header,/*!< in/out: space header, x-latched */ ulint space, /*!< in: space id */ ulint offset, /*!< in: page offset; if equal to the free limit, we try to add new extents to the space free list */ mtr_t* mtr) /*!< in: mtr handle */ { ulint limit; ulint size; ulint zip_size; ulint descr_page_no; page_t* descr_page; ut_ad(mtr); ut_ad(mtr_memo_contains(mtr, fil_space_get_latch(space, NULL), MTR_MEMO_X_LOCK)); ut_ad(mtr_memo_contains_page(mtr, sp_header, MTR_MEMO_PAGE_S_FIX) || mtr_memo_contains_page(mtr, sp_header, MTR_MEMO_PAGE_X_FIX)); ut_ad(page_offset(sp_header) == FSP_HEADER_OFFSET); /* Read free limit and space size */ limit = mach_read_from_4(sp_header + FSP_FREE_LIMIT); size = mach_read_from_4(sp_header + FSP_SIZE); zip_size = dict_table_flags_to_zip_size( mach_read_from_4(sp_header + FSP_SPACE_FLAGS)); /* If offset is >= size or > limit, return NULL */ if ((offset >= size) || (offset > limit)) { return(NULL); } /* If offset is == limit, fill free list of the space. */ if (offset == limit) { fsp_fill_free_list(FALSE, space, sp_header, mtr); } descr_page_no = xdes_calc_descriptor_page(zip_size, offset); if (descr_page_no == 0) { /* It is on the space header page */ descr_page = page_align(sp_header); } else { buf_block_t* block; block = buf_page_get(space, zip_size, descr_page_no, RW_X_LATCH, mtr); buf_block_dbg_add_level(block, SYNC_FSP_PAGE); descr_page = buf_block_get_frame(block); } return(descr_page + XDES_ARR_OFFSET + XDES_SIZE * xdes_calc_descriptor_index(zip_size, offset)); } /********************************************************************//** Gets pointer to a the extent descriptor of a page. The page where the extent descriptor resides is x-locked. If the page offset is equal to the free limit of the space, adds new extents from above the free limit to the space free list, if not free limit == space size. This adding is necessary to make the descriptor defined, as they are uninitialized above the free limit. @return pointer to the extent descriptor, NULL if the page does not exist in the space or if the offset exceeds the free limit */ static xdes_t* xdes_get_descriptor( /*================*/ ulint space, /*!< in: space id */ ulint zip_size,/*!< in: compressed page size in bytes or 0 for uncompressed pages */ ulint offset, /*!< in: page offset; if equal to the free limit, we try to add new extents to the space free list */ mtr_t* mtr) /*!< in: mtr handle */ { buf_block_t* block; fsp_header_t* sp_header; block = buf_page_get(space, zip_size, 0, RW_X_LATCH, mtr); buf_block_dbg_add_level(block, SYNC_FSP_PAGE); sp_header = FSP_HEADER_OFFSET + buf_block_get_frame(block); return(xdes_get_descriptor_with_space_hdr(sp_header, space, offset, mtr)); } /********************************************************************//** Gets pointer to a the extent descriptor if the file address of the descriptor list node is known. The page where the extent descriptor resides is x-locked. @return pointer to the extent descriptor */ UNIV_INLINE xdes_t* xdes_lst_get_descriptor( /*====================*/ ulint space, /*!< in: space id */ ulint zip_size,/*!< in: compressed page size in bytes or 0 for uncompressed pages */ fil_addr_t lst_node,/*!< in: file address of the list node contained in the descriptor */ mtr_t* mtr) /*!< in: mtr handle */ { xdes_t* descr; ut_ad(mtr); ut_ad(mtr_memo_contains(mtr, fil_space_get_latch(space, NULL), MTR_MEMO_X_LOCK)); descr = fut_get_ptr(space, zip_size, lst_node, RW_X_LATCH, mtr) - XDES_FLST_NODE; return(descr); } /********************************************************************//** Returns page offset of the first page in extent described by a descriptor. @return offset of the first page in extent */ UNIV_INLINE ulint xdes_get_offset( /*============*/ xdes_t* descr) /*!< in: extent descriptor */ { ut_ad(descr); return(page_get_page_no(page_align(descr)) + ((page_offset(descr) - XDES_ARR_OFFSET) / XDES_SIZE) * FSP_EXTENT_SIZE); } #endif /* !UNIV_HOTBACKUP */ /***********************************************************//** Inits a file page whose prior contents should be ignored. */ static void fsp_init_file_page_low( /*===================*/ buf_block_t* block) /*!< in: pointer to a page */ { page_t* page = buf_block_get_frame(block); page_zip_des_t* page_zip= buf_block_get_page_zip(block); #ifndef UNIV_HOTBACKUP block->check_index_page_at_flush = FALSE; #endif /* !UNIV_HOTBACKUP */ if (UNIV_LIKELY_NULL(page_zip)) { memset(page, 0, UNIV_PAGE_SIZE); memset(page_zip->data, 0, page_zip_get_size(page_zip)); mach_write_to_4(page + FIL_PAGE_OFFSET, buf_block_get_page_no(block)); mach_write_to_4(page + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID, buf_block_get_space(block)); memcpy(page_zip->data + FIL_PAGE_OFFSET, page + FIL_PAGE_OFFSET, 4); memcpy(page_zip->data + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID, page + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID, 4); return; } UNIV_MEM_INVALID(page, UNIV_PAGE_SIZE); mach_write_to_4(page + FIL_PAGE_OFFSET, buf_block_get_page_no(block)); memset(page + FIL_PAGE_LSN, 0, 8); mach_write_to_4(page + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID, buf_block_get_space(block)); memset(page + UNIV_PAGE_SIZE - FIL_PAGE_END_LSN_OLD_CHKSUM, 0, 8); } #ifndef UNIV_HOTBACKUP /***********************************************************//** Inits a file page whose prior contents should be ignored. */ static void fsp_init_file_page( /*===============*/ buf_block_t* block, /*!< in: pointer to a page */ mtr_t* mtr) /*!< in: mtr */ { fsp_init_file_page_low(block); mlog_write_initial_log_record(buf_block_get_frame(block), MLOG_INIT_FILE_PAGE, mtr); } #endif /* !UNIV_HOTBACKUP */ /***********************************************************//** Parses a redo log record of a file page init. @return end of log record or NULL */ UNIV_INTERN byte* fsp_parse_init_file_page( /*=====================*/ byte* ptr, /*!< in: buffer */ byte* end_ptr __attribute__((unused)), /*!< in: buffer end */ buf_block_t* block) /*!< in: block or NULL */ { ut_ad(ptr && end_ptr); if (block) { fsp_init_file_page_low(block); } return(ptr); } /**********************************************************************//** Initializes the fsp system. */ UNIV_INTERN void fsp_init(void) /*==========*/ { /* Does nothing at the moment */ } /**********************************************************************//** Writes the space id and compressed page size to a tablespace header. This function is used past the buffer pool when we in fil0fil.c create a new single-table tablespace. */ UNIV_INTERN void fsp_header_init_fields( /*===================*/ page_t* page, /*!< in/out: first page in the space */ ulint space_id, /*!< in: space id */ ulint flags) /*!< in: tablespace flags (FSP_SPACE_FLAGS): 0, or table->flags if newer than COMPACT */ { /* The tablespace flags (FSP_SPACE_FLAGS) should be 0 for ROW_FORMAT=COMPACT (table->flags == DICT_TF_COMPACT) and ROW_FORMAT=REDUNDANT (table->flags == 0). For any other format, the tablespace flags should equal table->flags. */ ut_a(flags != DICT_TF_COMPACT); mach_write_to_4(FSP_HEADER_OFFSET + FSP_SPACE_ID + page, space_id); mach_write_to_4(FSP_HEADER_OFFSET + FSP_SPACE_FLAGS + page, flags); } #ifndef UNIV_HOTBACKUP /**********************************************************************//** Initializes the space header of a new created space and creates also the insert buffer tree root if space == 0. */ UNIV_INTERN void fsp_header_init( /*============*/ ulint space, /*!< in: space id */ ulint size, /*!< in: current size in blocks */ mtr_t* mtr) /*!< in: mini-transaction handle */ { fsp_header_t* header; buf_block_t* block; page_t* page; ulint flags; ulint zip_size; ut_ad(mtr); mtr_x_lock(fil_space_get_latch(space, &flags), mtr); zip_size = dict_table_flags_to_zip_size(flags); block = buf_page_create(space, 0, zip_size, mtr); buf_page_get(space, zip_size, 0, RW_X_LATCH, mtr); buf_block_dbg_add_level(block, SYNC_FSP_PAGE); /* The prior contents of the file page should be ignored */ fsp_init_file_page(block, mtr); page = buf_block_get_frame(block); mlog_write_ulint(page + FIL_PAGE_TYPE, FIL_PAGE_TYPE_FSP_HDR, MLOG_2BYTES, mtr); header = FSP_HEADER_OFFSET + page; mlog_write_ulint(header + FSP_SPACE_ID, space, MLOG_4BYTES, mtr); mlog_write_ulint(header + FSP_NOT_USED, 0, MLOG_4BYTES, mtr); mlog_write_ulint(header + FSP_SIZE, size, MLOG_4BYTES, mtr); mlog_write_ulint(header + FSP_FREE_LIMIT, 0, MLOG_4BYTES, mtr); mlog_write_ulint(header + FSP_SPACE_FLAGS, flags, MLOG_4BYTES, mtr); mlog_write_ulint(header + FSP_FRAG_N_USED, 0, MLOG_4BYTES, mtr); flst_init(header + FSP_FREE, mtr); flst_init(header + FSP_FREE_FRAG, mtr); flst_init(header + FSP_FULL_FRAG, mtr); flst_init(header + FSP_SEG_INODES_FULL, mtr); flst_init(header + FSP_SEG_INODES_FREE, mtr); mlog_write_dulint(header + FSP_SEG_ID, ut_dulint_create(0, 1), mtr); if (space == 0) { fsp_fill_free_list(FALSE, space, header, mtr); btr_create(DICT_CLUSTERED | DICT_UNIVERSAL | DICT_IBUF, 0, 0, ut_dulint_add(DICT_IBUF_ID_MIN, space), dict_ind_redundant, mtr); } else { fsp_fill_free_list(TRUE, space, header, mtr); } } #endif /* !UNIV_HOTBACKUP */ /**********************************************************************//** Reads the space id from the first page of a tablespace. @return space id, ULINT UNDEFINED if error */ UNIV_INTERN ulint fsp_header_get_space_id( /*====================*/ const page_t* page) /*!< in: first page of a tablespace */ { ulint fsp_id; ulint id; fsp_id = mach_read_from_4(FSP_HEADER_OFFSET + page + FSP_SPACE_ID); id = mach_read_from_4(page + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID); if (id != fsp_id) { ib_logger(ib_stream, "InnoDB: Error: space id in fsp header %lu," " but in the page header %lu\n", (ulong) fsp_id, (ulong) id); return(ULINT_UNDEFINED); } return(id); } /**********************************************************************//** Reads the space flags from the first page of a tablespace. @return flags */ UNIV_INTERN ulint fsp_header_get_flags( /*=================*/ const page_t* page) /*!< in: first page of a tablespace */ { ut_ad(!page_offset(page)); return(mach_read_from_4(FSP_HEADER_OFFSET + FSP_SPACE_FLAGS + page)); } /**********************************************************************//** Reads the compressed page size from the first page of a tablespace. @return compressed page size in bytes, or 0 if uncompressed */ UNIV_INTERN ulint fsp_header_get_zip_size( /*====================*/ const page_t* page) /*!< in: first page of a tablespace */ { ulint flags = fsp_header_get_flags(page); return(dict_table_flags_to_zip_size(flags)); } #ifndef UNIV_HOTBACKUP /**********************************************************************//** Increases the space size field of a space. */ UNIV_INTERN void fsp_header_inc_size( /*================*/ ulint space, /*!< in: space id */ ulint size_inc,/*!< in: size increment in pages */ mtr_t* mtr) /*!< in: mini-transaction handle */ { fsp_header_t* header; ulint size; ulint flags; ut_ad(mtr); mtr_x_lock(fil_space_get_latch(space, &flags), mtr); header = fsp_get_space_header(space, dict_table_flags_to_zip_size(flags), mtr); size = mtr_read_ulint(header + FSP_SIZE, MLOG_4BYTES, mtr); mlog_write_ulint(header + FSP_SIZE, size + size_inc, MLOG_4BYTES, mtr); } /**********************************************************************//** Gets the current free limit of the system tablespace. The free limit means the place of the first page which has never been put to the free list for allocation. The space above that address is initialized to zero. Sets also the global variable log_fsp_current_free_limit. @return free limit in megabytes */ UNIV_INTERN ulint fsp_header_get_free_limit(void) /*===========================*/ { fsp_header_t* header; ulint limit; mtr_t mtr; mtr_start(&mtr); mtr_x_lock(fil_space_get_latch(0, NULL), &mtr); header = fsp_get_space_header(0, 0, &mtr); limit = mtr_read_ulint(header + FSP_FREE_LIMIT, MLOG_4BYTES, &mtr); limit /= ((1024 * 1024) / UNIV_PAGE_SIZE); log_fsp_current_free_limit_set_and_checkpoint(limit); mtr_commit(&mtr); return(limit); } /**********************************************************************//** Gets the size of the system tablespace from the tablespace header. If we do not have an auto-extending data file, this should be equal to the size of the data files. If there is an auto-extending data file, this can be smaller. @return size in pages */ UNIV_INTERN ulint fsp_header_get_tablespace_size(void) /*================================*/ { fsp_header_t* header; ulint size; mtr_t mtr; mtr_start(&mtr); mtr_x_lock(fil_space_get_latch(0, NULL), &mtr); header = fsp_get_space_header(0, 0, &mtr); size = mtr_read_ulint(header + FSP_SIZE, MLOG_4BYTES, &mtr); mtr_commit(&mtr); return(size); } /***********************************************************************//** Tries to extend a single-table tablespace so that a page would fit in the data file. @return TRUE if success */ static ibool fsp_try_extend_data_file_with_pages( /*================================*/ ulint space, /*!< in: space */ ulint page_no, /*!< in: page number */ fsp_header_t* header, /*!< in: space header */ mtr_t* mtr) /*!< in: mtr */ { ibool success; ulint actual_size; ulint size; ut_a(space != 0); size = mtr_read_ulint(header + FSP_SIZE, MLOG_4BYTES, mtr); ut_a(page_no >= size); success = fil_extend_space_to_desired_size(&actual_size, space, page_no + 1); /* actual_size now has the space size in pages; it may be less than we wanted if we ran out of disk space */ mlog_write_ulint(header + FSP_SIZE, actual_size, MLOG_4BYTES, mtr); return(success); } /***********************************************************************//** Tries to extend the last data file of a tablespace if it is auto-extending. @return FALSE if not auto-extending */ static ibool fsp_try_extend_data_file( /*=====================*/ ulint* actual_increase,/*!< out: actual increase in pages, where we measure the tablespace size from what the header field says; it may be the actual file size rounded down to megabyte */ ulint space, /*!< in: space */ fsp_header_t* header, /*!< in: space header */ mtr_t* mtr) /*!< in: mtr */ { ulint size; ulint zip_size; ulint new_size; ulint old_size; ulint size_increase; ulint actual_size; ibool success; *actual_increase = 0; if (space == 0 && !srv_auto_extend_last_data_file) { /* We print the error message only once to avoid spamming the error log. Note that we don't need to reset the flag to FALSE as dealing with this error requires server restart. */ if (fsp_tbs_full_error_printed == FALSE) { ib_logger(ib_stream, "InnoDB: Error: Data file(s) ran" " out of space.\n" "Please add another data file or" " use \'autoextend\' for the last" " data file.\n"); fsp_tbs_full_error_printed = TRUE; } return(FALSE); } size = mtr_read_ulint(header + FSP_SIZE, MLOG_4BYTES, mtr); zip_size = dict_table_flags_to_zip_size( mach_read_from_4(header + FSP_SPACE_FLAGS)); old_size = size; if (space == 0) { if (!srv_last_file_size_max) { size_increase = SRV_AUTO_EXTEND_INCREMENT; } else { if (srv_last_file_size_max < srv_data_file_sizes[srv_n_data_files - 1]) { ib_logger(ib_stream, "InnoDB: Error: Last data file size" " is %lu, max size allowed %lu\n", (ulong) srv_data_file_sizes[ srv_n_data_files - 1], (ulong) srv_last_file_size_max); } size_increase = srv_last_file_size_max - srv_data_file_sizes[srv_n_data_files - 1]; if (size_increase > SRV_AUTO_EXTEND_INCREMENT) { size_increase = SRV_AUTO_EXTEND_INCREMENT; } } } else { /* We extend single-table tablespaces first one extent at a time, but for bigger tablespaces more. It is not enough to extend always by one extent, because some extents are frag page extents. */ ulint extent_size; /*!< one megabyte, in pages */ if (!zip_size) { extent_size = FSP_EXTENT_SIZE; } else { extent_size = FSP_EXTENT_SIZE * UNIV_PAGE_SIZE / zip_size; } if (size < extent_size) { /* Let us first extend the file to extent_size */ success = fsp_try_extend_data_file_with_pages( space, extent_size - 1, header, mtr); if (!success) { new_size = mtr_read_ulint(header + FSP_SIZE, MLOG_4BYTES, mtr); *actual_increase = new_size - old_size; return(FALSE); } size = extent_size; } if (size < 32 * extent_size) { size_increase = extent_size; } else { /* Below in fsp_fill_free_list() we assume that we add at most FSP_FREE_ADD extents at a time */ size_increase = FSP_FREE_ADD * extent_size; } } if (size_increase == 0) { return(TRUE); } success = fil_extend_space_to_desired_size(&actual_size, space, size + size_increase); /* We ignore any fragments of a full megabyte when storing the size to the space header */ if (!zip_size) { new_size = ut_calc_align_down(actual_size, (1024 * 1024) / UNIV_PAGE_SIZE); } else { new_size = ut_calc_align_down(actual_size, (1024 * 1024) / zip_size); } mlog_write_ulint(header + FSP_SIZE, new_size, MLOG_4BYTES, mtr); *actual_increase = new_size - old_size; return(TRUE); } /**********************************************************************//** Puts new extents to the free list if there are free extents above the free limit. If an extent happens to contain an extent descriptor page, the extent is put to the FSP_FREE_FRAG list with the page marked as used. */ static void fsp_fill_free_list( /*===============*/ ibool init_space, /*!< in: TRUE if this is a single-table tablespace and we are only initing the tablespace's first extent descriptor page and ibuf bitmap page; then we do not allocate more extents */ ulint space, /*!< in: space */ fsp_header_t* header, /*!< in/out: space header */ mtr_t* mtr) /*!< in: mtr */ { ulint limit; ulint size; ulint zip_size; xdes_t* descr; ulint count = 0; ulint frag_n_used; ulint actual_increase; ulint i; mtr_t ibuf_mtr; ut_ad(header && mtr); ut_ad(page_offset(header) == FSP_HEADER_OFFSET); /* Check if we can fill free list from above the free list limit */ size = mtr_read_ulint(header + FSP_SIZE, MLOG_4BYTES, mtr); limit = mtr_read_ulint(header + FSP_FREE_LIMIT, MLOG_4BYTES, mtr); zip_size = dict_table_flags_to_zip_size( mach_read_from_4(FSP_SPACE_FLAGS + header)); ut_a(ut_is_2pow(zip_size)); ut_a(zip_size <= UNIV_PAGE_SIZE); ut_a(!zip_size || zip_size >= PAGE_ZIP_MIN_SIZE); if (space == 0 && srv_auto_extend_last_data_file && size < limit + FSP_EXTENT_SIZE * FSP_FREE_ADD) { /* Try to increase the last data file size */ fsp_try_extend_data_file(&actual_increase, space, header, mtr); size = mtr_read_ulint(header + FSP_SIZE, MLOG_4BYTES, mtr); } if (space != 0 && !init_space && size < limit + FSP_EXTENT_SIZE * FSP_FREE_ADD) { /* Try to increase the .ibd file size */ fsp_try_extend_data_file(&actual_increase, space, header, mtr); size = mtr_read_ulint(header + FSP_SIZE, MLOG_4BYTES, mtr); } i = limit; while ((init_space && i < 1) || ((i + FSP_EXTENT_SIZE <= size) && (count < FSP_FREE_ADD))) { ibool init_xdes; if (zip_size) { init_xdes = ut_2pow_remainder(i, zip_size) == 0; } else { init_xdes = ut_2pow_remainder(i, UNIV_PAGE_SIZE) == 0; } mlog_write_ulint(header + FSP_FREE_LIMIT, i + FSP_EXTENT_SIZE, MLOG_4BYTES, mtr); /* Update the free limit info in the log system and make a checkpoint */ if (space == 0) { ut_a(!zip_size); log_fsp_current_free_limit_set_and_checkpoint( (i + FSP_EXTENT_SIZE) / ((1024 * 1024) / UNIV_PAGE_SIZE)); } if (UNIV_UNLIKELY(init_xdes)) { buf_block_t* block; /* We are going to initialize a new descriptor page and a new ibuf bitmap page: the prior contents of the pages should be ignored. */ if (i > 0) { block = buf_page_create( space, i, zip_size, mtr); buf_page_get(space, zip_size, i, RW_X_LATCH, mtr); buf_block_dbg_add_level(block, SYNC_FSP_PAGE); fsp_init_file_page(block, mtr); mlog_write_ulint(buf_block_get_frame(block) + FIL_PAGE_TYPE, FIL_PAGE_TYPE_XDES, MLOG_2BYTES, mtr); } /* Initialize the ibuf bitmap page in a separate mini-transaction because it is low in the latching order, and we must be able to release its latch before returning from the fsp routine */ mtr_start(&ibuf_mtr); block = buf_page_create(space, i + FSP_IBUF_BITMAP_OFFSET, zip_size, &ibuf_mtr); buf_page_get(space, zip_size, i + FSP_IBUF_BITMAP_OFFSET, RW_X_LATCH, &ibuf_mtr); buf_block_dbg_add_level(block, SYNC_FSP_PAGE); fsp_init_file_page(block, &ibuf_mtr); ibuf_bitmap_page_init(block, &ibuf_mtr); mtr_commit(&ibuf_mtr); } descr = xdes_get_descriptor_with_space_hdr(header, space, i, mtr); xdes_init(descr, mtr); #if UNIV_PAGE_SIZE % FSP_EXTENT_SIZE # error "UNIV_PAGE_SIZE % FSP_EXTENT_SIZE != 0" #endif #if PAGE_ZIP_MIN_SIZE % FSP_EXTENT_SIZE # error "PAGE_ZIP_MIN_SIZE % FSP_EXTENT_SIZE != 0" #endif if (UNIV_UNLIKELY(init_xdes)) { /* The first page in the extent is a descriptor page and the second is an ibuf bitmap page: mark them used */ xdes_set_bit(descr, XDES_FREE_BIT, 0, FALSE, mtr); xdes_set_bit(descr, XDES_FREE_BIT, FSP_IBUF_BITMAP_OFFSET, FALSE, mtr); xdes_set_state(descr, XDES_FREE_FRAG, mtr); flst_add_last(header + FSP_FREE_FRAG, descr + XDES_FLST_NODE, mtr); frag_n_used = mtr_read_ulint(header + FSP_FRAG_N_USED, MLOG_4BYTES, mtr); mlog_write_ulint(header + FSP_FRAG_N_USED, frag_n_used + 2, MLOG_4BYTES, mtr); } else { flst_add_last(header + FSP_FREE, descr + XDES_FLST_NODE, mtr); count++; } i += FSP_EXTENT_SIZE; } } /**********************************************************************//** Allocates a new free extent. @return extent descriptor, NULL if cannot be allocated */ static xdes_t* fsp_alloc_free_extent( /*==================*/ ulint space, /*!< in: space id */ ulint zip_size,/*!< in: compressed page size in bytes or 0 for uncompressed pages */ ulint hint, /*!< in: hint of which extent would be desirable: any page offset in the extent goes; the hint must not be > FSP_FREE_LIMIT */ mtr_t* mtr) /*!< in: mtr */ { fsp_header_t* header; fil_addr_t first; xdes_t* descr; ut_ad(mtr); header = fsp_get_space_header(space, zip_size, mtr); descr = xdes_get_descriptor_with_space_hdr(header, space, hint, mtr); if (descr && (xdes_get_state(descr, mtr) == XDES_FREE)) { /* Ok, we can take this extent */ } else { /* Take the first extent in the free list */ first = flst_get_first(header + FSP_FREE, mtr); if (fil_addr_is_null(first)) { fsp_fill_free_list(FALSE, space, header, mtr); first = flst_get_first(header + FSP_FREE, mtr); } if (fil_addr_is_null(first)) { return(NULL); /* No free extents left */ } descr = xdes_lst_get_descriptor(space, zip_size, first, mtr); } flst_remove(header + FSP_FREE, descr + XDES_FLST_NODE, mtr); return(descr); } /**********************************************************************//** Allocates a single free page from a space. The page is marked as used. @return the page offset, FIL_NULL if no page could be allocated */ static ulint fsp_alloc_free_page( /*================*/ ulint space, /*!< in: space id */ ulint zip_size,/*!< in: compressed page size in bytes or 0 for uncompressed pages */ ulint hint, /*!< in: hint of which page would be desirable */ mtr_t* mtr) /*!< in: mtr handle */ { fsp_header_t* header; fil_addr_t first; xdes_t* descr; buf_block_t* block; ulint free; ulint frag_n_used; ulint page_no; ulint space_size; ibool success; ut_ad(mtr); header = fsp_get_space_header(space, zip_size, mtr); /* Get the hinted descriptor */ descr = xdes_get_descriptor_with_space_hdr(header, space, hint, mtr); if (descr && (xdes_get_state(descr, mtr) == XDES_FREE_FRAG)) { /* Ok, we can take this extent */ } else { /* Else take the first extent in free_frag list */ first = flst_get_first(header + FSP_FREE_FRAG, mtr); if (fil_addr_is_null(first)) { /* There are no partially full fragments: allocate a free extent and add it to the FREE_FRAG list. NOTE that the allocation may have as a side-effect that an extent containing a descriptor page is added to the FREE_FRAG list. But we will allocate our page from the the free extent anyway. */ descr = fsp_alloc_free_extent(space, zip_size, hint, mtr); if (descr == NULL) { /* No free space left */ return(FIL_NULL); } xdes_set_state(descr, XDES_FREE_FRAG, mtr); flst_add_last(header + FSP_FREE_FRAG, descr + XDES_FLST_NODE, mtr); } else { descr = xdes_lst_get_descriptor(space, zip_size, first, mtr); } /* Reset the hint */ hint = 0; } /* Now we have in descr an extent with at least one free page. Look for a free page in the extent. */ free = xdes_find_bit(descr, XDES_FREE_BIT, TRUE, hint % FSP_EXTENT_SIZE, mtr); if (free == ULINT_UNDEFINED) { ut_print_buf(ib_stream, ((byte*)descr) - 500, 1000); ib_logger(ib_stream, "\n"); ut_error; } page_no = xdes_get_offset(descr) + free; space_size = mtr_read_ulint(header + FSP_SIZE, MLOG_4BYTES, mtr); if (space_size <= page_no) { /* It must be that we are extending a single-table tablespace whose size is still < 64 pages */ ut_a(space != 0); if (page_no >= FSP_EXTENT_SIZE) { ib_logger(ib_stream, "InnoDB: Error: trying to extend a" " single-table tablespace %lu\n" "InnoDB: by single page(s) though the" " space size %lu. Page no %lu.\n", (ulong) space, (ulong) space_size, (ulong) page_no); return(FIL_NULL); } success = fsp_try_extend_data_file_with_pages(space, page_no, header, mtr); if (!success) { /* No disk space left */ return(FIL_NULL); } } xdes_set_bit(descr, XDES_FREE_BIT, free, FALSE, mtr); /* Update the FRAG_N_USED field */ frag_n_used = mtr_read_ulint(header + FSP_FRAG_N_USED, MLOG_4BYTES, mtr); frag_n_used++; mlog_write_ulint(header + FSP_FRAG_N_USED, frag_n_used, MLOG_4BYTES, mtr); if (xdes_is_full(descr, mtr)) { /* The fragment is full: move it to another list */ flst_remove(header + FSP_FREE_FRAG, descr + XDES_FLST_NODE, mtr); xdes_set_state(descr, XDES_FULL_FRAG, mtr); flst_add_last(header + FSP_FULL_FRAG, descr + XDES_FLST_NODE, mtr); mlog_write_ulint(header + FSP_FRAG_N_USED, frag_n_used - FSP_EXTENT_SIZE, MLOG_4BYTES, mtr); } /* Initialize the allocated page to the buffer pool, so that it can be obtained immediately with buf_page_get without need for a disk read. */ buf_page_create(space, page_no, zip_size, mtr); block = buf_page_get(space, zip_size, page_no, RW_X_LATCH, mtr); buf_block_dbg_add_level(block, SYNC_FSP_PAGE); /* Prior contents of the page should be ignored */ fsp_init_file_page(block, mtr); return(page_no); } /**********************************************************************//** Frees a single page of a space. The page is marked as free and clean. */ static void fsp_free_page( /*==========*/ ulint space, /*!< in: space id */ ulint zip_size,/*!< in: compressed page size in bytes or 0 for uncompressed pages */ ulint page, /*!< in: page offset */ mtr_t* mtr) /*!< in: mtr handle */ { fsp_header_t* header; xdes_t* descr; ulint state; ulint frag_n_used; ut_ad(mtr); /* ib_logger(ib_stream, "Freeing page %lu in space %lu\n", page, space); */ header = fsp_get_space_header(space, zip_size, mtr); descr = xdes_get_descriptor_with_space_hdr(header, space, page, mtr); state = xdes_get_state(descr, mtr); if (state != XDES_FREE_FRAG && state != XDES_FULL_FRAG) { ib_logger(ib_stream, "InnoDB: Error: File space extent descriptor" " of page %lu has state %lu\n", (ulong) page, (ulong) state); ib_logger(ib_stream, "InnoDB: Dump of descriptor: "); ut_print_buf(ib_stream, ((byte*)descr) - 50, 200); ib_logger(ib_stream, "\n"); if (state == XDES_FREE) { /* We put here some fault tolerance: if the page is already free, return without doing anything! */ return; } ut_error; } if (xdes_get_bit(descr, XDES_FREE_BIT, page % FSP_EXTENT_SIZE, mtr)) { ib_logger(ib_stream, "InnoDB: Error: File space extent descriptor" " of page %lu says it is free\n" "InnoDB: Dump of descriptor: ", (ulong) page); ut_print_buf(ib_stream, ((byte*)descr) - 50, 200); ib_logger(ib_stream, "\n"); /* We put here some fault tolerance: if the page is already free, return without doing anything! */ return; } xdes_set_bit(descr, XDES_FREE_BIT, page % FSP_EXTENT_SIZE, TRUE, mtr); xdes_set_bit(descr, XDES_CLEAN_BIT, page % FSP_EXTENT_SIZE, TRUE, mtr); frag_n_used = mtr_read_ulint(header + FSP_FRAG_N_USED, MLOG_4BYTES, mtr); if (state == XDES_FULL_FRAG) { /* The fragment was full: move it to another list */ flst_remove(header + FSP_FULL_FRAG, descr + XDES_FLST_NODE, mtr); xdes_set_state(descr, XDES_FREE_FRAG, mtr); flst_add_last(header + FSP_FREE_FRAG, descr + XDES_FLST_NODE, mtr); mlog_write_ulint(header + FSP_FRAG_N_USED, frag_n_used + FSP_EXTENT_SIZE - 1, MLOG_4BYTES, mtr); } else { ut_a(frag_n_used > 0); mlog_write_ulint(header + FSP_FRAG_N_USED, frag_n_used - 1, MLOG_4BYTES, mtr); } if (xdes_is_free(descr, mtr)) { /* The extent has become free: move it to another list */ flst_remove(header + FSP_FREE_FRAG, descr + XDES_FLST_NODE, mtr); fsp_free_extent(space, zip_size, page, mtr); } } /**********************************************************************//** Returns an extent to the free list of a space. */ static void fsp_free_extent( /*============*/ ulint space, /*!< in: space id */ ulint zip_size,/*!< in: compressed page size in bytes or 0 for uncompressed pages */ ulint page, /*!< in: page offset in the extent */ mtr_t* mtr) /*!< in: mtr */ { fsp_header_t* header; xdes_t* descr; ut_ad(mtr); header = fsp_get_space_header(space, zip_size, mtr); descr = xdes_get_descriptor_with_space_hdr(header, space, page, mtr); if (xdes_get_state(descr, mtr) == XDES_FREE) { ut_print_buf(ib_stream, (byte*)descr - 500, 1000); ib_logger(ib_stream, "\n"); ut_error; } xdes_init(descr, mtr); flst_add_last(header + FSP_FREE, descr + XDES_FLST_NODE, mtr); } /**********************************************************************//** Returns the nth inode slot on an inode page. @return segment inode */ UNIV_INLINE fseg_inode_t* fsp_seg_inode_page_get_nth_inode( /*=============================*/ page_t* page, /*!< in: segment inode page */ ulint i, /*!< in: inode index on page */ ulint zip_size __attribute__((unused)), /*!< in: compressed page size, or 0 */ mtr_t* mtr __attribute__((unused))) /*!< in: mini-transaction handle */ { ut_ad(i < FSP_SEG_INODES_PER_PAGE(zip_size)); ut_ad(mtr_memo_contains_page(mtr, page, MTR_MEMO_PAGE_X_FIX)); return(page + FSEG_ARR_OFFSET + FSEG_INODE_SIZE * i); } /**********************************************************************//** Looks for a used segment inode on a segment inode page. @return segment inode index, or ULINT_UNDEFINED if not found */ static ulint fsp_seg_inode_page_find_used( /*=========================*/ page_t* page, /*!< in: segment inode page */ ulint zip_size,/*!< in: compressed page size, or 0 */ mtr_t* mtr) /*!< in: mini-transaction handle */ { ulint i; fseg_inode_t* inode; for (i = 0; i < FSP_SEG_INODES_PER_PAGE(zip_size); i++) { inode = fsp_seg_inode_page_get_nth_inode( page, i, zip_size, mtr); if (!ut_dulint_is_zero(mach_read_from_8(inode + FSEG_ID))) { /* This is used */ ut_ad(mach_read_from_4(inode + FSEG_MAGIC_N) == FSEG_MAGIC_N_VALUE); return(i); } } return(ULINT_UNDEFINED); } /**********************************************************************//** Looks for an unused segment inode on a segment inode page. @return segment inode index, or ULINT_UNDEFINED if not found */ static ulint fsp_seg_inode_page_find_free( /*=========================*/ page_t* page, /*!< in: segment inode page */ ulint i, /*!< in: search forward starting from this index */ ulint zip_size,/*!< in: compressed page size, or 0 */ mtr_t* mtr) /*!< in: mini-transaction handle */ { fseg_inode_t* inode; for (; i < FSP_SEG_INODES_PER_PAGE(zip_size); i++) { inode = fsp_seg_inode_page_get_nth_inode( page, i, zip_size, mtr); if (ut_dulint_is_zero(mach_read_from_8(inode + FSEG_ID))) { /* This is unused */ return(i); } ut_ad(mach_read_from_4(inode + FSEG_MAGIC_N) == FSEG_MAGIC_N_VALUE); } return(ULINT_UNDEFINED); } /**********************************************************************//** Allocates a new file segment inode page. @return TRUE if could be allocated */ static ibool fsp_alloc_seg_inode_page( /*=====================*/ fsp_header_t* space_header, /*!< in: space header */ mtr_t* mtr) /*!< in: mini-transaction handle */ { fseg_inode_t* inode; buf_block_t* block; page_t* page; ulint page_no; ulint space; ulint zip_size; ulint i; ut_ad(page_offset(space_header) == FSP_HEADER_OFFSET); space = page_get_space_id(page_align(space_header)); zip_size = dict_table_flags_to_zip_size( mach_read_from_4(FSP_SPACE_FLAGS + space_header)); page_no = fsp_alloc_free_page(space, zip_size, 0, mtr); if (page_no == FIL_NULL) { return(FALSE); } block = buf_page_get(space, zip_size, page_no, RW_X_LATCH, mtr); buf_block_dbg_add_level(block, SYNC_FSP_PAGE); block->check_index_page_at_flush = FALSE; page = buf_block_get_frame(block); mlog_write_ulint(page + FIL_PAGE_TYPE, FIL_PAGE_INODE, MLOG_2BYTES, mtr); for (i = 0; i < FSP_SEG_INODES_PER_PAGE(zip_size); i++) { inode = fsp_seg_inode_page_get_nth_inode(page, i, zip_size, mtr); mlog_write_dulint(inode + FSEG_ID, ut_dulint_zero, mtr); } flst_add_last(space_header + FSP_SEG_INODES_FREE, page + FSEG_INODE_PAGE_NODE, mtr); return(TRUE); } /**********************************************************************//** Allocates a new file segment inode. @return segment inode, or NULL if not enough space */ static fseg_inode_t* fsp_alloc_seg_inode( /*================*/ fsp_header_t* space_header, /*!< in: space header */ mtr_t* mtr) /*!< in: mini-transaction handle */ { ulint page_no; buf_block_t* block; page_t* page; fseg_inode_t* inode; ibool success; ulint zip_size; ulint n; ut_ad(page_offset(space_header) == FSP_HEADER_OFFSET); if (flst_get_len(space_header + FSP_SEG_INODES_FREE, mtr) == 0) { /* Allocate a new segment inode page */ success = fsp_alloc_seg_inode_page(space_header, mtr); if (!success) { return(NULL); } } page_no = flst_get_first(space_header + FSP_SEG_INODES_FREE, mtr).page; zip_size = dict_table_flags_to_zip_size( mach_read_from_4(FSP_SPACE_FLAGS + space_header)); block = buf_page_get(page_get_space_id(page_align(space_header)), zip_size, page_no, RW_X_LATCH, mtr); buf_block_dbg_add_level(block, SYNC_FSP_PAGE); page = buf_block_get_frame(block); n = fsp_seg_inode_page_find_free(page, 0, zip_size, mtr); ut_a(n != ULINT_UNDEFINED); inode = fsp_seg_inode_page_get_nth_inode(page, n, zip_size, mtr); if (ULINT_UNDEFINED == fsp_seg_inode_page_find_free(page, n + 1, zip_size, mtr)) { /* There are no other unused headers left on the page: move it to another list */ flst_remove(space_header + FSP_SEG_INODES_FREE, page + FSEG_INODE_PAGE_NODE, mtr); flst_add_last(space_header + FSP_SEG_INODES_FULL, page + FSEG_INODE_PAGE_NODE, mtr); } ut_ad(ut_dulint_is_zero(mach_read_from_8(inode + FSEG_ID)) || mach_read_from_4(inode + FSEG_MAGIC_N) == FSEG_MAGIC_N_VALUE); return(inode); } /**********************************************************************//** Frees a file segment inode. */ static void fsp_free_seg_inode( /*===============*/ ulint space, /*!< in: space id */ ulint zip_size,/*!< in: compressed page size in bytes or 0 for uncompressed pages */ fseg_inode_t* inode, /*!< in: segment inode */ mtr_t* mtr) /*!< in: mini-transaction handle */ { page_t* page; fsp_header_t* space_header; page = page_align(inode); space_header = fsp_get_space_header(space, zip_size, mtr); ut_ad(mach_read_from_4(inode + FSEG_MAGIC_N) == FSEG_MAGIC_N_VALUE); if (ULINT_UNDEFINED == fsp_seg_inode_page_find_free(page, 0, zip_size, mtr)) { /* Move the page to another list */ flst_remove(space_header + FSP_SEG_INODES_FULL, page + FSEG_INODE_PAGE_NODE, mtr); flst_add_last(space_header + FSP_SEG_INODES_FREE, page + FSEG_INODE_PAGE_NODE, mtr); } mlog_write_dulint(inode + FSEG_ID, ut_dulint_zero, mtr); mlog_write_ulint(inode + FSEG_MAGIC_N, 0xfa051ce3, MLOG_4BYTES, mtr); if (ULINT_UNDEFINED == fsp_seg_inode_page_find_used(page, zip_size, mtr)) { /* There are no other used headers left on the page: free it */ flst_remove(space_header + FSP_SEG_INODES_FREE, page + FSEG_INODE_PAGE_NODE, mtr); fsp_free_page(space, zip_size, page_get_page_no(page), mtr); } } /**********************************************************************//** Returns the file segment inode, page x-latched. @return segment inode, page x-latched; NULL if the inode is free */ static fseg_inode_t* fseg_inode_try_get( /*===============*/ fseg_header_t* header, /*!< in: segment header */ ulint space, /*!< in: space id */ ulint zip_size,/*!< in: compressed page size in bytes or 0 for uncompressed pages */ mtr_t* mtr) /*!< in: mtr handle */ { fil_addr_t inode_addr; fseg_inode_t* inode; inode_addr.page = mach_read_from_4(header + FSEG_HDR_PAGE_NO); inode_addr.boffset = mach_read_from_2(header + FSEG_HDR_OFFSET); ut_ad(space == mach_read_from_4(header + FSEG_HDR_SPACE)); inode = fut_get_ptr(space, zip_size, inode_addr, RW_X_LATCH, mtr); if (UNIV_UNLIKELY (ut_dulint_is_zero(mach_read_from_8(inode + FSEG_ID)))) { inode = NULL; } else { ut_ad(mach_read_from_4(inode + FSEG_MAGIC_N) == FSEG_MAGIC_N_VALUE); } return(inode); } /**********************************************************************//** Returns the file segment inode, page x-latched. @return segment inode, page x-latched */ static fseg_inode_t* fseg_inode_get( /*===========*/ fseg_header_t* header, /*!< in: segment header */ ulint space, /*!< in: space id */ ulint zip_size,/*!< in: compressed page size in bytes or 0 for uncompressed pages */ mtr_t* mtr) /*!< in: mtr handle */ { fseg_inode_t* inode = fseg_inode_try_get(header, space, zip_size, mtr); ut_a(inode); return(inode); } /**********************************************************************//** Gets the page number from the nth fragment page slot. @return page number, FIL_NULL if not in use */ UNIV_INLINE ulint fseg_get_nth_frag_page_no( /*======================*/ fseg_inode_t* inode, /*!< in: segment inode */ ulint n, /*!< in: slot index */ mtr_t* mtr __attribute__((unused))) /*!< in: mtr handle */ { ut_ad(inode && mtr); ut_ad(n < FSEG_FRAG_ARR_N_SLOTS); ut_ad(mtr_memo_contains_page(mtr, inode, MTR_MEMO_PAGE_X_FIX)); ut_ad(mach_read_from_4(inode + FSEG_MAGIC_N) == FSEG_MAGIC_N_VALUE); return(mach_read_from_4(inode + FSEG_FRAG_ARR + n * FSEG_FRAG_SLOT_SIZE)); } /**********************************************************************//** Sets the page number in the nth fragment page slot. */ UNIV_INLINE void fseg_set_nth_frag_page_no( /*======================*/ fseg_inode_t* inode, /*!< in: segment inode */ ulint n, /*!< in: slot index */ ulint page_no,/*!< in: page number to set */ mtr_t* mtr) /*!< in: mtr handle */ { ut_ad(inode && mtr); ut_ad(n < FSEG_FRAG_ARR_N_SLOTS); ut_ad(mtr_memo_contains_page(mtr, inode, MTR_MEMO_PAGE_X_FIX)); ut_ad(mach_read_from_4(inode + FSEG_MAGIC_N) == FSEG_MAGIC_N_VALUE); mlog_write_ulint(inode + FSEG_FRAG_ARR + n * FSEG_FRAG_SLOT_SIZE, page_no, MLOG_4BYTES, mtr); } /**********************************************************************//** Finds a fragment page slot which is free. @return slot index; ULINT_UNDEFINED if none found */ static ulint fseg_find_free_frag_page_slot( /*==========================*/ fseg_inode_t* inode, /*!< in: segment inode */ mtr_t* mtr) /*!< in: mtr handle */ { ulint i; ulint page_no; ut_ad(inode && mtr); for (i = 0; i < FSEG_FRAG_ARR_N_SLOTS; i++) { page_no = fseg_get_nth_frag_page_no(inode, i, mtr); if (page_no == FIL_NULL) { return(i); } } return(ULINT_UNDEFINED); } /**********************************************************************//** Finds a fragment page slot which is used and last in the array. @return slot index; ULINT_UNDEFINED if none found */ static ulint fseg_find_last_used_frag_page_slot( /*===============================*/ fseg_inode_t* inode, /*!< in: segment inode */ mtr_t* mtr) /*!< in: mtr handle */ { ulint i; ulint page_no; ut_ad(inode && mtr); for (i = 0; i < FSEG_FRAG_ARR_N_SLOTS; i++) { page_no = fseg_get_nth_frag_page_no( inode, FSEG_FRAG_ARR_N_SLOTS - i - 1, mtr); if (page_no != FIL_NULL) { return(FSEG_FRAG_ARR_N_SLOTS - i - 1); } } return(ULINT_UNDEFINED); } /**********************************************************************//** Calculates reserved fragment page slots. @return number of fragment pages */ static ulint fseg_get_n_frag_pages( /*==================*/ fseg_inode_t* inode, /*!< in: segment inode */ mtr_t* mtr) /*!< in: mtr handle */ { ulint i; ulint count = 0; ut_ad(inode && mtr); for (i = 0; i < FSEG_FRAG_ARR_N_SLOTS; i++) { if (FIL_NULL != fseg_get_nth_frag_page_no(inode, i, mtr)) { count++; } } return(count); } /**********************************************************************//** Creates a new segment. @return the block where the segment header is placed, x-latched, NULL if could not create segment because of lack of space */ UNIV_INTERN buf_block_t* fseg_create_general( /*================*/ ulint space, /*!< in: space id */ ulint page, /*!< in: page where the segment header is placed: if this is != 0, the page must belong to another segment, if this is 0, a new page will be allocated and it will belong to the created segment */ ulint byte_offset, /*!< in: byte offset of the created segment header on the page */ ibool has_done_reservation, /*!< in: TRUE if the caller has already done the reservation for the pages with fsp_reserve_free_extents (at least 2 extents: one for the inode and the other for the segment) then there is no need to do the check for this individual operation */ mtr_t* mtr) /*!< in: mtr */ { ulint flags; ulint zip_size; fsp_header_t* space_header; fseg_inode_t* inode; dulint seg_id; buf_block_t* block = 0; /* remove warning */ fseg_header_t* header = 0; /* remove warning */ rw_lock_t* latch; ibool success; ulint n_reserved; ulint i; ut_ad(mtr); ut_ad(byte_offset + FSEG_HEADER_SIZE <= UNIV_PAGE_SIZE - FIL_PAGE_DATA_END); latch = fil_space_get_latch(space, &flags); zip_size = dict_table_flags_to_zip_size(flags); if (page != 0) { block = buf_page_get(space, zip_size, page, RW_X_LATCH, mtr); header = byte_offset + buf_block_get_frame(block); } ut_ad(!mutex_own(&kernel_mutex) || mtr_memo_contains(mtr, latch, MTR_MEMO_X_LOCK)); mtr_x_lock(latch, mtr); if (rw_lock_get_x_lock_count(latch) == 1) { /* This thread did not own the latch before this call: free excess pages from the insert buffer free list */ if (space == IBUF_SPACE_ID) { ibuf_free_excess_pages(); } } if (!has_done_reservation) { success = fsp_reserve_free_extents(&n_reserved, space, 2, FSP_NORMAL, mtr); if (!success) { return(NULL); } } space_header = fsp_get_space_header(space, zip_size, mtr); inode = fsp_alloc_seg_inode(space_header, mtr); if (inode == NULL) { goto funct_exit; } /* Read the next segment id from space header and increment the value in space header */ seg_id = mtr_read_dulint(space_header + FSP_SEG_ID, mtr); mlog_write_dulint(space_header + FSP_SEG_ID, ut_dulint_add(seg_id, 1), mtr); mlog_write_dulint(inode + FSEG_ID, seg_id, mtr); mlog_write_ulint(inode + FSEG_NOT_FULL_N_USED, 0, MLOG_4BYTES, mtr); flst_init(inode + FSEG_FREE, mtr); flst_init(inode + FSEG_NOT_FULL, mtr); flst_init(inode + FSEG_FULL, mtr); mlog_write_ulint(inode + FSEG_MAGIC_N, FSEG_MAGIC_N_VALUE, MLOG_4BYTES, mtr); for (i = 0; i < FSEG_FRAG_ARR_N_SLOTS; i++) { fseg_set_nth_frag_page_no(inode, i, FIL_NULL, mtr); } if (page == 0) { page = fseg_alloc_free_page_low(space, zip_size, inode, 0, FSP_UP, mtr); if (page == FIL_NULL) { fsp_free_seg_inode(space, zip_size, inode, mtr); goto funct_exit; } block = buf_page_get(space, zip_size, page, RW_X_LATCH, mtr); header = byte_offset + buf_block_get_frame(block); mlog_write_ulint(header - byte_offset + FIL_PAGE_TYPE, FIL_PAGE_TYPE_SYS, MLOG_2BYTES, mtr); } mlog_write_ulint(header + FSEG_HDR_OFFSET, page_offset(inode), MLOG_2BYTES, mtr); mlog_write_ulint(header + FSEG_HDR_PAGE_NO, page_get_page_no(page_align(inode)), MLOG_4BYTES, mtr); mlog_write_ulint(header + FSEG_HDR_SPACE, space, MLOG_4BYTES, mtr); funct_exit: if (!has_done_reservation) { fil_space_release_free_extents(space, n_reserved); } return(block); } /**********************************************************************//** Creates a new segment. @return the block where the segment header is placed, x-latched, NULL if could not create segment because of lack of space */ UNIV_INTERN buf_block_t* fseg_create( /*========*/ ulint space, /*!< in: space id */ ulint page, /*!< in: page where the segment header is placed: if this is != 0, the page must belong to another segment, if this is 0, a new page will be allocated and it will belong to the created segment */ ulint byte_offset, /*!< in: byte offset of the created segment header on the page */ mtr_t* mtr) /*!< in: mtr */ { return(fseg_create_general(space, page, byte_offset, FALSE, mtr)); } /**********************************************************************//** Calculates the number of pages reserved by a segment, and how many pages are currently used. @return number of reserved pages */ static ulint fseg_n_reserved_pages_low( /*======================*/ fseg_inode_t* inode, /*!< in: segment inode */ ulint* used, /*!< out: number of pages used (not more than reserved) */ mtr_t* mtr) /*!< in: mtr handle */ { ulint ret; ut_ad(inode && used && mtr); ut_ad(mtr_memo_contains_page(mtr, inode, MTR_MEMO_PAGE_X_FIX)); *used = mtr_read_ulint(inode + FSEG_NOT_FULL_N_USED, MLOG_4BYTES, mtr) + FSP_EXTENT_SIZE * flst_get_len(inode + FSEG_FULL, mtr) + fseg_get_n_frag_pages(inode, mtr); ret = fseg_get_n_frag_pages(inode, mtr) + FSP_EXTENT_SIZE * flst_get_len(inode + FSEG_FREE, mtr) + FSP_EXTENT_SIZE * flst_get_len(inode + FSEG_NOT_FULL, mtr) + FSP_EXTENT_SIZE * flst_get_len(inode + FSEG_FULL, mtr); return(ret); } /**********************************************************************//** Calculates the number of pages reserved by a segment, and how many pages are currently used. @return number of reserved pages */ UNIV_INTERN ulint fseg_n_reserved_pages( /*==================*/ fseg_header_t* header, /*!< in: segment header */ ulint* used, /*!< out: number of pages used (<= reserved) */ mtr_t* mtr) /*!< in: mtr handle */ { ulint ret; fseg_inode_t* inode; ulint space; ulint flags; ulint zip_size; rw_lock_t* latch; space = page_get_space_id(page_align(header)); latch = fil_space_get_latch(space, &flags); zip_size = dict_table_flags_to_zip_size(flags); ut_ad(!mutex_own(&kernel_mutex) || mtr_memo_contains(mtr, latch, MTR_MEMO_X_LOCK)); mtr_x_lock(latch, mtr); inode = fseg_inode_get(header, space, zip_size, mtr); ret = fseg_n_reserved_pages_low(inode, used, mtr); return(ret); } /*********************************************************************//** Tries to fill the free list of a segment with consecutive free extents. This happens if the segment is big enough to allow extents in the free list, the free list is empty, and the extents can be allocated consecutively from the hint onward. */ static void fseg_fill_free_list( /*================*/ fseg_inode_t* inode, /*!< in: segment inode */ ulint space, /*!< in: space id */ ulint zip_size,/*!< in: compressed page size in bytes or 0 for uncompressed pages */ ulint hint, /*!< in: hint which extent would be good as the first extent */ mtr_t* mtr) /*!< in: mtr */ { xdes_t* descr; ulint i; dulint seg_id; ulint reserved; ulint used; ut_ad(inode && mtr); ut_ad(!((page_offset(inode) - FSEG_ARR_OFFSET) % FSEG_INODE_SIZE)); reserved = fseg_n_reserved_pages_low(inode, &used, mtr); if (reserved < FSEG_FREE_LIST_LIMIT * FSP_EXTENT_SIZE) { /* The segment is too small to allow extents in free list */ return; } if (flst_get_len(inode + FSEG_FREE, mtr) > 0) { /* Free list is not empty */ return; } for (i = 0; i < FSEG_FREE_LIST_MAX_LEN; i++) { descr = xdes_get_descriptor(space, zip_size, hint, mtr); if ((descr == NULL) || (XDES_FREE != xdes_get_state(descr, mtr))) { /* We cannot allocate the desired extent: stop */ return; } descr = fsp_alloc_free_extent(space, zip_size, hint, mtr); xdes_set_state(descr, XDES_FSEG, mtr); seg_id = mtr_read_dulint(inode + FSEG_ID, mtr); ut_ad(mach_read_from_4(inode + FSEG_MAGIC_N) == FSEG_MAGIC_N_VALUE); mlog_write_dulint(descr + XDES_ID, seg_id, mtr); flst_add_last(inode + FSEG_FREE, descr + XDES_FLST_NODE, mtr); hint += FSP_EXTENT_SIZE; } } /*********************************************************************//** Allocates a free extent for the segment: looks first in the free list of the segment, then tries to allocate from the space free list. NOTE that the extent returned still resides in the segment free list, it is not yet taken off it! @return allocated extent, still placed in the segment free list, NULL if could not be allocated */ static xdes_t* fseg_alloc_free_extent( /*===================*/ fseg_inode_t* inode, /*!< in: segment inode */ ulint space, /*!< in: space id */ ulint zip_size,/*!< in: compressed page size in bytes or 0 for uncompressed pages */ mtr_t* mtr) /*!< in: mtr */ { xdes_t* descr; dulint seg_id; fil_addr_t first; ut_ad(!((page_offset(inode) - FSEG_ARR_OFFSET) % FSEG_INODE_SIZE)); ut_ad(mach_read_from_4(inode + FSEG_MAGIC_N) == FSEG_MAGIC_N_VALUE); if (flst_get_len(inode + FSEG_FREE, mtr) > 0) { /* Segment free list is not empty, allocate from it */ first = flst_get_first(inode + FSEG_FREE, mtr); descr = xdes_lst_get_descriptor(space, zip_size, first, mtr); } else { /* Segment free list was empty, allocate from space */ descr = fsp_alloc_free_extent(space, zip_size, 0, mtr); if (descr == NULL) { return(NULL); } seg_id = mtr_read_dulint(inode + FSEG_ID, mtr); xdes_set_state(descr, XDES_FSEG, mtr); mlog_write_dulint(descr + XDES_ID, seg_id, mtr); flst_add_last(inode + FSEG_FREE, descr + XDES_FLST_NODE, mtr); /* Try to fill the segment free list */ fseg_fill_free_list(inode, space, zip_size, xdes_get_offset(descr) + FSP_EXTENT_SIZE, mtr); } return(descr); } /**********************************************************************//** Allocates a single free page from a segment. This function implements the intelligent allocation strategy which tries to minimize file space fragmentation. @return the allocated page number, FIL_NULL if no page could be allocated */ static ulint fseg_alloc_free_page_low( /*=====================*/ ulint space, /*!< in: space */ ulint zip_size,/*!< in: compressed page size in bytes or 0 for uncompressed pages */ fseg_inode_t* seg_inode, /*!< in: segment inode */ ulint hint, /*!< in: hint of which page would be desirable */ byte direction, /*!< in: if the new page is needed because of an index page split, and records are inserted there in order, into which direction they go alphabetically: FSP_DOWN, FSP_UP, FSP_NO_DIR */ mtr_t* mtr) /*!< in: mtr handle */ { fsp_header_t* space_header; ulint space_size; dulint seg_id; ulint used; ulint reserved; xdes_t* descr; /*!< extent of the hinted page */ ulint ret_page; /*!< the allocated page offset, FIL_NULL if could not be allocated */ xdes_t* ret_descr; /*!< the extent of the allocated page */ ibool frag_page_allocated = FALSE; ibool success; ulint n; ut_ad(mtr); ut_ad((direction >= FSP_UP) && (direction <= FSP_NO_DIR)); ut_ad(mach_read_from_4(seg_inode + FSEG_MAGIC_N) == FSEG_MAGIC_N_VALUE); ut_ad(!((page_offset(seg_inode) - FSEG_ARR_OFFSET) % FSEG_INODE_SIZE)); seg_id = mtr_read_dulint(seg_inode + FSEG_ID, mtr); ut_ad(!ut_dulint_is_zero(seg_id)); reserved = fseg_n_reserved_pages_low(seg_inode, &used, mtr); space_header = fsp_get_space_header(space, zip_size, mtr); descr = xdes_get_descriptor_with_space_hdr(space_header, space, hint, mtr); if (descr == NULL) { /* Hint outside space or too high above free limit: reset hint */ hint = 0; descr = xdes_get_descriptor(space, zip_size, hint, mtr); } /* In the big if-else below we look for ret_page and ret_descr */ /*-------------------------------------------------------------*/ if ((xdes_get_state(descr, mtr) == XDES_FSEG) && (0 == ut_dulint_cmp(mtr_read_dulint(descr + XDES_ID, mtr), seg_id)) && (xdes_get_bit(descr, XDES_FREE_BIT, hint % FSP_EXTENT_SIZE, mtr) == TRUE)) { /* 1. We can take the hinted page =================================*/ ret_descr = descr; ret_page = hint; /*-----------------------------------------------------------*/ } else if ((xdes_get_state(descr, mtr) == XDES_FREE) && ((reserved - used) < reserved / FSEG_FILLFACTOR) && (used >= FSEG_FRAG_LIMIT)) { /* 2. We allocate the free extent from space and can take ========================================================= the hinted page ===============*/ ret_descr = fsp_alloc_free_extent(space, zip_size, hint, mtr); ut_a(ret_descr == descr); xdes_set_state(ret_descr, XDES_FSEG, mtr); mlog_write_dulint(ret_descr + XDES_ID, seg_id, mtr); flst_add_last(seg_inode + FSEG_FREE, ret_descr + XDES_FLST_NODE, mtr); /* Try to fill the segment free list */ fseg_fill_free_list(seg_inode, space, zip_size, hint + FSP_EXTENT_SIZE, mtr); ret_page = hint; /*-----------------------------------------------------------*/ } else if ((direction != FSP_NO_DIR) && ((reserved - used) < reserved / FSEG_FILLFACTOR) && (used >= FSEG_FRAG_LIMIT) && (!!(ret_descr = fseg_alloc_free_extent(seg_inode, space, zip_size, mtr)))) { /* 3. We take any free extent (which was already assigned above =============================================================== in the if-condition to ret_descr) and take the lowest or ======================================================== highest page in it, depending on the direction ==============================================*/ ret_page = xdes_get_offset(ret_descr); if (direction == FSP_DOWN) { ret_page += FSP_EXTENT_SIZE - 1; } /*-----------------------------------------------------------*/ } else if ((xdes_get_state(descr, mtr) == XDES_FSEG) && (0 == ut_dulint_cmp(mtr_read_dulint(descr + XDES_ID, mtr), seg_id)) && (!xdes_is_full(descr, mtr))) { /* 4. We can take the page from the same extent as the ====================================================== hinted page (and the extent already belongs to the ================================================== segment) ========*/ ret_descr = descr; ret_page = xdes_get_offset(ret_descr) + xdes_find_bit(ret_descr, XDES_FREE_BIT, TRUE, hint % FSP_EXTENT_SIZE, mtr); /*-----------------------------------------------------------*/ } else if (reserved - used > 0) { /* 5. We take any unused page from the segment ==============================================*/ fil_addr_t first; if (flst_get_len(seg_inode + FSEG_NOT_FULL, mtr) > 0) { first = flst_get_first(seg_inode + FSEG_NOT_FULL, mtr); } else if (flst_get_len(seg_inode + FSEG_FREE, mtr) > 0) { first = flst_get_first(seg_inode + FSEG_FREE, mtr); } else { ut_error; return(FIL_NULL); } ret_descr = xdes_lst_get_descriptor(space, zip_size, first, mtr); ret_page = xdes_get_offset(ret_descr) + xdes_find_bit(ret_descr, XDES_FREE_BIT, TRUE, 0, mtr); /*-----------------------------------------------------------*/ } else if (used < FSEG_FRAG_LIMIT) { /* 6. We allocate an individual page from the space ===================================================*/ ret_page = fsp_alloc_free_page(space, zip_size, hint, mtr); ret_descr = NULL; frag_page_allocated = TRUE; if (ret_page != FIL_NULL) { /* Put the page in the fragment page array of the segment */ n = fseg_find_free_frag_page_slot(seg_inode, mtr); ut_a(n != FIL_NULL); fseg_set_nth_frag_page_no(seg_inode, n, ret_page, mtr); } /*-----------------------------------------------------------*/ } else { /* 7. We allocate a new extent and take its first page ======================================================*/ ret_descr = fseg_alloc_free_extent(seg_inode, space, zip_size, mtr); if (ret_descr == NULL) { ret_page = FIL_NULL; } else { ret_page = xdes_get_offset(ret_descr); } } if (ret_page == FIL_NULL) { /* Page could not be allocated */ return(FIL_NULL); } if (space != 0) { space_size = fil_space_get_size(space); if (space_size <= ret_page) { /* It must be that we are extending a single-table tablespace whose size is still < 64 pages */ if (ret_page >= FSP_EXTENT_SIZE) { ib_logger(ib_stream, "InnoDB: Error (2): trying to extend" " a single-table tablespace %lu\n" "InnoDB: by single page(s) though" " the space size %lu. Page no %lu.\n", (ulong) space, (ulong) space_size, (ulong) ret_page); return(FIL_NULL); } success = fsp_try_extend_data_file_with_pages( space, ret_page, space_header, mtr); if (!success) { /* No disk space left */ return(FIL_NULL); } } } if (!frag_page_allocated) { /* Initialize the allocated page to buffer pool, so that it can be obtained immediately with buf_page_get without need for a disk read */ buf_block_t* block; ulint zip_size = dict_table_flags_to_zip_size( mach_read_from_4(FSP_SPACE_FLAGS + space_header)); block = buf_page_create(space, ret_page, zip_size, mtr); buf_block_dbg_add_level(block, SYNC_FSP_PAGE); if (UNIV_UNLIKELY(block != buf_page_get(space, zip_size, ret_page, RW_X_LATCH, mtr))) { ut_error; } /* The prior contents of the page should be ignored */ fsp_init_file_page(block, mtr); /* At this point we know the extent and the page offset. The extent is still in the appropriate list (FSEG_NOT_FULL or FSEG_FREE), and the page is not yet marked as used. */ ut_ad(xdes_get_descriptor(space, zip_size, ret_page, mtr) == ret_descr); ut_ad(xdes_get_bit(ret_descr, XDES_FREE_BIT, ret_page % FSP_EXTENT_SIZE, mtr) == TRUE); fseg_mark_page_used(seg_inode, space, zip_size, ret_page, mtr); } buf_reset_check_index_page_at_flush(space, ret_page); return(ret_page); } /**********************************************************************//** Allocates a single free page from a segment. This function implements the intelligent allocation strategy which tries to minimize file space fragmentation. @return allocated page offset, FIL_NULL if no page could be allocated */ UNIV_INTERN ulint fseg_alloc_free_page_general( /*=========================*/ fseg_header_t* seg_header,/*!< in: segment header */ ulint hint, /*!< in: hint of which page would be desirable */ byte direction,/*!< in: if the new page is needed because of an index page split, and records are inserted there in order, into which direction they go alphabetically: FSP_DOWN, FSP_UP, FSP_NO_DIR */ ibool has_done_reservation, /*!< in: TRUE if the caller has already done the reservation for the page with fsp_reserve_free_extents, then there is no need to do the check for this individual page */ mtr_t* mtr) /*!< in: mtr handle */ { fseg_inode_t* inode; ulint space; ulint flags; ulint zip_size; rw_lock_t* latch; ibool success; ulint page_no; ulint n_reserved; space = page_get_space_id(page_align(seg_header)); latch = fil_space_get_latch(space, &flags); zip_size = dict_table_flags_to_zip_size(flags); ut_ad(!mutex_own(&kernel_mutex) || mtr_memo_contains(mtr, latch, MTR_MEMO_X_LOCK)); mtr_x_lock(latch, mtr); if (rw_lock_get_x_lock_count(latch) == 1) { /* This thread did not own the latch before this call: free excess pages from the insert buffer free list */ if (space == IBUF_SPACE_ID) { ibuf_free_excess_pages(); } } inode = fseg_inode_get(seg_header, space, zip_size, mtr); if (!has_done_reservation) { success = fsp_reserve_free_extents(&n_reserved, space, 2, FSP_NORMAL, mtr); if (!success) { return(FIL_NULL); } } page_no = fseg_alloc_free_page_low(space, zip_size, inode, hint, direction, mtr); if (!has_done_reservation) { fil_space_release_free_extents(space, n_reserved); } return(page_no); } /**********************************************************************//** Allocates a single free page from a segment. This function implements the intelligent allocation strategy which tries to minimize file space fragmentation. @return allocated page offset, FIL_NULL if no page could be allocated */ UNIV_INTERN ulint fseg_alloc_free_page( /*=================*/ fseg_header_t* seg_header,/*!< in: segment header */ ulint hint, /*!< in: hint of which page would be desirable */ byte direction,/*!< in: if the new page is needed because of an index page split, and records are inserted there in order, into which direction they go alphabetically: FSP_DOWN, FSP_UP, FSP_NO_DIR */ mtr_t* mtr) /*!< in: mtr handle */ { return(fseg_alloc_free_page_general(seg_header, hint, direction, FALSE, mtr)); } /**********************************************************************//** Checks that we have at least 2 frag pages free in the first extent of a single-table tablespace, and they are also physically initialized to the data file. That is we have already extended the data file so that those pages are inside the data file. If not, this function extends the tablespace with pages. @return TRUE if there were >= 3 free pages, or we were able to extend */ static ibool fsp_reserve_free_pages( /*===================*/ ulint space, /*!< in: space id, must be != 0 */ fsp_header_t* space_header, /*!< in: header of that space, x-latched */ ulint size, /*!< in: size of the tablespace in pages, must be < FSP_EXTENT_SIZE / 2 */ mtr_t* mtr) /*!< in: mtr */ { xdes_t* descr; ulint n_used; ut_a(space != 0); ut_a(size < FSP_EXTENT_SIZE / 2); descr = xdes_get_descriptor_with_space_hdr(space_header, space, 0, mtr); n_used = xdes_get_n_used(descr, mtr); ut_a(n_used <= size); if (size >= n_used + 2) { return(TRUE); } return(fsp_try_extend_data_file_with_pages(space, n_used + 1, space_header, mtr)); } /**********************************************************************//** Reserves free pages from a tablespace. All mini-transactions which may use several pages from the tablespace should call this function beforehand and reserve enough free extents so that they certainly will be able to do their operation, like a B-tree page split, fully. Reservations must be released with function fil_space_release_free_extents! The alloc_type below has the following meaning: FSP_NORMAL means an operation which will probably result in more space usage, like an insert in a B-tree; FSP_UNDO means allocation to undo logs: if we are deleting rows, then this allocation will in the long run result in less space usage (after a purge); FSP_CLEANING means allocation done in a physical record delete (like in a purge) or other cleaning operation which will result in less space usage in the long run. We prefer the latter two types of allocation: when space is scarce, FSP_NORMAL allocations will not succeed, but the latter two allocations will succeed, if possible. The purpose is to avoid dead end where the database is full but the user cannot free any space because these freeing operations temporarily reserve some space. Single-table tablespaces whose size is < 32 pages are a special case. In this function we would liberally reserve several 64 page extents for every page split or merge in a B-tree. But we do not want to waste disk space if the table only occupies < 32 pages. That is why we apply different rules in that special case, just ensuring that there are 3 free pages available. @return TRUE if we were able to make the reservation */ UNIV_INTERN ibool fsp_reserve_free_extents( /*=====================*/ ulint* n_reserved,/*!< out: number of extents actually reserved; if we return TRUE and the tablespace size is < 64 pages, then this can be 0, otherwise it is n_ext */ ulint space, /*!< in: space id */ ulint n_ext, /*!< in: number of extents to reserve */ ulint alloc_type,/*!< in: FSP_NORMAL, FSP_UNDO, or FSP_CLEANING */ mtr_t* mtr) /*!< in: mtr */ { fsp_header_t* space_header; rw_lock_t* latch; ulint n_free_list_ext; ulint free_limit; ulint size; ulint flags; ulint zip_size; ulint n_free; ulint n_free_up; ulint reserve; ibool success; ulint n_pages_added; ut_ad(mtr); *n_reserved = n_ext; latch = fil_space_get_latch(space, &flags); zip_size = dict_table_flags_to_zip_size(flags); ut_ad(!mutex_own(&kernel_mutex) || mtr_memo_contains(mtr, latch, MTR_MEMO_X_LOCK)); mtr_x_lock(latch, mtr); space_header = fsp_get_space_header(space, zip_size, mtr); try_again: size = mtr_read_ulint(space_header + FSP_SIZE, MLOG_4BYTES, mtr); if (size < FSP_EXTENT_SIZE / 2) { /* Use different rules for small single-table tablespaces */ *n_reserved = 0; return(fsp_reserve_free_pages(space, space_header, size, mtr)); } n_free_list_ext = flst_get_len(space_header + FSP_FREE, mtr); free_limit = mtr_read_ulint(space_header + FSP_FREE_LIMIT, MLOG_4BYTES, mtr); /* Below we play safe when counting free extents above the free limit: some of them will contain extent descriptor pages, and therefore will not be free extents */ n_free_up = (size - free_limit) / FSP_EXTENT_SIZE; if (n_free_up > 0) { n_free_up--; if (!zip_size) { n_free_up -= n_free_up / (UNIV_PAGE_SIZE / FSP_EXTENT_SIZE); } else { n_free_up -= n_free_up / (zip_size / FSP_EXTENT_SIZE); } } n_free = n_free_list_ext + n_free_up; if (alloc_type == FSP_NORMAL) { /* We reserve 1 extent + 0.5 % of the space size to undo logs and 1 extent + 0.5 % to cleaning operations; NOTE: this source code is duplicated in the function below! */ reserve = 2 + ((size / FSP_EXTENT_SIZE) * 2) / 200; if (n_free <= reserve + n_ext) { goto try_to_extend; } } else if (alloc_type == FSP_UNDO) { /* We reserve 0.5 % of the space size to cleaning operations */ reserve = 1 + ((size / FSP_EXTENT_SIZE) * 1) / 200; if (n_free <= reserve + n_ext) { goto try_to_extend; } } else { ut_a(alloc_type == FSP_CLEANING); } success = fil_space_reserve_free_extents(space, n_free, n_ext); if (success) { return(TRUE); } try_to_extend: success = fsp_try_extend_data_file(&n_pages_added, space, space_header, mtr); if (success && n_pages_added > 0) { goto try_again; } return(FALSE); } /**********************************************************************//** This function should be used to get information on how much we still will be able to insert new data to the database without running out the tablespace. Only free extents are taken into account and we also subtract the safety margin required by the above function fsp_reserve_free_extents. @return available space in kB */ UNIV_INTERN ib_uint64_t fsp_get_available_space_in_free_extents( /*====================================*/ ulint space) /*!< in: space id */ { fsp_header_t* space_header; ulint n_free_list_ext; ulint free_limit; ulint size; ulint flags; ulint zip_size; ulint n_free; ulint n_free_up; ulint reserve; rw_lock_t* latch; mtr_t mtr; ut_ad(!mutex_own(&kernel_mutex)); mtr_start(&mtr); latch = fil_space_get_latch(space, &flags); zip_size = dict_table_flags_to_zip_size(flags); mtr_x_lock(latch, &mtr); space_header = fsp_get_space_header(space, zip_size, &mtr); size = mtr_read_ulint(space_header + FSP_SIZE, MLOG_4BYTES, &mtr); n_free_list_ext = flst_get_len(space_header + FSP_FREE, &mtr); free_limit = mtr_read_ulint(space_header + FSP_FREE_LIMIT, MLOG_4BYTES, &mtr); mtr_commit(&mtr); if (size < FSP_EXTENT_SIZE) { ut_a(space != 0); /* This must be a single-table tablespace */ return(0); /* TODO: count free frag pages and return a value based on that */ } /* Below we play safe when counting free extents above the free limit: some of them will contain extent descriptor pages, and therefore will not be free extents */ n_free_up = (size - free_limit) / FSP_EXTENT_SIZE; if (n_free_up > 0) { n_free_up--; if (!zip_size) { n_free_up -= n_free_up / (UNIV_PAGE_SIZE / FSP_EXTENT_SIZE); } else { n_free_up -= n_free_up / (zip_size / FSP_EXTENT_SIZE); } } n_free = n_free_list_ext + n_free_up; /* We reserve 1 extent + 0.5 % of the space size to undo logs and 1 extent + 0.5 % to cleaning operations; NOTE: this source code is duplicated in the function above! */ reserve = 2 + ((size / FSP_EXTENT_SIZE) * 2) / 200; if (reserve > n_free) { return(0); } if (!zip_size) { return((ib_uint64_t) (n_free - reserve) * FSP_EXTENT_SIZE * (UNIV_PAGE_SIZE / 1024)); } else { return((ib_uint64_t) (n_free - reserve) * FSP_EXTENT_SIZE * (zip_size / 1024)); } } /********************************************************************//** Marks a page used. The page must reside within the extents of the given segment. */ static void fseg_mark_page_used( /*================*/ fseg_inode_t* seg_inode,/*!< in: segment inode */ ulint space, /*!< in: space id */ ulint zip_size,/*!< in: compressed page size in bytes or 0 for uncompressed pages */ ulint page, /*!< in: page offset */ mtr_t* mtr) /*!< in: mtr */ { xdes_t* descr; ulint not_full_n_used; ut_ad(seg_inode && mtr); ut_ad(!((page_offset(seg_inode) - FSEG_ARR_OFFSET) % FSEG_INODE_SIZE)); ut_ad(mach_read_from_4(seg_inode + FSEG_MAGIC_N) == FSEG_MAGIC_N_VALUE); descr = xdes_get_descriptor(space, zip_size, page, mtr); ut_ad(mtr_read_ulint(seg_inode + FSEG_ID, MLOG_4BYTES, mtr) == mtr_read_ulint(descr + XDES_ID, MLOG_4BYTES, mtr)); if (xdes_is_free(descr, mtr)) { /* We move the extent from the free list to the NOT_FULL list */ flst_remove(seg_inode + FSEG_FREE, descr + XDES_FLST_NODE, mtr); flst_add_last(seg_inode + FSEG_NOT_FULL, descr + XDES_FLST_NODE, mtr); } ut_ad(xdes_get_bit(descr, XDES_FREE_BIT, page % FSP_EXTENT_SIZE, mtr) == TRUE); /* We mark the page as used */ xdes_set_bit(descr, XDES_FREE_BIT, page % FSP_EXTENT_SIZE, FALSE, mtr); not_full_n_used = mtr_read_ulint(seg_inode + FSEG_NOT_FULL_N_USED, MLOG_4BYTES, mtr); not_full_n_used++; mlog_write_ulint(seg_inode + FSEG_NOT_FULL_N_USED, not_full_n_used, MLOG_4BYTES, mtr); if (xdes_is_full(descr, mtr)) { /* We move the extent from the NOT_FULL list to the FULL list */ flst_remove(seg_inode + FSEG_NOT_FULL, descr + XDES_FLST_NODE, mtr); flst_add_last(seg_inode + FSEG_FULL, descr + XDES_FLST_NODE, mtr); mlog_write_ulint(seg_inode + FSEG_NOT_FULL_N_USED, not_full_n_used - FSP_EXTENT_SIZE, MLOG_4BYTES, mtr); } } /**********************************************************************//** Frees a single page of a segment. */ static void fseg_free_page_low( /*===============*/ fseg_inode_t* seg_inode, /*!< in: segment inode */ ulint space, /*!< in: space id */ ulint zip_size,/*!< in: compressed page size in bytes or 0 for uncompressed pages */ ulint page, /*!< in: page offset */ mtr_t* mtr) /*!< in: mtr handle */ { xdes_t* descr; ulint not_full_n_used; ulint state; dulint descr_id; dulint seg_id; ulint i; ut_ad(seg_inode && mtr); ut_ad(mach_read_from_4(seg_inode + FSEG_MAGIC_N) == FSEG_MAGIC_N_VALUE); ut_ad(!((page_offset(seg_inode) - FSEG_ARR_OFFSET) % FSEG_INODE_SIZE)); /* Drop search system page hash index if the page is found in the pool and is hashed */ btr_search_drop_page_hash_when_freed(space, zip_size, page); descr = xdes_get_descriptor(space, zip_size, page, mtr); ut_a(descr); if (xdes_get_bit(descr, XDES_FREE_BIT, page % FSP_EXTENT_SIZE, mtr)) { ib_logger("InnoDB: Dump of the tablespace extent descriptor: ", ib_stream); ut_print_buf(ib_stream, descr, 40); ib_logger(ib_stream, "\n" "InnoDB: Serious error! InnoDB is trying to" " free page %lu\n" "InnoDB: though it is already marked as free" " in the tablespace!\n" "InnoDB: The tablespace free space info is corrupt.\n" "InnoDB: You may need to dump your" " InnoDB tables and recreate the whole\n" "InnoDB: database!\n", (ulong) page); crash: ib_logger("InnoDB: Please refer to\n" "InnoDB: the InnoDB website for details" "InnoDB: about forcing recovery.\n", ib_stream); ut_error; } state = xdes_get_state(descr, mtr); if (state != XDES_FSEG) { /* The page is in the fragment pages of the segment */ for (i = 0;; i++) { if (fseg_get_nth_frag_page_no(seg_inode, i, mtr) == page) { fseg_set_nth_frag_page_no(seg_inode, i, FIL_NULL, mtr); break; } } fsp_free_page(space, zip_size, page, mtr); return; } /* If we get here, the page is in some extent of the segment */ descr_id = mtr_read_dulint(descr + XDES_ID, mtr); seg_id = mtr_read_dulint(seg_inode + FSEG_ID, mtr); #if 0 ib_logger(ib_stream, "InnoDB: InnoDB is freeing space %lu page %lu,\n" "InnoDB: which belongs to descr seg %lu %lu\n" "InnoDB: segment %lu %lu.\n", (ulong) space, (ulong) page, (ulong) ut_dulint_get_high(descr_id), (ulong) ut_dulint_get_low(descr_id), (ulong) ut_dulint_get_high(seg_id), (ulong) ut_dulint_get_low(seg_id)); #endif /* 0 */ if (0 != ut_dulint_cmp(descr_id, seg_id)) { ib_logger(ib_stream, "InnoDB: Dump of the tablespace extent descriptor: "); ut_print_buf(ib_stream, descr, 40); ib_logger(ib_stream, "\nInnoDB: Dump of the segment inode: "); ut_print_buf(ib_stream, seg_inode, 40); ib_logger(ib_stream, "\n"); ib_logger(ib_stream, "InnoDB: Serious error: InnoDB is trying to" " free space %lu page %lu,\n" "InnoDB: which does not belong to" " segment %lu %lu but belongs\n" "InnoDB: to segment %lu %lu.\n", (ulong) space, (ulong) page, (ulong) ut_dulint_get_high(descr_id), (ulong) ut_dulint_get_low(descr_id), (ulong) ut_dulint_get_high(seg_id), (ulong) ut_dulint_get_low(seg_id)); goto crash; } not_full_n_used = mtr_read_ulint(seg_inode + FSEG_NOT_FULL_N_USED, MLOG_4BYTES, mtr); if (xdes_is_full(descr, mtr)) { /* The fragment is full: move it to another list */ flst_remove(seg_inode + FSEG_FULL, descr + XDES_FLST_NODE, mtr); flst_add_last(seg_inode + FSEG_NOT_FULL, descr + XDES_FLST_NODE, mtr); mlog_write_ulint(seg_inode + FSEG_NOT_FULL_N_USED, not_full_n_used + FSP_EXTENT_SIZE - 1, MLOG_4BYTES, mtr); } else { ut_a(not_full_n_used > 0); mlog_write_ulint(seg_inode + FSEG_NOT_FULL_N_USED, not_full_n_used - 1, MLOG_4BYTES, mtr); } xdes_set_bit(descr, XDES_FREE_BIT, page % FSP_EXTENT_SIZE, TRUE, mtr); xdes_set_bit(descr, XDES_CLEAN_BIT, page % FSP_EXTENT_SIZE, TRUE, mtr); if (xdes_is_free(descr, mtr)) { /* The extent has become free: free it to space */ flst_remove(seg_inode + FSEG_NOT_FULL, descr + XDES_FLST_NODE, mtr); fsp_free_extent(space, zip_size, page, mtr); } } /**********************************************************************//** Frees a single page of a segment. */ UNIV_INTERN void fseg_free_page( /*===========*/ fseg_header_t* seg_header, /*!< in: segment header */ ulint space, /*!< in: space id */ ulint page, /*!< in: page offset */ mtr_t* mtr) /*!< in: mtr handle */ { ulint flags; ulint zip_size; fseg_inode_t* seg_inode; rw_lock_t* latch; latch = fil_space_get_latch(space, &flags); zip_size = dict_table_flags_to_zip_size(flags); ut_ad(!mutex_own(&kernel_mutex) || mtr_memo_contains(mtr, latch, MTR_MEMO_X_LOCK)); mtr_x_lock(latch, mtr); seg_inode = fseg_inode_get(seg_header, space, zip_size, mtr); fseg_free_page_low(seg_inode, space, zip_size, page, mtr); #ifdef UNIV_DEBUG_FILE_ACCESSES buf_page_set_file_page_was_freed(space, page); #endif } /**********************************************************************//** Frees an extent of a segment to the space free list. */ static void fseg_free_extent( /*=============*/ fseg_inode_t* seg_inode, /*!< in: segment inode */ ulint space, /*!< in: space id */ ulint zip_size,/*!< in: compressed page size in bytes or 0 for uncompressed pages */ ulint page, /*!< in: a page in the extent */ mtr_t* mtr) /*!< in: mtr handle */ { ulint first_page_in_extent; xdes_t* descr; ulint not_full_n_used; ulint descr_n_used; ulint i; ut_ad(seg_inode && mtr); descr = xdes_get_descriptor(space, zip_size, page, mtr); ut_a(xdes_get_state(descr, mtr) == XDES_FSEG); ut_a(0 == ut_dulint_cmp(mtr_read_dulint(descr + XDES_ID, mtr), mtr_read_dulint(seg_inode + FSEG_ID, mtr))); ut_ad(mach_read_from_4(seg_inode + FSEG_MAGIC_N) == FSEG_MAGIC_N_VALUE); first_page_in_extent = page - (page % FSP_EXTENT_SIZE); for (i = 0; i < FSP_EXTENT_SIZE; i++) { if (FALSE == xdes_get_bit(descr, XDES_FREE_BIT, i, mtr)) { /* Drop search system page hash index if the page is found in the pool and is hashed */ btr_search_drop_page_hash_when_freed( space, zip_size, first_page_in_extent + i); } } if (xdes_is_full(descr, mtr)) { flst_remove(seg_inode + FSEG_FULL, descr + XDES_FLST_NODE, mtr); } else if (xdes_is_free(descr, mtr)) { flst_remove(seg_inode + FSEG_FREE, descr + XDES_FLST_NODE, mtr); } else { flst_remove(seg_inode + FSEG_NOT_FULL, descr + XDES_FLST_NODE, mtr); not_full_n_used = mtr_read_ulint( seg_inode + FSEG_NOT_FULL_N_USED, MLOG_4BYTES, mtr); descr_n_used = xdes_get_n_used(descr, mtr); ut_a(not_full_n_used >= descr_n_used); mlog_write_ulint(seg_inode + FSEG_NOT_FULL_N_USED, not_full_n_used - descr_n_used, MLOG_4BYTES, mtr); } fsp_free_extent(space, zip_size, page, mtr); #ifdef UNIV_DEBUG_FILE_ACCESSES for (i = 0; i < FSP_EXTENT_SIZE; i++) { buf_page_set_file_page_was_freed(space, first_page_in_extent + i); } #endif } /**********************************************************************//** Frees part of a segment. This function can be used to free a segment by repeatedly calling this function in different mini-transactions. Doing the freeing in a single mini-transaction might result in too big a mini-transaction. @return TRUE if freeing completed */ UNIV_INTERN ibool fseg_free_step( /*===========*/ fseg_header_t* header, /*!< in, own: segment header; NOTE: if the header resides on the first page of the frag list of the segment, this pointer becomes obsolete after the last freeing step */ mtr_t* mtr) /*!< in: mtr */ { ulint n; ulint page; xdes_t* descr; fseg_inode_t* inode; ulint space; ulint flags; ulint zip_size; ulint header_page; rw_lock_t* latch; space = page_get_space_id(page_align(header)); header_page = page_get_page_no(page_align(header)); latch = fil_space_get_latch(space, &flags); zip_size = dict_table_flags_to_zip_size(flags); ut_ad(!mutex_own(&kernel_mutex) || mtr_memo_contains(mtr, latch, MTR_MEMO_X_LOCK)); mtr_x_lock(latch, mtr); descr = xdes_get_descriptor(space, zip_size, header_page, mtr); /* Check that the header resides on a page which has not been freed yet */ ut_a(descr); ut_a(xdes_get_bit(descr, XDES_FREE_BIT, header_page % FSP_EXTENT_SIZE, mtr) == FALSE); inode = fseg_inode_try_get(header, space, zip_size, mtr); if (UNIV_UNLIKELY(inode == NULL)) { ib_logger(ib_stream, "double free of inode from %u:%u\n", (unsigned) space, (unsigned) header_page); return(TRUE); } descr = fseg_get_first_extent(inode, space, zip_size, mtr); if (descr != NULL) { /* Free the extent held by the segment */ page = xdes_get_offset(descr); fseg_free_extent(inode, space, zip_size, page, mtr); return(FALSE); } /* Free a frag page */ n = fseg_find_last_used_frag_page_slot(inode, mtr); if (n == ULINT_UNDEFINED) { /* Freeing completed: free the segment inode */ fsp_free_seg_inode(space, zip_size, inode, mtr); return(TRUE); } fseg_free_page_low(inode, space, zip_size, fseg_get_nth_frag_page_no(inode, n, mtr), mtr); n = fseg_find_last_used_frag_page_slot(inode, mtr); if (n == ULINT_UNDEFINED) { /* Freeing completed: free the segment inode */ fsp_free_seg_inode(space, zip_size, inode, mtr); return(TRUE); } return(FALSE); } /**********************************************************************//** Frees part of a segment. Differs from fseg_free_step because this function leaves the header page unfreed. @return TRUE if freeing completed, except the header page */ UNIV_INTERN ibool fseg_free_step_not_header( /*======================*/ fseg_header_t* header, /*!< in: segment header which must reside on the first fragment page of the segment */ mtr_t* mtr) /*!< in: mtr */ { ulint n; ulint page; xdes_t* descr; fseg_inode_t* inode; ulint space; ulint flags; ulint zip_size; ulint page_no; rw_lock_t* latch; space = page_get_space_id(page_align(header)); latch = fil_space_get_latch(space, &flags); zip_size = dict_table_flags_to_zip_size(flags); ut_ad(!mutex_own(&kernel_mutex) || mtr_memo_contains(mtr, latch, MTR_MEMO_X_LOCK)); mtr_x_lock(latch, mtr); inode = fseg_inode_get(header, space, zip_size, mtr); descr = fseg_get_first_extent(inode, space, zip_size, mtr); if (descr != NULL) { /* Free the extent held by the segment */ page = xdes_get_offset(descr); fseg_free_extent(inode, space, zip_size, page, mtr); return(FALSE); } /* Free a frag page */ n = fseg_find_last_used_frag_page_slot(inode, mtr); if (n == ULINT_UNDEFINED) { ut_error; } page_no = fseg_get_nth_frag_page_no(inode, n, mtr); if (page_no == page_get_page_no(page_align(header))) { return(TRUE); } fseg_free_page_low(inode, space, zip_size, page_no, mtr); return(FALSE); } /**********************************************************************//** Returns the first extent descriptor for a segment. We think of the extent lists of the segment catenated in the order FSEG_FULL -> FSEG_NOT_FULL -> FSEG_FREE. @return the first extent descriptor, or NULL if none */ static xdes_t* fseg_get_first_extent( /*==================*/ fseg_inode_t* inode, /*!< in: segment inode */ ulint space, /*!< in: space id */ ulint zip_size,/*!< in: compressed page size in bytes or 0 for uncompressed pages */ mtr_t* mtr) /*!< in: mtr */ { fil_addr_t first; xdes_t* descr; ut_ad(inode && mtr); ut_ad(space == page_get_space_id(page_align(inode))); ut_ad(mach_read_from_4(inode + FSEG_MAGIC_N) == FSEG_MAGIC_N_VALUE); first = fil_addr_null; if (flst_get_len(inode + FSEG_FULL, mtr) > 0) { first = flst_get_first(inode + FSEG_FULL, mtr); } else if (flst_get_len(inode + FSEG_NOT_FULL, mtr) > 0) { first = flst_get_first(inode + FSEG_NOT_FULL, mtr); } else if (flst_get_len(inode + FSEG_FREE, mtr) > 0) { first = flst_get_first(inode + FSEG_FREE, mtr); } if (first.page == FIL_NULL) { return(NULL); } descr = xdes_lst_get_descriptor(space, zip_size, first, mtr); return(descr); } /*******************************************************************//** Validates a segment. @return TRUE if ok */ static ibool fseg_validate_low( /*==============*/ fseg_inode_t* inode, /*!< in: segment inode */ mtr_t* mtr2) /*!< in: mtr */ { ulint space; dulint seg_id; mtr_t mtr; xdes_t* descr; fil_addr_t node_addr; ulint n_used = 0; ulint n_used2 = 0; ut_ad(mtr_memo_contains_page(mtr2, inode, MTR_MEMO_PAGE_X_FIX)); ut_ad(mach_read_from_4(inode + FSEG_MAGIC_N) == FSEG_MAGIC_N_VALUE); space = page_get_space_id(page_align(inode)); seg_id = mtr_read_dulint(inode + FSEG_ID, mtr2); n_used = mtr_read_ulint(inode + FSEG_NOT_FULL_N_USED, MLOG_4BYTES, mtr2); flst_validate(inode + FSEG_FREE, mtr2); flst_validate(inode + FSEG_NOT_FULL, mtr2); flst_validate(inode + FSEG_FULL, mtr2); /* Validate FSEG_FREE list */ node_addr = flst_get_first(inode + FSEG_FREE, mtr2); while (!fil_addr_is_null(node_addr)) { ulint flags; ulint zip_size; mtr_start(&mtr); mtr_x_lock(fil_space_get_latch(space, &flags), &mtr); zip_size = dict_table_flags_to_zip_size(flags); descr = xdes_lst_get_descriptor(space, zip_size, node_addr, &mtr); ut_a(xdes_get_n_used(descr, &mtr) == 0); ut_a(xdes_get_state(descr, &mtr) == XDES_FSEG); ut_a(!ut_dulint_cmp(mtr_read_dulint(descr + XDES_ID, &mtr), seg_id)); node_addr = flst_get_next_addr(descr + XDES_FLST_NODE, &mtr); mtr_commit(&mtr); } /* Validate FSEG_NOT_FULL list */ node_addr = flst_get_first(inode + FSEG_NOT_FULL, mtr2); while (!fil_addr_is_null(node_addr)) { ulint flags; ulint zip_size; mtr_start(&mtr); mtr_x_lock(fil_space_get_latch(space, &flags), &mtr); zip_size = dict_table_flags_to_zip_size(flags); descr = xdes_lst_get_descriptor(space, zip_size, node_addr, &mtr); ut_a(xdes_get_n_used(descr, &mtr) > 0); ut_a(xdes_get_n_used(descr, &mtr) < FSP_EXTENT_SIZE); ut_a(xdes_get_state(descr, &mtr) == XDES_FSEG); ut_a(!ut_dulint_cmp(mtr_read_dulint(descr + XDES_ID, &mtr), seg_id)); n_used2 += xdes_get_n_used(descr, &mtr); node_addr = flst_get_next_addr(descr + XDES_FLST_NODE, &mtr); mtr_commit(&mtr); } /* Validate FSEG_FULL list */ node_addr = flst_get_first(inode + FSEG_FULL, mtr2); while (!fil_addr_is_null(node_addr)) { ulint flags; ulint zip_size; mtr_start(&mtr); mtr_x_lock(fil_space_get_latch(space, &flags), &mtr); zip_size = dict_table_flags_to_zip_size(flags); descr = xdes_lst_get_descriptor(space, zip_size, node_addr, &mtr); ut_a(xdes_get_n_used(descr, &mtr) == FSP_EXTENT_SIZE); ut_a(xdes_get_state(descr, &mtr) == XDES_FSEG); ut_a(!ut_dulint_cmp(mtr_read_dulint(descr + XDES_ID, &mtr), seg_id)); node_addr = flst_get_next_addr(descr + XDES_FLST_NODE, &mtr); mtr_commit(&mtr); } ut_a(n_used == n_used2); return(TRUE); } #ifdef UNIV_DEBUG /*******************************************************************//** Validates a segment. @return TRUE if ok */ UNIV_INTERN ibool fseg_validate( /*==========*/ fseg_header_t* header, /*!< in: segment header */ mtr_t* mtr) /*!< in: mtr */ { fseg_inode_t* inode; ibool ret; ulint space; ulint flags; ulint zip_size; space = page_get_space_id(page_align(header)); mtr_x_lock(fil_space_get_latch(space, &flags), mtr); zip_size = dict_table_flags_to_zip_size(flags); inode = fseg_inode_get(header, space, zip_size, mtr); ret = fseg_validate_low(inode, mtr); return(ret); } #endif /* UNIV_DEBUG */ /*******************************************************************//** Writes info of a segment. */ static void fseg_print_low( /*===========*/ fseg_inode_t* inode, /*!< in: segment inode */ mtr_t* mtr) /*!< in: mtr */ { ulint space; ulint seg_id_low; ulint seg_id_high; ulint n_used; ulint n_frag; ulint n_free; ulint n_not_full; ulint n_full; ulint reserved; ulint used; ulint page_no; dulint d_var; ut_ad(mtr_memo_contains_page(mtr, inode, MTR_MEMO_PAGE_X_FIX)); space = page_get_space_id(page_align(inode)); page_no = page_get_page_no(page_align(inode)); reserved = fseg_n_reserved_pages_low(inode, &used, mtr); d_var = mtr_read_dulint(inode + FSEG_ID, mtr); seg_id_low = ut_dulint_get_low(d_var); seg_id_high = ut_dulint_get_high(d_var); n_used = mtr_read_ulint(inode + FSEG_NOT_FULL_N_USED, MLOG_4BYTES, mtr); n_frag = fseg_get_n_frag_pages(inode, mtr); n_free = flst_get_len(inode + FSEG_FREE, mtr); n_not_full = flst_get_len(inode + FSEG_NOT_FULL, mtr); n_full = flst_get_len(inode + FSEG_FULL, mtr); ib_logger(ib_stream, "SEGMENT id %lu %lu space %lu; page %lu;" " res %lu used %lu; full ext %lu\n" "fragm pages %lu; free extents %lu;" " not full extents %lu: pages %lu\n", (ulong) seg_id_high, (ulong) seg_id_low, (ulong) space, (ulong) page_no, (ulong) reserved, (ulong) used, (ulong) n_full, (ulong) n_frag, (ulong) n_free, (ulong) n_not_full, (ulong) n_used); ut_ad(mach_read_from_4(inode + FSEG_MAGIC_N) == FSEG_MAGIC_N_VALUE); } #ifdef UNIV_BTR_PRINT /*******************************************************************//** Writes info of a segment. */ UNIV_INTERN void fseg_print( /*=======*/ fseg_header_t* header, /*!< in: segment header */ mtr_t* mtr) /*!< in: mtr */ { fseg_inode_t* inode; ulint space; ulint flags; ulint zip_size; space = page_get_space_id(page_align(header)); mtr_x_lock(fil_space_get_latch(space, &flags), mtr); zip_size = dict_table_flags_to_zip_size(flags); inode = fseg_inode_get(header, space, zip_size, mtr); fseg_print_low(inode, mtr); } #endif /* UNIV_BTR_PRINT */ /*******************************************************************//** Validates the file space system and its segments. @return TRUE if ok */ UNIV_INTERN ibool fsp_validate( /*=========*/ ulint space) /*!< in: space id */ { fsp_header_t* header; fseg_inode_t* seg_inode; page_t* seg_inode_page; rw_lock_t* latch; ulint size; ulint flags; ulint zip_size; ulint free_limit; ulint frag_n_used; mtr_t mtr; mtr_t mtr2; xdes_t* descr; fil_addr_t node_addr; fil_addr_t next_node_addr; ulint descr_count = 0; ulint n_used = 0; ulint n_used2 = 0; ulint n_full_frag_pages; ulint n; ulint seg_inode_len_free; ulint seg_inode_len_full; latch = fil_space_get_latch(space, &flags); zip_size = dict_table_flags_to_zip_size(flags); ut_a(ut_is_2pow(zip_size)); ut_a(zip_size <= UNIV_PAGE_SIZE); ut_a(!zip_size || zip_size >= PAGE_ZIP_MIN_SIZE); /* Start first a mini-transaction mtr2 to lock out all other threads from the fsp system */ mtr_start(&mtr2); mtr_x_lock(latch, &mtr2); mtr_start(&mtr); mtr_x_lock(latch, &mtr); header = fsp_get_space_header(space, zip_size, &mtr); size = mtr_read_ulint(header + FSP_SIZE, MLOG_4BYTES, &mtr); free_limit = mtr_read_ulint(header + FSP_FREE_LIMIT, MLOG_4BYTES, &mtr); frag_n_used = mtr_read_ulint(header + FSP_FRAG_N_USED, MLOG_4BYTES, &mtr); n_full_frag_pages = FSP_EXTENT_SIZE * flst_get_len(header + FSP_FULL_FRAG, &mtr); if (UNIV_UNLIKELY(free_limit > size)) { ut_a(space != 0); ut_a(size < FSP_EXTENT_SIZE); } flst_validate(header + FSP_FREE, &mtr); flst_validate(header + FSP_FREE_FRAG, &mtr); flst_validate(header + FSP_FULL_FRAG, &mtr); mtr_commit(&mtr); /* Validate FSP_FREE list */ mtr_start(&mtr); mtr_x_lock(latch, &mtr); header = fsp_get_space_header(space, zip_size, &mtr); node_addr = flst_get_first(header + FSP_FREE, &mtr); mtr_commit(&mtr); while (!fil_addr_is_null(node_addr)) { mtr_start(&mtr); mtr_x_lock(latch, &mtr); descr_count++; descr = xdes_lst_get_descriptor(space, zip_size, node_addr, &mtr); ut_a(xdes_get_n_used(descr, &mtr) == 0); ut_a(xdes_get_state(descr, &mtr) == XDES_FREE); node_addr = flst_get_next_addr(descr + XDES_FLST_NODE, &mtr); mtr_commit(&mtr); } /* Validate FSP_FREE_FRAG list */ mtr_start(&mtr); mtr_x_lock(latch, &mtr); header = fsp_get_space_header(space, zip_size, &mtr); node_addr = flst_get_first(header + FSP_FREE_FRAG, &mtr); mtr_commit(&mtr); while (!fil_addr_is_null(node_addr)) { mtr_start(&mtr); mtr_x_lock(latch, &mtr); descr_count++; descr = xdes_lst_get_descriptor(space, zip_size, node_addr, &mtr); ut_a(xdes_get_n_used(descr, &mtr) > 0); ut_a(xdes_get_n_used(descr, &mtr) < FSP_EXTENT_SIZE); ut_a(xdes_get_state(descr, &mtr) == XDES_FREE_FRAG); n_used += xdes_get_n_used(descr, &mtr); node_addr = flst_get_next_addr(descr + XDES_FLST_NODE, &mtr); mtr_commit(&mtr); } /* Validate FSP_FULL_FRAG list */ mtr_start(&mtr); mtr_x_lock(latch, &mtr); header = fsp_get_space_header(space, zip_size, &mtr); node_addr = flst_get_first(header + FSP_FULL_FRAG, &mtr); mtr_commit(&mtr); while (!fil_addr_is_null(node_addr)) { mtr_start(&mtr); mtr_x_lock(latch, &mtr); descr_count++; descr = xdes_lst_get_descriptor(space, zip_size, node_addr, &mtr); ut_a(xdes_get_n_used(descr, &mtr) == FSP_EXTENT_SIZE); ut_a(xdes_get_state(descr, &mtr) == XDES_FULL_FRAG); node_addr = flst_get_next_addr(descr + XDES_FLST_NODE, &mtr); mtr_commit(&mtr); } /* Validate segments */ mtr_start(&mtr); mtr_x_lock(latch, &mtr); header = fsp_get_space_header(space, zip_size, &mtr); node_addr = flst_get_first(header + FSP_SEG_INODES_FULL, &mtr); seg_inode_len_full = flst_get_len(header + FSP_SEG_INODES_FULL, &mtr); mtr_commit(&mtr); while (!fil_addr_is_null(node_addr)) { n = 0; do { mtr_start(&mtr); mtr_x_lock(latch, &mtr); seg_inode_page = fut_get_ptr( space, zip_size, node_addr, RW_X_LATCH, &mtr) - FSEG_INODE_PAGE_NODE; seg_inode = fsp_seg_inode_page_get_nth_inode( seg_inode_page, n, zip_size, &mtr); ut_a(!ut_dulint_is_zero( mach_read_from_8(seg_inode + FSEG_ID))); fseg_validate_low(seg_inode, &mtr); descr_count += flst_get_len(seg_inode + FSEG_FREE, &mtr); descr_count += flst_get_len(seg_inode + FSEG_FULL, &mtr); descr_count += flst_get_len(seg_inode + FSEG_NOT_FULL, &mtr); n_used2 += fseg_get_n_frag_pages(seg_inode, &mtr); next_node_addr = flst_get_next_addr( seg_inode_page + FSEG_INODE_PAGE_NODE, &mtr); mtr_commit(&mtr); } while (++n < FSP_SEG_INODES_PER_PAGE(zip_size)); node_addr = next_node_addr; } mtr_start(&mtr); mtr_x_lock(latch, &mtr); header = fsp_get_space_header(space, zip_size, &mtr); node_addr = flst_get_first(header + FSP_SEG_INODES_FREE, &mtr); seg_inode_len_free = flst_get_len(header + FSP_SEG_INODES_FREE, &mtr); mtr_commit(&mtr); while (!fil_addr_is_null(node_addr)) { n = 0; do { mtr_start(&mtr); mtr_x_lock(latch, &mtr); seg_inode_page = fut_get_ptr( space, zip_size, node_addr, RW_X_LATCH, &mtr) - FSEG_INODE_PAGE_NODE; seg_inode = fsp_seg_inode_page_get_nth_inode( seg_inode_page, n, zip_size, &mtr); if (!ut_dulint_is_zero( mach_read_from_8(seg_inode + FSEG_ID))) { fseg_validate_low(seg_inode, &mtr); descr_count += flst_get_len( seg_inode + FSEG_FREE, &mtr); descr_count += flst_get_len( seg_inode + FSEG_FULL, &mtr); descr_count += flst_get_len( seg_inode + FSEG_NOT_FULL, &mtr); n_used2 += fseg_get_n_frag_pages( seg_inode, &mtr); } next_node_addr = flst_get_next_addr( seg_inode_page + FSEG_INODE_PAGE_NODE, &mtr); mtr_commit(&mtr); } while (++n < FSP_SEG_INODES_PER_PAGE(zip_size)); node_addr = next_node_addr; } ut_a(descr_count * FSP_EXTENT_SIZE == free_limit); if (!zip_size) { ut_a(n_used + n_full_frag_pages == n_used2 + 2 * ((free_limit + (UNIV_PAGE_SIZE - 1)) / UNIV_PAGE_SIZE) + seg_inode_len_full + seg_inode_len_free); } else { ut_a(n_used + n_full_frag_pages == n_used2 + 2 * ((free_limit + (zip_size - 1)) / zip_size) + seg_inode_len_full + seg_inode_len_free); } ut_a(frag_n_used == n_used); mtr_commit(&mtr2); return(TRUE); } /*******************************************************************//** Prints info of a file space. */ UNIV_INTERN void fsp_print( /*======*/ ulint space) /*!< in: space id */ { fsp_header_t* header; fseg_inode_t* seg_inode; page_t* seg_inode_page; rw_lock_t* latch; ulint flags; ulint zip_size; ulint size; ulint free_limit; ulint frag_n_used; fil_addr_t node_addr; fil_addr_t next_node_addr; ulint n_free; ulint n_free_frag; ulint n_full_frag; ulint seg_id_low; ulint seg_id_high; ulint n; ulint n_segs = 0; dulint d_var; mtr_t mtr; mtr_t mtr2; latch = fil_space_get_latch(space, &flags); zip_size = dict_table_flags_to_zip_size(flags); /* Start first a mini-transaction mtr2 to lock out all other threads from the fsp system */ mtr_start(&mtr2); mtr_x_lock(latch, &mtr2); mtr_start(&mtr); mtr_x_lock(latch, &mtr); header = fsp_get_space_header(space, zip_size, &mtr); size = mtr_read_ulint(header + FSP_SIZE, MLOG_4BYTES, &mtr); free_limit = mtr_read_ulint(header + FSP_FREE_LIMIT, MLOG_4BYTES, &mtr); frag_n_used = mtr_read_ulint(header + FSP_FRAG_N_USED, MLOG_4BYTES, &mtr); n_free = flst_get_len(header + FSP_FREE, &mtr); n_free_frag = flst_get_len(header + FSP_FREE_FRAG, &mtr); n_full_frag = flst_get_len(header + FSP_FULL_FRAG, &mtr); d_var = mtr_read_dulint(header + FSP_SEG_ID, &mtr); seg_id_low = ut_dulint_get_low(d_var); seg_id_high = ut_dulint_get_high(d_var); ib_logger(ib_stream, "FILE SPACE INFO: id %lu\n" "size %lu, free limit %lu, free extents %lu\n" "not full frag extents %lu: used pages %lu," " full frag extents %lu\n" "first seg id not used %lu %lu\n", (ulong) space, (ulong) size, (ulong) free_limit, (ulong) n_free, (ulong) n_free_frag, (ulong) frag_n_used, (ulong) n_full_frag, (ulong) seg_id_high, (ulong) seg_id_low); mtr_commit(&mtr); /* Print segments */ mtr_start(&mtr); mtr_x_lock(latch, &mtr); header = fsp_get_space_header(space, zip_size, &mtr); node_addr = flst_get_first(header + FSP_SEG_INODES_FULL, &mtr); mtr_commit(&mtr); while (!fil_addr_is_null(node_addr)) { n = 0; do { mtr_start(&mtr); mtr_x_lock(latch, &mtr); seg_inode_page = fut_get_ptr( space, zip_size, node_addr, RW_X_LATCH, &mtr) - FSEG_INODE_PAGE_NODE; seg_inode = fsp_seg_inode_page_get_nth_inode( seg_inode_page, n, zip_size, &mtr); ut_a(!ut_dulint_is_zero( mach_read_from_8(seg_inode + FSEG_ID))); fseg_print_low(seg_inode, &mtr); n_segs++; next_node_addr = flst_get_next_addr( seg_inode_page + FSEG_INODE_PAGE_NODE, &mtr); mtr_commit(&mtr); } while (++n < FSP_SEG_INODES_PER_PAGE(zip_size)); node_addr = next_node_addr; } mtr_start(&mtr); mtr_x_lock(latch, &mtr); header = fsp_get_space_header(space, zip_size, &mtr); node_addr = flst_get_first(header + FSP_SEG_INODES_FREE, &mtr); mtr_commit(&mtr); while (!fil_addr_is_null(node_addr)) { n = 0; do { mtr_start(&mtr); mtr_x_lock(latch, &mtr); seg_inode_page = fut_get_ptr( space, zip_size, node_addr, RW_X_LATCH, &mtr) - FSEG_INODE_PAGE_NODE; seg_inode = fsp_seg_inode_page_get_nth_inode( seg_inode_page, n, zip_size, &mtr); if (!ut_dulint_is_zero( mach_read_from_8(seg_inode + FSEG_ID))) { fseg_print_low(seg_inode, &mtr); n_segs++; } next_node_addr = flst_get_next_addr( seg_inode_page + FSEG_INODE_PAGE_NODE, &mtr); mtr_commit(&mtr); } while (++n < FSP_SEG_INODES_PER_PAGE(zip_size)); node_addr = next_node_addr; } mtr_commit(&mtr2); ib_logger(ib_stream, "NUMBER of file segments: %lu\n", (ulong) n_segs); } #endif /* !UNIV_HOTBACKUP */ haildb-2.3.2/ut/0000755000175000017500000000000011513177437014272 5ustar00pcrewspcrews00000000000000haildb-2.3.2/ut/ut0rnd.c0000644000175000017500000000451011513177357015653 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1994, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /***************************************************************//** @file ut/ut0rnd.c Random numbers and hashing Created 5/11/1994 Heikki Tuuri ********************************************************************/ #include "ut0rnd.h" #ifdef UNIV_NONINL #include "ut0rnd.ic" #endif /** These random numbers are used in ut_find_prime */ /*@{*/ #define UT_RANDOM_1 1.0412321 #define UT_RANDOM_2 1.1131347 #define UT_RANDOM_3 1.0132677 /*@}*/ /** Seed value of ut_rnd_gen_ulint(). */ UNIV_INTERN ulint ut_rnd_ulint_counter = 65654363; /***********************************************************//** Looks for a prime number slightly greater than the given argument. The prime is chosen so that it is not near any power of 2. @return prime */ UNIV_INTERN ulint ut_find_prime( /*==========*/ ulint n) /*!< in: positive number > 100 */ { ulint pow2; ulint i; n += 100; pow2 = 1; while (pow2 * 2 < n) { pow2 = 2 * pow2; } if ((double)n < 1.05 * (double)pow2) { n = (ulint) ((double)n * UT_RANDOM_1); } pow2 = 2 * pow2; if ((double)n > 0.95 * (double)pow2) { n = (ulint) ((double)n * UT_RANDOM_2); } if (n > pow2 - 20) { n += 30; } /* Now we have n far enough from powers of 2. To make n more random (especially, if it was not near a power of 2), we then multiply it by a random number. */ n = (ulint) ((double)n * UT_RANDOM_3); for (;; n++) { i = 2; while (i * i <= n) { if (n % i == 0) { goto next_n; } i++; } /* Found a prime */ break; next_n: ; } return(n); } haildb-2.3.2/ut/ut0mem.c0000644000175000017500000004424411513177357015656 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1994, 2010, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /********************************************************************//** @file ut/ut0mem.c Memory primitives Created 5/11/1994 Heikki Tuuri *************************************************************************/ #include "ut0mem.h" #include #ifdef UNIV_NONINL #include "ut0mem.ic" #endif #ifndef UNIV_HOTBACKUP # include "os0thread.h" # include "srv0srv.h" # include "srv0start.h" #include /** This struct is placed first in every allocated memory block */ typedef struct ut_mem_block_struct ut_mem_block_t; /** The total amount of memory currently allocated from the operating system with os_mem_alloc_large() or malloc(). Does not count malloc() if srv_use_sys_malloc is set. Protected by ut_list_mutex. */ UNIV_INTERN ulint ut_total_allocated_memory = 0; /** Mutex protecting ut_total_allocated_memory and ut_mem_block_list */ UNIV_INTERN os_fast_mutex_t ut_list_mutex; /** Dynamically allocated memory block */ struct ut_mem_block_struct{ UT_LIST_NODE_T(ut_mem_block_t) mem_block_list; /*!< mem block list node */ ulint size; /*!< size of allocated memory */ ulint magic_n;/*!< magic number (UT_MEM_MAGIC_N) */ }; /** The value of ut_mem_block_struct::magic_n. Used in detecting memory corruption. */ #define UT_MEM_MAGIC_N 1601650166 /** List of all memory blocks allocated from the operating system with malloc. Protected by ut_list_mutex. */ static UT_LIST_BASE_NODE_T(ut_mem_block_t) ut_mem_block_list; /** Flag: has ut_mem_block_list been initialized? */ static ibool ut_mem_block_list_inited = FALSE; /** A dummy pointer for generating a null pointer exception in ut_malloc_low() */ static ulint* ut_mem_null_ptr = NULL; /**********************************************************************//** Reset the variables. */ UNIV_INTERN void ut_mem_var_init(void) /*=================*/ { ut_total_allocated_memory = 0; memset(&ut_mem_block_list, 0x0, sizeof(ut_mem_block_list)); memset(&ut_list_mutex, 0x0, sizeof(ut_list_mutex)); ut_mem_block_list_inited = FALSE; ut_mem_null_ptr = NULL; } /************************************************************************** Initializes the mem block list at database startup. */ UNIV_INTERN void ut_mem_init(void) /*=============*/ { ut_a(!srv_was_started); if (!ut_mem_block_list_inited) { os_fast_mutex_init(&ut_list_mutex); UT_LIST_INIT(ut_mem_block_list); ut_mem_block_list_inited = TRUE; } } #endif /* !UNIV_HOTBACKUP */ /**********************************************************************//** Allocates memory. Sets it also to zero if UNIV_SET_MEM_TO_ZERO is defined and set_to_zero is TRUE. @return own: allocated memory */ UNIV_INTERN void* ut_malloc_low( /*==========*/ ulint n, /*!< in: number of bytes to allocate */ ibool set_to_zero, /*!< in: TRUE if allocated memory should be set to zero if UNIV_SET_MEM_TO_ZERO is defined */ ibool assert_on_error)/*!< in: if TRUE, we crash the server if the memory cannot be allocated */ { #ifndef UNIV_HOTBACKUP ulint retry_count; void* ret; if (UNIV_LIKELY(srv_use_sys_malloc)) { ret = malloc(n); ut_a(ret || !assert_on_error); #ifdef UNIV_SET_MEM_TO_ZERO if (set_to_zero) { memset(ret, '\0', n); UNIV_MEM_ALLOC(ret, n); } #endif return(ret); } ut_ad((sizeof(ut_mem_block_t) % 8) == 0); /* check alignment ok */ ut_a(ut_mem_block_list_inited); retry_count = 0; retry: os_fast_mutex_lock(&ut_list_mutex); ret = malloc(n + sizeof(ut_mem_block_t)); if (ret == NULL && retry_count < 60) { if (retry_count == 0) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Error: cannot allocate" " %lu bytes of\n" "InnoDB: memory with malloc!" " Total allocated memory\n" "InnoDB: by InnoDB %lu bytes." " Operating system errno: %lu\n" "InnoDB: Check if you should" " increase the swap file or\n" "InnoDB: ulimits of your operating system.\n" "InnoDB: On FreeBSD check you" " have compiled the OS with\n" "InnoDB: a big enough maximum process size.\n" "InnoDB: Note that in most 32-bit" " computers the process\n" "InnoDB: memory space is limited" " to 2 GB or 4 GB.\n" "InnoDB: We keep retrying" " the allocation for 60 seconds...\n", (ulong) n, (ulong) ut_total_allocated_memory, #ifdef __WIN__ (ulong) GetLastError() #else (ulong) errno #endif ); } os_fast_mutex_unlock(&ut_list_mutex); /* Sleep for a second and retry the allocation; maybe this is just a temporary shortage of memory */ os_thread_sleep(1000000); retry_count++; goto retry; } if (ret == NULL) { os_fast_mutex_unlock(&ut_list_mutex); /* Make an intentional seg fault so that we get a stack trace */ /* Intentional segfault on NetWare causes an abend. Avoid this by graceful exit handling in ut_a(). */ #if (!defined __NETWARE__) if (assert_on_error) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: We now intentionally" " generate a seg fault so that\n" "InnoDB: on Linux we get a stack trace.\n"); if (*ut_mem_null_ptr) { ut_mem_null_ptr = 0; } } else { return(NULL); } #else ut_error; #endif } if (set_to_zero) { #ifdef UNIV_SET_MEM_TO_ZERO memset(ret, '\0', n + sizeof(ut_mem_block_t)); #endif } UNIV_MEM_ALLOC(ret, n + sizeof(ut_mem_block_t)); ((ut_mem_block_t*)ret)->size = n + sizeof(ut_mem_block_t); ((ut_mem_block_t*)ret)->magic_n = UT_MEM_MAGIC_N; ut_total_allocated_memory += n + sizeof(ut_mem_block_t); UT_LIST_ADD_FIRST(mem_block_list, ut_mem_block_list, ((ut_mem_block_t*)ret)); os_fast_mutex_unlock(&ut_list_mutex); return((void*)((byte*)ret + sizeof(ut_mem_block_t))); #else /* !UNIV_HOTBACKUP */ void* ret = malloc(n); ut_a(ret || !assert_on_error); # ifdef UNIV_SET_MEM_TO_ZERO if (set_to_zero) { memset(ret, '\0', n); } # endif return(ret); #endif /* !UNIV_HOTBACKUP */ } /**********************************************************************//** Allocates memory. Sets it also to zero if UNIV_SET_MEM_TO_ZERO is defined. @return own: allocated memory */ UNIV_INTERN void* ut_malloc( /*======*/ ulint n) /*!< in: number of bytes to allocate */ { #ifndef UNIV_HOTBACKUP return(ut_malloc_low(n, TRUE, TRUE)); #else /* !UNIV_HOTBACKUP */ return(malloc(n)); #endif /* !UNIV_HOTBACKUP */ } #ifndef UNIV_HOTBACKUP /**********************************************************************//** Tests if malloc of n bytes would succeed. ut_malloc() asserts if memory runs out. It cannot be used if we want to return an error message. Prints to ib_stream a message if fails. @return TRUE if succeeded */ UNIV_INTERN ibool ut_test_malloc( /*===========*/ ulint n) /*!< in: try to allocate this many bytes */ { void* ret; ret = malloc(n); if (ret == NULL) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Error: cannot allocate" " %lu bytes of memory for\n" "InnoDB: a BLOB with malloc! Total allocated memory\n" "InnoDB: by InnoDB %lu bytes." " Operating system errno: %d\n" "InnoDB: Check if you should increase" " the swap file or\n" "InnoDB: ulimits of your operating system.\n" "InnoDB: On FreeBSD check you have" " compiled the OS with\n" "InnoDB: a big enough maximum process size.\n", (ulong) n, (ulong) ut_total_allocated_memory, (int) errno); return(FALSE); } free(ret); return(TRUE); } #endif /* !UNIV_HOTBACKUP */ /**********************************************************************//** Frees a memory block allocated with ut_malloc. */ UNIV_INTERN void ut_free( /*====*/ void* ptr) /*!< in, own: memory block */ { #ifndef UNIV_HOTBACKUP ut_mem_block_t* block; if (!ptr) { return; } else if (UNIV_LIKELY(srv_use_sys_malloc)) { free(ptr); return; } block = (ut_mem_block_t*)((byte*)ptr - sizeof(ut_mem_block_t)); os_fast_mutex_lock(&ut_list_mutex); ut_a(block->magic_n == UT_MEM_MAGIC_N); ut_a(ut_total_allocated_memory >= block->size); ut_total_allocated_memory -= block->size; UT_LIST_REMOVE(mem_block_list, ut_mem_block_list, block); free(block); os_fast_mutex_unlock(&ut_list_mutex); #else /* !UNIV_HOTBACKUP */ free(ptr); #endif /* !UNIV_HOTBACKUP */ } #ifndef UNIV_HOTBACKUP /**********************************************************************//** Implements realloc. This is needed by /pars/lexyy.c. Otherwise, you should not use this function because the allocation functions in mem0mem.h are the recommended ones in InnoDB. man realloc in Linux, 2004: realloc() changes the size of the memory block pointed to by ptr to size bytes. The contents will be unchanged to the minimum of the old and new sizes; newly allocated mem- ory will be uninitialized. If ptr is NULL, the call is equivalent to malloc(size); if size is equal to zero, the call is equivalent to free(ptr). Unless ptr is NULL, it must have been returned by an earlier call to malloc(), calloc() or realloc(). RETURN VALUE realloc() returns a pointer to the newly allocated memory, which is suitably aligned for any kind of variable and may be different from ptr, or NULL if the request fails. If size was equal to 0, either NULL or a pointer suitable to be passed to free() is returned. If realloc() fails the original block is left untouched - it is not freed or moved. @return own: pointer to new mem block or NULL */ UNIV_INTERN void* ut_realloc( /*=======*/ void* ptr, /*!< in: pointer to old block or NULL */ ulint size) /*!< in: desired size */ { ut_mem_block_t* block; ulint old_size; ulint min_size; void* new_ptr; if (UNIV_LIKELY(srv_use_sys_malloc)) { return(realloc(ptr, size)); } if (ptr == NULL) { return(ut_malloc(size)); } if (size == 0) { ut_free(ptr); return(NULL); } block = (ut_mem_block_t*)((byte*)ptr - sizeof(ut_mem_block_t)); ut_a(block->magic_n == UT_MEM_MAGIC_N); old_size = block->size - sizeof(ut_mem_block_t); if (size < old_size) { min_size = size; } else { min_size = old_size; } new_ptr = ut_malloc(size); if (new_ptr == NULL) { return(NULL); } /* Copy the old data from ptr */ ut_memcpy(new_ptr, ptr, min_size); ut_free(ptr); return(new_ptr); } /**********************************************************************//** Frees in shutdown all allocated memory not freed yet. */ UNIV_INTERN void ut_free_all_mem(void) /*=================*/ { ut_mem_block_t* block; /* If the sub-system hasn't been initialized, then ignore request. */ if (!ut_mem_block_list_inited) { return; } os_fast_mutex_free(&ut_list_mutex); while ((block = UT_LIST_GET_FIRST(ut_mem_block_list))) { ut_a(block->magic_n == UT_MEM_MAGIC_N); ut_a(ut_total_allocated_memory >= block->size); ut_total_allocated_memory -= block->size; UT_LIST_REMOVE(mem_block_list, ut_mem_block_list, block); free(block); } if (ut_total_allocated_memory != 0) { ib_logger(ib_stream, "InnoDB: Warning: after shutdown" " total allocated memory is %lu\n", (ulong) ut_total_allocated_memory); } ut_mem_block_list_inited = FALSE; } #endif /* !UNIV_HOTBACKUP */ /**********************************************************************//** Copies up to size - 1 characters from the NUL-terminated string src to dst, NUL-terminating the result. Returns strlen(src), so truncation occurred if the return value >= size. @return strlen(src) */ UNIV_INTERN ulint ut_strlcpy( /*=======*/ char* dst, /*!< in: destination buffer */ const char* src, /*!< in: source buffer */ ulint size) /*!< in: size of destination buffer */ { ulint src_size = strlen(src); if (size != 0) { ulint n = ut_min(src_size, size - 1); memcpy(dst, src, n); dst[n] = '\0'; } return(src_size); } /**********************************************************************//** Like ut_strlcpy, but if src doesn't fit in dst completely, copies the last (size - 1) bytes of src, not the first. @return strlen(src) */ UNIV_INTERN ulint ut_strlcpy_rev( /*===========*/ char* dst, /*!< in: destination buffer */ const char* src, /*!< in: source buffer */ ulint size) /*!< in: size of destination buffer */ { ulint src_size = strlen(src); if (size != 0) { ulint n = ut_min(src_size, size - 1); memcpy(dst, src + src_size - n, n + 1); } return(src_size); } /**********************************************************************//** Make a quoted copy of a NUL-terminated string. Leading and trailing quotes will not be included; only embedded quotes will be escaped. See also ut_strlenq() and ut_memcpyq(). @return pointer to end of dest */ UNIV_INTERN char* ut_strcpyq( /*=======*/ char* dest, /*!< in: output buffer */ char q, /*!< in: the quote character */ const char* src) /*!< in: null-terminated string */ { while (*src) { if ((*dest++ = *src++) == q) { *dest++ = q; } } return(dest); } /**********************************************************************//** Make a quoted copy of a fixed-length string. Leading and trailing quotes will not be included; only embedded quotes will be escaped. See also ut_strlenq() and ut_strcpyq(). @return pointer to end of dest */ UNIV_INTERN char* ut_memcpyq( /*=======*/ char* dest, /*!< in: output buffer */ char q, /*!< in: the quote character */ const char* src, /*!< in: string to be quoted */ ulint len) /*!< in: length of src */ { const char* srcend = src + len; while (src < srcend) { if ((*dest++ = *src++) == q) { *dest++ = q; } } return(dest); } #ifndef UNIV_HOTBACKUP /**********************************************************************//** Return the number of times s2 occurs in s1. Overlapping instances of s2 are only counted once. @return the number of times s2 occurs in s1 */ UNIV_INTERN ulint ut_strcount( /*========*/ const char* s1, /*!< in: string to search in */ const char* s2) /*!< in: string to search for */ { ulint count = 0; ulint len = strlen(s2); if (len == 0) { return(0); } for (;;) { s1 = strstr(s1, s2); if (!s1) { break; } count++; s1 += len; } return(count); } /**********************************************************************//** Replace every occurrence of s1 in str with s2. Overlapping instances of s1 are only replaced once. @return own: modified string, must be freed with mem_free() */ UNIV_INTERN char* ut_strreplace( /*==========*/ const char* str, /*!< in: string to operate on */ const char* s1, /*!< in: string to replace */ const char* s2) /*!< in: string to replace s1 with */ { char* new_str; char* ptr; const char* str_end; ulint str_len = strlen(str); ulint s1_len = strlen(s1); ulint s2_len = strlen(s2); ulint count = 0; int len_delta = (int)s2_len - (int)s1_len; str_end = str + str_len; if (len_delta <= 0) { len_delta = 0; } else { count = ut_strcount(str, s1); } new_str = mem_alloc(str_len + count * len_delta + 1); ptr = new_str; while (str) { const char* next = strstr(str, s1); if (!next) { next = str_end; } memcpy(ptr, str, next - str); ptr += next - str; if (next == str_end) { break; } memcpy(ptr, s2, s2_len); ptr += s2_len; str = next + s1_len; } *ptr = '\0'; return(new_str); } #ifdef UNIV_COMPILE_TEST_FUNCS void test_ut_str_sql_format() { char buf[128]; ulint ret; #define CALL_AND_TEST(str, str_len, buf, buf_size, ret_expected, buf_expected)\ do {\ ibool ok = TRUE;\ memset(buf, 'x', 10);\ buf[10] = '\0';\ ib_logger(ib_stream, "TESTING \"%s\", %lu, %lu\n",\ str, (ulint) str_len, (ulint) buf_size);\ ret = ut_str_sql_format(str, str_len, buf, buf_size);\ if (ret != ret_expected) {\ ib_logger(ib_stream, "expected ret %lu, got %lu\n",\ (ulint) ret_expected, ret);\ ok = FALSE;\ }\ if (strcmp((char*) buf, buf_expected) != 0) {\ ib_logger(ib_stream, "expected buf \"%s\", got \"%s\"\n",\ buf_expected, buf);\ ok = FALSE;\ }\ if (ok) {\ ib_logger(ib_stream, "OK: %lu, \"%s\"\n\n",\ (ulint) ret, buf);\ } else {\ return;\ }\ } while (0) CALL_AND_TEST("abcd", 4, buf, 0, 0, "xxxxxxxxxx"); CALL_AND_TEST("abcd", 4, buf, 1, 1, ""); CALL_AND_TEST("abcd", 4, buf, 2, 1, ""); CALL_AND_TEST("abcd", 0, buf, 3, 3, "''"); CALL_AND_TEST("abcd", 1, buf, 3, 1, ""); CALL_AND_TEST("abcd", 2, buf, 3, 1, ""); CALL_AND_TEST("abcd", 3, buf, 3, 1, ""); CALL_AND_TEST("abcd", 4, buf, 3, 1, ""); CALL_AND_TEST("abcd", 0, buf, 4, 3, "''"); CALL_AND_TEST("abcd", 1, buf, 4, 4, "'a'"); CALL_AND_TEST("abcd", 2, buf, 4, 4, "'a'"); CALL_AND_TEST("abcd", 3, buf, 4, 4, "'a'"); CALL_AND_TEST("abcd", 4, buf, 4, 4, "'a'"); CALL_AND_TEST("abcde", 5, buf, 4, 4, "'a'"); CALL_AND_TEST("'", 1, buf, 4, 3, "''"); CALL_AND_TEST("''", 2, buf, 4, 3, "''"); CALL_AND_TEST("a'", 2, buf, 4, 4, "'a'"); CALL_AND_TEST("'a", 2, buf, 4, 3, "''"); CALL_AND_TEST("ab", 2, buf, 4, 4, "'a'"); CALL_AND_TEST("abcdef", 0, buf, 5, 3, "''"); CALL_AND_TEST("abcdef", 1, buf, 5, 4, "'a'"); CALL_AND_TEST("abcdef", 2, buf, 5, 5, "'ab'"); CALL_AND_TEST("abcdef", 3, buf, 5, 5, "'ab'"); CALL_AND_TEST("abcdef", 4, buf, 5, 5, "'ab'"); CALL_AND_TEST("abcdef", 5, buf, 5, 5, "'ab'"); CALL_AND_TEST("abcdef", 6, buf, 5, 5, "'ab'"); CALL_AND_TEST("'", 1, buf, 5, 5, "''''"); CALL_AND_TEST("''", 2, buf, 5, 5, "''''"); CALL_AND_TEST("a'", 2, buf, 5, 4, "'a'"); CALL_AND_TEST("'a", 2, buf, 5, 5, "''''"); CALL_AND_TEST("ab", 2, buf, 5, 5, "'ab'"); CALL_AND_TEST("abc", 3, buf, 5, 5, "'ab'"); CALL_AND_TEST("ab", 2, buf, 6, 5, "'ab'"); CALL_AND_TEST("a'b'c", 5, buf, 32, 10, "'a''b''c'"); CALL_AND_TEST("a'b'c'", 6, buf, 32, 12, "'a''b''c'''"); } #endif /* UNIV_COMPILE_TEST_FUNCS */ #endif /* !UNIV_HOTBACKUP */ haildb-2.3.2/ut/ut0byte.c0000644000175000017500000000353611513177357016042 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1994, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /***************************************************************//** @file ut/ut0byte.c Byte utilities Created 5/11/1994 Heikki Tuuri ********************************************************************/ #include "ut0byte.h" #ifdef UNIV_NONINL #include "ut0byte.ic" #endif /** Zero value for a dulint */ UNIV_INTERN const dulint ut_dulint_zero = {0, 0}; /** Maximum value for a dulint */ UNIV_INTERN const dulint ut_dulint_max = {0xFFFFFFFFUL, 0xFFFFFFFFUL}; #ifdef notdefined /* unused code */ #include "ut0sort.h" /************************************************************//** Sort function for dulint arrays. */ UNIV_INTERN void ut_dulint_sort( /*===========*/ dulint* arr, /*!< in/out: array to be sorted */ dulint* aux_arr,/*!< in/out: auxiliary array (same size as arr) */ ulint low, /*!< in: low bound of sort interval, inclusive */ ulint high) /*!< in: high bound of sort interval, noninclusive */ { UT_SORT_FUNCTION_BODY(ut_dulint_sort, arr, aux_arr, low, high, ut_dulint_cmp); } #endif /* notdefined */ haildb-2.3.2/ut/ut0dbg.c0000644000175000017500000001275711513177357015640 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1994, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /*****************************************************************//** @file ut/ut0dbg.c Debug utilities for Innobase. Created 1/30/1994 Heikki Tuuri **********************************************************************/ #include "univ.i" #include "ut0dbg.h" #if defined(__GNUC__) && (__GNUC__ > 2) #else /** This is used to eliminate compiler warnings */ UNIV_INTERN ulint ut_dbg_zero = 0; #endif #if defined(UNIV_SYNC_DEBUG) || !defined(UT_DBG_USE_ABORT) /** If this is set to TRUE by ut_dbg_assertion_failed(), all threads will stop at the next ut_a() or ut_ad(). */ UNIV_INTERN ibool ut_dbg_stop_threads = FALSE; #endif #ifdef __NETWARE__ /** Flag for ignoring further assertion failures. This is set to TRUE when on NetWare there happens an InnoDB assertion failure or other fatal error condition that requires an immediate shutdown. */ UNIV_INTERN ibool panic_shutdown = FALSE; #elif !defined(UT_DBG_USE_ABORT) /** A null pointer that will be dereferenced to trigger a memory trap */ UNIV_INTERN ulint* ut_dbg_null_ptr = NULL; #endif /*************************************************************//** Report a failed assertion. */ UNIV_INTERN void ut_dbg_assertion_failed( /*====================*/ const char* expr, /*!< in: the failed assertion (optional) */ const char* file, /*!< in: source file containing the assertion */ ulint line) /*!< in: line number of the assertion */ { ut_print_timestamp(ib_stream); #ifdef UNIV_HOTBACKUP ib_logger(ib_stream, " InnoDB: Assertion failure in file %s line %lu\n", file, line); #else /* UNIV_HOTBACKUP */ ib_logger(ib_stream, " InnoDB: Assertion failure in thread %lu" " in file %s line %lu\n", os_thread_pf(os_thread_get_curr_id()), file, line); #endif /* UNIV_HOTBACKUP */ if (expr) { ib_logger(ib_stream, "InnoDB: Failing assertion: %s\n", expr); } ib_logger(ib_stream, "InnoDB: We intentionally generate a memory trap.\n" "InnoDB: Submit a detailed bug report, " "check the InnoDB website for details\n" "InnoDB: If you get repeated assertion failures" " or crashes, even\n" "InnoDB: immediately after the server startup, there may be\n" "InnoDB: corruption in the InnoDB tablespace. Please refer to\n" "InnoDB: the InnoDB website for details\n" "InnoDB: about forcing recovery.\n"); #if defined(UNIV_SYNC_DEBUG) || !defined(UT_DBG_USE_ABORT) ut_dbg_stop_threads = TRUE; #endif } #ifdef __NETWARE__ /*************************************************************//** Shut down InnoDB after assertion failure. */ UNIV_INTERN void ut_dbg_panic(void) /*==============*/ { if (!panic_shutdown) { panic_shutdown = TRUE; innobase_shutdown(IB_SHUTDOWN_NORMAL); } exit(1); } #else /* __NETWARE__ */ # if defined(UNIV_SYNC_DEBUG) || !defined(UT_DBG_USE_ABORT) /*************************************************************//** Stop a thread after assertion failure. */ UNIV_INTERN void ut_dbg_stop_thread( /*===============*/ const char* file, ulint line) { #ifndef UNIV_HOTBACKUP ib_logger(ib_stream, "InnoDB: Thread %lu stopped in file %s line %lu\n", os_thread_pf(os_thread_get_curr_id()), file, line); os_thread_sleep(1000000000); #endif /* !UNIV_HOTBACKUP */ } # endif #endif /* __NETWARE__ */ #ifdef UNIV_COMPILE_TEST_FUNCS #include #include #include #ifdef HAVE_UNISTD_H #include #endif #ifndef timersub #define timersub(a, b, r) \ do { \ (r)->tv_sec = (a)->tv_sec - (b)->tv_sec; \ (r)->tv_usec = (a)->tv_usec - (b)->tv_usec; \ if ((r)->tv_usec < 0) { \ (r)->tv_sec--; \ (r)->tv_usec += 1000000; \ } \ } while (0) #endif /* timersub */ /*******************************************************************//** Resets a speedo (records the current time in it). */ UNIV_INTERN void speedo_reset( /*=========*/ speedo_t* speedo) /*!< out: speedo */ { gettimeofday(&speedo->tv, NULL); getrusage(RUSAGE_SELF, &speedo->ru); } /*******************************************************************//** Shows the time elapsed and usage statistics since the last reset of a speedo. */ UNIV_INTERN void speedo_show( /*========*/ const speedo_t* speedo) /*!< in: speedo */ { struct rusage ru_now; struct timeval tv_now; struct timeval tv_diff; getrusage(RUSAGE_SELF, &ru_now); gettimeofday(&tv_now, NULL); #define PRINT_TIMEVAL(prefix, tvp) \ ib_logger(ib_stream, "%s% 5ld.%06ld sec\n", \ prefix, (tvp)->tv_sec, (tvp)->tv_usec) timersub(&tv_now, &speedo->tv, &tv_diff); PRINT_TIMEVAL("real", &tv_diff); timersub(&ru_now.ru_utime, &speedo->ru.ru_utime, &tv_diff); PRINT_TIMEVAL("user", &tv_diff); timersub(&ru_now.ru_stime, &speedo->ru.ru_stime, &tv_diff); PRINT_TIMEVAL("sys ", &tv_diff); } #endif /* UNIV_COMPILE_TEST_FUNCS */ haildb-2.3.2/ut/ut0rbt.c0000644000175000017500000007371211513177357015671 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 2006, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /*******************************************************************//** @file ut/ut0rbt.c Red-Black tree implementation Created 2007-03-20 Sunny Bains ***********************************************************************/ #include "ut0rbt.h" /************************************************************************ Definition of a red-black tree ============================== A red-black tree is a binary search tree which has the following red-black properties: 1. Every node is either red or black. 2. Every leaf (NULL - in our case tree->nil) is black. 3. If a node is red, then both its children are black. 4. Every simple path from a node to a descendant leaf contains the same number of black nodes. from (3) above, the implication is that on any path from the root to a leaf, red nodes must not be adjacent. However, any number of black nodes may appear in a sequence. */ #if defined(IB_RBT_TESTING) #warning "Testing enabled!" #endif #define ROOT(t) (t->root->left) #define SIZEOF_NODE(t) ((sizeof(ib_rbt_node_t) + t->sizeof_value) - 1) /****************************************************************//** Print out the sub-tree recursively. */ static void rbt_print_subtree( /*==============*/ const ib_rbt_t* tree, /*!< in: tree to traverse */ const ib_rbt_node_t* node, /*!< in: node to print */ ib_rbt_print_node print) /*!< in: print key function */ { /* FIXME: Doesn't do anything yet */ if (node != tree->nil) { print(node); rbt_print_subtree(tree, node->left, print); rbt_print_subtree(tree, node->right, print); } } /****************************************************************//** Verify that the keys are in order. @return TRUE of OK. FALSE if not ordered */ static ibool rbt_check_ordering( /*===============*/ const ib_rbt_t* tree) /*!< in: tree to verfify */ { const ib_rbt_node_t* node; const ib_rbt_node_t* prev = NULL; /* Iterate over all the nodes, comparing each node with the prev */ for (node = rbt_first(tree); node; node = rbt_next(tree, prev)) { if (prev && tree->compare(prev->value, node->value) >= 0) { return(FALSE); } prev = node; } return(TRUE); } /****************************************************************//** Check that every path from the root to the leaves has the same count. Count is expressed in the number of black nodes. @return 0 on failure else black height of the subtree */ static ibool rbt_count_black_nodes( /*==================*/ const ib_rbt_t* tree, /*!< in: tree to verify */ const ib_rbt_node_t* node) /*!< in: start of sub-tree */ { ulint result; if (node != tree->nil) { ulint left_height = rbt_count_black_nodes(tree, node->left); ulint right_height = rbt_count_black_nodes(tree, node->right); if (left_height == 0 || right_height == 0 || left_height != right_height) { result = 0; } else if (node->color == IB_RBT_RED) { /* Case 3 */ if (node->left->color != IB_RBT_BLACK || node->right->color != IB_RBT_BLACK) { result = 0; } else { result = left_height; } /* Check if it's anything other than RED or BLACK. */ } else if (node->color != IB_RBT_BLACK) { result = 0; } else { result = right_height + 1; } } else { result = 1; } return(result); } /****************************************************************//** Turn the node's right child's left sub-tree into node's right sub-tree. This will also make node's right child it's parent. */ static void rbt_rotate_left( /*============*/ const ib_rbt_node_t* nil, /*!< in: nil node of the tree */ ib_rbt_node_t* node) /*!< in: node to rotate */ { ib_rbt_node_t* right = node->right; node->right = right->left; if (right->left != nil) { right->left->parent = node; } /* Right's new parent was node's parent. */ right->parent = node->parent; /* Since root's parent is tree->nil and root->parent->left points back to root, we can avoid the check. */ if (node == node->parent->left) { /* Node was on the left of its parent. */ node->parent->left = right; } else { /* Node must have been on the right. */ node->parent->right = right; } /* Finally, put node on right's left. */ right->left = node; node->parent = right; } /****************************************************************//** Turn the node's left child's right sub-tree into node's left sub-tree. This also make node's left child it's parent. */ static void rbt_rotate_right( /*=============*/ const ib_rbt_node_t* nil, /*!< in: nil node of tree */ ib_rbt_node_t* node) /*!< in: node to rotate */ { ib_rbt_node_t* left = node->left; node->left = left->right; if (left->right != nil) { left->right->parent = node; } /* Left's new parent was node's parent. */ left->parent = node->parent; /* Since root's parent is tree->nil and root->parent->left points back to root, we can avoid the check. */ if (node == node->parent->right) { /* Node was on the left of its parent. */ node->parent->right = left; } else { /* Node must have been on the left. */ node->parent->left = left; } /* Finally, put node on left's right. */ left->right = node; node->parent = left; } /****************************************************************//** Append a node to the tree. @return inserted node */ static ib_rbt_node_t* rbt_tree_add_child( /*===============*/ const ib_rbt_t* tree, /*!< in: rbt tree */ ib_rbt_bound_t* parent, /*!< in: node's parent */ ib_rbt_node_t* node) /*!< in: node to add */ { /* Cast away the const. */ ib_rbt_node_t* last = (ib_rbt_node_t*) parent->last; if (last == tree->root || parent->result < 0) { last->left = node; } else { /* FIXME: We don't handle duplicates (yet)! */ ut_a(parent->result != 0); last->right = node; } node->parent = last; return(node); } /****************************************************************//** Generic binary tree insert @return inserted node */ static ib_rbt_node_t* rbt_tree_insert( /*============*/ ib_rbt_t* tree, /*!< in: rb tree */ const void* key, /*!< in: key for ordering */ ib_rbt_node_t* node) /*!< in: node hold the insert value */ { ib_rbt_bound_t parent; ib_rbt_node_t* current = ROOT(tree); parent.result = 0; parent.last = tree->root; /* Regular binary search. */ while (current != tree->nil) { parent.last = current; parent.result = tree->compare(key, current->value); if (parent.result < 0) { current = current->left; } else { current = current->right; } } ut_a(current == tree->nil); rbt_tree_add_child(tree, &parent, node); return(node); } /****************************************************************//** Balance a tree after inserting a node. */ static void rbt_balance_tree( /*=============*/ const ib_rbt_t* tree, /*!< in: tree to balance */ ib_rbt_node_t* node) /*!< in: node that was inserted */ { const ib_rbt_node_t* nil = tree->nil; ib_rbt_node_t* parent = node->parent; /* Restore the red-black property. */ node->color = IB_RBT_RED; while (node != ROOT(tree) && parent->color == IB_RBT_RED) { ib_rbt_node_t* grand_parent = parent->parent; if (parent == grand_parent->left) { ib_rbt_node_t* uncle = grand_parent->right; if (uncle->color == IB_RBT_RED) { /* Case 1 - change the colors. */ uncle->color = IB_RBT_BLACK; parent->color = IB_RBT_BLACK; grand_parent->color = IB_RBT_RED; /* Move node up the tree. */ node = grand_parent; } else { if (node == parent->right) { /* Right is a black node and node is to the right, case 2 - move node up and rotate. */ node = parent; rbt_rotate_left(nil, node); } grand_parent = node->parent->parent; /* Case 3. */ node->parent->color = IB_RBT_BLACK; grand_parent->color = IB_RBT_RED; rbt_rotate_right(nil, grand_parent); } } else { ib_rbt_node_t* uncle = grand_parent->left; if (uncle->color == IB_RBT_RED) { /* Case 1 - change the colors. */ uncle->color = IB_RBT_BLACK; parent->color = IB_RBT_BLACK; grand_parent->color = IB_RBT_RED; /* Move node up the tree. */ node = grand_parent; } else { if (node == parent->left) { /* Left is a black node and node is to the right, case 2 - move node up and rotate. */ node = parent; rbt_rotate_right(nil, node); } grand_parent = node->parent->parent; /* Case 3. */ node->parent->color = IB_RBT_BLACK; grand_parent->color = IB_RBT_RED; rbt_rotate_left(nil, grand_parent); } } parent = node->parent; } /* Color the root black. */ ROOT(tree)->color = IB_RBT_BLACK; } /****************************************************************//** Find the given node's successor. @return successor node or NULL if no successor */ static ib_rbt_node_t* rbt_find_successor( /*===============*/ const ib_rbt_t* tree, /*!< in: rb tree */ const ib_rbt_node_t* current)/*!< in: this is declared const because it can be called via rbt_next() */ { const ib_rbt_node_t* nil = tree->nil; ib_rbt_node_t* next = current->right; /* Is there a sub-tree to the right that we can follow. */ if (next != nil) { /* Follow the left most links of the current right child. */ while (next->left != nil) { next = next->left; } } else { /* We will have to go up the tree to find the successor. */ ib_rbt_node_t* parent = current->parent; /* Cast away the const. */ next = (ib_rbt_node_t*) current; while (parent != tree->root && next == parent->right) { next = parent; parent = next->parent; } next = (parent == tree->root) ? NULL : parent; } return(next); } /****************************************************************//** Find the given node's precedecessor. @return predecessor node or NULL if no predecesor */ static ib_rbt_node_t* rbt_find_predecessor( /*=================*/ const ib_rbt_t* tree, /*!< in: rb tree */ const ib_rbt_node_t* current) /*!< in: this is declared const because it can be called via rbt_prev() */ { const ib_rbt_node_t* nil = tree->nil; ib_rbt_node_t* prev = current->left; /* Is there a sub-tree to the left that we can follow. */ if (prev != nil) { /* Follow the right most links of the current left child. */ while (prev->right != nil) { prev = prev->right; } } else { /* We will have to go up the tree to find the precedecessor. */ ib_rbt_node_t* parent = current->parent; /* Cast away the const. */ prev = (ib_rbt_node_t*)current; while (parent != tree->root && prev == parent->left) { prev = parent; parent = prev->parent; } prev = (parent == tree->root) ? NULL : parent; } return(prev); } /****************************************************************//** Replace node with child. After applying transformations eject becomes an orphan. */ static void rbt_eject_node( /*===========*/ ib_rbt_node_t* eject, /*!< in: node to eject */ ib_rbt_node_t* node) /*!< in: node to replace with */ { /* Update the to be ejected node's parent's child pointers. */ if (eject->parent->left == eject) { eject->parent->left = node; } else if (eject->parent->right == eject) { eject->parent->right = node; } else { ut_a(0); } /* eject is now an orphan but otherwise its pointers and color are left intact. */ node->parent = eject->parent; } /****************************************************************//** Replace a node with another node. */ static void rbt_replace_node( /*=============*/ ib_rbt_node_t* replace, /*!< in: node to replace */ ib_rbt_node_t* node) /*!< in: node to replace with */ { ib_rbt_color_t color = node->color; /* Update the node pointers. */ node->left = replace->left; node->right = replace->right; /* Update the child node pointers. */ node->left->parent = node; node->right->parent = node; /* Make the parent of replace point to node. */ rbt_eject_node(replace, node); /* Swap the colors. */ node->color = replace->color; replace->color = color; } /****************************************************************//** Detach node from the tree replacing it with one of it's children. @return the child node that now occupies the position of the detached node */ static ib_rbt_node_t* rbt_detach_node( /*============*/ const ib_rbt_t* tree, /*!< in: rb tree */ ib_rbt_node_t* node) /*!< in: node to detach */ { ib_rbt_node_t* child; const ib_rbt_node_t* nil = tree->nil; if (node->left != nil && node->right != nil) { /* Case where the node to be deleted has two children. */ ib_rbt_node_t* successor = rbt_find_successor(tree, node); ut_a(successor != nil); ut_a(successor->parent != nil); ut_a(successor->left == nil); child = successor->right; /* Remove the successor node and replace with its child. */ rbt_eject_node(successor, child); /* Replace the node to delete with its successor node. */ rbt_replace_node(node, successor); } else { ut_a(node->left == nil || node->right == nil); child = (node->left != nil) ? node->left : node->right; /* Replace the node to delete with one of it's children. */ rbt_eject_node(node, child); } /* Reset the node links. */ node->parent = node->right = node->left = tree->nil; return(child); } /****************************************************************//** Rebalance the right sub-tree after deletion. @return node to rebalance if more rebalancing required else NULL */ static ib_rbt_node_t* rbt_balance_right( /*==============*/ const ib_rbt_node_t* nil, /*!< in: rb tree nil node */ ib_rbt_node_t* parent, /*!< in: parent node */ ib_rbt_node_t* sibling)/*!< in: sibling node */ { ib_rbt_node_t* node = NULL; ut_a(sibling != nil); /* Case 3. */ if (sibling->color == IB_RBT_RED) { parent->color = IB_RBT_RED; sibling->color = IB_RBT_BLACK; rbt_rotate_left(nil, parent); sibling = parent->right; ut_a(sibling != nil); } /* Since this will violate case 3 because of the change above. */ if (sibling->left->color == IB_RBT_BLACK && sibling->right->color == IB_RBT_BLACK) { node = parent; /* Parent needs to be rebalanced too. */ sibling->color = IB_RBT_RED; } else { if (sibling->right->color == IB_RBT_BLACK) { ut_a(sibling->left->color == IB_RBT_RED); sibling->color = IB_RBT_RED; sibling->left->color = IB_RBT_BLACK; rbt_rotate_right(nil, sibling); sibling = parent->right; ut_a(sibling != nil); } sibling->color = parent->color; sibling->right->color = IB_RBT_BLACK; parent->color = IB_RBT_BLACK; rbt_rotate_left(nil, parent); } return(node); } /****************************************************************//** Rebalance the left sub-tree after deletion. @return node to rebalance if more rebalancing required else NULL */ static ib_rbt_node_t* rbt_balance_left( /*=============*/ const ib_rbt_node_t* nil, /*!< in: rb tree nil node */ ib_rbt_node_t* parent, /*!< in: parent node */ ib_rbt_node_t* sibling)/*!< in: sibling node */ { ib_rbt_node_t* node = NULL; ut_a(sibling != nil); /* Case 3. */ if (sibling->color == IB_RBT_RED) { parent->color = IB_RBT_RED; sibling->color = IB_RBT_BLACK; rbt_rotate_right(nil, parent); sibling = parent->left; ut_a(sibling != nil); } /* Since this will violate case 3 because of the change above. */ if (sibling->right->color == IB_RBT_BLACK && sibling->left->color == IB_RBT_BLACK) { node = parent; /* Parent needs to be rebalanced too. */ sibling->color = IB_RBT_RED; } else { if (sibling->left->color == IB_RBT_BLACK) { ut_a(sibling->right->color == IB_RBT_RED); sibling->color = IB_RBT_RED; sibling->right->color = IB_RBT_BLACK; rbt_rotate_left(nil, sibling); sibling = parent->left; ut_a(sibling != nil); } sibling->color = parent->color; sibling->left->color = IB_RBT_BLACK; parent->color = IB_RBT_BLACK; rbt_rotate_right(nil, parent); } return(node); } /****************************************************************//** Delete the node and rebalance the tree if necessary */ static void rbt_remove_node_and_rebalance( /*==========================*/ ib_rbt_t* tree, /*!< in: rb tree */ ib_rbt_node_t* node) /*!< in: node to remove */ { /* Detach node and get the node that will be used as rebalance start. */ ib_rbt_node_t* child = rbt_detach_node(tree, node); if (node->color == IB_RBT_BLACK) { ib_rbt_node_t* last = child; ROOT(tree)->color = IB_RBT_RED; while (child && child->color == IB_RBT_BLACK) { ib_rbt_node_t* parent = child->parent; /* Did the deletion cause an imbalance in the parents left sub-tree. */ if (parent->left == child) { child = rbt_balance_right( tree->nil, parent, parent->right); } else if (parent->right == child) { child = rbt_balance_left( tree->nil, parent, parent->left); } else { ut_error; } if (child) { last = child; } } ut_a(last); last->color = IB_RBT_BLACK; ROOT(tree)->color = IB_RBT_BLACK; } /* Note that we have removed a node from the tree. */ --tree->n_nodes; } /****************************************************************//** Recursively free the nodes. */ static void rbt_free_node( /*==========*/ ib_rbt_node_t* node, /*!< in: node to free */ ib_rbt_node_t* nil) /*!< in: rb tree nil node */ { if (node != nil) { rbt_free_node(node->left, nil); rbt_free_node(node->right, nil); ut_free(node); } } /****************************************************************//** Free all the nodes and free the tree. */ UNIV_INTERN void rbt_free( /*=====*/ ib_rbt_t* tree) /*!< in: rb tree to free */ { rbt_free_node(tree->root, tree->nil); ut_free(tree->nil); ut_free(tree); } /****************************************************************//** Create an instance of a red black tree. @return an empty rb tree */ UNIV_INTERN ib_rbt_t* rbt_create( /*=======*/ size_t sizeof_value, /*!< in: sizeof data item */ ib_rbt_compare compare) /*!< in: fn to compare items */ { ib_rbt_t* tree; ib_rbt_node_t* node; tree = (ib_rbt_t*) ut_malloc(sizeof(*tree)); memset(tree, 0, sizeof(*tree)); tree->sizeof_value = sizeof_value; /* Create the sentinel (NIL) node. */ node = tree->nil = (ib_rbt_node_t*) ut_malloc(sizeof(*node)); memset(node, 0, sizeof(*node)); node->color = IB_RBT_BLACK; node->parent = node->left = node->right = node; /* Create the "fake" root, the real root node will be the left child of this node. */ node = tree->root = (ib_rbt_node_t*) ut_malloc(sizeof(*node)); memset(node, 0, sizeof(*node)); node->color = IB_RBT_BLACK; node->parent = node->left = node->right = tree->nil; tree->compare = compare; return(tree); } /****************************************************************//** Generic insert of a value in the rb tree. @return inserted node */ UNIV_INTERN const ib_rbt_node_t* rbt_insert( /*=======*/ ib_rbt_t* tree, /*!< in: rb tree */ const void* key, /*!< in: key for ordering */ const void* value) /*!< in: value of key, this value is copied to the node */ { ib_rbt_node_t* node; /* Create the node that will hold the value data. */ node = (ib_rbt_node_t*) ut_malloc(SIZEOF_NODE(tree)); memcpy(node->value, value, tree->sizeof_value); node->parent = node->left = node->right = tree->nil; /* Insert in the tree in the usual way. */ rbt_tree_insert(tree, key, node); rbt_balance_tree(tree, node); ++tree->n_nodes; return(node); } /****************************************************************//** Add a new node to the tree, useful for data that is pre-sorted. @return appended node */ UNIV_INTERN const ib_rbt_node_t* rbt_add_node( /*=========*/ ib_rbt_t* tree, /*!< in: rb tree */ ib_rbt_bound_t* parent, /*!< in: bounds */ const void* value) /*!< in: this value is copied to the node */ { ib_rbt_node_t* node; /* Create the node that will hold the value data */ node = (ib_rbt_node_t*) ut_malloc(SIZEOF_NODE(tree)); memcpy(node->value, value, tree->sizeof_value); node->parent = node->left = node->right = tree->nil; /* If tree is empty */ if (parent->last == NULL) { parent->last = tree->root; } /* Append the node, the hope here is that the caller knows what s/he is doing. */ rbt_tree_add_child(tree, parent, node); rbt_balance_tree(tree, node); ++tree->n_nodes; #if defined(IB_RBT_TESTING) ut_a(rbt_validate(tree)); #endif return(node); } /****************************************************************//** Find a matching node in the rb tree. @return NULL if not found else the node where key was found */ UNIV_INTERN const ib_rbt_node_t* rbt_lookup( /*=======*/ const ib_rbt_t* tree, /*!< in: rb tree */ const void* key) /*!< in: key to use for search */ { const ib_rbt_node_t* current = ROOT(tree); /* Regular binary search. */ while (current != tree->nil) { int result = tree->compare(key, current->value); if (result < 0) { current = current->left; } else if (result > 0) { current = current->right; } else { break; } } return(current != tree->nil ? current : NULL); } /****************************************************************//** Delete a node from the red black tree, identified by key. @return TRUE if success FALSE if not found */ UNIV_INTERN ibool rbt_delete( /*=======*/ ib_rbt_t* tree, /*!< in: rb tree */ const void* key) /*!< in: key to delete */ { ibool deleted = FALSE; ib_rbt_node_t* node = (ib_rbt_node_t*) rbt_lookup(tree, key); if (node) { rbt_remove_node_and_rebalance(tree, node); ut_free(node); deleted = TRUE; } return(deleted); } /****************************************************************//** Remove a node from the rb tree, the node is not free'd, that is the callers responsibility. @return deleted node but without the const */ UNIV_INTERN ib_rbt_node_t* rbt_remove_node( /*============*/ ib_rbt_t* tree, /*!< in: rb tree */ const ib_rbt_node_t* const_node) /*!< in: node to delete, this is a fudge and declared const because the caller can access only const nodes */ { /* Cast away the const. */ rbt_remove_node_and_rebalance(tree, (ib_rbt_node_t*) const_node); /* This is to make it easier to do something like this: ut_free(rbt_remove_node(node)); */ return((ib_rbt_node_t*) const_node); } /****************************************************************//** Find the node that has the lowest key that is >= key. @return node satisfying the lower bound constraint or NULL */ UNIV_INTERN const ib_rbt_node_t* rbt_lower_bound( /*============*/ const ib_rbt_t* tree, /*!< in: rb tree */ const void* key) /*!< in: key to search */ { ib_rbt_node_t* lb_node = NULL; ib_rbt_node_t* current = ROOT(tree); while (current != tree->nil) { int result = tree->compare(key, current->value); if (result > 0) { current = current->right; } else if (result < 0) { lb_node = current; current = current->left; } else { lb_node = current; break; } } return(lb_node); } /****************************************************************//** Find the node that has the greatest key that is <= key. @return node satisfying the upper bound constraint or NULL */ UNIV_INTERN const ib_rbt_node_t* rbt_upper_bound( /*============*/ const ib_rbt_t* tree, /*!< in: rb tree */ const void* key) /*!< in: key to search */ { ib_rbt_node_t* ub_node = NULL; ib_rbt_node_t* current = ROOT(tree); while (current != tree->nil) { int result = tree->compare(key, current->value); if (result > 0) { ub_node = current; current = current->right; } else if (result < 0) { current = current->left; } else { ub_node = current; break; } } return(ub_node); } /****************************************************************//** Find the node that has the greatest key that is <= key. @return value of result */ UNIV_INTERN int rbt_search( /*=======*/ const ib_rbt_t* tree, /*!< in: rb tree */ ib_rbt_bound_t* parent, /*!< in: search bounds */ const void* key) /*!< in: key to search */ { ib_rbt_node_t* current = ROOT(tree); /* Every thing is greater than the NULL root. */ parent->result = 1; parent->last = NULL; while (current != tree->nil) { parent->last = current; parent->result = tree->compare(key, current->value); if (parent->result > 0) { current = current->right; } else if (parent->result < 0) { current = current->left; } else { break; } } return(parent->result); } /****************************************************************//** Find the node that has the greatest key that is <= key. But use the supplied comparison function. @return value of result */ UNIV_INTERN int rbt_search_cmp( /*===========*/ const ib_rbt_t* tree, /*!< in: rb tree */ ib_rbt_bound_t* parent, /*!< in: search bounds */ const void* key, /*!< in: key to search */ ib_rbt_compare compare) /*!< in: fn to compare items */ { ib_rbt_node_t* current = ROOT(tree); /* Every thing is greater than the NULL root. */ parent->result = 1; parent->last = NULL; while (current != tree->nil) { parent->last = current; parent->result = compare(key, current->value); if (parent->result > 0) { current = current->right; } else if (parent->result < 0) { current = current->left; } else { break; } } return(parent->result); } /****************************************************************//** Get the leftmost node. Return the left most node in the tree. */ UNIV_INTERN const ib_rbt_node_t* rbt_first( /*======*/ const ib_rbt_t* tree) /* in: rb tree */ { ib_rbt_node_t* first = NULL; ib_rbt_node_t* current = ROOT(tree); while (current != tree->nil) { first = current; current = current->left; } return(first); } /****************************************************************//** Return the right most node in the tree. @return the rightmost node or NULL */ UNIV_INTERN const ib_rbt_node_t* rbt_last( /*=====*/ const ib_rbt_t* tree) /*!< in: rb tree */ { ib_rbt_node_t* last = NULL; ib_rbt_node_t* current = ROOT(tree); while (current != tree->nil) { last = current; current = current->right; } return(last); } /****************************************************************//** Return the next node. @return node next from current */ UNIV_INTERN const ib_rbt_node_t* rbt_next( /*=====*/ const ib_rbt_t* tree, /*!< in: rb tree */ const ib_rbt_node_t* current)/*!< in: current node */ { return(current ? rbt_find_successor(tree, current) : NULL); } /****************************************************************//** Return the previous node. @return node prev from current */ UNIV_INTERN const ib_rbt_node_t* rbt_prev( /*=====*/ const ib_rbt_t* tree, /*!< in: rb tree */ const ib_rbt_node_t* current)/*!< in: current node */ { return(current ? rbt_find_predecessor(tree, current) : NULL); } /****************************************************************//** Reset the tree. Delete all the nodes. */ UNIV_INTERN void rbt_clear( /*======*/ ib_rbt_t* tree) /*!< in: rb tree */ { rbt_free_node(ROOT(tree), tree->nil); tree->n_nodes = 0; tree->root->left = tree->root->right = tree->nil; } /****************************************************************//** Merge the node from dst into src. Return the number of nodes merged. @return no. of recs merged */ UNIV_INTERN ulint rbt_merge_uniq( /*===========*/ ib_rbt_t* dst, /*!< in: dst rb tree */ const ib_rbt_t* src) /*!< in: src rb tree */ { ib_rbt_bound_t parent; ulint n_merged = 0; const ib_rbt_node_t* src_node = rbt_first(src); if (rbt_empty(src) || dst == src) { return(0); } for (/* No op */; src_node; src_node = rbt_next(src, src_node)) { if (rbt_search(dst, &parent, src_node->value) != 0) { rbt_add_node(dst, &parent, src_node->value); ++n_merged; } } return(n_merged); } /****************************************************************//** Merge the node from dst into src. Return the number of nodes merged. Delete the nodes from src after copying node to dst. As a side effect the duplicates will be left untouched in the src. @return no. of recs merged */ UNIV_INTERN ulint rbt_merge_uniq_destructive( /*=======================*/ ib_rbt_t* dst, /*!< in: dst rb tree */ ib_rbt_t* src) /*!< in: src rb tree */ { ib_rbt_bound_t parent; ib_rbt_node_t* src_node; ulint old_size = rbt_size(dst); if (rbt_empty(src) || dst == src) { return(0); } for (src_node = (ib_rbt_node_t*) rbt_first(src); src_node; /* */) { ib_rbt_node_t* prev = src_node; src_node = (ib_rbt_node_t*)rbt_next(src, prev); /* Skip duplicates. */ if (rbt_search(dst, &parent, prev->value) != 0) { /* Remove and reset the node but preserve the node (data) value. */ rbt_remove_node_and_rebalance(src, prev); /* The nil should be taken from the dst tree. */ prev->parent = prev->left = prev->right = dst->nil; rbt_tree_add_child(dst, &parent, prev); rbt_balance_tree(dst, prev); ++dst->n_nodes; } } #if defined(IB_RBT_TESTING) ut_a(rbt_validate(dst)); ut_a(rbt_validate(src)); #endif return(rbt_size(dst) - old_size); } /****************************************************************//** Check that every path from the root to the leaves has the same count and the tree nodes are in order. @return TRUE if OK FALSE otherwise */ UNIV_INTERN ibool rbt_validate( /*=========*/ const ib_rbt_t* tree) /*!< in: RB tree to validate */ { if (rbt_count_black_nodes(tree, ROOT(tree)) > 0) { return(rbt_check_ordering(tree)); } return(FALSE); } /****************************************************************//** Iterate over the tree in depth first order. */ UNIV_INTERN void rbt_print( /*======*/ const ib_rbt_t* tree, /*!< in: tree to traverse */ ib_rbt_print_node print) /*!< in: print function */ { rbt_print_subtree(tree, ROOT(tree), print); } haildb-2.3.2/ut/ut0vec.c0000644000175000017500000000430711513177357015651 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 2006, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /*******************************************************************//** @file ut/ut0vec.c A vector of pointers to data items Created 4/6/2006 Osku Salerma ************************************************************************/ #include "ut0vec.h" #ifdef UNIV_NONINL #include "ut0vec.ic" #endif #include /****************************************************************//** Create a new vector with the given initial size. @return vector */ UNIV_INTERN ib_vector_t* ib_vector_create( /*=============*/ mem_heap_t* heap, /*!< in: heap */ ulint size) /*!< in: initial size */ { ib_vector_t* vec; ut_a(size > 0); vec = mem_heap_alloc(heap, sizeof(*vec)); vec->heap = heap; vec->data = mem_heap_alloc(heap, sizeof(void*) * size); vec->used = 0; vec->total = size; return(vec); } /****************************************************************//** Push a new element to the vector, increasing its size if necessary. */ UNIV_INTERN void ib_vector_push( /*===========*/ ib_vector_t* vec, /*!< in: vector */ void* elem) /*!< in: data element */ { if (vec->used >= vec->total) { void** new_data; ulint new_total = vec->total * 2; new_data = mem_heap_alloc(vec->heap, sizeof(void*) * new_total); memcpy(new_data, vec->data, sizeof(void*) * vec->total); vec->data = new_data; vec->total = new_total; } vec->data[vec->used] = elem; vec->used++; } haildb-2.3.2/ut/ut0list.c0000644000175000017500000000734711513177357016056 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 2006, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /*******************************************************************//** @file ut/ut0list.c A double-linked list Created 4/26/2006 Osku Salerma ************************************************************************/ #include "ut0list.h" #ifdef UNIV_NONINL #include "ut0list.ic" #endif /****************************************************************//** Create a new list. @return list */ UNIV_INTERN ib_list_t* ib_list_create(void) /*=================*/ { ib_list_t* list = mem_alloc(sizeof(ib_list_t)); list->first = NULL; list->last = NULL; list->is_heap_list = FALSE; return(list); } /****************************************************************//** Free a list. */ UNIV_INTERN void ib_list_free( /*=========*/ ib_list_t* list) /*!< in: list */ { ut_a(!list->is_heap_list); /* We don't check that the list is empty because it's entirely valid to e.g. have all the nodes allocated from a single heap that is then freed after the list itself is freed. */ mem_free(list); } /****************************************************************//** Add the data to the end of the list. @return new list node */ UNIV_INTERN ib_list_node_t* ib_list_add_last( /*=============*/ ib_list_t* list, /*!< in: list */ void* data, /*!< in: data */ mem_heap_t* heap) /*!< in: memory heap to use */ { return(ib_list_add_after(list, ib_list_get_last(list), data, heap)); } /****************************************************************//** Add the data after the indicated node. @return new list node */ UNIV_INTERN ib_list_node_t* ib_list_add_after( /*==============*/ ib_list_t* list, /*!< in: list */ ib_list_node_t* prev_node, /*!< in: node preceding new node (can be NULL) */ void* data, /*!< in: data */ mem_heap_t* heap) /*!< in: memory heap to use */ { ib_list_node_t* node = mem_heap_alloc(heap, sizeof(ib_list_node_t)); node->data = data; if (!list->first) { /* Empty list. */ ut_a(!prev_node); node->prev = NULL; node->next = NULL; list->first = node; list->last = node; } else if (!prev_node) { /* Start of list. */ node->prev = NULL; node->next = list->first; list->first->prev = node; list->first = node; } else { /* Middle or end of list. */ node->prev = prev_node; node->next = prev_node->next; prev_node->next = node; if (node->next) { node->next->prev = node; } else { list->last = node; } } return(node); } /****************************************************************//** Remove the node from the list. */ UNIV_INTERN void ib_list_remove( /*===========*/ ib_list_t* list, /*!< in: list */ ib_list_node_t* node) /*!< in: node to remove */ { if (node->prev) { node->prev->next = node->next; } else { /* First item in list. */ ut_ad(list->first == node); list->first = node->next; } if (node->next) { node->next->prev = node->prev; } else { /* Last item in list. */ ut_ad(list->last == node); list->last = node->prev; } } haildb-2.3.2/ut/ut0ut.c0000644000175000017500000003311511513177357015523 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1994, 2009, Innobase Oy. All Rights Reserved. Copyright (c) 2009, Sun Microsystems, Inc. Portions of this file contain modifications contributed and copyrighted by Sun Microsystems, Inc. Those modifications are gratefully acknowledged and are described briefly in the InnoDB documentation. The contributions by Sun Microsystems are incorporated with their permission, and subject to the conditions contained in the file COPYING.Sun_Microsystems. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /***************************************************************//** @file ut/ut0ut.c Various utilities for Innobase. Created 5/11/1994 Heikki Tuuri ********************************************************************/ #include "ut0ut.h" #ifdef UNIV_NONINL #include "ut0ut.ic" #endif #include #include #include #include #include #ifndef UNIV_HOTBACKUP #include "trx0trx.h" #include "api0ucode.h" #endif /* UNIV_HOTBACKUP */ static ibool ut_always_false = FALSE; #ifdef __WIN__ /*****************************************************************//** NOTE: The Windows epoch starts from 1601/01/01 whereas the Unix epoch starts from 1970/1/1. For selection of constant see: http://support.microsoft.com/kb/167296/ */ #define WIN_TO_UNIX_DELTA_USEC ((ib_int64_t) 11644473600000000ULL) /*****************************************************************//** This is the Windows version of gettimeofday(2). @return 0 if all OK else -1 */ static int ut_gettimeofday( /*============*/ struct timeval* tv, /*!< out: Values are relative to Unix epoch */ void* tz) /*!< in: not used */ { FILETIME ft; ib_int64_t tm; if (!tv) { errno = EINVAL; return(-1); } GetSystemTimeAsFileTime(&ft); tm = (ib_int64_t) ft.dwHighDateTime << 32; tm |= ft.dwLowDateTime; ut_a(tm >= 0); /* If tm wraps over to negative, the quotient / 10 does not work */ tm /= 10; /* Convert from 100 nsec periods to usec */ /* If we don't convert to the Unix epoch the value for struct timeval::tv_sec will overflow.*/ tm -= WIN_TO_UNIX_DELTA_USEC; tv->tv_sec = (long) (tm / 1000000L); tv->tv_usec = (long) (tm % 1000000L); return(0); } #else #include /** An alias for gettimeofday(2). On Microsoft Windows, we have to reimplement this function. */ #define ut_gettimeofday gettimeofday #endif /********************************************************//** Gets the high 32 bits in a ulint. That is makes a shift >> 32, but since there seem to be compiler bugs in both gcc and Visual C++, we do this by a special conversion. @return a >> 32 */ UNIV_INTERN ulint ut_get_high32( /*==========*/ ulint a) /*!< in: ulint */ { ib_int64_t i; i = (ib_int64_t)a; i = i >> 32; return((ulint)i); } /**********************************************************//** Returns system time. We do not specify the format of the time returned: the only way to manipulate it is to use the function ut_difftime. @return system time */ UNIV_INTERN ib_time_t ut_time(void) /*=========*/ { return(time(NULL)); } #ifndef UNIV_HOTBACKUP /**********************************************************//** Returns system time. Upon successful completion, the value 0 is returned; otherwise the value -1 is returned and the global variable errno is set to indicate the error. @return 0 on success, -1 otherwise */ UNIV_INTERN int ut_usectime( /*========*/ ulint* sec, /*!< out: seconds since the Epoch */ ulint* ms) /*!< out: microseconds since the Epoch+*sec */ { struct timeval tv; int ret; int errno_gettimeofday; int i; for (i = 0; i < 10; i++) { ret = ut_gettimeofday(&tv, NULL); if (ret == -1) { errno_gettimeofday = errno; ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: gettimeofday(): %s\n", strerror(errno_gettimeofday)); os_thread_sleep(100000); /* 0.1 sec */ errno = errno_gettimeofday; } else { break; } } if (ret != -1) { *sec = (ulint) tv.tv_sec; *ms = (ulint) tv.tv_usec; } return(ret); } /**********************************************************//** Returns the number of microseconds since epoch. Similar to time(3), the return value is also stored in *tloc, provided that tloc is non-NULL. @return us since epoch */ UNIV_INTERN ib_uint64_t ut_time_us( /*=======*/ ib_uint64_t* tloc) /*!< out: us since epoch, if non-NULL */ { struct timeval tv; ib_uint64_t us; ut_gettimeofday(&tv, NULL); us = (ib_uint64_t) tv.tv_sec * 1000000 + tv.tv_usec; if (tloc != NULL) { *tloc = us; } return(us); } /**********************************************************//** Returns the number of milliseconds since some epoch. The value may wrap around. It should only be used for heuristic purposes. @return ms since epoch */ UNIV_INTERN ulint ut_time_ms(void) /*============*/ { struct timeval tv; ut_gettimeofday(&tv, NULL); return((ulint) tv.tv_sec * 1000 + tv.tv_usec / 1000); } #endif /* !UNIV_HOTBACKUP */ /**********************************************************//** Returns the difference of two times in seconds. @return time2 - time1 expressed in seconds */ UNIV_INTERN double ut_difftime( /*========*/ ib_time_t time2, /*!< in: time */ ib_time_t time1) /*!< in: time */ { return(difftime(time2, time1)); } /**********************************************************//** Prints a timestamp to a file. */ UNIV_INTERN void ut_print_timestamp( /*===============*/ ib_stream_t ib_stream) /*!< in: file where to print */ { #ifdef __WIN__ SYSTEMTIME cal_tm; GetLocalTime(&cal_tm); ib_logger(ib_stream,"%02d%02d%02d %2d:%02d:%02d", (int)cal_tm.wYear % 100, (int)cal_tm.wMonth, (int)cal_tm.wDay, (int)cal_tm.wHour, (int)cal_tm.wMinute, (int)cal_tm.wSecond); #else struct tm cal_tm; struct tm* cal_tm_ptr; time_t tm; time(&tm); #ifdef HAVE_LOCALTIME_R localtime_r(&tm, &cal_tm); cal_tm_ptr = &cal_tm; #else cal_tm_ptr = localtime(&tm); #endif ib_logger(ib_stream,"%02d%02d%02d %2d:%02d:%02d", cal_tm_ptr->tm_year % 100, cal_tm_ptr->tm_mon + 1, cal_tm_ptr->tm_mday, cal_tm_ptr->tm_hour, cal_tm_ptr->tm_min, cal_tm_ptr->tm_sec); #endif } /**********************************************************//** Sprintfs a timestamp to a buffer, 13..14 chars plus terminating NUL. */ UNIV_INTERN void ut_sprintf_timestamp( /*=================*/ char* buf) /*!< in: buffer where to sprintf */ { #ifdef __WIN__ SYSTEMTIME cal_tm; GetLocalTime(&cal_tm); sprintf(buf, "%02d%02d%02d %2d:%02d:%02d", (int)cal_tm.wYear % 100, (int)cal_tm.wMonth, (int)cal_tm.wDay, (int)cal_tm.wHour, (int)cal_tm.wMinute, (int)cal_tm.wSecond); #else struct tm cal_tm; struct tm* cal_tm_ptr; time_t tm; time(&tm); #ifdef HAVE_LOCALTIME_R localtime_r(&tm, &cal_tm); cal_tm_ptr = &cal_tm; #else cal_tm_ptr = localtime(&tm); #endif sprintf(buf, "%02d%02d%02d %2d:%02d:%02d", cal_tm_ptr->tm_year % 100, cal_tm_ptr->tm_mon + 1, cal_tm_ptr->tm_mday, cal_tm_ptr->tm_hour, cal_tm_ptr->tm_min, cal_tm_ptr->tm_sec); #endif } #ifdef UNIV_HOTBACKUP /**********************************************************//** Sprintfs a timestamp to a buffer with no spaces and with ':' characters replaced by '_'. */ UNIV_INTERN void ut_sprintf_timestamp_without_extra_chars( /*=====================================*/ char* buf) /*!< in: buffer where to sprintf */ { #ifdef __WIN__ SYSTEMTIME cal_tm; GetLocalTime(&cal_tm); sprintf(buf, "%02d%02d%02d_%2d_%02d_%02d", (int)cal_tm.wYear % 100, (int)cal_tm.wMonth, (int)cal_tm.wDay, (int)cal_tm.wHour, (int)cal_tm.wMinute, (int)cal_tm.wSecond); #else struct tm cal_tm; struct tm* cal_tm_ptr; time_t tm; time(&tm); #ifdef HAVE_LOCALTIME_R localtime_r(&tm, &cal_tm); cal_tm_ptr = &cal_tm; #else cal_tm_ptr = localtime(&tm); #endif sprintf(buf, "%02d%02d%02d_%2d_%02d_%02d", cal_tm_ptr->tm_year % 100, cal_tm_ptr->tm_mon + 1, cal_tm_ptr->tm_mday, cal_tm_ptr->tm_hour, cal_tm_ptr->tm_min, cal_tm_ptr->tm_sec); #endif } /**********************************************************//** Returns current year, month, day. */ UNIV_INTERN void ut_get_year_month_day( /*==================*/ ulint* year, /*!< out: current year */ ulint* month, /*!< out: month */ ulint* day) /*!< out: day */ { #ifdef __WIN__ SYSTEMTIME cal_tm; GetLocalTime(&cal_tm); *year = (ulint)cal_tm.wYear; *month = (ulint)cal_tm.wMonth; *day = (ulint)cal_tm.wDay; #else struct tm cal_tm; struct tm* cal_tm_ptr; time_t tm; time(&tm); #ifdef HAVE_LOCALTIME_R localtime_r(&tm, &cal_tm); cal_tm_ptr = &cal_tm; #else cal_tm_ptr = localtime(&tm); #endif *year = (ulint)cal_tm_ptr->tm_year + 1900; *month = (ulint)cal_tm_ptr->tm_mon + 1; *day = (ulint)cal_tm_ptr->tm_mday; #endif } #endif /* UNIV_HOTBACKUP */ #ifndef UNIV_HOTBACKUP /*************************************************************//** Runs an idle loop on CPU. The argument gives the desired delay in microseconds on 100 MHz Pentium + Visual C++. @return dummy value */ UNIV_INTERN ulint ut_delay( /*=====*/ ulint delay) /*!< in: delay in microseconds on 100 MHz Pentium */ { ulint i, j; j = 0; for (i = 0; i < delay * 50; i++) { j += i; UT_RELAX_CPU(); } if (ut_always_false) { ut_always_false = (ibool) j; } return(j); } #endif /* !UNIV_HOTBACKUP */ /*************************************************************//** Prints the contents of a memory buffer in hex and ascii. */ UNIV_INTERN void ut_print_buf( /*=========*/ ib_stream_t ib_stream, /*!< in: stream where to print */ const void* buf, /*!< in: memory buffer */ ulint len) /*!< in: length of the buffer */ { const byte* data; ulint i; UNIV_MEM_ASSERT_RW(buf, len); ib_logger(ib_stream, " len %lu; hex ", len); for (data = (const byte*)buf, i = 0; i < len; i++) { ib_logger(ib_stream, "%02lx", (ulong)*data++); } ib_logger(ib_stream, "; asc "); data = (const byte*)buf; for (i = 0; i < len; i++) { int c = (int) *data++; ib_logger(ib_stream, "%c", isprint(c) ? c : ' '); } ib_logger(ib_stream, ";"); } /*************************************************************//** Calculates fast the number rounded up to the nearest power of 2. @return first power of 2 which is >= n */ UNIV_INTERN ulint ut_2_power_up( /*==========*/ ulint n) /*!< in: number != 0 */ { ulint res; res = 1; ut_ad(n > 0); while (res < n) { res = res * 2; } return(res); } /**********************************************************************//** Outputs a NUL-terminated file name, quoted with apostrophes. */ UNIV_INTERN void ut_print_filename( /*==============*/ ib_stream_t ib_stream, /*!< in: output stream */ const char* name) /*!< in: name to print */ { ib_logger(ib_stream, "'"); for (;;) { int c = *name++; switch (c) { case 0: goto done; case '\'': ib_logger(ib_stream, "%c", c); /* fall through */ default: ib_logger(ib_stream, "%c", c); } } done: ib_logger(ib_stream, "'"); } #ifndef UNIV_HOTBACKUP /**********************************************************************//** Outputs a fixed-length string, quoted as an SQL identifier. If the string contains a slash '/', the string will be output as two identifiers separated by a period (.), as in SQL database_name.identifier. */ UNIV_INTERN void ut_print_name( /*==========*/ ib_stream_t ib_stream, /*!< in: output stream */ trx_t* trx, /*!< in: transaction */ ibool table_id, /*!< in: TRUE=print a table name, FALSE=print other identifier */ const char* name) /*!< in: name to print */ { ut_print_namel(ib_stream, name, strlen(name)); } /**********************************************************************//** Outputs a fixed-length string, quoted as an SQL identifier. If the string contains a slash '/', the string will be output as two identifiers separated by a period (.), as in SQL database_name.identifier. */ UNIV_INTERN void ut_print_namel( /*===========*/ ib_stream_t ib_stream, /*!< in: output stream */ const char* name, /*!< in: name to print */ ulint namelen) /*!< in: length of name */ { int len; /* 2 * NAME_LEN for database and table name, and some slack for the extra prefix and quotes */ char buf[3 * NAME_LEN]; len = ut_snprintf(buf, sizeof(buf), "%.*s", (int) namelen, name); ut_a(len >= (int) namelen); ib_logger(ib_stream, "%.*s", len, buf); } #endif /* !UNIV_HOTBACKUP */ #ifdef __WIN__ # include /**********************************************************************//** A substitute for snprintf(3), formatted output conversion into a limited buffer. @return number of characters that would have been printed if the size were unlimited, not including the terminating '\0'. */ UNIV_INTERN int ut_snprintf( /*========*/ char* str, /*!< out: string */ size_t size, /*!< in: str size */ const char* fmt, /*!< in: format */ ...) /*!< in: format values */ { int res; va_list ap1; va_list ap2; va_start(ap1, fmt); va_start(ap2, fmt); res = _vscprintf(fmt, ap1); ut_a(res != -1); if (size > 0) { _vsnprintf(str, size, fmt, ap2); if ((size_t) res >= size) { str[size - 1] = '\0'; } } va_end(ap1); va_end(ap2); return(res); } #endif /* __WIN__ */ haildb-2.3.2/mtr/0000755000175000017500000000000011513177437014444 5ustar00pcrewspcrews00000000000000haildb-2.3.2/mtr/mtr0mtr.c0000644000175000017500000002230211513177357016215 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file mtr/mtr0mtr.c Mini-transaction buffer Created 11/26/1995 Heikki Tuuri *******************************************************/ #include "mtr0mtr.h" #ifdef UNIV_NONINL #include "mtr0mtr.ic" #endif #include "buf0buf.h" #include "page0types.h" #include "mtr0log.h" #include "log0log.h" #ifndef UNIV_HOTBACKUP # include "log0recv.h" /*****************************************************************//** Releases the item in the slot given. */ UNIV_INLINE void mtr_memo_slot_release( /*==================*/ mtr_t* mtr, /*!< in: mtr */ mtr_memo_slot_t* slot) /*!< in: memo slot */ { void* object; ulint type; ut_ad(mtr && slot); object = slot->object; type = slot->type; if (UNIV_LIKELY(object != NULL)) { if (type <= MTR_MEMO_BUF_FIX) { buf_page_release((buf_block_t*)object, type, mtr); } else if (type == MTR_MEMO_S_LOCK) { rw_lock_s_unlock((rw_lock_t*)object); #ifdef UNIV_DEBUG } else if (type != MTR_MEMO_X_LOCK) { ut_ad(type == MTR_MEMO_MODIFY); ut_ad(mtr_memo_contains(mtr, object, MTR_MEMO_PAGE_X_FIX)); #endif /* UNIV_DEBUG */ } else { rw_lock_x_unlock((rw_lock_t*)object); } } slot->object = NULL; } /**********************************************************//** Releases the mlocks and other objects stored in an mtr memo. They are released in the order opposite to which they were pushed to the memo. NOTE! It is essential that the x-rw-lock on a modified buffer page is not released before buf_page_note_modification is called for that page! Otherwise, some thread might race to modify it, and the flush list sort order on lsn would be destroyed. */ UNIV_INLINE void mtr_memo_pop_all( /*=============*/ mtr_t* mtr) /*!< in: mtr */ { mtr_memo_slot_t* slot; dyn_array_t* memo; ulint offset; ut_ad(mtr); ut_ad(mtr->magic_n == MTR_MAGIC_N); ut_ad(mtr->state == MTR_COMMITTING); /* Currently only used in commit */ memo = &(mtr->memo); offset = dyn_array_get_data_size(memo); while (offset > 0) { offset -= sizeof(mtr_memo_slot_t); slot = dyn_array_get_element(memo, offset); mtr_memo_slot_release(mtr, slot); } } /************************************************************//** Writes the contents of a mini-transaction log, if any, to the database log. */ static void mtr_log_reserve_and_write( /*======================*/ mtr_t* mtr, /*!< in: mtr */ ulint recovery) /*!< in: recovery flag */ { dyn_array_t* mlog; dyn_block_t* block; ulint data_size; byte* first_data; ut_ad(mtr); mlog = &mtr->log; first_data = dyn_block_get_data(mlog); if (mtr->n_log_recs > 1) { mlog_catenate_ulint(mtr, MLOG_MULTI_REC_END, MLOG_1BYTE); } else { *first_data = (byte)((ulint)*first_data | MLOG_SINGLE_REC_FLAG); } if (mlog->heap == NULL) { log_acquire(); mtr->end_lsn = log_reserve_and_write_fast( first_data, dyn_block_get_used(mlog), &mtr->start_lsn); if (mtr->end_lsn != 0) { /* We were able to successfully write to the log. */ return; } log_release(); } data_size = dyn_array_get_data_size(mlog); /* Open the database log for log_write_low */ mtr->start_lsn = log_reserve_and_open(data_size); if (mtr->log_mode == MTR_LOG_ALL) { block = mlog; while (block != NULL) { log_write_low(dyn_block_get_data(block), dyn_block_get_used(block)); block = dyn_array_get_next_block(mlog, block); } } else { ut_ad(mtr->log_mode == MTR_LOG_NONE); /* Do nothing */ } mtr->end_lsn = log_close(recovery); } #endif /* !UNIV_HOTBACKUP */ /***************************************************************//** Commits a mini-transaction. */ UNIV_INTERN void mtr_commit( /*=======*/ mtr_t* mtr) /*!< in: mini-transaction */ { #ifndef UNIV_HOTBACKUP ibool write_log; #endif /* !UNIV_HOTBACKUP */ ut_ad(mtr); ut_ad(mtr->magic_n == MTR_MAGIC_N); ut_ad(mtr->state == MTR_ACTIVE); ut_d(mtr->state = MTR_COMMITTING); #ifndef UNIV_HOTBACKUP /* This is a dirty read, for debugging. */ ut_ad(!recv_no_log_write); write_log = mtr->modifications && mtr->n_log_recs; if (write_log) { mtr_log_reserve_and_write(mtr, srv_force_recovery); } /* We first update the modification info to buffer pages, and only after that release the log mutex: this guarantees that when the log mutex is free, all buffer pages contain an up-to-date info of their modifications. This fact is used in making a checkpoint when we look at the oldest modification of any page in the buffer pool. It is also required when we insert modified buffer pages in to the flush list which must be sorted on oldest_modification. */ mtr_memo_pop_all(mtr); if (write_log) { log_release(); } #endif /* !UNIV_HOTBACKUP */ ut_d(mtr->state = MTR_COMMITTED); dyn_array_free(&(mtr->memo)); dyn_array_free(&(mtr->log)); } #ifndef UNIV_HOTBACKUP /**********************************************************//** Releases the latches stored in an mtr memo down to a savepoint. NOTE! The mtr must not have made changes to buffer pages after the savepoint, as these can be handled only by mtr_commit. */ UNIV_INTERN void mtr_rollback_to_savepoint( /*======================*/ mtr_t* mtr, /*!< in: mtr */ ulint savepoint) /*!< in: savepoint */ { mtr_memo_slot_t* slot; dyn_array_t* memo; ulint offset; ut_ad(mtr); ut_ad(mtr->magic_n == MTR_MAGIC_N); ut_ad(mtr->state == MTR_ACTIVE); memo = &(mtr->memo); offset = dyn_array_get_data_size(memo); ut_ad(offset >= savepoint); while (offset > savepoint) { offset -= sizeof(mtr_memo_slot_t); slot = dyn_array_get_element(memo, offset); ut_ad(slot->type != MTR_MEMO_MODIFY); mtr_memo_slot_release(mtr, slot); } } /***************************************************//** Releases an object in the memo stack. */ UNIV_INTERN void mtr_memo_release( /*=============*/ mtr_t* mtr, /*!< in: mtr */ void* object, /*!< in: object */ ulint type) /*!< in: object type: MTR_MEMO_S_LOCK, ... */ { mtr_memo_slot_t* slot; dyn_array_t* memo; ulint offset; ut_ad(mtr); ut_ad(mtr->magic_n == MTR_MAGIC_N); ut_ad(mtr->state == MTR_ACTIVE); memo = &(mtr->memo); offset = dyn_array_get_data_size(memo); while (offset > 0) { offset -= sizeof(mtr_memo_slot_t); slot = dyn_array_get_element(memo, offset); if ((object == slot->object) && (type == slot->type)) { mtr_memo_slot_release(mtr, slot); break; } } } #endif /* !UNIV_HOTBACKUP */ /********************************************************//** Reads 1 - 4 bytes from a file page buffered in the buffer pool. @return value read */ UNIV_INTERN ulint mtr_read_ulint( /*===========*/ const byte* ptr, /*!< in: pointer from where to read */ ulint type, /*!< in: MLOG_1BYTE, MLOG_2BYTES, MLOG_4BYTES */ mtr_t* mtr __attribute__((unused))) /*!< in: mini-transaction handle */ { ut_ad(mtr->state == MTR_ACTIVE); ut_ad(mtr_memo_contains_page(mtr, ptr, MTR_MEMO_PAGE_S_FIX) || mtr_memo_contains_page(mtr, ptr, MTR_MEMO_PAGE_X_FIX)); if (type == MLOG_1BYTE) { return(mach_read_from_1(ptr)); } else if (type == MLOG_2BYTES) { return(mach_read_from_2(ptr)); } else { ut_ad(type == MLOG_4BYTES); return(mach_read_from_4(ptr)); } } /********************************************************//** Reads 8 bytes from a file page buffered in the buffer pool. @return value read */ UNIV_INTERN dulint mtr_read_dulint( /*============*/ const byte* ptr, /*!< in: pointer from where to read */ mtr_t* mtr __attribute__((unused))) /*!< in: mini-transaction handle */ { ut_ad(mtr->state == MTR_ACTIVE); ut_ad(mtr_memo_contains_page(mtr, ptr, MTR_MEMO_PAGE_S_FIX) || mtr_memo_contains_page(mtr, ptr, MTR_MEMO_PAGE_X_FIX)); return(mach_read_from_8(ptr)); } #ifdef UNIV_DEBUG # ifndef UNIV_HOTBACKUP /**********************************************************//** Checks if memo contains the given page. @return TRUE if contains */ UNIV_INTERN ibool mtr_memo_contains_page( /*===================*/ mtr_t* mtr, /*!< in: mtr */ const byte* ptr, /*!< in: pointer to buffer frame */ ulint type) /*!< in: type of object */ { return(mtr_memo_contains(mtr, buf_block_align(ptr), type)); } /*********************************************************//** Prints info of an mtr handle. */ UNIV_INTERN void mtr_print( /*======*/ mtr_t* mtr) /*!< in: mtr */ { ib_logger(ib_stream, "Mini-transaction handle: memo size %lu bytes" " log size %lu bytes\n", (ulong) dyn_array_get_data_size(&(mtr->memo)), (ulong) dyn_array_get_data_size(&(mtr->log))); } # endif /* !UNIV_HOTBACKUP */ #endif /* UNIV_DEBUG */ haildb-2.3.2/mtr/mtr0log.c0000644000175000017500000003507311513177357016205 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file mtr/mtr0log.c Mini-transaction log routines Created 12/7/1995 Heikki Tuuri *******************************************************/ #include "mtr0log.h" #ifdef UNIV_NONINL #include "mtr0log.ic" #endif #include "buf0buf.h" #include "dict0dict.h" #include "log0recv.h" #include "page0page.h" #ifndef UNIV_HOTBACKUP # include "dict0boot.h" /********************************************************//** Catenates n bytes to the mtr log. */ UNIV_INTERN void mlog_catenate_string( /*=================*/ mtr_t* mtr, /*!< in: mtr */ const byte* str, /*!< in: string to write */ ulint len) /*!< in: string length */ { dyn_array_t* mlog; if (mtr_get_log_mode(mtr) == MTR_LOG_NONE) { return; } mlog = &(mtr->log); dyn_push_string(mlog, str, len); } /********************************************************//** Writes the initial part of a log record consisting of one-byte item type and four-byte space and page numbers. Also pushes info to the mtr memo that a buffer page has been modified. */ UNIV_INTERN void mlog_write_initial_log_record( /*==========================*/ const byte* ptr, /*!< in: pointer to (inside) a buffer frame holding the file page where modification is made */ byte type, /*!< in: log item type: MLOG_1BYTE, ... */ mtr_t* mtr) /*!< in: mini-transaction handle */ { byte* log_ptr; ut_ad(type <= MLOG_BIGGEST_TYPE); ut_ad(type > MLOG_8BYTES); log_ptr = mlog_open(mtr, 11); /* If no logging is requested, we may return now */ if (log_ptr == NULL) { return; } log_ptr = mlog_write_initial_log_record_fast(ptr, type, log_ptr, mtr); mlog_close(mtr, log_ptr); } #endif /* !UNIV_HOTBACKUP */ /********************************************************//** Parses an initial log record written by mlog_write_initial_log_record. @return parsed record end, NULL if not a complete record */ UNIV_INTERN byte* mlog_parse_initial_log_record( /*==========================*/ byte* ptr, /*!< in: buffer */ byte* end_ptr,/*!< in: buffer end */ byte* type, /*!< out: log record type: MLOG_1BYTE, ... */ ulint* space, /*!< out: space id */ ulint* page_no)/*!< out: page number */ { if (end_ptr < ptr + 1) { return(NULL); } *type = (byte)((ulint)*ptr & ~MLOG_SINGLE_REC_FLAG); ut_ad(*type <= MLOG_BIGGEST_TYPE); ptr++; if (end_ptr < ptr + 2) { return(NULL); } ptr = mach_parse_compressed(ptr, end_ptr, space); if (ptr == NULL) { return(NULL); } ptr = mach_parse_compressed(ptr, end_ptr, page_no); return(ptr); } /********************************************************//** Parses a log record written by mlog_write_ulint or mlog_write_dulint. @return parsed record end, NULL if not a complete record or a corrupt record */ UNIV_INTERN byte* mlog_parse_nbytes( /*==============*/ ulint type, /*!< in: log record type: MLOG_1BYTE, ... */ byte* ptr, /*!< in: buffer */ byte* end_ptr,/*!< in: buffer end */ byte* page, /*!< in: page where to apply the log record, or NULL */ void* page_zip)/*!< in/out: compressed page, or NULL */ { ulint offset; ulint val; dulint dval; ut_a(type <= MLOG_8BYTES); ut_a(!page || !page_zip || fil_page_get_type(page) != FIL_PAGE_INDEX); if (end_ptr < ptr + 2) { return(NULL); } offset = mach_read_from_2(ptr); ptr += 2; if (offset >= UNIV_PAGE_SIZE) { recv_sys->found_corrupt_log = TRUE; return(NULL); } if (type == MLOG_8BYTES) { ptr = mach_dulint_parse_compressed(ptr, end_ptr, &dval); if (ptr == NULL) { return(NULL); } if (page) { if (UNIV_LIKELY_NULL(page_zip)) { mach_write_to_8 (((page_zip_des_t*) page_zip)->data + offset, dval); } mach_write_to_8(page + offset, dval); } return(ptr); } ptr = mach_parse_compressed(ptr, end_ptr, &val); if (ptr == NULL) { return(NULL); } switch (type) { case MLOG_1BYTE: if (UNIV_UNLIKELY(val > 0xFFUL)) { goto corrupt; } if (page) { if (UNIV_LIKELY_NULL(page_zip)) { mach_write_to_1 (((page_zip_des_t*) page_zip)->data + offset, val); } mach_write_to_1(page + offset, val); } break; case MLOG_2BYTES: if (UNIV_UNLIKELY(val > 0xFFFFUL)) { goto corrupt; } if (page) { if (UNIV_LIKELY_NULL(page_zip)) { mach_write_to_2 (((page_zip_des_t*) page_zip)->data + offset, val); } mach_write_to_2(page + offset, val); } break; case MLOG_4BYTES: if (page) { if (UNIV_LIKELY_NULL(page_zip)) { mach_write_to_4 (((page_zip_des_t*) page_zip)->data + offset, val); } mach_write_to_4(page + offset, val); } break; default: corrupt: recv_sys->found_corrupt_log = TRUE; ptr = NULL; } return(ptr); } /********************************************************//** Writes 1 - 4 bytes to a file page buffered in the buffer pool. Writes the corresponding log record to the mini-transaction log. */ UNIV_INTERN void mlog_write_ulint( /*=============*/ byte* ptr, /*!< in: pointer where to write */ ulint val, /*!< in: value to write */ byte type, /*!< in: MLOG_1BYTE, MLOG_2BYTES, MLOG_4BYTES */ mtr_t* mtr) /*!< in: mini-transaction handle */ { byte* log_ptr; switch (type) { case MLOG_1BYTE: mach_write_to_1(ptr, val); break; case MLOG_2BYTES: mach_write_to_2(ptr, val); break; case MLOG_4BYTES: mach_write_to_4(ptr, val); break; default: ut_error; } log_ptr = mlog_open(mtr, 11 + 2 + 5); /* If no logging is requested, we may return now */ if (log_ptr == NULL) { return; } log_ptr = mlog_write_initial_log_record_fast(ptr, type, log_ptr, mtr); mach_write_to_2(log_ptr, page_offset(ptr)); log_ptr += 2; log_ptr += mach_write_compressed(log_ptr, val); mlog_close(mtr, log_ptr); } /********************************************************//** Writes 8 bytes to a file page buffered in the buffer pool. Writes the corresponding log record to the mini-transaction log. */ UNIV_INTERN void mlog_write_dulint( /*==============*/ byte* ptr, /*!< in: pointer where to write */ dulint val, /*!< in: value to write */ mtr_t* mtr) /*!< in: mini-transaction handle */ { byte* log_ptr; ut_ad(ptr && mtr); mach_write_to_8(ptr, val); log_ptr = mlog_open(mtr, 11 + 2 + 9); /* If no logging is requested, we may return now */ if (log_ptr == NULL) { return; } log_ptr = mlog_write_initial_log_record_fast(ptr, MLOG_8BYTES, log_ptr, mtr); mach_write_to_2(log_ptr, page_offset(ptr)); log_ptr += 2; log_ptr += mach_dulint_write_compressed(log_ptr, val); mlog_close(mtr, log_ptr); } #ifndef UNIV_HOTBACKUP /********************************************************//** Writes a string to a file page buffered in the buffer pool. Writes the corresponding log record to the mini-transaction log. */ UNIV_INTERN void mlog_write_string( /*==============*/ byte* ptr, /*!< in: pointer where to write */ const byte* str, /*!< in: string to write */ ulint len, /*!< in: string length */ mtr_t* mtr) /*!< in: mini-transaction handle */ { ut_ad(ptr && mtr); ut_a(len < UNIV_PAGE_SIZE); memcpy(ptr, str, len); mlog_log_string(ptr, len, mtr); } /********************************************************//** Logs a write of a string to a file page buffered in the buffer pool. Writes the corresponding log record to the mini-transaction log. */ UNIV_INTERN void mlog_log_string( /*============*/ byte* ptr, /*!< in: pointer written to */ ulint len, /*!< in: string length */ mtr_t* mtr) /*!< in: mini-transaction handle */ { byte* log_ptr; ut_ad(ptr && mtr); ut_ad(len <= UNIV_PAGE_SIZE); log_ptr = mlog_open(mtr, 30); /* If no logging is requested, we may return now */ if (log_ptr == NULL) { return; } log_ptr = mlog_write_initial_log_record_fast(ptr, MLOG_WRITE_STRING, log_ptr, mtr); mach_write_to_2(log_ptr, page_offset(ptr)); log_ptr += 2; mach_write_to_2(log_ptr, len); log_ptr += 2; mlog_close(mtr, log_ptr); mlog_catenate_string(mtr, ptr, len); } #endif /* !UNIV_HOTBACKUP */ /********************************************************//** Parses a log record written by mlog_write_string. @return parsed record end, NULL if not a complete record */ UNIV_INTERN byte* mlog_parse_string( /*==============*/ byte* ptr, /*!< in: buffer */ byte* end_ptr,/*!< in: buffer end */ byte* page, /*!< in: page where to apply the log record, or NULL */ void* page_zip)/*!< in/out: compressed page, or NULL */ { ulint offset; ulint len; ut_a(!page || !page_zip || fil_page_get_type(page) != FIL_PAGE_INDEX); if (end_ptr < ptr + 4) { return(NULL); } offset = mach_read_from_2(ptr); ptr += 2; len = mach_read_from_2(ptr); ptr += 2; if (UNIV_UNLIKELY(offset >= UNIV_PAGE_SIZE) || UNIV_UNLIKELY(len + offset) > UNIV_PAGE_SIZE) { recv_sys->found_corrupt_log = TRUE; return(NULL); } if (end_ptr < ptr + len) { return(NULL); } if (page) { if (UNIV_LIKELY_NULL(page_zip)) { memcpy(((page_zip_des_t*) page_zip)->data + offset, ptr, len); } memcpy(page + offset, ptr, len); } return(ptr + len); } #ifndef UNIV_HOTBACKUP /********************************************************//** Opens a buffer for mlog, writes the initial log record and, if needed, the field lengths of an index. @return buffer, NULL if log mode MTR_LOG_NONE */ UNIV_INTERN byte* mlog_open_and_write_index( /*======================*/ mtr_t* mtr, /*!< in: mtr */ const byte* rec, /*!< in: index record or page */ dict_index_t* index, /*!< in: record descriptor */ byte type, /*!< in: log item type */ ulint size) /*!< in: requested buffer size in bytes (if 0, calls mlog_close() and returns NULL) */ { byte* log_ptr; const byte* log_start; const byte* log_end; ut_ad(!!page_rec_is_comp(rec) == dict_table_is_comp(index->table)); if (!page_rec_is_comp(rec)) { log_start = log_ptr = mlog_open(mtr, 11 + size); if (!log_ptr) { return(NULL); /* logging is disabled */ } log_ptr = mlog_write_initial_log_record_fast(rec, type, log_ptr, mtr); log_end = log_ptr + 11 + size; } else { ulint i; ulint n = dict_index_get_n_fields(index); /* total size needed */ ulint total = 11 + size + (n + 2) * 2; ulint alloc = total; /* allocate at most DYN_ARRAY_DATA_SIZE at a time */ if (alloc > DYN_ARRAY_DATA_SIZE) { alloc = DYN_ARRAY_DATA_SIZE; } log_start = log_ptr = mlog_open(mtr, alloc); if (!log_ptr) { return(NULL); /* logging is disabled */ } log_end = log_ptr + alloc; log_ptr = mlog_write_initial_log_record_fast(rec, type, log_ptr, mtr); mach_write_to_2(log_ptr, n); log_ptr += 2; mach_write_to_2(log_ptr, dict_index_get_n_unique_in_tree(index)); log_ptr += 2; for (i = 0; i < n; i++) { dict_field_t* field; const dict_col_t* col; ulint len; field = dict_index_get_nth_field(index, i); col = dict_field_get_col(field); len = field->fixed_len; ut_ad(len < 0x7fff); if (len == 0 && (col->len > 255 || col->mtype == DATA_BLOB)) { /* variable-length field with maximum length > 255 */ len = 0x7fff; } if (col->prtype & DATA_NOT_NULL) { len |= 0x8000; } if (log_ptr + 2 > log_end) { mlog_close(mtr, log_ptr); ut_a(total > (ulint) (log_ptr - log_start)); total -= log_ptr - log_start; alloc = total; if (alloc > DYN_ARRAY_DATA_SIZE) { alloc = DYN_ARRAY_DATA_SIZE; } log_start = log_ptr = mlog_open(mtr, alloc); if (!log_ptr) { return(NULL); /* logging is disabled */ } log_end = log_ptr + alloc; } mach_write_to_2(log_ptr, len); log_ptr += 2; } } if (size == 0) { mlog_close(mtr, log_ptr); log_ptr = NULL; } else if (log_ptr + size > log_end) { mlog_close(mtr, log_ptr); log_ptr = mlog_open(mtr, size); } return(log_ptr); } #endif /* !UNIV_HOTBACKUP */ /********************************************************//** Parses a log record written by mlog_open_and_write_index. @return parsed record end, NULL if not a complete record */ UNIV_INTERN byte* mlog_parse_index( /*=============*/ byte* ptr, /*!< in: buffer */ const byte* end_ptr,/*!< in: buffer end */ ibool comp, /*!< in: TRUE=compact record format */ dict_index_t** index) /*!< out, own: dummy index */ { ulint i, n, n_uniq; dict_table_t* table; dict_index_t* ind; ut_ad(comp == FALSE || comp == TRUE); if (comp) { if (end_ptr < ptr + 4) { return(NULL); } n = mach_read_from_2(ptr); ptr += 2; n_uniq = mach_read_from_2(ptr); ptr += 2; ut_ad(n_uniq <= n); if (end_ptr < ptr + n * 2) { return(NULL); } } else { n = n_uniq = 1; } table = dict_mem_table_create("LOG_DUMMY", DICT_HDR_SPACE, n, comp ? DICT_TF_COMPACT : 0); ind = dict_mem_index_create("LOG_DUMMY", "LOG_DUMMY", DICT_HDR_SPACE, 0, n); ind->table = table; ind->n_uniq = (unsigned int) n_uniq; if (n_uniq != n) { ut_a(n_uniq + DATA_ROLL_PTR <= n); ind->type = DICT_CLUSTERED; } if (comp) { for (i = 0; i < n; i++) { ulint len = mach_read_from_2(ptr); ptr += 2; /* The high-order bit of len is the NOT NULL flag; the rest is 0 or 0x7fff for variable-length fields, and 1..0x7ffe for fixed-length fields. */ dict_mem_table_add_col( table, NULL, NULL, ((len + 1) & 0x7fff) <= 1 ? DATA_BINARY : DATA_FIXBINARY, len & 0x8000 ? DATA_NOT_NULL : 0, len & 0x7fff); dict_index_add_col(ind, table, dict_table_get_nth_col(table, i), 0); } dict_table_add_system_columns(table, table->heap); if (n_uniq != n) { /* Identify DB_TRX_ID and DB_ROLL_PTR in the index. */ ut_a(DATA_TRX_ID_LEN == dict_index_get_nth_col(ind, DATA_TRX_ID - 1 + n_uniq)->len); ut_a(DATA_ROLL_PTR_LEN == dict_index_get_nth_col(ind, DATA_ROLL_PTR - 1 + n_uniq)->len); ind->fields[DATA_TRX_ID - 1 + n_uniq].col = &table->cols[n + DATA_TRX_ID]; ind->fields[DATA_ROLL_PTR - 1 + n_uniq].col = &table->cols[n + DATA_ROLL_PTR]; } } /* avoid ut_ad(index->cached) in dict_index_get_n_unique_in_tree */ ind->cached = TRUE; *index = ind; return(ptr); } haildb-2.3.2/m4/0000755000175000017500000000000011513177437014162 5ustar00pcrewspcrews00000000000000haildb-2.3.2/m4/lib-ld.m40000644000175000017500000000653111513177357015575 0ustar00pcrewspcrews00000000000000# lib-ld.m4 serial 3 (gettext-0.13) dnl Copyright (C) 1996-2003 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl Subroutines of libtool.m4, dnl with replacements s/AC_/AC_LIB/ and s/lt_cv/acl_cv/ to avoid collision dnl with libtool.m4. dnl From libtool-1.4. Sets the variable with_gnu_ld to yes or no. AC_DEFUN([AC_LIB_PROG_LD_GNU], [AC_CACHE_CHECK([if the linker ($LD) is GNU ld], acl_cv_prog_gnu_ld, [# I'd rather use --version here, but apparently some GNU ld's only accept -v. case `$LD -v 2>&1 conf$$.sh echo "exit 0" >>conf$$.sh chmod +x conf$$.sh if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then PATH_SEPARATOR=';' else PATH_SEPARATOR=: fi rm -f conf$$.sh fi ac_prog=ld if test "$GCC" = yes; then # Check if gcc -print-prog-name=ld gives a path. AC_MSG_CHECKING([for ld used by GCC]) case $host in *-*-mingw*) # gcc leaves a trailing carriage return which upsets mingw ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; *) ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; esac case $ac_prog in # Accept absolute paths. [[\\/]* | [A-Za-z]:[\\/]*)] [re_direlt='/[^/][^/]*/\.\./'] # Canonicalize the path of ld ac_prog=`echo $ac_prog| sed 's%\\\\%/%g'` while echo $ac_prog | grep "$re_direlt" > /dev/null 2>&1; do ac_prog=`echo $ac_prog| sed "s%$re_direlt%/%"` done test -z "$LD" && LD="$ac_prog" ;; "") # If it fails, then pretend we aren't using GCC. ac_prog=ld ;; *) # If it is relative, then search for the first ld in PATH. with_gnu_ld=unknown ;; esac elif test "$with_gnu_ld" = yes; then AC_MSG_CHECKING([for GNU ld]) else AC_MSG_CHECKING([for non-GNU ld]) fi AC_CACHE_VAL(acl_cv_path_LD, [if test -z "$LD"; then IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}${PATH_SEPARATOR-:}" for ac_dir in $PATH; do test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then acl_cv_path_LD="$ac_dir/$ac_prog" # Check to see if the program is GNU ld. I'd rather use --version, # but apparently some GNU ld's only accept -v. # Break only if it was the GNU/non-GNU ld that we prefer. case `"$acl_cv_path_LD" -v 2>&1 < /dev/null` in *GNU* | *'with BFD'*) test "$with_gnu_ld" != no && break ;; *) test "$with_gnu_ld" != yes && break ;; esac fi done IFS="$ac_save_ifs" else acl_cv_path_LD="$LD" # Let the user override the test with a path. fi]) LD="$acl_cv_path_LD" if test -n "$LD"; then AC_MSG_RESULT($LD) else AC_MSG_RESULT(no) fi test -z "$LD" && AC_MSG_ERROR([no acceptable ld found in \$PATH]) AC_LIB_PROG_LD_GNU ]) haildb-2.3.2/m4/pandora_with_memcached.m40000644000175000017500000000247011513177357021075 0ustar00pcrewspcrews00000000000000dnl Copyright (C) 2009 Sun Microsystems, Inc. dnl This file is free software; Sun Microsystems, Inc. dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. AC_DEFUN([_PANDORA_SEARCH_MEMCACHED],[ AC_ARG_WITH([memcached], [AS_HELP_STRING([--with-memcached], [Memcached binary to use for make test])], [ac_cv_with_memcached="$withval"], [ac_cv_with_memcached=memcached]) # just ignore the user if --without-memcached is passed.. it is # only used by make test AS_IF([test "x$ac_cv_with_memcached" = "xno"],[ ac_cv_with_memcached=memcached MEMCACHED_BINARY=memcached ],[ AS_IF([test -f "$ac_cv_with_memcached"],[ MEMCACHED_BINARY=$ac_cv_with_memcached ],[ AC_PATH_PROG([MEMCACHED_BINARY], [$ac_cv_with_memcached], "no") ]) ]) AC_DEFINE_UNQUOTED([MEMCACHED_BINARY], "$MEMCACHED_BINARY", [Name of the memcached binary used in make test]) AM_CONDITIONAL([HAVE_MEMCACHED],[test "x$MEMCACHED_BINARY" != "xno"]) ]) AC_DEFUN([PANDORA_HAVE_MEMCACHED],[ AC_REQUIRE([_PANDORA_SEARCH_MEMCACHED]) ]) AC_DEFUN([PANDORA_REQUIRE_MEMCACHED],[ AC_REQUIRE([PANDORA_HAVE_MEMCACHED]) AS_IF([test "x$MEMCACHED_BINARY" = "xno"],[ AC_MSG_ERROR(["could not find memcached binary"]) ]) ]) haildb-2.3.2/m4/libtool.m40000644000175000017500000077464711513177375016122 0ustar00pcrewspcrews00000000000000# libtool.m4 - Configure libtool for the host system. -*-Autoconf-*- # # Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, # 2006, 2007, 2008 Free Software Foundation, Inc. # Written by Gordon Matzigkeit, 1996 # # This file is free software; the Free Software Foundation gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. m4_define([_LT_COPYING], [dnl # Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, # 2006, 2007, 2008 Free Software Foundation, Inc. # Written by Gordon Matzigkeit, 1996 # # This file is part of GNU Libtool. # # GNU Libtool is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. # # As a special exception to the GNU General Public License, # if you distribute this file as part of a program or library that # is built using GNU Libtool, you may include this file under the # same distribution terms that you use for the rest of that program. # # GNU Libtool 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNU Libtool; see the file COPYING. If not, a copy # can be downloaded from http://www.gnu.org/licenses/gpl.html, or # obtained by writing to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ]) # serial 56 LT_INIT # LT_PREREQ(VERSION) # ------------------ # Complain and exit if this libtool version is less that VERSION. m4_defun([LT_PREREQ], [m4_if(m4_version_compare(m4_defn([LT_PACKAGE_VERSION]), [$1]), -1, [m4_default([$3], [m4_fatal([Libtool version $1 or higher is required], 63)])], [$2])]) # _LT_CHECK_BUILDDIR # ------------------ # Complain if the absolute build directory name contains unusual characters m4_defun([_LT_CHECK_BUILDDIR], [case `pwd` in *\ * | *\ *) AC_MSG_WARN([Libtool does not cope well with whitespace in `pwd`]) ;; esac ]) # LT_INIT([OPTIONS]) # ------------------ AC_DEFUN([LT_INIT], [AC_PREREQ([2.58])dnl We use AC_INCLUDES_DEFAULT AC_BEFORE([$0], [LT_LANG])dnl AC_BEFORE([$0], [LT_OUTPUT])dnl AC_BEFORE([$0], [LTDL_INIT])dnl m4_require([_LT_CHECK_BUILDDIR])dnl dnl Autoconf doesn't catch unexpanded LT_ macros by default: m4_pattern_forbid([^_?LT_[A-Z_]+$])dnl m4_pattern_allow([^(_LT_EOF|LT_DLGLOBAL|LT_DLLAZY_OR_NOW|LT_MULTI_MODULE)$])dnl dnl aclocal doesn't pull ltoptions.m4, ltsugar.m4, or ltversion.m4 dnl unless we require an AC_DEFUNed macro: AC_REQUIRE([LTOPTIONS_VERSION])dnl AC_REQUIRE([LTSUGAR_VERSION])dnl AC_REQUIRE([LTVERSION_VERSION])dnl AC_REQUIRE([LTOBSOLETE_VERSION])dnl m4_require([_LT_PROG_LTMAIN])dnl dnl Parse OPTIONS _LT_SET_OPTIONS([$0], [$1]) # This can be used to rebuild libtool when needed LIBTOOL_DEPS="$ltmain" # Always use our own libtool. LIBTOOL='$(SHELL) $(top_builddir)/libtool' AC_SUBST(LIBTOOL)dnl _LT_SETUP # Only expand once: m4_define([LT_INIT]) ])# LT_INIT # Old names: AU_ALIAS([AC_PROG_LIBTOOL], [LT_INIT]) AU_ALIAS([AM_PROG_LIBTOOL], [LT_INIT]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_PROG_LIBTOOL], []) dnl AC_DEFUN([AM_PROG_LIBTOOL], []) # _LT_CC_BASENAME(CC) # ------------------- # Calculate cc_basename. Skip known compiler wrappers and cross-prefix. m4_defun([_LT_CC_BASENAME], [for cc_temp in $1""; do case $cc_temp in compile | *[[\\/]]compile | ccache | *[[\\/]]ccache ) ;; distcc | *[[\\/]]distcc | purify | *[[\\/]]purify ) ;; \-*) ;; *) break;; esac done cc_basename=`$ECHO "X$cc_temp" | $Xsed -e 's%.*/%%' -e "s%^$host_alias-%%"` ]) # _LT_FILEUTILS_DEFAULTS # ---------------------- # It is okay to use these file commands and assume they have been set # sensibly after `m4_require([_LT_FILEUTILS_DEFAULTS])'. m4_defun([_LT_FILEUTILS_DEFAULTS], [: ${CP="cp -f"} : ${MV="mv -f"} : ${RM="rm -f"} ])# _LT_FILEUTILS_DEFAULTS # _LT_SETUP # --------- m4_defun([_LT_SETUP], [AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_REQUIRE([AC_CANONICAL_BUILD])dnl _LT_DECL([], [host_alias], [0], [The host system])dnl _LT_DECL([], [host], [0])dnl _LT_DECL([], [host_os], [0])dnl dnl _LT_DECL([], [build_alias], [0], [The build system])dnl _LT_DECL([], [build], [0])dnl _LT_DECL([], [build_os], [0])dnl dnl AC_REQUIRE([AC_PROG_CC])dnl AC_REQUIRE([LT_PATH_LD])dnl AC_REQUIRE([LT_PATH_NM])dnl dnl AC_REQUIRE([AC_PROG_LN_S])dnl test -z "$LN_S" && LN_S="ln -s" _LT_DECL([], [LN_S], [1], [Whether we need soft or hard links])dnl dnl AC_REQUIRE([LT_CMD_MAX_LEN])dnl _LT_DECL([objext], [ac_objext], [0], [Object file suffix (normally "o")])dnl _LT_DECL([], [exeext], [0], [Executable file suffix (normally "")])dnl dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_CHECK_SHELL_FEATURES])dnl m4_require([_LT_CMD_RELOAD])dnl m4_require([_LT_CHECK_MAGIC_METHOD])dnl m4_require([_LT_CMD_OLD_ARCHIVE])dnl m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl _LT_CONFIG_LIBTOOL_INIT([ # See if we are running on zsh, and set the options which allow our # commands through without removal of \ escapes INIT. if test -n "\${ZSH_VERSION+set}" ; then setopt NO_GLOB_SUBST fi ]) if test -n "${ZSH_VERSION+set}" ; then setopt NO_GLOB_SUBST fi _LT_CHECK_OBJDIR m4_require([_LT_TAG_COMPILER])dnl _LT_PROG_ECHO_BACKSLASH case $host_os in aix3*) # AIX sometimes has problems with the GCC collect2 program. For some # reason, if we set the COLLECT_NAMES environment variable, the problems # vanish in a puff of smoke. if test "X${COLLECT_NAMES+set}" != Xset; then COLLECT_NAMES= export COLLECT_NAMES fi ;; esac # Sed substitution that helps us do robust quoting. It backslashifies # metacharacters that are still active within double-quoted strings. sed_quote_subst='s/\([["`$\\]]\)/\\\1/g' # Same as above, but do not quote variable references. double_quote_subst='s/\([["`\\]]\)/\\\1/g' # Sed substitution to delay expansion of an escaped shell variable in a # double_quote_subst'ed string. delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g' # Sed substitution to delay expansion of an escaped single quote. delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g' # Sed substitution to avoid accidental globbing in evaled expressions no_glob_subst='s/\*/\\\*/g' # Global variables: ofile=libtool can_build_shared=yes # All known linkers require a `.a' archive for static linking (except MSVC, # which needs '.lib'). libext=a with_gnu_ld="$lt_cv_prog_gnu_ld" old_CC="$CC" old_CFLAGS="$CFLAGS" # Set sane defaults for various variables test -z "$CC" && CC=cc test -z "$LTCC" && LTCC=$CC test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS test -z "$LD" && LD=ld test -z "$ac_objext" && ac_objext=o _LT_CC_BASENAME([$compiler]) # Only perform the check for file, if the check method requires it test -z "$MAGIC_CMD" && MAGIC_CMD=file case $deplibs_check_method in file_magic*) if test "$file_magic_cmd" = '$MAGIC_CMD'; then _LT_PATH_MAGIC fi ;; esac # Use C for the default configuration in the libtool script LT_SUPPORTED_TAG([CC]) _LT_LANG_C_CONFIG _LT_LANG_DEFAULT_CONFIG _LT_CONFIG_COMMANDS ])# _LT_SETUP # _LT_PROG_LTMAIN # --------------- # Note that this code is called both from `configure', and `config.status' # now that we use AC_CONFIG_COMMANDS to generate libtool. Notably, # `config.status' has no value for ac_aux_dir unless we are using Automake, # so we pass a copy along to make sure it has a sensible value anyway. m4_defun([_LT_PROG_LTMAIN], [m4_ifdef([AC_REQUIRE_AUX_FILE], [AC_REQUIRE_AUX_FILE([ltmain.sh])])dnl _LT_CONFIG_LIBTOOL_INIT([ac_aux_dir='$ac_aux_dir']) ltmain="$ac_aux_dir/ltmain.sh" ])# _LT_PROG_LTMAIN ## ------------------------------------- ## ## Accumulate code for creating libtool. ## ## ------------------------------------- ## # So that we can recreate a full libtool script including additional # tags, we accumulate the chunks of code to send to AC_CONFIG_COMMANDS # in macros and then make a single call at the end using the `libtool' # label. # _LT_CONFIG_LIBTOOL_INIT([INIT-COMMANDS]) # ---------------------------------------- # Register INIT-COMMANDS to be passed to AC_CONFIG_COMMANDS later. m4_define([_LT_CONFIG_LIBTOOL_INIT], [m4_ifval([$1], [m4_append([_LT_OUTPUT_LIBTOOL_INIT], [$1 ])])]) # Initialize. m4_define([_LT_OUTPUT_LIBTOOL_INIT]) # _LT_CONFIG_LIBTOOL([COMMANDS]) # ------------------------------ # Register COMMANDS to be passed to AC_CONFIG_COMMANDS later. m4_define([_LT_CONFIG_LIBTOOL], [m4_ifval([$1], [m4_append([_LT_OUTPUT_LIBTOOL_COMMANDS], [$1 ])])]) # Initialize. m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS]) # _LT_CONFIG_SAVE_COMMANDS([COMMANDS], [INIT_COMMANDS]) # ----------------------------------------------------- m4_defun([_LT_CONFIG_SAVE_COMMANDS], [_LT_CONFIG_LIBTOOL([$1]) _LT_CONFIG_LIBTOOL_INIT([$2]) ]) # _LT_FORMAT_COMMENT([COMMENT]) # ----------------------------- # Add leading comment marks to the start of each line, and a trailing # full-stop to the whole comment if one is not present already. m4_define([_LT_FORMAT_COMMENT], [m4_ifval([$1], [ m4_bpatsubst([m4_bpatsubst([$1], [^ *], [# ])], [['`$\]], [\\\&])]m4_bmatch([$1], [[!?.]$], [], [.]) )]) ## ------------------------ ## ## FIXME: Eliminate VARNAME ## ## ------------------------ ## # _LT_DECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION], [IS-TAGGED?]) # ------------------------------------------------------------------- # CONFIGNAME is the name given to the value in the libtool script. # VARNAME is the (base) name used in the configure script. # VALUE may be 0, 1 or 2 for a computed quote escaped value based on # VARNAME. Any other value will be used directly. m4_define([_LT_DECL], [lt_if_append_uniq([lt_decl_varnames], [$2], [, ], [lt_dict_add_subkey([lt_decl_dict], [$2], [libtool_name], [m4_ifval([$1], [$1], [$2])]) lt_dict_add_subkey([lt_decl_dict], [$2], [value], [$3]) m4_ifval([$4], [lt_dict_add_subkey([lt_decl_dict], [$2], [description], [$4])]) lt_dict_add_subkey([lt_decl_dict], [$2], [tagged?], [m4_ifval([$5], [yes], [no])])]) ]) # _LT_TAGDECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION]) # -------------------------------------------------------- m4_define([_LT_TAGDECL], [_LT_DECL([$1], [$2], [$3], [$4], [yes])]) # lt_decl_tag_varnames([SEPARATOR], [VARNAME1...]) # ------------------------------------------------ m4_define([lt_decl_tag_varnames], [_lt_decl_filter([tagged?], [yes], $@)]) # _lt_decl_filter(SUBKEY, VALUE, [SEPARATOR], [VARNAME1..]) # --------------------------------------------------------- m4_define([_lt_decl_filter], [m4_case([$#], [0], [m4_fatal([$0: too few arguments: $#])], [1], [m4_fatal([$0: too few arguments: $#: $1])], [2], [lt_dict_filter([lt_decl_dict], [$1], [$2], [], lt_decl_varnames)], [3], [lt_dict_filter([lt_decl_dict], [$1], [$2], [$3], lt_decl_varnames)], [lt_dict_filter([lt_decl_dict], $@)])[]dnl ]) # lt_decl_quote_varnames([SEPARATOR], [VARNAME1...]) # -------------------------------------------------- m4_define([lt_decl_quote_varnames], [_lt_decl_filter([value], [1], $@)]) # lt_decl_dquote_varnames([SEPARATOR], [VARNAME1...]) # --------------------------------------------------- m4_define([lt_decl_dquote_varnames], [_lt_decl_filter([value], [2], $@)]) # lt_decl_varnames_tagged([SEPARATOR], [VARNAME1...]) # --------------------------------------------------- m4_define([lt_decl_varnames_tagged], [m4_assert([$# <= 2])dnl _$0(m4_quote(m4_default([$1], [[, ]])), m4_ifval([$2], [[$2]], [m4_dquote(lt_decl_tag_varnames)]), m4_split(m4_normalize(m4_quote(_LT_TAGS)), [ ]))]) m4_define([_lt_decl_varnames_tagged], [m4_ifval([$3], [lt_combine([$1], [$2], [_], $3)])]) # lt_decl_all_varnames([SEPARATOR], [VARNAME1...]) # ------------------------------------------------ m4_define([lt_decl_all_varnames], [_$0(m4_quote(m4_default([$1], [[, ]])), m4_if([$2], [], m4_quote(lt_decl_varnames), m4_quote(m4_shift($@))))[]dnl ]) m4_define([_lt_decl_all_varnames], [lt_join($@, lt_decl_varnames_tagged([$1], lt_decl_tag_varnames([[, ]], m4_shift($@))))dnl ]) # _LT_CONFIG_STATUS_DECLARE([VARNAME]) # ------------------------------------ # Quote a variable value, and forward it to `config.status' so that its # declaration there will have the same value as in `configure'. VARNAME # must have a single quote delimited value for this to work. m4_define([_LT_CONFIG_STATUS_DECLARE], [$1='`$ECHO "X$][$1" | $Xsed -e "$delay_single_quote_subst"`']) # _LT_CONFIG_STATUS_DECLARATIONS # ------------------------------ # We delimit libtool config variables with single quotes, so when # we write them to config.status, we have to be sure to quote all # embedded single quotes properly. In configure, this macro expands # each variable declared with _LT_DECL (and _LT_TAGDECL) into: # # ='`$ECHO "X$" | $Xsed -e "$delay_single_quote_subst"`' m4_defun([_LT_CONFIG_STATUS_DECLARATIONS], [m4_foreach([_lt_var], m4_quote(lt_decl_all_varnames), [m4_n([_LT_CONFIG_STATUS_DECLARE(_lt_var)])])]) # _LT_LIBTOOL_TAGS # ---------------- # Output comment and list of tags supported by the script m4_defun([_LT_LIBTOOL_TAGS], [_LT_FORMAT_COMMENT([The names of the tagged configurations supported by this script])dnl available_tags="_LT_TAGS"dnl ]) # _LT_LIBTOOL_DECLARE(VARNAME, [TAG]) # ----------------------------------- # Extract the dictionary values for VARNAME (optionally with TAG) and # expand to a commented shell variable setting: # # # Some comment about what VAR is for. # visible_name=$lt_internal_name m4_define([_LT_LIBTOOL_DECLARE], [_LT_FORMAT_COMMENT(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [description])))[]dnl m4_pushdef([_libtool_name], m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [libtool_name])))[]dnl m4_case(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [value])), [0], [_libtool_name=[$]$1], [1], [_libtool_name=$lt_[]$1], [2], [_libtool_name=$lt_[]$1], [_libtool_name=lt_dict_fetch([lt_decl_dict], [$1], [value])])[]dnl m4_ifval([$2], [_$2])[]m4_popdef([_libtool_name])[]dnl ]) # _LT_LIBTOOL_CONFIG_VARS # ----------------------- # Produce commented declarations of non-tagged libtool config variables # suitable for insertion in the LIBTOOL CONFIG section of the `libtool' # script. Tagged libtool config variables (even for the LIBTOOL CONFIG # section) are produced by _LT_LIBTOOL_TAG_VARS. m4_defun([_LT_LIBTOOL_CONFIG_VARS], [m4_foreach([_lt_var], m4_quote(_lt_decl_filter([tagged?], [no], [], lt_decl_varnames)), [m4_n([_LT_LIBTOOL_DECLARE(_lt_var)])])]) # _LT_LIBTOOL_TAG_VARS(TAG) # ------------------------- m4_define([_LT_LIBTOOL_TAG_VARS], [m4_foreach([_lt_var], m4_quote(lt_decl_tag_varnames), [m4_n([_LT_LIBTOOL_DECLARE(_lt_var, [$1])])])]) # _LT_TAGVAR(VARNAME, [TAGNAME]) # ------------------------------ m4_define([_LT_TAGVAR], [m4_ifval([$2], [$1_$2], [$1])]) # _LT_CONFIG_COMMANDS # ------------------- # Send accumulated output to $CONFIG_STATUS. Thanks to the lists of # variables for single and double quote escaping we saved from calls # to _LT_DECL, we can put quote escaped variables declarations # into `config.status', and then the shell code to quote escape them in # for loops in `config.status'. Finally, any additional code accumulated # from calls to _LT_CONFIG_LIBTOOL_INIT is expanded. m4_defun([_LT_CONFIG_COMMANDS], [AC_PROVIDE_IFELSE([LT_OUTPUT], dnl If the libtool generation code has been placed in $CONFIG_LT, dnl instead of duplicating it all over again into config.status, dnl then we will have config.status run $CONFIG_LT later, so it dnl needs to know what name is stored there: [AC_CONFIG_COMMANDS([libtool], [$SHELL $CONFIG_LT || AS_EXIT(1)], [CONFIG_LT='$CONFIG_LT'])], dnl If the libtool generation code is destined for config.status, dnl expand the accumulated commands and init code now: [AC_CONFIG_COMMANDS([libtool], [_LT_OUTPUT_LIBTOOL_COMMANDS], [_LT_OUTPUT_LIBTOOL_COMMANDS_INIT])]) ])#_LT_CONFIG_COMMANDS # Initialize. m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS_INIT], [ # The HP-UX ksh and POSIX shell print the target directory to stdout # if CDPATH is set. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH sed_quote_subst='$sed_quote_subst' double_quote_subst='$double_quote_subst' delay_variable_subst='$delay_variable_subst' _LT_CONFIG_STATUS_DECLARATIONS LTCC='$LTCC' LTCFLAGS='$LTCFLAGS' compiler='$compiler_DEFAULT' # Quote evaled strings. for var in lt_decl_all_varnames([[ \ ]], lt_decl_quote_varnames); do case \`eval \\\\\$ECHO "X\\\\\$\$var"\` in *[[\\\\\\\`\\"\\\$]]*) eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"X\\\$\$var\\" | \\\$Xsed -e \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" ;; *) eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" ;; esac done # Double-quote double-evaled strings. for var in lt_decl_all_varnames([[ \ ]], lt_decl_dquote_varnames); do case \`eval \\\\\$ECHO "X\\\\\$\$var"\` in *[[\\\\\\\`\\"\\\$]]*) eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"X\\\$\$var\\" | \\\$Xsed -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" ;; *) eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" ;; esac done # Fix-up fallback echo if it was mangled by the above quoting rules. case \$lt_ECHO in *'\\\[$]0 --fallback-echo"')dnl " lt_ECHO=\`\$ECHO "X\$lt_ECHO" | \$Xsed -e 's/\\\\\\\\\\\\\\\[$]0 --fallback-echo"\[$]/\[$]0 --fallback-echo"/'\` ;; esac _LT_OUTPUT_LIBTOOL_INIT ]) # LT_OUTPUT # --------- # This macro allows early generation of the libtool script (before # AC_OUTPUT is called), incase it is used in configure for compilation # tests. AC_DEFUN([LT_OUTPUT], [: ${CONFIG_LT=./config.lt} AC_MSG_NOTICE([creating $CONFIG_LT]) cat >"$CONFIG_LT" <<_LTEOF #! $SHELL # Generated by $as_me. # Run this file to recreate a libtool stub with the current configuration. lt_cl_silent=false SHELL=\${CONFIG_SHELL-$SHELL} _LTEOF cat >>"$CONFIG_LT" <<\_LTEOF AS_SHELL_SANITIZE _AS_PREPARE exec AS_MESSAGE_FD>&1 exec AS_MESSAGE_LOG_FD>>config.log { echo AS_BOX([Running $as_me.]) } >&AS_MESSAGE_LOG_FD lt_cl_help="\ \`$as_me' creates a local libtool stub from the current configuration, for use in further configure time tests before the real libtool is generated. Usage: $[0] [[OPTIONS]] -h, --help print this help, then exit -V, --version print version number, then exit -q, --quiet do not print progress messages -d, --debug don't remove temporary files Report bugs to ." lt_cl_version="\ m4_ifset([AC_PACKAGE_NAME], [AC_PACKAGE_NAME ])config.lt[]dnl m4_ifset([AC_PACKAGE_VERSION], [ AC_PACKAGE_VERSION]) configured by $[0], generated by m4_PACKAGE_STRING. Copyright (C) 2008 Free Software Foundation, Inc. This config.lt script is free software; the Free Software Foundation gives unlimited permision to copy, distribute and modify it." while test $[#] != 0 do case $[1] in --version | --v* | -V ) echo "$lt_cl_version"; exit 0 ;; --help | --h* | -h ) echo "$lt_cl_help"; exit 0 ;; --debug | --d* | -d ) debug=: ;; --quiet | --q* | --silent | --s* | -q ) lt_cl_silent=: ;; -*) AC_MSG_ERROR([unrecognized option: $[1] Try \`$[0] --help' for more information.]) ;; *) AC_MSG_ERROR([unrecognized argument: $[1] Try \`$[0] --help' for more information.]) ;; esac shift done if $lt_cl_silent; then exec AS_MESSAGE_FD>/dev/null fi _LTEOF cat >>"$CONFIG_LT" <<_LTEOF _LT_OUTPUT_LIBTOOL_COMMANDS_INIT _LTEOF cat >>"$CONFIG_LT" <<\_LTEOF AC_MSG_NOTICE([creating $ofile]) _LT_OUTPUT_LIBTOOL_COMMANDS AS_EXIT(0) _LTEOF chmod +x "$CONFIG_LT" # configure is writing to config.log, but config.lt does its own redirection, # appending to config.log, which fails on DOS, as config.log is still kept # open by configure. Here we exec the FD to /dev/null, effectively closing # config.log, so it can be properly (re)opened and appended to by config.lt. if test "$no_create" != yes; then lt_cl_success=: test "$silent" = yes && lt_config_lt_args="$lt_config_lt_args --quiet" exec AS_MESSAGE_LOG_FD>/dev/null $SHELL "$CONFIG_LT" $lt_config_lt_args || lt_cl_success=false exec AS_MESSAGE_LOG_FD>>config.log $lt_cl_success || AS_EXIT(1) fi ])# LT_OUTPUT # _LT_CONFIG(TAG) # --------------- # If TAG is the built-in tag, create an initial libtool script with a # default configuration from the untagged config vars. Otherwise add code # to config.status for appending the configuration named by TAG from the # matching tagged config vars. m4_defun([_LT_CONFIG], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl _LT_CONFIG_SAVE_COMMANDS([ m4_define([_LT_TAG], m4_if([$1], [], [C], [$1]))dnl m4_if(_LT_TAG, [C], [ # See if we are running on zsh, and set the options which allow our # commands through without removal of \ escapes. if test -n "${ZSH_VERSION+set}" ; then setopt NO_GLOB_SUBST fi cfgfile="${ofile}T" trap "$RM \"$cfgfile\"; exit 1" 1 2 15 $RM "$cfgfile" cat <<_LT_EOF >> "$cfgfile" #! $SHELL # `$ECHO "$ofile" | sed 's%^.*/%%'` - Provide generalized library-building support services. # Generated automatically by $as_me ($PACKAGE$TIMESTAMP) $VERSION # Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`: # NOTE: Changes made to this file will be lost: look at ltmain.sh. # _LT_COPYING _LT_LIBTOOL_TAGS # ### BEGIN LIBTOOL CONFIG _LT_LIBTOOL_CONFIG_VARS _LT_LIBTOOL_TAG_VARS # ### END LIBTOOL CONFIG _LT_EOF case $host_os in aix3*) cat <<\_LT_EOF >> "$cfgfile" # AIX sometimes has problems with the GCC collect2 program. For some # reason, if we set the COLLECT_NAMES environment variable, the problems # vanish in a puff of smoke. if test "X${COLLECT_NAMES+set}" != Xset; then COLLECT_NAMES= export COLLECT_NAMES fi _LT_EOF ;; esac _LT_PROG_LTMAIN # We use sed instead of cat because bash on DJGPP gets confused if # if finds mixed CR/LF and LF-only lines. Since sed operates in # text mode, it properly converts lines to CR/LF. This bash problem # is reportedly fixed, but why not run on old versions too? sed '/^# Generated shell functions inserted here/q' "$ltmain" >> "$cfgfile" \ || (rm -f "$cfgfile"; exit 1) _LT_PROG_XSI_SHELLFNS sed -n '/^# Generated shell functions inserted here/,$p' "$ltmain" >> "$cfgfile" \ || (rm -f "$cfgfile"; exit 1) mv -f "$cfgfile" "$ofile" || (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile") chmod +x "$ofile" ], [cat <<_LT_EOF >> "$ofile" dnl Unfortunately we have to use $1 here, since _LT_TAG is not expanded dnl in a comment (ie after a #). # ### BEGIN LIBTOOL TAG CONFIG: $1 _LT_LIBTOOL_TAG_VARS(_LT_TAG) # ### END LIBTOOL TAG CONFIG: $1 _LT_EOF ])dnl /m4_if ], [m4_if([$1], [], [ PACKAGE='$PACKAGE' VERSION='$VERSION' TIMESTAMP='$TIMESTAMP' RM='$RM' ofile='$ofile'], []) ])dnl /_LT_CONFIG_SAVE_COMMANDS ])# _LT_CONFIG # LT_SUPPORTED_TAG(TAG) # --------------------- # Trace this macro to discover what tags are supported by the libtool # --tag option, using: # autoconf --trace 'LT_SUPPORTED_TAG:$1' AC_DEFUN([LT_SUPPORTED_TAG], []) # C support is built-in for now m4_define([_LT_LANG_C_enabled], []) m4_define([_LT_TAGS], []) # LT_LANG(LANG) # ------------- # Enable libtool support for the given language if not already enabled. AC_DEFUN([LT_LANG], [AC_BEFORE([$0], [LT_OUTPUT])dnl m4_case([$1], [C], [_LT_LANG(C)], [C++], [_LT_LANG(CXX)], [Java], [_LT_LANG(GCJ)], [Fortran 77], [_LT_LANG(F77)], [Fortran], [_LT_LANG(FC)], [Windows Resource], [_LT_LANG(RC)], [m4_ifdef([_LT_LANG_]$1[_CONFIG], [_LT_LANG($1)], [m4_fatal([$0: unsupported language: "$1"])])])dnl ])# LT_LANG # _LT_LANG(LANGNAME) # ------------------ m4_defun([_LT_LANG], [m4_ifdef([_LT_LANG_]$1[_enabled], [], [LT_SUPPORTED_TAG([$1])dnl m4_append([_LT_TAGS], [$1 ])dnl m4_define([_LT_LANG_]$1[_enabled], [])dnl _LT_LANG_$1_CONFIG($1)])dnl ])# _LT_LANG # _LT_LANG_DEFAULT_CONFIG # ----------------------- m4_defun([_LT_LANG_DEFAULT_CONFIG], [AC_PROVIDE_IFELSE([AC_PROG_CXX], [LT_LANG(CXX)], [m4_define([AC_PROG_CXX], defn([AC_PROG_CXX])[LT_LANG(CXX)])]) AC_PROVIDE_IFELSE([AC_PROG_F77], [LT_LANG(F77)], [m4_define([AC_PROG_F77], defn([AC_PROG_F77])[LT_LANG(F77)])]) AC_PROVIDE_IFELSE([AC_PROG_FC], [LT_LANG(FC)], [m4_define([AC_PROG_FC], defn([AC_PROG_FC])[LT_LANG(FC)])]) dnl The call to [A][M_PROG_GCJ] is quoted like that to stop aclocal dnl pulling things in needlessly. AC_PROVIDE_IFELSE([AC_PROG_GCJ], [LT_LANG(GCJ)], [AC_PROVIDE_IFELSE([A][M_PROG_GCJ], [LT_LANG(GCJ)], [AC_PROVIDE_IFELSE([LT_PROG_GCJ], [LT_LANG(GCJ)], [m4_ifdef([AC_PROG_GCJ], [m4_define([AC_PROG_GCJ], defn([AC_PROG_GCJ])[LT_LANG(GCJ)])]) m4_ifdef([A][M_PROG_GCJ], [m4_define([A][M_PROG_GCJ], defn([A][M_PROG_GCJ])[LT_LANG(GCJ)])]) m4_ifdef([LT_PROG_GCJ], [m4_define([LT_PROG_GCJ], defn([LT_PROG_GCJ])[LT_LANG(GCJ)])])])])]) AC_PROVIDE_IFELSE([LT_PROG_RC], [LT_LANG(RC)], [m4_define([LT_PROG_RC], defn([LT_PROG_RC])[LT_LANG(RC)])]) ])# _LT_LANG_DEFAULT_CONFIG # Obsolete macros: AU_DEFUN([AC_LIBTOOL_CXX], [LT_LANG(C++)]) AU_DEFUN([AC_LIBTOOL_F77], [LT_LANG(Fortran 77)]) AU_DEFUN([AC_LIBTOOL_FC], [LT_LANG(Fortran)]) AU_DEFUN([AC_LIBTOOL_GCJ], [LT_LANG(Java)]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_CXX], []) dnl AC_DEFUN([AC_LIBTOOL_F77], []) dnl AC_DEFUN([AC_LIBTOOL_FC], []) dnl AC_DEFUN([AC_LIBTOOL_GCJ], []) # _LT_TAG_COMPILER # ---------------- m4_defun([_LT_TAG_COMPILER], [AC_REQUIRE([AC_PROG_CC])dnl _LT_DECL([LTCC], [CC], [1], [A C compiler])dnl _LT_DECL([LTCFLAGS], [CFLAGS], [1], [LTCC compiler flags])dnl _LT_TAGDECL([CC], [compiler], [1], [A language specific compiler])dnl _LT_TAGDECL([with_gcc], [GCC], [0], [Is the compiler the GNU compiler?])dnl # If no C compiler was specified, use CC. LTCC=${LTCC-"$CC"} # If no C compiler flags were specified, use CFLAGS. LTCFLAGS=${LTCFLAGS-"$CFLAGS"} # Allow CC to be a program name with arguments. compiler=$CC ])# _LT_TAG_COMPILER # _LT_COMPILER_BOILERPLATE # ------------------------ # Check for compiler boilerplate output or warnings with # the simple compiler test code. m4_defun([_LT_COMPILER_BOILERPLATE], [m4_require([_LT_DECL_SED])dnl ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" >conftest.$ac_ext eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_compiler_boilerplate=`cat conftest.err` $RM conftest* ])# _LT_COMPILER_BOILERPLATE # _LT_LINKER_BOILERPLATE # ---------------------- # Check for linker boilerplate output or warnings with # the simple link test code. m4_defun([_LT_LINKER_BOILERPLATE], [m4_require([_LT_DECL_SED])dnl ac_outfile=conftest.$ac_objext echo "$lt_simple_link_test_code" >conftest.$ac_ext eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_linker_boilerplate=`cat conftest.err` $RM -r conftest* ])# _LT_LINKER_BOILERPLATE # _LT_REQUIRED_DARWIN_CHECKS # ------------------------- m4_defun_once([_LT_REQUIRED_DARWIN_CHECKS],[ case $host_os in rhapsody* | darwin*) AC_CHECK_TOOL([DSYMUTIL], [dsymutil], [:]) AC_CHECK_TOOL([NMEDIT], [nmedit], [:]) AC_CHECK_TOOL([LIPO], [lipo], [:]) AC_CHECK_TOOL([OTOOL], [otool], [:]) AC_CHECK_TOOL([OTOOL64], [otool64], [:]) _LT_DECL([], [DSYMUTIL], [1], [Tool to manipulate archived DWARF debug symbol files on Mac OS X]) _LT_DECL([], [NMEDIT], [1], [Tool to change global to local symbols on Mac OS X]) _LT_DECL([], [LIPO], [1], [Tool to manipulate fat objects and archives on Mac OS X]) _LT_DECL([], [OTOOL], [1], [ldd/readelf like tool for Mach-O binaries on Mac OS X]) _LT_DECL([], [OTOOL64], [1], [ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4]) AC_CACHE_CHECK([for -single_module linker flag],[lt_cv_apple_cc_single_mod], [lt_cv_apple_cc_single_mod=no if test -z "${LT_MULTI_MODULE}"; then # By default we will add the -single_module flag. You can override # by either setting the environment variable LT_MULTI_MODULE # non-empty at configure time, or by adding -multi_module to the # link flags. rm -rf libconftest.dylib* echo "int foo(void){return 1;}" > conftest.c echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ -dynamiclib -Wl,-single_module conftest.c" >&AS_MESSAGE_LOG_FD $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ -dynamiclib -Wl,-single_module conftest.c 2>conftest.err _lt_result=$? if test -f libconftest.dylib && test ! -s conftest.err && test $_lt_result = 0; then lt_cv_apple_cc_single_mod=yes else cat conftest.err >&AS_MESSAGE_LOG_FD fi rm -rf libconftest.dylib* rm -f conftest.* fi]) AC_CACHE_CHECK([for -exported_symbols_list linker flag], [lt_cv_ld_exported_symbols_list], [lt_cv_ld_exported_symbols_list=no save_LDFLAGS=$LDFLAGS echo "_main" > conftest.sym LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym" AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], [lt_cv_ld_exported_symbols_list=yes], [lt_cv_ld_exported_symbols_list=no]) LDFLAGS="$save_LDFLAGS" ]) case $host_os in rhapsody* | darwin1.[[012]]) _lt_dar_allow_undefined='${wl}-undefined ${wl}suppress' ;; darwin1.*) _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;; darwin*) # darwin 5.x on # if running on 10.5 or later, the deployment target defaults # to the OS version, if on x86, and 10.4, the deployment # target defaults to 10.4. Don't you love it? case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in 10.0,*86*-darwin8*|10.0,*-darwin[[91]]*) _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;; 10.[[012]]*) _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;; 10.*) _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;; esac ;; esac if test "$lt_cv_apple_cc_single_mod" = "yes"; then _lt_dar_single_mod='$single_module' fi if test "$lt_cv_ld_exported_symbols_list" = "yes"; then _lt_dar_export_syms=' ${wl}-exported_symbols_list,$output_objdir/${libname}-symbols.expsym' else _lt_dar_export_syms='~$NMEDIT -s $output_objdir/${libname}-symbols.expsym ${lib}' fi if test "$DSYMUTIL" != ":"; then _lt_dsymutil='~$DSYMUTIL $lib || :' else _lt_dsymutil= fi ;; esac ]) # _LT_DARWIN_LINKER_FEATURES # -------------------------- # Checks for linker and compiler features on darwin m4_defun([_LT_DARWIN_LINKER_FEATURES], [ m4_require([_LT_REQUIRED_DARWIN_CHECKS]) _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_automatic, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported _LT_TAGVAR(whole_archive_flag_spec, $1)='' _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(allow_undefined_flag, $1)="$_lt_dar_allow_undefined" case $cc_basename in ifort*) _lt_dar_can_shared=yes ;; *) _lt_dar_can_shared=$GCC ;; esac if test "$_lt_dar_can_shared" = "yes"; then output_verbose_link_cmd=echo _LT_TAGVAR(archive_cmds, $1)="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" _LT_TAGVAR(module_cmds, $1)="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" _LT_TAGVAR(module_expsym_cmds, $1)="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" m4_if([$1], [CXX], [ if test "$lt_cv_apple_cc_single_mod" != "yes"; then _LT_TAGVAR(archive_cmds, $1)="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dsymutil}" _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dar_export_syms}${_lt_dsymutil}" fi ],[]) else _LT_TAGVAR(ld_shlibs, $1)=no fi ]) # _LT_SYS_MODULE_PATH_AIX # ----------------------- # Links a minimal program and checks the executable # for the system default hardcoded library path. In most cases, # this is /usr/lib:/lib, but when the MPI compilers are used # the location of the communication and MPI libs are included too. # If we don't find anything, use the default library path according # to the aix ld manual. m4_defun([_LT_SYS_MODULE_PATH_AIX], [m4_require([_LT_DECL_SED])dnl AC_LINK_IFELSE(AC_LANG_PROGRAM,[ lt_aix_libpath_sed=' /Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/ p } }' aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` # Check for a 64-bit object if we didn't find anything. if test -z "$aix_libpath"; then aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` fi],[]) if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi ])# _LT_SYS_MODULE_PATH_AIX # _LT_SHELL_INIT(ARG) # ------------------- m4_define([_LT_SHELL_INIT], [ifdef([AC_DIVERSION_NOTICE], [AC_DIVERT_PUSH(AC_DIVERSION_NOTICE)], [AC_DIVERT_PUSH(NOTICE)]) $1 AC_DIVERT_POP ])# _LT_SHELL_INIT # _LT_PROG_ECHO_BACKSLASH # ----------------------- # Add some code to the start of the generated configure script which # will find an echo command which doesn't interpret backslashes. m4_defun([_LT_PROG_ECHO_BACKSLASH], [_LT_SHELL_INIT([ # Check that we are running under the correct shell. SHELL=${CONFIG_SHELL-/bin/sh} case X$lt_ECHO in X*--fallback-echo) # Remove one level of quotation (which was required for Make). ECHO=`echo "$lt_ECHO" | sed 's,\\\\\[$]\\[$]0,'[$]0','` ;; esac ECHO=${lt_ECHO-echo} if test "X[$]1" = X--no-reexec; then # Discard the --no-reexec flag, and continue. shift elif test "X[$]1" = X--fallback-echo; then # Avoid inline document here, it may be left over : elif test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t' ; then # Yippee, $ECHO works! : else # Restart under the correct shell. exec $SHELL "[$]0" --no-reexec ${1+"[$]@"} fi if test "X[$]1" = X--fallback-echo; then # used as fallback echo shift cat <<_LT_EOF [$]* _LT_EOF exit 0 fi # The HP-UX ksh and POSIX shell print the target directory to stdout # if CDPATH is set. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH if test -z "$lt_ECHO"; then if test "X${echo_test_string+set}" != Xset; then # find a string as large as possible, as long as the shell can cope with it for cmd in 'sed 50q "[$]0"' 'sed 20q "[$]0"' 'sed 10q "[$]0"' 'sed 2q "[$]0"' 'echo test'; do # expected sizes: less than 2Kb, 1Kb, 512 bytes, 16 bytes, ... if { echo_test_string=`eval $cmd`; } 2>/dev/null && { test "X$echo_test_string" = "X$echo_test_string"; } 2>/dev/null then break fi done fi if test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t' && echo_testing_string=`{ $ECHO "$echo_test_string"; } 2>/dev/null` && test "X$echo_testing_string" = "X$echo_test_string"; then : else # The Solaris, AIX, and Digital Unix default echo programs unquote # backslashes. This makes it impossible to quote backslashes using # echo "$something" | sed 's/\\/\\\\/g' # # So, first we look for a working echo in the user's PATH. lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR for dir in $PATH /usr/ucb; do IFS="$lt_save_ifs" if (test -f $dir/echo || test -f $dir/echo$ac_exeext) && test "X`($dir/echo '\t') 2>/dev/null`" = 'X\t' && echo_testing_string=`($dir/echo "$echo_test_string") 2>/dev/null` && test "X$echo_testing_string" = "X$echo_test_string"; then ECHO="$dir/echo" break fi done IFS="$lt_save_ifs" if test "X$ECHO" = Xecho; then # We didn't find a better echo, so look for alternatives. if test "X`{ print -r '\t'; } 2>/dev/null`" = 'X\t' && echo_testing_string=`{ print -r "$echo_test_string"; } 2>/dev/null` && test "X$echo_testing_string" = "X$echo_test_string"; then # This shell has a builtin print -r that does the trick. ECHO='print -r' elif { test -f /bin/ksh || test -f /bin/ksh$ac_exeext; } && test "X$CONFIG_SHELL" != X/bin/ksh; then # If we have ksh, try running configure again with it. ORIGINAL_CONFIG_SHELL=${CONFIG_SHELL-/bin/sh} export ORIGINAL_CONFIG_SHELL CONFIG_SHELL=/bin/ksh export CONFIG_SHELL exec $CONFIG_SHELL "[$]0" --no-reexec ${1+"[$]@"} else # Try using printf. ECHO='printf %s\n' if test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t' && echo_testing_string=`{ $ECHO "$echo_test_string"; } 2>/dev/null` && test "X$echo_testing_string" = "X$echo_test_string"; then # Cool, printf works : elif echo_testing_string=`($ORIGINAL_CONFIG_SHELL "[$]0" --fallback-echo '\t') 2>/dev/null` && test "X$echo_testing_string" = 'X\t' && echo_testing_string=`($ORIGINAL_CONFIG_SHELL "[$]0" --fallback-echo "$echo_test_string") 2>/dev/null` && test "X$echo_testing_string" = "X$echo_test_string"; then CONFIG_SHELL=$ORIGINAL_CONFIG_SHELL export CONFIG_SHELL SHELL="$CONFIG_SHELL" export SHELL ECHO="$CONFIG_SHELL [$]0 --fallback-echo" elif echo_testing_string=`($CONFIG_SHELL "[$]0" --fallback-echo '\t') 2>/dev/null` && test "X$echo_testing_string" = 'X\t' && echo_testing_string=`($CONFIG_SHELL "[$]0" --fallback-echo "$echo_test_string") 2>/dev/null` && test "X$echo_testing_string" = "X$echo_test_string"; then ECHO="$CONFIG_SHELL [$]0 --fallback-echo" else # maybe with a smaller string... prev=: for cmd in 'echo test' 'sed 2q "[$]0"' 'sed 10q "[$]0"' 'sed 20q "[$]0"' 'sed 50q "[$]0"'; do if { test "X$echo_test_string" = "X`eval $cmd`"; } 2>/dev/null then break fi prev="$cmd" done if test "$prev" != 'sed 50q "[$]0"'; then echo_test_string=`eval $prev` export echo_test_string exec ${ORIGINAL_CONFIG_SHELL-${CONFIG_SHELL-/bin/sh}} "[$]0" ${1+"[$]@"} else # Oops. We lost completely, so just stick with echo. ECHO=echo fi fi fi fi fi fi # Copy echo and quote the copy suitably for passing to libtool from # the Makefile, instead of quoting the original, which is used later. lt_ECHO=$ECHO if test "X$lt_ECHO" = "X$CONFIG_SHELL [$]0 --fallback-echo"; then lt_ECHO="$CONFIG_SHELL \\\$\[$]0 --fallback-echo" fi AC_SUBST(lt_ECHO) ]) _LT_DECL([], [SHELL], [1], [Shell to use when invoking shell scripts]) _LT_DECL([], [ECHO], [1], [An echo program that does not interpret backslashes]) ])# _LT_PROG_ECHO_BACKSLASH # _LT_ENABLE_LOCK # --------------- m4_defun([_LT_ENABLE_LOCK], [AC_ARG_ENABLE([libtool-lock], [AS_HELP_STRING([--disable-libtool-lock], [avoid locking (might break parallel builds)])]) test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes # Some flags need to be propagated to the compiler or linker for good # libtool support. case $host in ia64-*-hpux*) # Find out which ABI we are using. echo 'int i;' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then case `/usr/bin/file conftest.$ac_objext` in *ELF-32*) HPUX_IA64_MODE="32" ;; *ELF-64*) HPUX_IA64_MODE="64" ;; esac fi rm -rf conftest* ;; *-*-irix6*) # Find out which ABI we are using. echo '[#]line __oline__ "configure"' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then if test "$lt_cv_prog_gnu_ld" = yes; then case `/usr/bin/file conftest.$ac_objext` in *32-bit*) LD="${LD-ld} -melf32bsmip" ;; *N32*) LD="${LD-ld} -melf32bmipn32" ;; *64-bit*) LD="${LD-ld} -melf64bmip" ;; esac else case `/usr/bin/file conftest.$ac_objext` in *32-bit*) LD="${LD-ld} -32" ;; *N32*) LD="${LD-ld} -n32" ;; *64-bit*) LD="${LD-ld} -64" ;; esac fi fi rm -rf conftest* ;; x86_64-*kfreebsd*-gnu|x86_64-*linux*|ppc*-*linux*|powerpc*-*linux*| \ s390*-*linux*|s390*-*tpf*|sparc*-*linux*) # Find out which ABI we are using. echo 'int i;' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then case `/usr/bin/file conftest.o` in *32-bit*) case $host in x86_64-*kfreebsd*-gnu) LD="${LD-ld} -m elf_i386_fbsd" ;; x86_64-*linux*) LD="${LD-ld} -m elf_i386" ;; ppc64-*linux*|powerpc64-*linux*) LD="${LD-ld} -m elf32ppclinux" ;; s390x-*linux*) LD="${LD-ld} -m elf_s390" ;; sparc64-*linux*) LD="${LD-ld} -m elf32_sparc" ;; esac ;; *64-bit*) case $host in x86_64-*kfreebsd*-gnu) LD="${LD-ld} -m elf_x86_64_fbsd" ;; x86_64-*linux*) LD="${LD-ld} -m elf_x86_64" ;; ppc*-*linux*|powerpc*-*linux*) LD="${LD-ld} -m elf64ppc" ;; s390*-*linux*|s390*-*tpf*) LD="${LD-ld} -m elf64_s390" ;; sparc*-*linux*) LD="${LD-ld} -m elf64_sparc" ;; esac ;; esac fi rm -rf conftest* ;; *-*-sco3.2v5*) # On SCO OpenServer 5, we need -belf to get full-featured binaries. SAVE_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -belf" AC_CACHE_CHECK([whether the C compiler needs -belf], lt_cv_cc_needs_belf, [AC_LANG_PUSH(C) AC_LINK_IFELSE([AC_LANG_PROGRAM([[]],[[]])],[lt_cv_cc_needs_belf=yes],[lt_cv_cc_needs_belf=no]) AC_LANG_POP]) if test x"$lt_cv_cc_needs_belf" != x"yes"; then # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf CFLAGS="$SAVE_CFLAGS" fi ;; sparc*-*solaris*) # Find out which ABI we are using. echo 'int i;' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then case `/usr/bin/file conftest.o` in *64-bit*) case $lt_cv_prog_gnu_ld in yes*) LD="${LD-ld} -m elf64_sparc" ;; *) if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then LD="${LD-ld} -64" fi ;; esac ;; esac fi rm -rf conftest* ;; esac need_locks="$enable_libtool_lock" ])# _LT_ENABLE_LOCK # _LT_CMD_OLD_ARCHIVE # ------------------- m4_defun([_LT_CMD_OLD_ARCHIVE], [AC_CHECK_TOOL(AR, ar, false) test -z "$AR" && AR=ar test -z "$AR_FLAGS" && AR_FLAGS=cru _LT_DECL([], [AR], [1], [The archiver]) _LT_DECL([], [AR_FLAGS], [1]) AC_CHECK_TOOL(STRIP, strip, :) test -z "$STRIP" && STRIP=: _LT_DECL([], [STRIP], [1], [A symbol stripping program]) AC_CHECK_TOOL(RANLIB, ranlib, :) test -z "$RANLIB" && RANLIB=: _LT_DECL([], [RANLIB], [1], [Commands used to install an old-style archive]) # Determine commands to create old-style static archives. old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs' old_postinstall_cmds='chmod 644 $oldlib' old_postuninstall_cmds= if test -n "$RANLIB"; then case $host_os in openbsd*) old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$oldlib" ;; *) old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$oldlib" ;; esac old_archive_cmds="$old_archive_cmds~\$RANLIB \$oldlib" fi _LT_DECL([], [old_postinstall_cmds], [2]) _LT_DECL([], [old_postuninstall_cmds], [2]) _LT_TAGDECL([], [old_archive_cmds], [2], [Commands used to build an old-style archive]) ])# _LT_CMD_OLD_ARCHIVE # _LT_COMPILER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, # [OUTPUT-FILE], [ACTION-SUCCESS], [ACTION-FAILURE]) # ---------------------------------------------------------------- # Check whether the given compiler option works AC_DEFUN([_LT_COMPILER_OPTION], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_SED])dnl AC_CACHE_CHECK([$1], [$2], [$2=no m4_if([$4], , [ac_outfile=conftest.$ac_objext], [ac_outfile=$4]) echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="$3" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. # The option is referenced via a variable to avoid confusing sed. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:__oline__: $lt_compile\"" >&AS_MESSAGE_LOG_FD) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&AS_MESSAGE_LOG_FD echo "$as_me:__oline__: \$? = $ac_status" >&AS_MESSAGE_LOG_FD if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' >conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then $2=yes fi fi $RM conftest* ]) if test x"[$]$2" = xyes; then m4_if([$5], , :, [$5]) else m4_if([$6], , :, [$6]) fi ])# _LT_COMPILER_OPTION # Old name: AU_ALIAS([AC_LIBTOOL_COMPILER_OPTION], [_LT_COMPILER_OPTION]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_COMPILER_OPTION], []) # _LT_LINKER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, # [ACTION-SUCCESS], [ACTION-FAILURE]) # ---------------------------------------------------- # Check whether the given linker option works AC_DEFUN([_LT_LINKER_OPTION], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_SED])dnl AC_CACHE_CHECK([$1], [$2], [$2=no save_LDFLAGS="$LDFLAGS" LDFLAGS="$LDFLAGS $3" echo "$lt_simple_link_test_code" > conftest.$ac_ext if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then # The linker can only warn and ignore the option if not recognized # So say no if there are warnings if test -s conftest.err; then # Append any errors to the config.log. cat conftest.err 1>&AS_MESSAGE_LOG_FD $ECHO "X$_lt_linker_boilerplate" | $Xsed -e '/^$/d' > conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if diff conftest.exp conftest.er2 >/dev/null; then $2=yes fi else $2=yes fi fi $RM -r conftest* LDFLAGS="$save_LDFLAGS" ]) if test x"[$]$2" = xyes; then m4_if([$4], , :, [$4]) else m4_if([$5], , :, [$5]) fi ])# _LT_LINKER_OPTION # Old name: AU_ALIAS([AC_LIBTOOL_LINKER_OPTION], [_LT_LINKER_OPTION]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_LINKER_OPTION], []) # LT_CMD_MAX_LEN #--------------- AC_DEFUN([LT_CMD_MAX_LEN], [AC_REQUIRE([AC_CANONICAL_HOST])dnl # find the maximum length of command line arguments AC_MSG_CHECKING([the maximum length of command line arguments]) AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [dnl i=0 teststring="ABCD" case $build_os in msdosdjgpp*) # On DJGPP, this test can blow up pretty badly due to problems in libc # (any single argument exceeding 2000 bytes causes a buffer overrun # during glob expansion). Even if it were fixed, the result of this # check would be larger than it should be. lt_cv_sys_max_cmd_len=12288; # 12K is about right ;; gnu*) # Under GNU Hurd, this test is not required because there is # no limit to the length of command line arguments. # Libtool will interpret -1 as no limit whatsoever lt_cv_sys_max_cmd_len=-1; ;; cygwin* | mingw* | cegcc*) # On Win9x/ME, this test blows up -- it succeeds, but takes # about 5 minutes as the teststring grows exponentially. # Worse, since 9x/ME are not pre-emptively multitasking, # you end up with a "frozen" computer, even though with patience # the test eventually succeeds (with a max line length of 256k). # Instead, let's just punt: use the minimum linelength reported by # all of the supported platforms: 8192 (on NT/2K/XP). lt_cv_sys_max_cmd_len=8192; ;; amigaos*) # On AmigaOS with pdksh, this test takes hours, literally. # So we just punt and use a minimum line length of 8192. lt_cv_sys_max_cmd_len=8192; ;; netbsd* | freebsd* | openbsd* | darwin* | dragonfly*) # This has been around since 386BSD, at least. Likely further. if test -x /sbin/sysctl; then lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax` elif test -x /usr/sbin/sysctl; then lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax` else lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs fi # And add a safety zone lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` ;; interix*) # We know the value 262144 and hardcode it with a safety zone (like BSD) lt_cv_sys_max_cmd_len=196608 ;; osf*) # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not # nice to cause kernel panics so lets avoid the loop below. # First set a reasonable default. lt_cv_sys_max_cmd_len=16384 # if test -x /sbin/sysconfig; then case `/sbin/sysconfig -q proc exec_disable_arg_limit` in *1*) lt_cv_sys_max_cmd_len=-1 ;; esac fi ;; sco3.2v5*) lt_cv_sys_max_cmd_len=102400 ;; sysv5* | sco5v6* | sysv4.2uw2*) kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null` if test -n "$kargmax"; then lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[[ ]]//'` else lt_cv_sys_max_cmd_len=32768 fi ;; *) lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null` if test -n "$lt_cv_sys_max_cmd_len"; then lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` else # Make teststring a little bigger before we do anything with it. # a 1K string should be a reasonable start. for i in 1 2 3 4 5 6 7 8 ; do teststring=$teststring$teststring done SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}} # If test is not a shell built-in, we'll probably end up computing a # maximum length that is only half of the actual maximum length, but # we can't tell. while { test "X"`$SHELL [$]0 --fallback-echo "X$teststring$teststring" 2>/dev/null` \ = "XX$teststring$teststring"; } >/dev/null 2>&1 && test $i != 17 # 1/2 MB should be enough do i=`expr $i + 1` teststring=$teststring$teststring done # Only check the string length outside the loop. lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1` teststring= # Add a significant safety factor because C++ compilers can tack on # massive amounts of additional arguments before passing them to the # linker. It appears as though 1/2 is a usable value. lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2` fi ;; esac ]) if test -n $lt_cv_sys_max_cmd_len ; then AC_MSG_RESULT($lt_cv_sys_max_cmd_len) else AC_MSG_RESULT(none) fi max_cmd_len=$lt_cv_sys_max_cmd_len _LT_DECL([], [max_cmd_len], [0], [What is the maximum length of a command?]) ])# LT_CMD_MAX_LEN # Old name: AU_ALIAS([AC_LIBTOOL_SYS_MAX_CMD_LEN], [LT_CMD_MAX_LEN]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_SYS_MAX_CMD_LEN], []) # _LT_HEADER_DLFCN # ---------------- m4_defun([_LT_HEADER_DLFCN], [AC_CHECK_HEADERS([dlfcn.h], [], [], [AC_INCLUDES_DEFAULT])dnl ])# _LT_HEADER_DLFCN # _LT_TRY_DLOPEN_SELF (ACTION-IF-TRUE, ACTION-IF-TRUE-W-USCORE, # ACTION-IF-FALSE, ACTION-IF-CROSS-COMPILING) # ---------------------------------------------------------------- m4_defun([_LT_TRY_DLOPEN_SELF], [m4_require([_LT_HEADER_DLFCN])dnl if test "$cross_compiling" = yes; then : [$4] else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF [#line __oline__ "configure" #include "confdefs.h" #if HAVE_DLFCN_H #include #endif #include #ifdef RTLD_GLOBAL # define LT_DLGLOBAL RTLD_GLOBAL #else # ifdef DL_GLOBAL # define LT_DLGLOBAL DL_GLOBAL # else # define LT_DLGLOBAL 0 # endif #endif /* We may have to define LT_DLLAZY_OR_NOW in the command line if we find out it does not work in some platform. */ #ifndef LT_DLLAZY_OR_NOW # ifdef RTLD_LAZY # define LT_DLLAZY_OR_NOW RTLD_LAZY # else # ifdef DL_LAZY # define LT_DLLAZY_OR_NOW DL_LAZY # else # ifdef RTLD_NOW # define LT_DLLAZY_OR_NOW RTLD_NOW # else # ifdef DL_NOW # define LT_DLLAZY_OR_NOW DL_NOW # else # define LT_DLLAZY_OR_NOW 0 # endif # endif # endif # endif #endif void fnord() { int i=42;} int main () { void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); int status = $lt_dlunknown; if (self) { if (dlsym (self,"fnord")) status = $lt_dlno_uscore; else if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; /* dlclose (self); */ } else puts (dlerror ()); return status; }] _LT_EOF if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext} 2>/dev/null; then (./conftest; exit; ) >&AS_MESSAGE_LOG_FD 2>/dev/null lt_status=$? case x$lt_status in x$lt_dlno_uscore) $1 ;; x$lt_dlneed_uscore) $2 ;; x$lt_dlunknown|x*) $3 ;; esac else : # compilation failed $3 fi fi rm -fr conftest* ])# _LT_TRY_DLOPEN_SELF # LT_SYS_DLOPEN_SELF # ------------------ AC_DEFUN([LT_SYS_DLOPEN_SELF], [m4_require([_LT_HEADER_DLFCN])dnl if test "x$enable_dlopen" != xyes; then enable_dlopen=unknown enable_dlopen_self=unknown enable_dlopen_self_static=unknown else lt_cv_dlopen=no lt_cv_dlopen_libs= case $host_os in beos*) lt_cv_dlopen="load_add_on" lt_cv_dlopen_libs= lt_cv_dlopen_self=yes ;; mingw* | pw32* | cegcc*) lt_cv_dlopen="LoadLibrary" lt_cv_dlopen_libs= ;; cygwin*) lt_cv_dlopen="dlopen" lt_cv_dlopen_libs= ;; darwin*) # if libdl is installed we need to link against it AC_CHECK_LIB([dl], [dlopen], [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"],[ lt_cv_dlopen="dyld" lt_cv_dlopen_libs= lt_cv_dlopen_self=yes ]) ;; *) AC_CHECK_FUNC([shl_load], [lt_cv_dlopen="shl_load"], [AC_CHECK_LIB([dld], [shl_load], [lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-ldld"], [AC_CHECK_FUNC([dlopen], [lt_cv_dlopen="dlopen"], [AC_CHECK_LIB([dl], [dlopen], [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"], [AC_CHECK_LIB([svld], [dlopen], [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld"], [AC_CHECK_LIB([dld], [dld_link], [lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-ldld"]) ]) ]) ]) ]) ]) ;; esac if test "x$lt_cv_dlopen" != xno; then enable_dlopen=yes else enable_dlopen=no fi case $lt_cv_dlopen in dlopen) save_CPPFLAGS="$CPPFLAGS" test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" save_LDFLAGS="$LDFLAGS" wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" save_LIBS="$LIBS" LIBS="$lt_cv_dlopen_libs $LIBS" AC_CACHE_CHECK([whether a program can dlopen itself], lt_cv_dlopen_self, [dnl _LT_TRY_DLOPEN_SELF( lt_cv_dlopen_self=yes, lt_cv_dlopen_self=yes, lt_cv_dlopen_self=no, lt_cv_dlopen_self=cross) ]) if test "x$lt_cv_dlopen_self" = xyes; then wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\" AC_CACHE_CHECK([whether a statically linked program can dlopen itself], lt_cv_dlopen_self_static, [dnl _LT_TRY_DLOPEN_SELF( lt_cv_dlopen_self_static=yes, lt_cv_dlopen_self_static=yes, lt_cv_dlopen_self_static=no, lt_cv_dlopen_self_static=cross) ]) fi CPPFLAGS="$save_CPPFLAGS" LDFLAGS="$save_LDFLAGS" LIBS="$save_LIBS" ;; esac case $lt_cv_dlopen_self in yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;; *) enable_dlopen_self=unknown ;; esac case $lt_cv_dlopen_self_static in yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;; *) enable_dlopen_self_static=unknown ;; esac fi _LT_DECL([dlopen_support], [enable_dlopen], [0], [Whether dlopen is supported]) _LT_DECL([dlopen_self], [enable_dlopen_self], [0], [Whether dlopen of programs is supported]) _LT_DECL([dlopen_self_static], [enable_dlopen_self_static], [0], [Whether dlopen of statically linked programs is supported]) ])# LT_SYS_DLOPEN_SELF # Old name: AU_ALIAS([AC_LIBTOOL_DLOPEN_SELF], [LT_SYS_DLOPEN_SELF]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_DLOPEN_SELF], []) # _LT_COMPILER_C_O([TAGNAME]) # --------------------------- # Check to see if options -c and -o are simultaneously supported by compiler. # This macro does not hard code the compiler like AC_PROG_CC_C_O. m4_defun([_LT_COMPILER_C_O], [m4_require([_LT_DECL_SED])dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_TAG_COMPILER])dnl AC_CACHE_CHECK([if $compiler supports -c -o file.$ac_objext], [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)], [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=no $RM -r conftest 2>/dev/null mkdir conftest cd conftest mkdir out echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-o out/conftest2.$ac_objext" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:__oline__: $lt_compile\"" >&AS_MESSAGE_LOG_FD) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&AS_MESSAGE_LOG_FD echo "$as_me:__oline__: \$? = $ac_status" >&AS_MESSAGE_LOG_FD if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' > out/conftest.exp $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then _LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes fi fi chmod u+w . 2>&AS_MESSAGE_LOG_FD $RM conftest* # SGI C++ compiler will create directory out/ii_files/ for # template instantiation test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files $RM out/* && rmdir out cd .. $RM -r conftest $RM conftest* ]) _LT_TAGDECL([compiler_c_o], [lt_cv_prog_compiler_c_o], [1], [Does compiler simultaneously support -c and -o options?]) ])# _LT_COMPILER_C_O # _LT_COMPILER_FILE_LOCKS([TAGNAME]) # ---------------------------------- # Check to see if we can do hard links to lock some files if needed m4_defun([_LT_COMPILER_FILE_LOCKS], [m4_require([_LT_ENABLE_LOCK])dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl _LT_COMPILER_C_O([$1]) hard_links="nottested" if test "$_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)" = no && test "$need_locks" != no; then # do not overwrite the value of need_locks provided by the user AC_MSG_CHECKING([if we can lock with hard links]) hard_links=yes $RM conftest* ln conftest.a conftest.b 2>/dev/null && hard_links=no touch conftest.a ln conftest.a conftest.b 2>&5 || hard_links=no ln conftest.a conftest.b 2>/dev/null && hard_links=no AC_MSG_RESULT([$hard_links]) if test "$hard_links" = no; then AC_MSG_WARN([`$CC' does not support `-c -o', so `make -j' may be unsafe]) need_locks=warn fi else need_locks=no fi _LT_DECL([], [need_locks], [1], [Must we lock files when doing compilation?]) ])# _LT_COMPILER_FILE_LOCKS # _LT_CHECK_OBJDIR # ---------------- m4_defun([_LT_CHECK_OBJDIR], [AC_CACHE_CHECK([for objdir], [lt_cv_objdir], [rm -f .libs 2>/dev/null mkdir .libs 2>/dev/null if test -d .libs; then lt_cv_objdir=.libs else # MS-DOS does not allow filenames that begin with a dot. lt_cv_objdir=_libs fi rmdir .libs 2>/dev/null]) objdir=$lt_cv_objdir _LT_DECL([], [objdir], [0], [The name of the directory that contains temporary libtool files])dnl m4_pattern_allow([LT_OBJDIR])dnl AC_DEFINE_UNQUOTED(LT_OBJDIR, "$lt_cv_objdir/", [Define to the sub-directory in which libtool stores uninstalled libraries.]) ])# _LT_CHECK_OBJDIR # _LT_LINKER_HARDCODE_LIBPATH([TAGNAME]) # -------------------------------------- # Check hardcoding attributes. m4_defun([_LT_LINKER_HARDCODE_LIBPATH], [AC_MSG_CHECKING([how to hardcode library paths into programs]) _LT_TAGVAR(hardcode_action, $1)= if test -n "$_LT_TAGVAR(hardcode_libdir_flag_spec, $1)" || test -n "$_LT_TAGVAR(runpath_var, $1)" || test "X$_LT_TAGVAR(hardcode_automatic, $1)" = "Xyes" ; then # We can hardcode non-existent directories. if test "$_LT_TAGVAR(hardcode_direct, $1)" != no && # If the only mechanism to avoid hardcoding is shlibpath_var, we # have to relink, otherwise we might link with an installed library # when we should be linking with a yet-to-be-installed one ## test "$_LT_TAGVAR(hardcode_shlibpath_var, $1)" != no && test "$_LT_TAGVAR(hardcode_minus_L, $1)" != no; then # Linking always hardcodes the temporary library directory. _LT_TAGVAR(hardcode_action, $1)=relink else # We can link without hardcoding, and we can hardcode nonexisting dirs. _LT_TAGVAR(hardcode_action, $1)=immediate fi else # We cannot hardcode anything, or else we can only hardcode existing # directories. _LT_TAGVAR(hardcode_action, $1)=unsupported fi AC_MSG_RESULT([$_LT_TAGVAR(hardcode_action, $1)]) if test "$_LT_TAGVAR(hardcode_action, $1)" = relink || test "$_LT_TAGVAR(inherit_rpath, $1)" = yes; then # Fast installation is not supported enable_fast_install=no elif test "$shlibpath_overrides_runpath" = yes || test "$enable_shared" = no; then # Fast installation is not necessary enable_fast_install=needless fi _LT_TAGDECL([], [hardcode_action], [0], [How to hardcode a shared library path into an executable]) ])# _LT_LINKER_HARDCODE_LIBPATH # _LT_CMD_STRIPLIB # ---------------- m4_defun([_LT_CMD_STRIPLIB], [m4_require([_LT_DECL_EGREP]) striplib= old_striplib= AC_MSG_CHECKING([whether stripping libraries is possible]) if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then test -z "$old_striplib" && old_striplib="$STRIP --strip-debug" test -z "$striplib" && striplib="$STRIP --strip-unneeded" AC_MSG_RESULT([yes]) else # FIXME - insert some real tests, host_os isn't really good enough case $host_os in darwin*) if test -n "$STRIP" ; then striplib="$STRIP -x" old_striplib="$STRIP -S" AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) fi ;; *) AC_MSG_RESULT([no]) ;; esac fi _LT_DECL([], [old_striplib], [1], [Commands to strip libraries]) _LT_DECL([], [striplib], [1]) ])# _LT_CMD_STRIPLIB # _LT_SYS_DYNAMIC_LINKER([TAG]) # ----------------------------- # PORTME Fill in your ld.so characteristics m4_defun([_LT_SYS_DYNAMIC_LINKER], [AC_REQUIRE([AC_CANONICAL_HOST])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_OBJDUMP])dnl m4_require([_LT_DECL_SED])dnl AC_MSG_CHECKING([dynamic linker characteristics]) m4_if([$1], [], [ if test "$GCC" = yes; then case $host_os in darwin*) lt_awk_arg="/^libraries:/,/LR/" ;; *) lt_awk_arg="/^libraries:/" ;; esac lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e "s,=/,/,g"` if $ECHO "$lt_search_path_spec" | $GREP ';' >/dev/null ; then # if the path contains ";" then we assume it to be the separator # otherwise default to the standard path separator (i.e. ":") - it is # assumed that no part of a normal pathname contains ";" but that should # okay in the real world where ";" in dirpaths is itself problematic. lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED -e 's/;/ /g'` else lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` fi # Ok, now we have the path, separated by spaces, we can step through it # and add multilib dir if necessary. lt_tmp_lt_search_path_spec= lt_multi_os_dir=`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null` for lt_sys_path in $lt_search_path_spec; do if test -d "$lt_sys_path/$lt_multi_os_dir"; then lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path/$lt_multi_os_dir" else test -d "$lt_sys_path" && \ lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path" fi done lt_search_path_spec=`$ECHO $lt_tmp_lt_search_path_spec | awk ' BEGIN {RS=" "; FS="/|\n";} { lt_foo=""; lt_count=0; for (lt_i = NF; lt_i > 0; lt_i--) { if ($lt_i != "" && $lt_i != ".") { if ($lt_i == "..") { lt_count++; } else { if (lt_count == 0) { lt_foo="/" $lt_i lt_foo; } else { lt_count--; } } } } if (lt_foo != "") { lt_freq[[lt_foo]]++; } if (lt_freq[[lt_foo]] == 1) { print lt_foo; } }'` sys_lib_search_path_spec=`$ECHO $lt_search_path_spec` else sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" fi]) library_names_spec= libname_spec='lib$name' soname_spec= shrext_cmds=".so" postinstall_cmds= postuninstall_cmds= finish_cmds= finish_eval= shlibpath_var= shlibpath_overrides_runpath=unknown version_type=none dynamic_linker="$host_os ld.so" sys_lib_dlsearch_path_spec="/lib /usr/lib" need_lib_prefix=unknown hardcode_into_libs=no # when you set need_version to no, make sure it does not cause -set_version # flags to be left without arguments need_version=unknown case $host_os in aix3*) version_type=linux library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a' shlibpath_var=LIBPATH # AIX 3 has no versioning support, so we append a major version to the name. soname_spec='${libname}${release}${shared_ext}$major' ;; aix[[4-9]]*) version_type=linux need_lib_prefix=no need_version=no hardcode_into_libs=yes if test "$host_cpu" = ia64; then # AIX 5 supports IA64 library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}' shlibpath_var=LD_LIBRARY_PATH else # With GCC up to 2.95.x, collect2 would create an import file # for dependence libraries. The import file would start with # the line `#! .'. This would cause the generated library to # depend on `.', always an invalid library. This was fixed in # development snapshots of GCC prior to 3.0. case $host_os in aix4 | aix4.[[01]] | aix4.[[01]].*) if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' echo ' yes ' echo '#endif'; } | ${CC} -E - | $GREP yes > /dev/null; then : else can_build_shared=no fi ;; esac # AIX (on Power*) has no versioning support, so currently we can not hardcode correct # soname into executable. Probably we can add versioning support to # collect2, so additional links can be useful in future. if test "$aix_use_runtimelinking" = yes; then # If using run time linking (on AIX 4.2 or later) use lib.so # instead of lib.a to let people know that these are not # typical AIX shared libraries. library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' else # We preserve .a as extension for shared libraries through AIX4.2 # and later when we are not doing run time linking. library_names_spec='${libname}${release}.a $libname.a' soname_spec='${libname}${release}${shared_ext}$major' fi shlibpath_var=LIBPATH fi ;; amigaos*) case $host_cpu in powerpc) # Since July 2007 AmigaOS4 officially supports .so libraries. # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' ;; m68k) library_names_spec='$libname.ixlibrary $libname.a' # Create ${libname}_ixlibrary.a entries in /sys/libs. finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`$ECHO "X$lib" | $Xsed -e '\''s%^.*/\([[^/]]*\)\.ixlibrary$%\1%'\''`; test $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' ;; esac ;; beos*) library_names_spec='${libname}${shared_ext}' dynamic_linker="$host_os ld.so" shlibpath_var=LIBRARY_PATH ;; bsdi[[45]]*) version_type=linux need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" # the default ld.so.conf also contains /usr/contrib/lib and # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow # libtool to hard-code these into programs ;; cygwin* | mingw* | pw32* | cegcc*) version_type=windows shrext_cmds=".dll" need_version=no need_lib_prefix=no case $GCC,$host_os in yes,cygwin* | yes,mingw* | yes,pw32* | yes,cegcc*) library_names_spec='$libname.dll.a' # DLL is installed to $(libdir)/../bin by postinstall_cmds postinstall_cmds='base_file=`basename \${file}`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname~ chmod a+x \$dldir/$dlname~ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; fi' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' shlibpath_overrides_runpath=yes case $host_os in cygwin*) # Cygwin DLLs use 'cyg' prefix rather than 'lib' soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' sys_lib_search_path_spec="/usr/lib /lib/w32api /lib /usr/local/lib" ;; mingw* | cegcc*) # MinGW DLLs use traditional 'lib' prefix soname_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' sys_lib_search_path_spec=`$CC -print-search-dirs | $GREP "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"` if $ECHO "$sys_lib_search_path_spec" | [$GREP ';[c-zC-Z]:/' >/dev/null]; then # It is most probably a Windows format PATH printed by # mingw gcc, but we are running on Cygwin. Gcc prints its search # path with ; separators, and with drive letters. We can handle the # drive letters (cygwin fileutils understands them), so leave them, # especially as we might pass files found there to a mingw objdump, # which wouldn't understand a cygwinified path. Ahh. sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` else sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` fi ;; pw32*) # pw32 DLLs use 'pw' prefix rather than 'lib' library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' ;; esac ;; *) library_names_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext} $libname.lib' ;; esac dynamic_linker='Win32 ld.exe' # FIXME: first we should search . and the directory the executable is in shlibpath_var=PATH ;; darwin* | rhapsody*) dynamic_linker="$host_os dyld" version_type=darwin need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${major}$shared_ext ${libname}$shared_ext' soname_spec='${libname}${release}${major}$shared_ext' shlibpath_overrides_runpath=yes shlibpath_var=DYLD_LIBRARY_PATH shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' m4_if([$1], [],[ sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib"]) sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' ;; dgux*) version_type=linux need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH ;; freebsd1*) dynamic_linker=no ;; freebsd* | dragonfly*) # DragonFly does not have aout. When/if they implement a new # versioning mechanism, adjust this. if test -x /usr/bin/objformat; then objformat=`/usr/bin/objformat` else case $host_os in freebsd[[123]]*) objformat=aout ;; *) objformat=elf ;; esac fi version_type=freebsd-$objformat case $version_type in freebsd-elf*) library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' need_version=no need_lib_prefix=no ;; freebsd-*) library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix' need_version=yes ;; esac shlibpath_var=LD_LIBRARY_PATH case $host_os in freebsd2*) shlibpath_overrides_runpath=yes ;; freebsd3.[[01]]* | freebsdelf3.[[01]]*) shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; freebsd3.[[2-9]]* | freebsdelf3.[[2-9]]* | \ freebsd4.[[0-5]] | freebsdelf4.[[0-5]] | freebsd4.1.1 | freebsdelf4.1.1) shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; *) # from 4.6 on, and DragonFly shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; esac ;; gnu*) version_type=linux need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH hardcode_into_libs=yes ;; hpux9* | hpux10* | hpux11*) # Give a soname corresponding to the major version so that dld.sl refuses to # link against other versions. version_type=sunos need_lib_prefix=no need_version=no case $host_cpu in ia64*) shrext_cmds='.so' hardcode_into_libs=yes dynamic_linker="$host_os dld.so" shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' if test "X$HPUX_IA64_MODE" = X32; then sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" else sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" fi sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; hppa*64*) shrext_cmds='.sl' hardcode_into_libs=yes dynamic_linker="$host_os dld.sl" shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; *) shrext_cmds='.sl' dynamic_linker="$host_os dld.sl" shlibpath_var=SHLIB_PATH shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' ;; esac # HP-UX runs *really* slowly unless shared libraries are mode 555. postinstall_cmds='chmod 555 $lib' ;; interix[[3-9]]*) version_type=linux need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; irix5* | irix6* | nonstopux*) case $host_os in nonstopux*) version_type=nonstopux ;; *) if test "$lt_cv_prog_gnu_ld" = yes; then version_type=linux else version_type=irix fi ;; esac need_lib_prefix=no need_version=no soname_spec='${libname}${release}${shared_ext}$major' library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}' case $host_os in irix5* | nonstopux*) libsuff= shlibsuff= ;; *) case $LD in # libtool.m4 will add one of these switches to LD *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") libsuff= shlibsuff= libmagic=32-bit;; *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") libsuff=32 shlibsuff=N32 libmagic=N32;; *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") libsuff=64 shlibsuff=64 libmagic=64-bit;; *) libsuff= shlibsuff= libmagic=never-match;; esac ;; esac shlibpath_var=LD_LIBRARY${shlibsuff}_PATH shlibpath_overrides_runpath=no sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}" sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}" hardcode_into_libs=yes ;; # No shared lib support for Linux oldld, aout, or coff. linux*oldld* | linux*aout* | linux*coff*) dynamic_linker=no ;; # This must be Linux ELF. linux* | k*bsd*-gnu | kopensolaris*-gnu) version_type=linux need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no # Some binutils ld are patched to set DT_RUNPATH save_LDFLAGS=$LDFLAGS save_libdir=$libdir eval "libdir=/foo; wl=\"$_LT_TAGVAR(lt_prog_compiler_wl, $1)\"; \ LDFLAGS=\"\$LDFLAGS $_LT_TAGVAR(hardcode_libdir_flag_spec, $1)\"" AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], [AS_IF([ ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null], [shlibpath_overrides_runpath=yes])]) LDFLAGS=$save_LDFLAGS libdir=$save_libdir # This implies no fast_install, which is unacceptable. # Some rework will be needed to allow for fast_install # before this can be enabled. hardcode_into_libs=yes # Append ld.so.conf contents to the search path if test -f /etc/ld.so.conf; then lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \[$]2)); skip = 1; } { if (!skip) print \[$]0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;/^$/d' | tr '\n' ' '` sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" fi # We used to test for /lib/ld.so.1 and disable shared libraries on # powerpc, because MkLinux only supported shared libraries with the # GNU dynamic linker. Since this was broken with cross compilers, # most powerpc-linux boxes support dynamic linking these days and # people can always --disable-shared, the test was removed, and we # assume the GNU/Linux dynamic linker is in use. dynamic_linker='GNU/Linux ld.so' ;; netbsdelf*-gnu) version_type=linux need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes dynamic_linker='NetBSD ld.elf_so' ;; netbsd*) version_type=sunos need_lib_prefix=no need_version=no if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' dynamic_linker='NetBSD (a.out) ld.so' else library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' dynamic_linker='NetBSD ld.elf_so' fi shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; newsos6) version_type=linux library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes ;; *nto* | *qnx*) version_type=qnx need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes dynamic_linker='ldqnx.so' ;; openbsd*) version_type=sunos sys_lib_dlsearch_path_spec="/usr/lib" need_lib_prefix=no # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs. case $host_os in openbsd3.3 | openbsd3.3.*) need_version=yes ;; *) need_version=no ;; esac library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' shlibpath_var=LD_LIBRARY_PATH if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then case $host_os in openbsd2.[[89]] | openbsd2.[[89]].*) shlibpath_overrides_runpath=no ;; *) shlibpath_overrides_runpath=yes ;; esac else shlibpath_overrides_runpath=yes fi ;; os2*) libname_spec='$name' shrext_cmds=".dll" need_lib_prefix=no library_names_spec='$libname${shared_ext} $libname.a' dynamic_linker='OS/2 ld.exe' shlibpath_var=LIBPATH ;; osf3* | osf4* | osf5*) version_type=osf need_lib_prefix=no need_version=no soname_spec='${libname}${release}${shared_ext}$major' library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec" ;; rdos*) dynamic_linker=no ;; solaris*) version_type=linux need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes # ldd complains unless libraries are executable postinstall_cmds='chmod +x $lib' ;; sunos4*) version_type=sunos library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes if test "$with_gnu_ld" = yes; then need_lib_prefix=no fi need_version=yes ;; sysv4 | sysv4.3*) version_type=linux library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH case $host_vendor in sni) shlibpath_overrides_runpath=no need_lib_prefix=no runpath_var=LD_RUN_PATH ;; siemens) need_lib_prefix=no ;; motorola) need_lib_prefix=no need_version=no shlibpath_overrides_runpath=no sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' ;; esac ;; sysv4*MP*) if test -d /usr/nec ;then version_type=linux library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}' soname_spec='$libname${shared_ext}.$major' shlibpath_var=LD_LIBRARY_PATH fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) version_type=freebsd-elf need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes if test "$with_gnu_ld" = yes; then sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' else sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' case $host_os in sco3.2v5*) sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" ;; esac fi sys_lib_dlsearch_path_spec='/usr/lib' ;; tpf*) # TPF is a cross-target only. Preferred cross-host = GNU/Linux. version_type=linux need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; uts4*) version_type=linux library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH ;; *) dynamic_linker=no ;; esac AC_MSG_RESULT([$dynamic_linker]) test "$dynamic_linker" = no && can_build_shared=no variables_saved_for_relink="PATH $shlibpath_var $runpath_var" if test "$GCC" = yes; then variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" fi if test "${lt_cv_sys_lib_search_path_spec+set}" = set; then sys_lib_search_path_spec="$lt_cv_sys_lib_search_path_spec" fi if test "${lt_cv_sys_lib_dlsearch_path_spec+set}" = set; then sys_lib_dlsearch_path_spec="$lt_cv_sys_lib_dlsearch_path_spec" fi _LT_DECL([], [variables_saved_for_relink], [1], [Variables whose values should be saved in libtool wrapper scripts and restored at link time]) _LT_DECL([], [need_lib_prefix], [0], [Do we need the "lib" prefix for modules?]) _LT_DECL([], [need_version], [0], [Do we need a version for libraries?]) _LT_DECL([], [version_type], [0], [Library versioning type]) _LT_DECL([], [runpath_var], [0], [Shared library runtime path variable]) _LT_DECL([], [shlibpath_var], [0],[Shared library path variable]) _LT_DECL([], [shlibpath_overrides_runpath], [0], [Is shlibpath searched before the hard-coded library search path?]) _LT_DECL([], [libname_spec], [1], [Format of library name prefix]) _LT_DECL([], [library_names_spec], [1], [[List of archive names. First name is the real one, the rest are links. The last name is the one that the linker finds with -lNAME]]) _LT_DECL([], [soname_spec], [1], [[The coded name of the library, if different from the real name]]) _LT_DECL([], [postinstall_cmds], [2], [Command to use after installation of a shared archive]) _LT_DECL([], [postuninstall_cmds], [2], [Command to use after uninstallation of a shared archive]) _LT_DECL([], [finish_cmds], [2], [Commands used to finish a libtool library installation in a directory]) _LT_DECL([], [finish_eval], [1], [[As "finish_cmds", except a single script fragment to be evaled but not shown]]) _LT_DECL([], [hardcode_into_libs], [0], [Whether we should hardcode library paths into libraries]) _LT_DECL([], [sys_lib_search_path_spec], [2], [Compile-time system search path for libraries]) _LT_DECL([], [sys_lib_dlsearch_path_spec], [2], [Run-time system search path for libraries]) ])# _LT_SYS_DYNAMIC_LINKER # _LT_PATH_TOOL_PREFIX(TOOL) # -------------------------- # find a file program which can recognize shared library AC_DEFUN([_LT_PATH_TOOL_PREFIX], [m4_require([_LT_DECL_EGREP])dnl AC_MSG_CHECKING([for $1]) AC_CACHE_VAL(lt_cv_path_MAGIC_CMD, [case $MAGIC_CMD in [[\\/*] | ?:[\\/]*]) lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path. ;; *) lt_save_MAGIC_CMD="$MAGIC_CMD" lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR dnl $ac_dummy forces splitting on constant user-supplied paths. dnl POSIX.2 word splitting is done only on the output of word expansions, dnl not every word. This closes a longstanding sh security hole. ac_dummy="m4_if([$2], , $PATH, [$2])" for ac_dir in $ac_dummy; do IFS="$lt_save_ifs" test -z "$ac_dir" && ac_dir=. if test -f $ac_dir/$1; then lt_cv_path_MAGIC_CMD="$ac_dir/$1" if test -n "$file_magic_test_file"; then case $deplibs_check_method in "file_magic "*) file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` MAGIC_CMD="$lt_cv_path_MAGIC_CMD" if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | $EGREP "$file_magic_regex" > /dev/null; then : else cat <<_LT_EOF 1>&2 *** Warning: the command libtool uses to detect shared libraries, *** $file_magic_cmd, produces output that libtool cannot recognize. *** The result is that libtool may fail to recognize shared libraries *** as such. This will affect the creation of libtool libraries that *** depend on shared libraries, but programs linked with such libtool *** libraries will work regardless of this problem. Nevertheless, you *** may want to report the problem to your system manager and/or to *** bug-libtool@gnu.org _LT_EOF fi ;; esac fi break fi done IFS="$lt_save_ifs" MAGIC_CMD="$lt_save_MAGIC_CMD" ;; esac]) MAGIC_CMD="$lt_cv_path_MAGIC_CMD" if test -n "$MAGIC_CMD"; then AC_MSG_RESULT($MAGIC_CMD) else AC_MSG_RESULT(no) fi _LT_DECL([], [MAGIC_CMD], [0], [Used to examine libraries when file_magic_cmd begins with "file"])dnl ])# _LT_PATH_TOOL_PREFIX # Old name: AU_ALIAS([AC_PATH_TOOL_PREFIX], [_LT_PATH_TOOL_PREFIX]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_PATH_TOOL_PREFIX], []) # _LT_PATH_MAGIC # -------------- # find a file program which can recognize a shared library m4_defun([_LT_PATH_MAGIC], [_LT_PATH_TOOL_PREFIX(${ac_tool_prefix}file, /usr/bin$PATH_SEPARATOR$PATH) if test -z "$lt_cv_path_MAGIC_CMD"; then if test -n "$ac_tool_prefix"; then _LT_PATH_TOOL_PREFIX(file, /usr/bin$PATH_SEPARATOR$PATH) else MAGIC_CMD=: fi fi ])# _LT_PATH_MAGIC # LT_PATH_LD # ---------- # find the pathname to the GNU or non-GNU linker AC_DEFUN([LT_PATH_LD], [AC_REQUIRE([AC_PROG_CC])dnl AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_REQUIRE([AC_CANONICAL_BUILD])dnl m4_require([_LT_DECL_SED])dnl m4_require([_LT_DECL_EGREP])dnl AC_ARG_WITH([gnu-ld], [AS_HELP_STRING([--with-gnu-ld], [assume the C compiler uses GNU ld @<:@default=no@:>@])], [test "$withval" = no || with_gnu_ld=yes], [with_gnu_ld=no])dnl ac_prog=ld if test "$GCC" = yes; then # Check if gcc -print-prog-name=ld gives a path. AC_MSG_CHECKING([for ld used by $CC]) case $host in *-*-mingw*) # gcc leaves a trailing carriage return which upsets mingw ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; *) ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; esac case $ac_prog in # Accept absolute paths. [[\\/]]* | ?:[[\\/]]*) re_direlt='/[[^/]][[^/]]*/\.\./' # Canonicalize the pathname of ld ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` done test -z "$LD" && LD="$ac_prog" ;; "") # If it fails, then pretend we aren't using GCC. ac_prog=ld ;; *) # If it is relative, then search for the first ld in PATH. with_gnu_ld=unknown ;; esac elif test "$with_gnu_ld" = yes; then AC_MSG_CHECKING([for GNU ld]) else AC_MSG_CHECKING([for non-GNU ld]) fi AC_CACHE_VAL(lt_cv_path_LD, [if test -z "$LD"; then lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR for ac_dir in $PATH; do IFS="$lt_save_ifs" test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then lt_cv_path_LD="$ac_dir/$ac_prog" # Check to see if the program is GNU ld. I'd rather use --version, # but apparently some variants of GNU ld only accept -v. # Break only if it was the GNU/non-GNU ld that we prefer. case `"$lt_cv_path_LD" -v 2>&1 &1 /dev/null 2>&1; then lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' lt_cv_file_magic_cmd='func_win32_libid' else lt_cv_deplibs_check_method='file_magic file format pei*-i386(.*architecture: i386)?' lt_cv_file_magic_cmd='$OBJDUMP -f' fi ;; cegcc) # use the weaker test based on 'objdump'. See mingw*. lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?' lt_cv_file_magic_cmd='$OBJDUMP -f' ;; darwin* | rhapsody*) lt_cv_deplibs_check_method=pass_all ;; freebsd* | dragonfly*) if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then case $host_cpu in i*86 ) # Not sure whether the presence of OpenBSD here was a mistake. # Let's accept both of them until this is cleared up. lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[[3-9]]86 (compact )?demand paged shared library' lt_cv_file_magic_cmd=/usr/bin/file lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*` ;; esac else lt_cv_deplibs_check_method=pass_all fi ;; gnu*) lt_cv_deplibs_check_method=pass_all ;; hpux10.20* | hpux11*) lt_cv_file_magic_cmd=/usr/bin/file case $host_cpu in ia64*) lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|ELF-[[0-9]][[0-9]]) shared object file - IA64' lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so ;; hppa*64*) [lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - PA-RISC [0-9].[0-9]'] lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl ;; *) lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|PA-RISC[[0-9]].[[0-9]]) shared library' lt_cv_file_magic_test_file=/usr/lib/libc.sl ;; esac ;; interix[[3-9]]*) # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|\.a)$' ;; irix5* | irix6* | nonstopux*) case $LD in *-32|*"-32 ") libmagic=32-bit;; *-n32|*"-n32 ") libmagic=N32;; *-64|*"-64 ") libmagic=64-bit;; *) libmagic=never-match;; esac lt_cv_deplibs_check_method=pass_all ;; # This must be Linux ELF. linux* | k*bsd*-gnu | kopensolaris*-gnu) lt_cv_deplibs_check_method=pass_all ;; netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' else lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|_pic\.a)$' fi ;; newos6*) lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (executable|dynamic lib)' lt_cv_file_magic_cmd=/usr/bin/file lt_cv_file_magic_test_file=/usr/lib/libnls.so ;; *nto* | *qnx*) lt_cv_deplibs_check_method=pass_all ;; openbsd*) if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|\.so|_pic\.a)$' else lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' fi ;; osf3* | osf4* | osf5*) lt_cv_deplibs_check_method=pass_all ;; rdos*) lt_cv_deplibs_check_method=pass_all ;; solaris*) lt_cv_deplibs_check_method=pass_all ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) lt_cv_deplibs_check_method=pass_all ;; sysv4 | sysv4.3*) case $host_vendor in motorola) lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib) M[[0-9]][[0-9]]* Version [[0-9]]' lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*` ;; ncr) lt_cv_deplibs_check_method=pass_all ;; sequent) lt_cv_file_magic_cmd='/bin/file' lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB (shared object|dynamic lib )' ;; sni) lt_cv_file_magic_cmd='/bin/file' lt_cv_deplibs_check_method="file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB dynamic lib" lt_cv_file_magic_test_file=/lib/libc.so ;; siemens) lt_cv_deplibs_check_method=pass_all ;; pc) lt_cv_deplibs_check_method=pass_all ;; esac ;; tpf*) lt_cv_deplibs_check_method=pass_all ;; esac ]) file_magic_cmd=$lt_cv_file_magic_cmd deplibs_check_method=$lt_cv_deplibs_check_method test -z "$deplibs_check_method" && deplibs_check_method=unknown _LT_DECL([], [deplibs_check_method], [1], [Method to check whether dependent libraries are shared objects]) _LT_DECL([], [file_magic_cmd], [1], [Command to use when deplibs_check_method == "file_magic"]) ])# _LT_CHECK_MAGIC_METHOD # LT_PATH_NM # ---------- # find the pathname to a BSD- or MS-compatible name lister AC_DEFUN([LT_PATH_NM], [AC_REQUIRE([AC_PROG_CC])dnl AC_CACHE_CHECK([for BSD- or MS-compatible name lister (nm)], lt_cv_path_NM, [if test -n "$NM"; then # Let the user override the test. lt_cv_path_NM="$NM" else lt_nm_to_check="${ac_tool_prefix}nm" if test -n "$ac_tool_prefix" && test "$build" = "$host"; then lt_nm_to_check="$lt_nm_to_check nm" fi for lt_tmp_nm in $lt_nm_to_check; do lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do IFS="$lt_save_ifs" test -z "$ac_dir" && ac_dir=. tmp_nm="$ac_dir/$lt_tmp_nm" if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext" ; then # Check to see if the nm accepts a BSD-compat flag. # Adding the `sed 1q' prevents false positives on HP-UX, which says: # nm: unknown option "B" ignored # Tru64's nm complains that /dev/null is an invalid object file case `"$tmp_nm" -B /dev/null 2>&1 | sed '1q'` in */dev/null* | *'Invalid file or object type'*) lt_cv_path_NM="$tmp_nm -B" break ;; *) case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in */dev/null*) lt_cv_path_NM="$tmp_nm -p" break ;; *) lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but continue # so that we can try to find one that supports BSD flags ;; esac ;; esac fi done IFS="$lt_save_ifs" done : ${lt_cv_path_NM=no} fi]) if test "$lt_cv_path_NM" != "no"; then NM="$lt_cv_path_NM" else # Didn't find any BSD compatible name lister, look for dumpbin. AC_CHECK_TOOLS(DUMPBIN, ["dumpbin -symbols" "link -dump -symbols"], :) AC_SUBST([DUMPBIN]) if test "$DUMPBIN" != ":"; then NM="$DUMPBIN" fi fi test -z "$NM" && NM=nm AC_SUBST([NM]) _LT_DECL([], [NM], [1], [A BSD- or MS-compatible name lister])dnl AC_CACHE_CHECK([the name lister ($NM) interface], [lt_cv_nm_interface], [lt_cv_nm_interface="BSD nm" echo "int some_variable = 0;" > conftest.$ac_ext (eval echo "\"\$as_me:__oline__: $ac_compile\"" >&AS_MESSAGE_LOG_FD) (eval "$ac_compile" 2>conftest.err) cat conftest.err >&AS_MESSAGE_LOG_FD (eval echo "\"\$as_me:__oline__: $NM \\\"conftest.$ac_objext\\\"\"" >&AS_MESSAGE_LOG_FD) (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) cat conftest.err >&AS_MESSAGE_LOG_FD (eval echo "\"\$as_me:__oline__: output\"" >&AS_MESSAGE_LOG_FD) cat conftest.out >&AS_MESSAGE_LOG_FD if $GREP 'External.*some_variable' conftest.out > /dev/null; then lt_cv_nm_interface="MS dumpbin" fi rm -f conftest*]) ])# LT_PATH_NM # Old names: AU_ALIAS([AM_PROG_NM], [LT_PATH_NM]) AU_ALIAS([AC_PROG_NM], [LT_PATH_NM]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AM_PROG_NM], []) dnl AC_DEFUN([AC_PROG_NM], []) # LT_LIB_M # -------- # check for math library AC_DEFUN([LT_LIB_M], [AC_REQUIRE([AC_CANONICAL_HOST])dnl LIBM= case $host in *-*-beos* | *-*-cygwin* | *-*-pw32* | *-*-darwin*) # These system don't have libm, or don't need it ;; *-ncr-sysv4.3*) AC_CHECK_LIB(mw, _mwvalidcheckl, LIBM="-lmw") AC_CHECK_LIB(m, cos, LIBM="$LIBM -lm") ;; *) AC_CHECK_LIB(m, cos, LIBM="-lm") ;; esac AC_SUBST([LIBM]) ])# LT_LIB_M # Old name: AU_ALIAS([AC_CHECK_LIBM], [LT_LIB_M]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_CHECK_LIBM], []) # _LT_COMPILER_NO_RTTI([TAGNAME]) # ------------------------------- m4_defun([_LT_COMPILER_NO_RTTI], [m4_require([_LT_TAG_COMPILER])dnl _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= if test "$GCC" = yes; then _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' _LT_COMPILER_OPTION([if $compiler supports -fno-rtti -fno-exceptions], lt_cv_prog_compiler_rtti_exceptions, [-fno-rtti -fno-exceptions], [], [_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)="$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1) -fno-rtti -fno-exceptions"]) fi _LT_TAGDECL([no_builtin_flag], [lt_prog_compiler_no_builtin_flag], [1], [Compiler flag to turn off builtin functions]) ])# _LT_COMPILER_NO_RTTI # _LT_CMD_GLOBAL_SYMBOLS # ---------------------- m4_defun([_LT_CMD_GLOBAL_SYMBOLS], [AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_REQUIRE([AC_PROG_CC])dnl AC_REQUIRE([LT_PATH_NM])dnl AC_REQUIRE([LT_PATH_LD])dnl m4_require([_LT_DECL_SED])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_TAG_COMPILER])dnl # Check for command to grab the raw symbol name followed by C symbol from nm. AC_MSG_CHECKING([command to parse $NM output from $compiler object]) AC_CACHE_VAL([lt_cv_sys_global_symbol_pipe], [ # These are sane defaults that work on at least a few old systems. # [They come from Ultrix. What could be older than Ultrix?!! ;)] # Character class describing NM global symbol codes. symcode='[[BCDEGRST]]' # Regexp to match symbols that can be accessed directly from C. sympat='\([[_A-Za-z]][[_A-Za-z0-9]]*\)' # Define system-specific variables. case $host_os in aix*) symcode='[[BCDT]]' ;; cygwin* | mingw* | pw32* | cegcc*) symcode='[[ABCDGISTW]]' ;; hpux*) if test "$host_cpu" = ia64; then symcode='[[ABCDEGRST]]' fi ;; irix* | nonstopux*) symcode='[[BCDEGRST]]' ;; osf*) symcode='[[BCDEGQRST]]' ;; solaris*) symcode='[[BDRT]]' ;; sco3.2v5*) symcode='[[DT]]' ;; sysv4.2uw2*) symcode='[[DT]]' ;; sysv5* | sco5v6* | unixware* | OpenUNIX*) symcode='[[ABDT]]' ;; sysv4) symcode='[[DFNSTU]]' ;; esac # If we're using GNU nm, then use its standard symbol codes. case `$NM -V 2>&1` in *GNU* | *'with BFD'*) symcode='[[ABCDGIRSTW]]' ;; esac # Transform an extracted symbol line into a proper C declaration. # Some systems (esp. on ia64) link data and code symbols differently, # so use this general approach. lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'" # Transform an extracted symbol line into symbol name and symbol address lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([[^ ]]*\) $/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([[^ ]]*\) \([[^ ]]*\)$/ {\"\2\", (void *) \&\2},/p'" lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n -e 's/^: \([[^ ]]*\) $/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([[^ ]]*\) \(lib[[^ ]]*\)$/ {\"\2\", (void *) \&\2},/p' -e 's/^$symcode* \([[^ ]]*\) \([[^ ]]*\)$/ {\"lib\2\", (void *) \&\2},/p'" # Handle CRLF in mingw tool chain opt_cr= case $build_os in mingw*) opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp ;; esac # Try without a prefix underscore, then with it. for ac_symprfx in "" "_"; do # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol. symxfrm="\\1 $ac_symprfx\\2 \\2" # Write the raw and C identifiers. if test "$lt_cv_nm_interface" = "MS dumpbin"; then # Fake it for dumpbin and say T for any non-static function # and D for any global variable. # Also find C++ and __fastcall symbols from MSVC++, # which start with @ or ?. lt_cv_sys_global_symbol_pipe="$AWK ['"\ " {last_section=section; section=\$ 3};"\ " /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\ " \$ 0!~/External *\|/{next};"\ " / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\ " {if(hide[section]) next};"\ " {f=0}; \$ 0~/\(\).*\|/{f=1}; {printf f ? \"T \" : \"D \"};"\ " {split(\$ 0, a, /\||\r/); split(a[2], s)};"\ " s[1]~/^[@?]/{print s[1], s[1]; next};"\ " s[1]~prfx {split(s[1],t,\"@\"); print t[1], substr(t[1],length(prfx))}"\ " ' prfx=^$ac_symprfx]" else lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[[ ]]\($symcode$symcode*\)[[ ]][[ ]]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'" fi # Check to see that the pipe works correctly. pipe_works=no rm -f conftest* cat > conftest.$ac_ext <<_LT_EOF #ifdef __cplusplus extern "C" { #endif char nm_test_var; void nm_test_func(void); void nm_test_func(void){} #ifdef __cplusplus } #endif int main(){nm_test_var='a';nm_test_func();return(0);} _LT_EOF if AC_TRY_EVAL(ac_compile); then # Now try to grab the symbols. nlist=conftest.nm if AC_TRY_EVAL(NM conftest.$ac_objext \| $lt_cv_sys_global_symbol_pipe \> $nlist) && test -s "$nlist"; then # Try sorting and uniquifying the output. if sort "$nlist" | uniq > "$nlist"T; then mv -f "$nlist"T "$nlist" else rm -f "$nlist"T fi # Make sure that we snagged all the symbols we need. if $GREP ' nm_test_var$' "$nlist" >/dev/null; then if $GREP ' nm_test_func$' "$nlist" >/dev/null; then cat <<_LT_EOF > conftest.$ac_ext #ifdef __cplusplus extern "C" { #endif _LT_EOF # Now generate the symbol file. eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext' cat <<_LT_EOF >> conftest.$ac_ext /* The mapping between symbol names and symbols. */ const struct { const char *name; void *address; } lt__PROGRAM__LTX_preloaded_symbols[[]] = { { "@PROGRAM@", (void *) 0 }, _LT_EOF $SED "s/^$symcode$symcode* \(.*\) \(.*\)$/ {\"\2\", (void *) \&\2},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext cat <<\_LT_EOF >> conftest.$ac_ext {0, (void *) 0} }; /* This works around a problem in FreeBSD linker */ #ifdef FREEBSD_WORKAROUND static const void *lt_preloaded_setup() { return lt__PROGRAM__LTX_preloaded_symbols; } #endif #ifdef __cplusplus } #endif _LT_EOF # Now try linking the two files. mv conftest.$ac_objext conftstm.$ac_objext lt_save_LIBS="$LIBS" lt_save_CFLAGS="$CFLAGS" LIBS="conftstm.$ac_objext" CFLAGS="$CFLAGS$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)" if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext}; then pipe_works=yes fi LIBS="$lt_save_LIBS" CFLAGS="$lt_save_CFLAGS" else echo "cannot find nm_test_func in $nlist" >&AS_MESSAGE_LOG_FD fi else echo "cannot find nm_test_var in $nlist" >&AS_MESSAGE_LOG_FD fi else echo "cannot run $lt_cv_sys_global_symbol_pipe" >&AS_MESSAGE_LOG_FD fi else echo "$progname: failed program was:" >&AS_MESSAGE_LOG_FD cat conftest.$ac_ext >&5 fi rm -rf conftest* conftst* # Do not use the global_symbol_pipe unless it works. if test "$pipe_works" = yes; then break else lt_cv_sys_global_symbol_pipe= fi done ]) if test -z "$lt_cv_sys_global_symbol_pipe"; then lt_cv_sys_global_symbol_to_cdecl= fi if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then AC_MSG_RESULT(failed) else AC_MSG_RESULT(ok) fi _LT_DECL([global_symbol_pipe], [lt_cv_sys_global_symbol_pipe], [1], [Take the output of nm and produce a listing of raw symbols and C names]) _LT_DECL([global_symbol_to_cdecl], [lt_cv_sys_global_symbol_to_cdecl], [1], [Transform the output of nm in a proper C declaration]) _LT_DECL([global_symbol_to_c_name_address], [lt_cv_sys_global_symbol_to_c_name_address], [1], [Transform the output of nm in a C name address pair]) _LT_DECL([global_symbol_to_c_name_address_lib_prefix], [lt_cv_sys_global_symbol_to_c_name_address_lib_prefix], [1], [Transform the output of nm in a C name address pair when lib prefix is needed]) ]) # _LT_CMD_GLOBAL_SYMBOLS # _LT_COMPILER_PIC([TAGNAME]) # --------------------------- m4_defun([_LT_COMPILER_PIC], [m4_require([_LT_TAG_COMPILER])dnl _LT_TAGVAR(lt_prog_compiler_wl, $1)= _LT_TAGVAR(lt_prog_compiler_pic, $1)= _LT_TAGVAR(lt_prog_compiler_static, $1)= AC_MSG_CHECKING([for $compiler option to produce PIC]) m4_if([$1], [CXX], [ # C++ specific cases for pic, static, wl, etc. if test "$GXX" = yes; then _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' case $host_os in aix*) # All AIX code is PIC. if test "$host_cpu" = ia64; then # AIX 5 now supports IA64 processor _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; m68k) # FIXME: we need at least 68020 code to build shared libraries, but # adding the `-m68020' flag to GCC prevents building anything better, # like `-m68040'. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' ;; esac ;; beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) # PIC is the default for these OSes. ;; mingw* | cygwin* | os2* | pw32* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). # Although the cygwin gcc ignores -fPIC, still need this for old-style # (--disable-auto-import) libraries m4_if([$1], [GCJ], [], [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) ;; darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' ;; *djgpp*) # DJGPP does not support shared libraries at all _LT_TAGVAR(lt_prog_compiler_pic, $1)= ;; interix[[3-9]]*) # Interix 3.x gcc -fpic/-fPIC options generate broken code. # Instead, we relocate shared libraries at runtime. ;; sysv4*MP*) if test -d /usr/nec; then _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic fi ;; hpux*) # PIC is the default for 64-bit PA HP-UX, but not for 32-bit # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag # sets the default TLS model and affects inlining. case $host_cpu in hppa*64*) ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; esac ;; *qnx* | *nto*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; esac else case $host_os in aix[[4-9]]*) # All AIX code is PIC. if test "$host_cpu" = ia64; then # AIX 5 now supports IA64 processor _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' else _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' fi ;; chorus*) case $cc_basename in cxch68*) # Green Hills C++ Compiler # _LT_TAGVAR(lt_prog_compiler_static, $1)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a" ;; esac ;; dgux*) case $cc_basename in ec++*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' ;; ghcx*) # Green Hills C++ Compiler _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' ;; *) ;; esac ;; freebsd* | dragonfly*) # FreeBSD uses GNU C++ ;; hpux9* | hpux10* | hpux11*) case $cc_basename in CC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive' if test "$host_cpu" != ia64; then _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' fi ;; aCC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive' case $host_cpu in hppa*64*|ia64*) # +Z the default ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' ;; esac ;; *) ;; esac ;; interix*) # This is c89, which is MS Visual C++ (no shared libs) # Anyone wants to do a port? ;; irix5* | irix6* | nonstopux*) case $cc_basename in CC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' # CC pic flag -KPIC is the default. ;; *) ;; esac ;; linux* | k*bsd*-gnu | kopensolaris*-gnu) case $cc_basename in KCC*) # KAI C++ Compiler _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; ecpc* ) # old Intel C++ for x86_64 which still supported -KPIC. _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; icpc* ) # Intel C++, used to be incompatible with GCC. # ICC 10 doesn't accept -KPIC any more. _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; pgCC* | pgcpp*) # Portland Group C++ compiler _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; cxx*) # Compaq C++ # Make sure the PIC flag is empty. It appears that all Alpha # Linux and Compaq Tru64 Unix objects are PIC. _LT_TAGVAR(lt_prog_compiler_pic, $1)= _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; xlc* | xlC*) # IBM XL 8.0 on PPC _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink' ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C++ 5.9 _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' ;; esac ;; esac ;; lynxos*) ;; m88k*) ;; mvs*) case $cc_basename in cxx*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-W c,exportall' ;; *) ;; esac ;; netbsd* | netbsdelf*-gnu) ;; *qnx* | *nto*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' ;; osf3* | osf4* | osf5*) case $cc_basename in KCC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' ;; RCC*) # Rational C++ 2.4.1 _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' ;; cxx*) # Digital/Compaq C++ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # Make sure the PIC flag is empty. It appears that all Alpha # Linux and Compaq Tru64 Unix objects are PIC. _LT_TAGVAR(lt_prog_compiler_pic, $1)= _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; *) ;; esac ;; psos*) ;; solaris*) case $cc_basename in CC*) # Sun C++ 4.2, 5.x and Centerline C++ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' ;; gcx*) # Green Hills C++ Compiler _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' ;; *) ;; esac ;; sunos4*) case $cc_basename in CC*) # Sun C++ 4.x _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; lcc*) # Lucid _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' ;; *) ;; esac ;; sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) case $cc_basename in CC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; esac ;; tandem*) case $cc_basename in NCC*) # NonStop-UX NCC 3.20 _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' ;; *) ;; esac ;; vxworks*) ;; *) _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no ;; esac fi ], [ if test "$GCC" = yes; then _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' case $host_os in aix*) # All AIX code is PIC. if test "$host_cpu" = ia64; then # AIX 5 now supports IA64 processor _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; m68k) # FIXME: we need at least 68020 code to build shared libraries, but # adding the `-m68020' flag to GCC prevents building anything better, # like `-m68040'. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' ;; esac ;; beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) # PIC is the default for these OSes. ;; mingw* | cygwin* | pw32* | os2* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). # Although the cygwin gcc ignores -fPIC, still need this for old-style # (--disable-auto-import) libraries m4_if([$1], [GCJ], [], [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) ;; darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' ;; hpux*) # PIC is the default for 64-bit PA HP-UX, but not for 32-bit # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag # sets the default TLS model and affects inlining. case $host_cpu in hppa*64*) # +Z the default ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; esac ;; interix[[3-9]]*) # Interix 3.x gcc -fpic/-fPIC options generate broken code. # Instead, we relocate shared libraries at runtime. ;; msdosdjgpp*) # Just because we use GCC doesn't mean we suddenly get shared libraries # on systems that don't support them. _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no enable_shared=no ;; *nto* | *qnx*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' ;; sysv4*MP*) if test -d /usr/nec; then _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic fi ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; esac else # PORTME Check for flag to pass linker flags through the system compiler. case $host_os in aix*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' if test "$host_cpu" = ia64; then # AIX 5 now supports IA64 processor _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' else _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' fi ;; mingw* | cygwin* | pw32* | os2* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). m4_if([$1], [GCJ], [], [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) ;; hpux9* | hpux10* | hpux11*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but # not for PA HP-UX. case $host_cpu in hppa*64*|ia64*) # +Z the default ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' ;; esac # Is there a better lt_prog_compiler_static that works with the bundled CC? _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive' ;; irix5* | irix6* | nonstopux*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # PIC (with -KPIC) is the default. _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; linux* | k*bsd*-gnu | kopensolaris*-gnu) case $cc_basename in # old Intel for x86_64 which still supported -KPIC. ecc*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; # icc used to be incompatible with GCC. # ICC 10 doesn't accept -KPIC any more. icc* | ifort*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; # Lahey Fortran 8.1. lf95*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='--shared' _LT_TAGVAR(lt_prog_compiler_static, $1)='--static' ;; pgcc* | pgf77* | pgf90* | pgf95*) # Portland Group compilers (*not* the Pentium gcc compiler, # which looks to be a dead project) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; ccc*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # All Alpha code is PIC. _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; xl*) # IBM XL C 8.0/Fortran 10.1 on PPC _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink' ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C 5.9 _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' ;; *Sun\ F*) # Sun Fortran 8.3 passes all unrecognized flags to the linker _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='' ;; esac ;; esac ;; newsos6) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; *nto* | *qnx*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' ;; osf3* | osf4* | osf5*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # All OSF/1 code is PIC. _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; rdos*) _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; solaris*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' case $cc_basename in f77* | f90* | f95*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ';; *) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,';; esac ;; sunos4*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; sysv4 | sysv4.2uw2* | sysv4.3*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; sysv4*MP*) if test -d /usr/nec ;then _LT_TAGVAR(lt_prog_compiler_pic, $1)='-Kconform_pic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' fi ;; sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; unicos*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no ;; uts4*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; *) _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no ;; esac fi ]) case $host_os in # For platforms which do not support PIC, -DPIC is meaningless: *djgpp*) _LT_TAGVAR(lt_prog_compiler_pic, $1)= ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)="$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])" ;; esac AC_MSG_RESULT([$_LT_TAGVAR(lt_prog_compiler_pic, $1)]) _LT_TAGDECL([wl], [lt_prog_compiler_wl], [1], [How to pass a linker flag through the compiler]) # # Check to make sure the PIC flag actually works. # if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then _LT_COMPILER_OPTION([if $compiler PIC flag $_LT_TAGVAR(lt_prog_compiler_pic, $1) works], [_LT_TAGVAR(lt_cv_prog_compiler_pic_works, $1)], [$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])], [], [case $_LT_TAGVAR(lt_prog_compiler_pic, $1) in "" | " "*) ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)=" $_LT_TAGVAR(lt_prog_compiler_pic, $1)" ;; esac], [_LT_TAGVAR(lt_prog_compiler_pic, $1)= _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no]) fi _LT_TAGDECL([pic_flag], [lt_prog_compiler_pic], [1], [Additional compiler flags for building library objects]) # # Check to make sure the static flag actually works. # wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) eval lt_tmp_static_flag=\"$_LT_TAGVAR(lt_prog_compiler_static, $1)\" _LT_LINKER_OPTION([if $compiler static flag $lt_tmp_static_flag works], _LT_TAGVAR(lt_cv_prog_compiler_static_works, $1), $lt_tmp_static_flag, [], [_LT_TAGVAR(lt_prog_compiler_static, $1)=]) _LT_TAGDECL([link_static_flag], [lt_prog_compiler_static], [1], [Compiler flag to prevent dynamic linking]) ])# _LT_COMPILER_PIC # _LT_LINKER_SHLIBS([TAGNAME]) # ---------------------------- # See if the linker supports building shared libraries. m4_defun([_LT_LINKER_SHLIBS], [AC_REQUIRE([LT_PATH_LD])dnl AC_REQUIRE([LT_PATH_NM])dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_DECL_SED])dnl m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl m4_require([_LT_TAG_COMPILER])dnl AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) m4_if([$1], [CXX], [ _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' case $host_os in aix[[4-9]]*) # If we're using GNU nm, then we don't want the "-C" option. # -C means demangle to AIX nm, but means don't demangle with GNU nm if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' else _LT_TAGVAR(export_symbols_cmds, $1)='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' fi ;; pw32*) _LT_TAGVAR(export_symbols_cmds, $1)="$ltdll_cmds" ;; cygwin* | mingw* | cegcc*) _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;/^.*[[ ]]__nm__/s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols' ;; linux* | k*bsd*-gnu) _LT_TAGVAR(link_all_deplibs, $1)=no ;; *) _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' ;; esac _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'] ], [ runpath_var= _LT_TAGVAR(allow_undefined_flag, $1)= _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(archive_cmds, $1)= _LT_TAGVAR(archive_expsym_cmds, $1)= _LT_TAGVAR(compiler_needs_object, $1)=no _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' _LT_TAGVAR(hardcode_automatic, $1)=no _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)= _LT_TAGVAR(hardcode_libdir_separator, $1)= _LT_TAGVAR(hardcode_minus_L, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported _LT_TAGVAR(inherit_rpath, $1)=no _LT_TAGVAR(link_all_deplibs, $1)=unknown _LT_TAGVAR(module_cmds, $1)= _LT_TAGVAR(module_expsym_cmds, $1)= _LT_TAGVAR(old_archive_from_new_cmds, $1)= _LT_TAGVAR(old_archive_from_expsyms_cmds, $1)= _LT_TAGVAR(thread_safe_flag_spec, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= # include_expsyms should be a list of space-separated symbols to be *always* # included in the symbol list _LT_TAGVAR(include_expsyms, $1)= # exclude_expsyms can be an extended regexp of symbols to exclude # it will be wrapped by ` (' and `)$', so one must not match beginning or # end of line. Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc', # as well as any symbol that contains `d'. _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'] # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out # platforms (ab)use it in PIC code, but their linkers get confused if # the symbol is explicitly referenced. Since portable code cannot # rely on this symbol name, it's probably fine to never include it in # preloaded symbol tables. # Exclude shared library initialization/finalization symbols. dnl Note also adjust exclude_expsyms for C++ above. extract_expsyms_cmds= case $host_os in cygwin* | mingw* | pw32* | cegcc*) # FIXME: the MSVC++ port hasn't been tested in a loooong time # When not using gcc, we currently assume that we are using # Microsoft Visual C++. if test "$GCC" != yes; then with_gnu_ld=no fi ;; interix*) # we just hope/assume this is gcc and not c89 (= MSVC++) with_gnu_ld=yes ;; openbsd*) with_gnu_ld=no ;; linux* | k*bsd*-gnu) _LT_TAGVAR(link_all_deplibs, $1)=no ;; esac _LT_TAGVAR(ld_shlibs, $1)=yes if test "$with_gnu_ld" = yes; then # If archive_cmds runs LD, not CC, wlarc should be empty wlarc='${wl}' # Set some defaults for GNU ld with shared library support. These # are reset later if shared libraries are not supported. Putting them # here allows them to be overridden if necessary. runpath_var=LD_RUN_PATH _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' # ancient GNU ld didn't support --whole-archive et. al. if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' else _LT_TAGVAR(whole_archive_flag_spec, $1)= fi supports_anon_versioning=no case `$LD -v 2>&1` in *GNU\ gold*) supports_anon_versioning=yes ;; *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.10.*) ;; # catch versions < 2.11 *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ... *\ 2.11.*) ;; # other 2.11 versions *) supports_anon_versioning=yes ;; esac # See if GNU ld supports shared libraries. case $host_os in aix[[3-9]]*) # On AIX/PPC, the GNU linker is very broken if test "$host_cpu" != ia64; then _LT_TAGVAR(ld_shlibs, $1)=no cat <<_LT_EOF 1>&2 *** Warning: the GNU linker, at least up to release 2.9.1, is reported *** to be unable to reliably create shared libraries on AIX. *** Therefore, libtool is disabling shared libraries support. If you *** really care for shared libraries, you may want to modify your PATH *** so that a non-GNU linker is found, and then restart. _LT_EOF fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='' ;; m68k) _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_minus_L, $1)=yes ;; esac ;; beos*) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(allow_undefined_flag, $1)=unsupported # Joseph Beckenbach says some releases of gcc # support --undefined. This deserves some investigation. FIXME _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; cygwin* | mingw* | pw32* | cegcc*) # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, # as there is no search path for DLLs. _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/'\'' | $SED -e '\''/^[[AITW]][[ ]]/s/.*[[ ]]//'\'' | sort | uniq > $export_symbols' if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' # If the export-symbols file already is a .def file (1st line # is EXPORTS), use it as is; otherwise, prepend... _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then cp $export_symbols $output_objdir/$soname.def; else echo EXPORTS > $output_objdir/$soname.def; cat $export_symbols >> $output_objdir/$soname.def; fi~ $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; interix[[3-9]]*) _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. # Instead, shared libraries are loaded at an image base (0x10000000 by # default) and relocated if they conflict, which is a slow very memory # consuming and fragmenting process. To avoid this, we pick a random, # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link # time. Moving up from 0x10000000 also allows more sbrk(2) space. _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' ;; gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu) tmp_diet=no if test "$host_os" = linux-dietlibc; then case $cc_basename in diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn) esac fi if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \ && test "$tmp_diet" = no then tmp_addflag= tmp_sharedflag='-shared' case $cc_basename,$host_cpu in pgcc*) # Portland Group C compiler _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive' tmp_addflag=' $pic_flag' ;; pgf77* | pgf90* | pgf95*) # Portland Group f77 and f90 compilers _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive' tmp_addflag=' $pic_flag -Mnomain' ;; ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64 tmp_addflag=' -i_dynamic' ;; efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64 tmp_addflag=' -i_dynamic -nofor_main' ;; ifc* | ifort*) # Intel Fortran compiler tmp_addflag=' -nofor_main' ;; lf95*) # Lahey Fortran 8.1 _LT_TAGVAR(whole_archive_flag_spec, $1)= tmp_sharedflag='--shared' ;; xl[[cC]]*) # IBM XL C 8.0 on PPC (deal with xlf below) tmp_sharedflag='-qmkshrobj' tmp_addflag= ;; esac case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C 5.9 _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive' _LT_TAGVAR(compiler_needs_object, $1)=yes tmp_sharedflag='-G' ;; *Sun\ F*) # Sun Fortran 8.3 tmp_sharedflag='-G' ;; esac _LT_TAGVAR(archive_cmds, $1)='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' if test "x$supports_anon_versioning" = xyes; then _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' fi case $cc_basename in xlf*) # IBM XL Fortran 10.1 on PPC cannot create shared libs itself _LT_TAGVAR(whole_archive_flag_spec, $1)='--whole-archive$convenience --no-whole-archive' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)='-rpath $libdir' _LT_TAGVAR(archive_cmds, $1)='$LD -shared $libobjs $deplibs $compiler_flags -soname $soname -o $lib' if test "x$supports_anon_versioning" = xyes; then _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $LD -shared $libobjs $deplibs $compiler_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib' fi ;; esac else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' wlarc= else _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' fi ;; solaris*) if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then _LT_TAGVAR(ld_shlibs, $1)=no cat <<_LT_EOF 1>&2 *** Warning: The releases 2.8.* of the GNU linker cannot reliably *** create shared libraries on Solaris systems. Therefore, libtool *** is disabling shared libraries support. We urge you to upgrade GNU *** binutils to release 2.9.1 or newer. Another option is to modify *** your PATH or compiler configuration so that the native linker is *** used, and then restart. _LT_EOF elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) case `$LD -v 2>&1` in *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.1[[0-5]].*) _LT_TAGVAR(ld_shlibs, $1)=no cat <<_LT_EOF 1>&2 *** Warning: Releases of the GNU linker prior to 2.16.91.0.3 can not *** reliably create shared libraries on SCO systems. Therefore, libtool *** is disabling shared libraries support. We urge you to upgrade GNU *** binutils to release 2.16.91.0.3 or newer. Another option is to modify *** your PATH or compiler configuration so that the native linker is *** used, and then restart. _LT_EOF ;; *) # For security reasons, it is highly recommended that you always # use absolute paths for naming shared libraries, and exclude the # DT_RUNPATH tag from executables and libraries. But doing so # requires that you compile everything twice, which is a pain. if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; sunos4*) _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' wlarc= _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac if test "$_LT_TAGVAR(ld_shlibs, $1)" = no; then runpath_var= _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= fi else # PORTME fill in a description of your system's linker (not GNU ld) case $host_os in aix3*) _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=yes _LT_TAGVAR(archive_expsym_cmds, $1)='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' # Note: this linker hardcodes the directories in LIBPATH if there # are no directories specified by -L. _LT_TAGVAR(hardcode_minus_L, $1)=yes if test "$GCC" = yes && test -z "$lt_prog_compiler_static"; then # Neither direct hardcoding nor static linking is supported with a # broken collect2. _LT_TAGVAR(hardcode_direct, $1)=unsupported fi ;; aix[[4-9]]*) if test "$host_cpu" = ia64; then # On IA64, the linker does run time linking by default, so we don't # have to do anything special. aix_use_runtimelinking=no exp_sym_flag='-Bexport' no_entry_flag="" else # If we're using GNU nm, then we don't want the "-C" option. # -C means demangle to AIX nm, but means don't demangle with GNU nm if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' else _LT_TAGVAR(export_symbols_cmds, $1)='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' fi aix_use_runtimelinking=no # Test if we are trying to use run time linking or normal # AIX style linking. If -brtl is somewhere in LDFLAGS, we # need to do runtime linking. case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*) for ld_flag in $LDFLAGS; do if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then aix_use_runtimelinking=yes break fi done ;; esac exp_sym_flag='-bexport' no_entry_flag='-bnoentry' fi # When large executables or shared objects are built, AIX ld can # have problems creating the table of contents. If linking a library # or program results in "error TOC overflow" add -mminimal-toc to # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. _LT_TAGVAR(archive_cmds, $1)='' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(hardcode_libdir_separator, $1)=':' _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(file_list_spec, $1)='${wl}-f,' if test "$GCC" = yes; then case $host_os in aix4.[[012]]|aix4.[[012]].*) # We only want to do this on AIX 4.2 and lower, the check # below for broken collect2 doesn't work under 4.3+ collect2name=`${CC} -print-prog-name=collect2` if test -f "$collect2name" && strings "$collect2name" | $GREP resolve_lib_name >/dev/null then # We have reworked collect2 : else # We have old collect2 _LT_TAGVAR(hardcode_direct, $1)=unsupported # It fails to find uninstalled libraries when the uninstalled # path is not listed in the libpath. Setting hardcode_minus_L # to unsupported forces relinking _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)= fi ;; esac shared_flag='-shared' if test "$aix_use_runtimelinking" = yes; then shared_flag="$shared_flag "'${wl}-G' fi _LT_TAGVAR(link_all_deplibs, $1)=no else # not using gcc if test "$host_cpu" = ia64; then # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release # chokes on -Wl,-G. The following line is correct: shared_flag='-G' else if test "$aix_use_runtimelinking" = yes; then shared_flag='${wl}-G' else shared_flag='${wl}-bM:SRE' fi fi fi _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-bexpall' # It seems that -bexpall does not export symbols beginning with # underscore (_), so it is better to generate a list of symbols to export. _LT_TAGVAR(always_export_symbols, $1)=yes if test "$aix_use_runtimelinking" = yes; then # Warning - without using the other runtime loading flags (-brtl), # -berok will link without error, but may produce a broken library. _LT_TAGVAR(allow_undefined_flag, $1)='-berok' # Determine the default libpath from the value encoded in an # empty executable. _LT_SYS_MODULE_PATH_AIX _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then $ECHO "X${wl}${allow_undefined_flag}" | $Xsed; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" else if test "$host_cpu" = ia64; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib' _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs" _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" else # Determine the default libpath from the value encoded in an # empty executable. _LT_SYS_MODULE_PATH_AIX _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" # Warning - without using the other run time loading flags, # -berok will link without error, but may produce a broken library. _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok' _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok' # Exported symbols can be pulled into shared objects from archives _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience' _LT_TAGVAR(archive_cmds_need_lc, $1)=yes # This is similar to how AIX traditionally builds its shared libraries. _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' fi fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='' ;; m68k) _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_minus_L, $1)=yes ;; esac ;; bsdi[[45]]*) _LT_TAGVAR(export_dynamic_flag_spec, $1)=-rdynamic ;; cygwin* | mingw* | pw32* | cegcc*) # When not using gcc, we currently assume that we are using # Microsoft Visual C++. # hardcode_libdir_flag_spec is actually meaningless, as there is # no search path for DLLs. _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=".dll" # FIXME: Setting linknames here is a bad hack. _LT_TAGVAR(archive_cmds, $1)='$CC -o $lib $libobjs $compiler_flags `$ECHO "X$deplibs" | $Xsed -e '\''s/ -lc$//'\''` -link -dll~linknames=' # The linker will automatically build a .lib file if we build a DLL. _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' # FIXME: Should let the user specify the lib program. _LT_TAGVAR(old_archive_cmds, $1)='lib -OUT:$oldlib$oldobjs$old_deplibs' _LT_TAGVAR(fix_srcfile_path, $1)='`cygpath -w "$srcfile"`' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes ;; darwin* | rhapsody*) _LT_DARWIN_LINKER_FEATURES($1) ;; dgux*) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; freebsd1*) _LT_TAGVAR(ld_shlibs, $1)=no ;; # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor # support. Future versions do this automatically, but an explicit c++rt0.o # does not break anything, and helps significantly (at the cost of a little # extra space). freebsd2.2*) _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; # Unfortunately, older versions of FreeBSD 2 do not have this feature. freebsd2*) _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; # FreeBSD 3 and greater uses gcc -shared to do shared libraries. freebsd* | dragonfly*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; hpux9*) if test "$GCC" = yes; then _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' else _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(hardcode_direct, $1)=yes # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' ;; hpux10*) if test "$GCC" = yes -a "$with_gnu_ld" = no; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' else _LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' fi if test "$with_gnu_ld" = no; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' _LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)='+b $libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. _LT_TAGVAR(hardcode_minus_L, $1)=yes fi ;; hpux11*) if test "$GCC" = yes -a "$with_gnu_ld" = no; then case $host_cpu in hppa*64*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' ;; ia64*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' ;; esac else case $host_cpu in hppa*64*) _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' ;; ia64*) _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' ;; esac fi if test "$with_gnu_ld" = no; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: case $host_cpu in hppa*64*|ia64*) _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *) _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. _LT_TAGVAR(hardcode_minus_L, $1)=yes ;; esac fi ;; irix5* | irix6* | nonstopux*) if test "$GCC" = yes; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' # Try to use the -exported_symbol ld option, if it does not # work, assume that -exports_file does not work either and # implicitly export all symbols. save_LDFLAGS="$LDFLAGS" LDFLAGS="$LDFLAGS -shared ${wl}-exported_symbol ${wl}foo ${wl}-update_registry ${wl}/dev/null" AC_LINK_IFELSE(int foo(void) {}, _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations ${wl}-exports_file ${wl}$export_symbols -o $lib' ) LDFLAGS="$save_LDFLAGS" else _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -exports_file $export_symbols -o $lib' fi _LT_TAGVAR(archive_cmds_need_lc, $1)='no' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(inherit_rpath, $1)=yes _LT_TAGVAR(link_all_deplibs, $1)=yes ;; netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out else _LT_TAGVAR(archive_cmds, $1)='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; newsos6) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *nto* | *qnx*) ;; openbsd*) if test -f /usr/libexec/ld.so; then _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=yes if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-retain-symbols-file,$export_symbols' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' else case $host_os in openbsd[[01]].* | openbsd2.[[0-7]] | openbsd2.[[0-7]].*) _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' ;; esac fi else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; os2*) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~$ECHO DATA >> $output_objdir/$libname.def~$ECHO " SINGLE NONSHARED" >> $output_objdir/$libname.def~$ECHO EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def' _LT_TAGVAR(old_archive_from_new_cmds, $1)='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def' ;; osf3*) if test "$GCC" = yes; then _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' else _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib' fi _LT_TAGVAR(archive_cmds_need_lc, $1)='no' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: ;; osf4* | osf5*) # as osf3* with the addition of -msym flag if test "$GCC" = yes; then _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' else _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~ $CC -shared${allow_undefined_flag} ${wl}-input ${wl}$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib~$RM $lib.exp' # Both c and cxx compiler support -rpath directly _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' fi _LT_TAGVAR(archive_cmds_need_lc, $1)='no' _LT_TAGVAR(hardcode_libdir_separator, $1)=: ;; solaris*) _LT_TAGVAR(no_undefined_flag, $1)=' -z defs' if test "$GCC" = yes; then wlarc='${wl}' _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-z ${wl}text ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -shared ${wl}-z ${wl}text ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' else case `$CC -V 2>&1` in *"Compilers 5.0"*) wlarc='' _LT_TAGVAR(archive_cmds, $1)='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp' ;; *) wlarc='${wl}' _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' ;; esac fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no case $host_os in solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; *) # The compiler driver will combine and reorder linker options, # but understands `-z linker_flag'. GCC discards it without `$wl', # but is careful enough not to reorder. # Supported since Solaris 2.6 (maybe 2.5.1?) if test "$GCC" = yes; then _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract' else _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' fi ;; esac _LT_TAGVAR(link_all_deplibs, $1)=yes ;; sunos4*) if test "x$host_vendor" = xsequent; then # Use $CC to link under sequent, because it throws in some extra .o # files that make .init and .fini sections work. _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags' else _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; sysv4) case $host_vendor in sni) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_direct, $1)=yes # is this really true??? ;; siemens) ## LD is ld it makes a PLAMLIB ## CC just makes a GrossModule. _LT_TAGVAR(archive_cmds, $1)='$LD -G -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(reload_cmds, $1)='$CC -r -o $output$reload_objs' _LT_TAGVAR(hardcode_direct, $1)=no ;; motorola) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_direct, $1)=no #Motorola manual says yes, but my tests say they lie ;; esac runpath_var='LD_RUN_PATH' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; sysv4.3*) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(export_dynamic_flag_spec, $1)='-Bexport' ;; sysv4*MP*) if test -d /usr/nec; then _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no runpath_var=LD_RUN_PATH hardcode_runpath_var=yes _LT_TAGVAR(ld_shlibs, $1)=yes fi ;; sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*) _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no runpath_var='LD_RUN_PATH' if test "$GCC" = yes; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' else _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' fi ;; sysv5* | sco3.2v5* | sco5v6*) # Note: We can NOT use -z defs as we might desire, because we do not # link with -lc, and that would cause any symbols used from libc to # always be unresolved, which means just about no library would # ever link correctly. If we're not using GNU ld we use -z text # though, which does catch some bad symbols but isn't as heavy-handed # as -z defs. _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' _LT_TAGVAR(allow_undefined_flag, $1)='${wl}-z,nodefs' _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R,$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=':' _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Bexport' runpath_var='LD_RUN_PATH' if test "$GCC" = yes; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' else _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' fi ;; uts4*) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *) _LT_TAGVAR(ld_shlibs, $1)=no ;; esac if test x$host_vendor = xsni; then case $host in sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Blargedynsym' ;; esac fi fi ]) AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)]) test "$_LT_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no _LT_TAGVAR(with_gnu_ld, $1)=$with_gnu_ld _LT_DECL([], [libext], [0], [Old archive suffix (normally "a")])dnl _LT_DECL([], [shrext_cmds], [1], [Shared library suffix (normally ".so")])dnl _LT_DECL([], [extract_expsyms_cmds], [2], [The commands to extract the exported symbol list from a shared archive]) # # Do we need to explicitly link libc? # case "x$_LT_TAGVAR(archive_cmds_need_lc, $1)" in x|xyes) # Assume -lc should be added _LT_TAGVAR(archive_cmds_need_lc, $1)=yes if test "$enable_shared" = yes && test "$GCC" = yes; then case $_LT_TAGVAR(archive_cmds, $1) in *'~'*) # FIXME: we may have to deal with multi-command sequences. ;; '$CC '*) # Test whether the compiler implicitly links with -lc since on some # systems, -lgcc has to come before -lc. If gcc already passes -lc # to ld, don't add -lc before -lgcc. AC_MSG_CHECKING([whether -lc should be explicitly linked in]) $RM conftest* echo "$lt_simple_compile_test_code" > conftest.$ac_ext if AC_TRY_EVAL(ac_compile) 2>conftest.err; then soname=conftest lib=conftest libobjs=conftest.$ac_objext deplibs= wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) pic_flag=$_LT_TAGVAR(lt_prog_compiler_pic, $1) compiler_flags=-v linker_flags=-v verstring= output_objdir=. libname=conftest lt_save_allow_undefined_flag=$_LT_TAGVAR(allow_undefined_flag, $1) _LT_TAGVAR(allow_undefined_flag, $1)= if AC_TRY_EVAL(_LT_TAGVAR(archive_cmds, $1) 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) then _LT_TAGVAR(archive_cmds_need_lc, $1)=no else _LT_TAGVAR(archive_cmds_need_lc, $1)=yes fi _LT_TAGVAR(allow_undefined_flag, $1)=$lt_save_allow_undefined_flag else cat conftest.err 1>&5 fi $RM conftest* AC_MSG_RESULT([$_LT_TAGVAR(archive_cmds_need_lc, $1)]) ;; esac fi ;; esac _LT_TAGDECL([build_libtool_need_lc], [archive_cmds_need_lc], [0], [Whether or not to add -lc for building shared libraries]) _LT_TAGDECL([allow_libtool_libs_with_static_runtimes], [enable_shared_with_static_runtimes], [0], [Whether or not to disallow shared libs when runtime libs are static]) _LT_TAGDECL([], [export_dynamic_flag_spec], [1], [Compiler flag to allow reflexive dlopens]) _LT_TAGDECL([], [whole_archive_flag_spec], [1], [Compiler flag to generate shared objects directly from archives]) _LT_TAGDECL([], [compiler_needs_object], [1], [Whether the compiler copes with passing no objects directly]) _LT_TAGDECL([], [old_archive_from_new_cmds], [2], [Create an old-style archive from a shared archive]) _LT_TAGDECL([], [old_archive_from_expsyms_cmds], [2], [Create a temporary old-style archive to link instead of a shared archive]) _LT_TAGDECL([], [archive_cmds], [2], [Commands used to build a shared archive]) _LT_TAGDECL([], [archive_expsym_cmds], [2]) _LT_TAGDECL([], [module_cmds], [2], [Commands used to build a loadable module if different from building a shared archive.]) _LT_TAGDECL([], [module_expsym_cmds], [2]) _LT_TAGDECL([], [with_gnu_ld], [1], [Whether we are building with GNU ld or not]) _LT_TAGDECL([], [allow_undefined_flag], [1], [Flag that allows shared libraries with undefined symbols to be built]) _LT_TAGDECL([], [no_undefined_flag], [1], [Flag that enforces no undefined symbols]) _LT_TAGDECL([], [hardcode_libdir_flag_spec], [1], [Flag to hardcode $libdir into a binary during linking. This must work even if $libdir does not exist]) _LT_TAGDECL([], [hardcode_libdir_flag_spec_ld], [1], [[If ld is used when linking, flag to hardcode $libdir into a binary during linking. This must work even if $libdir does not exist]]) _LT_TAGDECL([], [hardcode_libdir_separator], [1], [Whether we need a single "-rpath" flag with a separated argument]) _LT_TAGDECL([], [hardcode_direct], [0], [Set to "yes" if using DIR/libNAME${shared_ext} during linking hardcodes DIR into the resulting binary]) _LT_TAGDECL([], [hardcode_direct_absolute], [0], [Set to "yes" if using DIR/libNAME${shared_ext} during linking hardcodes DIR into the resulting binary and the resulting library dependency is "absolute", i.e impossible to change by setting ${shlibpath_var} if the library is relocated]) _LT_TAGDECL([], [hardcode_minus_L], [0], [Set to "yes" if using the -LDIR flag during linking hardcodes DIR into the resulting binary]) _LT_TAGDECL([], [hardcode_shlibpath_var], [0], [Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR into the resulting binary]) _LT_TAGDECL([], [hardcode_automatic], [0], [Set to "yes" if building a shared library automatically hardcodes DIR into the library and all subsequent libraries and executables linked against it]) _LT_TAGDECL([], [inherit_rpath], [0], [Set to yes if linker adds runtime paths of dependent libraries to runtime path list]) _LT_TAGDECL([], [link_all_deplibs], [0], [Whether libtool must link a program against all its dependency libraries]) _LT_TAGDECL([], [fix_srcfile_path], [1], [Fix the shell variable $srcfile for the compiler]) _LT_TAGDECL([], [always_export_symbols], [0], [Set to "yes" if exported symbols are required]) _LT_TAGDECL([], [export_symbols_cmds], [2], [The commands to list exported symbols]) _LT_TAGDECL([], [exclude_expsyms], [1], [Symbols that should not be listed in the preloaded symbols]) _LT_TAGDECL([], [include_expsyms], [1], [Symbols that must always be exported]) _LT_TAGDECL([], [prelink_cmds], [2], [Commands necessary for linking programs (against libraries) with templates]) _LT_TAGDECL([], [file_list_spec], [1], [Specify filename containing input files]) dnl FIXME: Not yet implemented dnl _LT_TAGDECL([], [thread_safe_flag_spec], [1], dnl [Compiler flag to generate thread safe objects]) ])# _LT_LINKER_SHLIBS # _LT_LANG_C_CONFIG([TAG]) # ------------------------ # Ensure that the configuration variables for a C compiler are suitably # defined. These variables are subsequently used by _LT_CONFIG to write # the compiler configuration to `libtool'. m4_defun([_LT_LANG_C_CONFIG], [m4_require([_LT_DECL_EGREP])dnl lt_save_CC="$CC" AC_LANG_PUSH(C) # Source file extension for C test sources. ac_ext=c # Object file extension for compiled C test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # Code to be used in simple compile tests lt_simple_compile_test_code="int some_variable = 0;" # Code to be used in simple link tests lt_simple_link_test_code='int main(){return(0);}' _LT_TAG_COMPILER # Save the default compiler, since it gets overwritten when the other # tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP. compiler_DEFAULT=$CC # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... if test -n "$compiler"; then _LT_COMPILER_NO_RTTI($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_SYS_DYNAMIC_LINKER($1) _LT_LINKER_HARDCODE_LIBPATH($1) LT_SYS_DLOPEN_SELF _LT_CMD_STRIPLIB # Report which library types will actually be built AC_MSG_CHECKING([if libtool supports shared libraries]) AC_MSG_RESULT([$can_build_shared]) AC_MSG_CHECKING([whether to build shared libraries]) test "$can_build_shared" = "no" && enable_shared=no # On AIX, shared libraries and static libraries use the same namespace, and # are all built from PIC. case $host_os in aix3*) test "$enable_shared" = yes && enable_static=no if test -n "$RANLIB"; then archive_cmds="$archive_cmds~\$RANLIB \$lib" postinstall_cmds='$RANLIB $lib' fi ;; aix[[4-9]]*) if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then test "$enable_shared" = yes && enable_static=no fi ;; esac AC_MSG_RESULT([$enable_shared]) AC_MSG_CHECKING([whether to build static libraries]) # Make sure either enable_shared or enable_static is yes. test "$enable_shared" = yes || enable_static=yes AC_MSG_RESULT([$enable_static]) _LT_CONFIG($1) fi AC_LANG_POP CC="$lt_save_CC" ])# _LT_LANG_C_CONFIG # _LT_PROG_CXX # ------------ # Since AC_PROG_CXX is broken, in that it returns g++ if there is no c++ # compiler, we have our own version here. m4_defun([_LT_PROG_CXX], [ pushdef([AC_MSG_ERROR], [_lt_caught_CXX_error=yes]) AC_PROG_CXX if test -n "$CXX" && ( test "X$CXX" != "Xno" && ( (test "X$CXX" = "Xg++" && `g++ -v >/dev/null 2>&1` ) || (test "X$CXX" != "Xg++"))) ; then AC_PROG_CXXCPP else _lt_caught_CXX_error=yes fi popdef([AC_MSG_ERROR]) ])# _LT_PROG_CXX dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([_LT_PROG_CXX], []) # _LT_LANG_CXX_CONFIG([TAG]) # -------------------------- # Ensure that the configuration variables for a C++ compiler are suitably # defined. These variables are subsequently used by _LT_CONFIG to write # the compiler configuration to `libtool'. m4_defun([_LT_LANG_CXX_CONFIG], [AC_REQUIRE([_LT_PROG_CXX])dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_EGREP])dnl AC_LANG_PUSH(C++) _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(allow_undefined_flag, $1)= _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(archive_expsym_cmds, $1)= _LT_TAGVAR(compiler_needs_object, $1)=no _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)= _LT_TAGVAR(hardcode_libdir_separator, $1)= _LT_TAGVAR(hardcode_minus_L, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported _LT_TAGVAR(hardcode_automatic, $1)=no _LT_TAGVAR(inherit_rpath, $1)=no _LT_TAGVAR(module_cmds, $1)= _LT_TAGVAR(module_expsym_cmds, $1)= _LT_TAGVAR(link_all_deplibs, $1)=unknown _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(no_undefined_flag, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no # Source file extension for C++ test sources. ac_ext=cpp # Object file extension for compiled C++ test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # No sense in running all these tests if we already determined that # the CXX compiler isn't working. Some variables (like enable_shared) # are currently assumed to apply to all compilers on this platform, # and will be corrupted by setting them based on a non-working compiler. if test "$_lt_caught_CXX_error" != yes; then # Code to be used in simple compile tests lt_simple_compile_test_code="int some_variable = 0;" # Code to be used in simple link tests lt_simple_link_test_code='int main(int, char *[[]]) { return(0); }' # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_LD=$LD lt_save_GCC=$GCC GCC=$GXX lt_save_with_gnu_ld=$with_gnu_ld lt_save_path_LD=$lt_cv_path_LD if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx else $as_unset lt_cv_prog_gnu_ld fi if test -n "${lt_cv_path_LDCXX+set}"; then lt_cv_path_LD=$lt_cv_path_LDCXX else $as_unset lt_cv_path_LD fi test -z "${LDCXX+set}" || LD=$LDCXX CC=${CXX-"c++"} compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_CC_BASENAME([$compiler]) if test -n "$compiler"; then # We don't want -fno-exception when compiling C++ code, so set the # no_builtin_flag separately if test "$GXX" = yes; then _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' else _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= fi if test "$GXX" = yes; then # Set up default GNU C++ configuration LT_PATH_LD # Check if GNU C++ uses GNU ld as the underlying linker, since the # archiving commands below assume that GNU ld is being used. if test "$with_gnu_ld" = yes; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' # If archive_cmds runs LD, not CC, wlarc should be empty # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to # investigate it a little bit more. (MM) wlarc='${wl}' # ancient GNU ld didn't support --whole-archive et. al. if eval "`$CC -print-prog-name=ld` --help 2>&1" | $GREP 'no-whole-archive' > /dev/null; then _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' else _LT_TAGVAR(whole_archive_flag_spec, $1)= fi else with_gnu_ld=no wlarc= # A generic and very simple default shared library creation # command for GNU C++ for the case where it uses the native # linker, instead of GNU ld. If possible, this setting should # overridden to take advantage of the native linker features on # the platform it is being used on. _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' fi # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "\-L"' else GXX=no with_gnu_ld=no wlarc= fi # PORTME: fill in a description of your system's C++ link characteristics AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) _LT_TAGVAR(ld_shlibs, $1)=yes case $host_os in aix3*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; aix[[4-9]]*) if test "$host_cpu" = ia64; then # On IA64, the linker does run time linking by default, so we don't # have to do anything special. aix_use_runtimelinking=no exp_sym_flag='-Bexport' no_entry_flag="" else aix_use_runtimelinking=no # Test if we are trying to use run time linking or normal # AIX style linking. If -brtl is somewhere in LDFLAGS, we # need to do runtime linking. case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*) for ld_flag in $LDFLAGS; do case $ld_flag in *-brtl*) aix_use_runtimelinking=yes break ;; esac done ;; esac exp_sym_flag='-bexport' no_entry_flag='-bnoentry' fi # When large executables or shared objects are built, AIX ld can # have problems creating the table of contents. If linking a library # or program results in "error TOC overflow" add -mminimal-toc to # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. _LT_TAGVAR(archive_cmds, $1)='' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(hardcode_libdir_separator, $1)=':' _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(file_list_spec, $1)='${wl}-f,' if test "$GXX" = yes; then case $host_os in aix4.[[012]]|aix4.[[012]].*) # We only want to do this on AIX 4.2 and lower, the check # below for broken collect2 doesn't work under 4.3+ collect2name=`${CC} -print-prog-name=collect2` if test -f "$collect2name" && strings "$collect2name" | $GREP resolve_lib_name >/dev/null then # We have reworked collect2 : else # We have old collect2 _LT_TAGVAR(hardcode_direct, $1)=unsupported # It fails to find uninstalled libraries when the uninstalled # path is not listed in the libpath. Setting hardcode_minus_L # to unsupported forces relinking _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)= fi esac shared_flag='-shared' if test "$aix_use_runtimelinking" = yes; then shared_flag="$shared_flag "'${wl}-G' fi else # not using gcc if test "$host_cpu" = ia64; then # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release # chokes on -Wl,-G. The following line is correct: shared_flag='-G' else if test "$aix_use_runtimelinking" = yes; then shared_flag='${wl}-G' else shared_flag='${wl}-bM:SRE' fi fi fi _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-bexpall' # It seems that -bexpall does not export symbols beginning with # underscore (_), so it is better to generate a list of symbols to # export. _LT_TAGVAR(always_export_symbols, $1)=yes if test "$aix_use_runtimelinking" = yes; then # Warning - without using the other runtime loading flags (-brtl), # -berok will link without error, but may produce a broken library. _LT_TAGVAR(allow_undefined_flag, $1)='-berok' # Determine the default libpath from the value encoded in an empty # executable. _LT_SYS_MODULE_PATH_AIX _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then $ECHO "X${wl}${allow_undefined_flag}" | $Xsed; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" else if test "$host_cpu" = ia64; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib' _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs" _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" else # Determine the default libpath from the value encoded in an # empty executable. _LT_SYS_MODULE_PATH_AIX _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" # Warning - without using the other run time loading flags, # -berok will link without error, but may produce a broken library. _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok' _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok' # Exported symbols can be pulled into shared objects from archives _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience' _LT_TAGVAR(archive_cmds_need_lc, $1)=yes # This is similar to how AIX traditionally builds its shared # libraries. _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' fi fi ;; beos*) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(allow_undefined_flag, $1)=unsupported # Joseph Beckenbach says some releases of gcc # support --undefined. This deserves some investigation. FIXME _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; chorus*) case $cc_basename in *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; cygwin* | mingw* | pw32* | cegcc*) # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, # as there is no search path for DLLs. _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' # If the export-symbols file already is a .def file (1st line # is EXPORTS), use it as is; otherwise, prepend... _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then cp $export_symbols $output_objdir/$soname.def; else echo EXPORTS > $output_objdir/$soname.def; cat $export_symbols >> $output_objdir/$soname.def; fi~ $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; darwin* | rhapsody*) _LT_DARWIN_LINKER_FEATURES($1) ;; dgux*) case $cc_basename in ec++*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; ghcx*) # Green Hills C++ Compiler # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; freebsd[[12]]*) # C++ shared libraries reported to be fairly broken before # switch to ELF _LT_TAGVAR(ld_shlibs, $1)=no ;; freebsd-elf*) _LT_TAGVAR(archive_cmds_need_lc, $1)=no ;; freebsd* | dragonfly*) # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF # conventions _LT_TAGVAR(ld_shlibs, $1)=yes ;; gnu*) ;; hpux9*) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, # but as the default # location of the library. case $cc_basename in CC*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; aCC*) _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -b ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed' ;; *) if test "$GXX" = yes; then _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared -nostdlib -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' else # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; hpux10*|hpux11*) if test $with_gnu_ld = no; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: case $host_cpu in hppa*64*|ia64*) ;; *) _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' ;; esac fi case $host_cpu in hppa*64*|ia64*) _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *) _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, # but as the default # location of the library. ;; esac case $cc_basename in CC*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; aCC*) case $host_cpu in hppa*64*) _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; ia64*) _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; esac # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed' ;; *) if test "$GXX" = yes; then if test $with_gnu_ld = no; then case $host_cpu in hppa*64*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; ia64*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; esac fi else # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; interix[[3-9]]*) _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. # Instead, shared libraries are loaded at an image base (0x10000000 by # default) and relocated if they conflict, which is a slow very memory # consuming and fragmenting process. To avoid this, we pick a random, # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link # time. Moving up from 0x10000000 also allows more sbrk(2) space. _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' ;; irix5* | irix6*) case $cc_basename in CC*) # SGI C++ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib' # Archives containing C++ object files must be created using # "CC -ar", where "CC" is the IRIX C++ compiler. This is # necessary to make sure instantiated templates are included # in the archive. _LT_TAGVAR(old_archive_cmds, $1)='$CC -ar -WR,-u -o $oldlib $oldobjs' ;; *) if test "$GXX" = yes; then if test "$with_gnu_ld" = no; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' else _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` -o $lib' fi fi _LT_TAGVAR(link_all_deplibs, $1)=yes ;; esac _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(inherit_rpath, $1)=yes ;; linux* | k*bsd*-gnu | kopensolaris*-gnu) case $cc_basename in KCC*) # Kuck and Associates, Inc. (KAI) C++ Compiler # KCC will only create a shared library if the output file # ends with ".so" (or ".sl" for HP-UX), so rename the library # to its proper name (with version) after linking. _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib ${wl}-retain-symbols-file,$export_symbols; mv \$templib $lib' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' # Archives containing C++ object files must be created using # "CC -Bstatic", where "CC" is the KAI C++ compiler. _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' ;; icpc* | ecpc* ) # Intel C++ with_gnu_ld=yes # version 8.0 and above of icpc choke on multiply defined symbols # if we add $predep_objects and $postdep_objects, however 7.1 and # earlier do not add the objects themselves. case `$CC -V 2>&1` in *"Version 7."*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' ;; *) # Version 8.0 or newer tmp_idyn= case $host_cpu in ia64*) tmp_idyn=' -i_dynamic';; esac _LT_TAGVAR(archive_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' ;; esac _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive$convenience ${wl}--no-whole-archive' ;; pgCC* | pgcpp*) # Portland Group C++ compiler case `$CC -V` in *pgCC\ [[1-5]]* | *pgcpp\ [[1-5]]*) _LT_TAGVAR(prelink_cmds, $1)='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~ compile_command="$compile_command `find $tpldir -name \*.o | $NL2SP`"' _LT_TAGVAR(old_archive_cmds, $1)='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~ $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | $NL2SP`~ $RANLIB $oldlib' _LT_TAGVAR(archive_cmds, $1)='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib' ;; *) # Version 6 will use weak symbols _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib' ;; esac _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}--rpath ${wl}$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive' ;; cxx*) # Compaq C++ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib ${wl}-retain-symbols-file $wl$export_symbols' runpath_var=LD_RUN_PATH _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`$ECHO "X$templist" | $Xsed -e "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed' ;; xl*) # IBM XL 8.0 on PPC, with GNU ld _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' _LT_TAGVAR(archive_cmds, $1)='$CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' if test "x$supports_anon_versioning" = xyes; then _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' fi ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C++ 5.9 _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs' _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file ${wl}$export_symbols' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive' _LT_TAGVAR(compiler_needs_object, $1)=yes # Not sure whether something based on # $CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 # would be better. output_verbose_link_cmd='echo' # Archives containing C++ object files must be created using # "CC -xar", where "CC" is the Sun C++ compiler. This is # necessary to make sure instantiated templates are included # in the archive. _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs' ;; esac ;; esac ;; lynxos*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; m88k*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; mvs*) case $cc_basename in cxx*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; netbsd*) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags' wlarc= _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no fi # Workaround some broken pre-1.5 toolchains output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"' ;; *nto* | *qnx*) _LT_TAGVAR(ld_shlibs, $1)=yes ;; openbsd2*) # C++ shared libraries are fairly broken _LT_TAGVAR(ld_shlibs, $1)=no ;; openbsd*) if test -f /usr/libexec/ld.so; then _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file,$export_symbols -o $lib' _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' fi output_verbose_link_cmd=echo else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; osf3* | osf4* | osf5*) case $cc_basename in KCC*) # Kuck and Associates, Inc. (KAI) C++ Compiler # KCC will only create a shared library if the output file # ends with ".so" (or ".sl" for HP-UX), so rename the library # to its proper name (with version) after linking. _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: # Archives containing C++ object files must be created using # the KAI C++ compiler. case $host in osf3*) _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' ;; *) _LT_TAGVAR(old_archive_cmds, $1)='$CC -o $oldlib $oldobjs' ;; esac ;; RCC*) # Rational C++ 2.4.1 # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; cxx*) case $host in osf3*) _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $soname `test -n "$verstring" && $ECHO "X${wl}-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' ;; *) _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~ echo "-hidden">> $lib.exp~ $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname ${wl}-input ${wl}$lib.exp `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib~ $RM $lib.exp' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' ;; esac _LT_TAGVAR(hardcode_libdir_separator, $1)=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`$ECHO "X$templist" | $Xsed -e "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed' ;; *) if test "$GXX" = yes && test "$with_gnu_ld" = no; then _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' case $host in osf3*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' ;; esac _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "\-L"' else # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; psos*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; sunos4*) case $cc_basename in CC*) # Sun C++ 4.x # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; lcc*) # Lucid # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; solaris*) case $cc_basename in CC*) # Sun C++ 4.2, 5.x and Centerline C++ _LT_TAGVAR(archive_cmds_need_lc,$1)=yes _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs' _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G${allow_undefined_flag} ${wl}-M ${wl}$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no case $host_os in solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; *) # The compiler driver will combine and reorder linker options, # but understands `-z linker_flag'. # Supported since Solaris 2.6 (maybe 2.5.1?) _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' ;; esac _LT_TAGVAR(link_all_deplibs, $1)=yes output_verbose_link_cmd='echo' # Archives containing C++ object files must be created using # "CC -xar", where "CC" is the Sun C++ compiler. This is # necessary to make sure instantiated templates are included # in the archive. _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs' ;; gcx*) # Green Hills C++ Compiler _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' # The C++ compiler must be used to create the archive. _LT_TAGVAR(old_archive_cmds, $1)='$CC $LDFLAGS -archive -o $oldlib $oldobjs' ;; *) # GNU C++ compiler with Solaris linker if test "$GXX" = yes && test "$with_gnu_ld" = no; then _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-z ${wl}defs' if $CC --version | $GREP -v '^2\.7' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -shared -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "\-L"' else # g++ 2.7 appears to require `-G' NOT `-shared' on this # platform. _LT_TAGVAR(archive_cmds, $1)='$CC -G -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP "\-L"' fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $wl$libdir' case $host_os in solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; *) _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract' ;; esac fi ;; esac ;; sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*) _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no runpath_var='LD_RUN_PATH' case $cc_basename in CC*) _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; esac ;; sysv5* | sco3.2v5* | sco5v6*) # Note: We can NOT use -z defs as we might desire, because we do not # link with -lc, and that would cause any symbols used from libc to # always be unresolved, which means just about no library would # ever link correctly. If we're not using GNU ld we use -z text # though, which does catch some bad symbols but isn't as heavy-handed # as -z defs. _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' _LT_TAGVAR(allow_undefined_flag, $1)='${wl}-z,nodefs' _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R,$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=':' _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Bexport' runpath_var='LD_RUN_PATH' case $cc_basename in CC*) _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; esac ;; tandem*) case $cc_basename in NCC*) # NonStop-UX NCC 3.20 # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; vxworks*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)]) test "$_LT_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no _LT_TAGVAR(GCC, $1)="$GXX" _LT_TAGVAR(LD, $1)="$LD" ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... _LT_SYS_HIDDEN_LIBDEPS($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_SYS_DYNAMIC_LINKER($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi # test -n "$compiler" CC=$lt_save_CC LDCXX=$LD LD=$lt_save_LD GCC=$lt_save_GCC with_gnu_ld=$lt_save_with_gnu_ld lt_cv_path_LDCXX=$lt_cv_path_LD lt_cv_path_LD=$lt_save_path_LD lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld fi # test "$_lt_caught_CXX_error" != yes AC_LANG_POP ])# _LT_LANG_CXX_CONFIG # _LT_SYS_HIDDEN_LIBDEPS([TAGNAME]) # --------------------------------- # Figure out "hidden" library dependencies from verbose # compiler output when linking a shared library. # Parse the compiler output and extract the necessary # objects, libraries and library flags. m4_defun([_LT_SYS_HIDDEN_LIBDEPS], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl # Dependencies to place before and after the object being linked: _LT_TAGVAR(predep_objects, $1)= _LT_TAGVAR(postdep_objects, $1)= _LT_TAGVAR(predeps, $1)= _LT_TAGVAR(postdeps, $1)= _LT_TAGVAR(compiler_lib_search_path, $1)= dnl we can't use the lt_simple_compile_test_code here, dnl because it contains code intended for an executable, dnl not a library. It's possible we should let each dnl tag define a new lt_????_link_test_code variable, dnl but it's only used here... m4_if([$1], [], [cat > conftest.$ac_ext <<_LT_EOF int a; void foo (void) { a = 0; } _LT_EOF ], [$1], [CXX], [cat > conftest.$ac_ext <<_LT_EOF class Foo { public: Foo (void) { a = 0; } private: int a; }; _LT_EOF ], [$1], [F77], [cat > conftest.$ac_ext <<_LT_EOF subroutine foo implicit none integer*4 a a=0 return end _LT_EOF ], [$1], [FC], [cat > conftest.$ac_ext <<_LT_EOF subroutine foo implicit none integer a a=0 return end _LT_EOF ], [$1], [GCJ], [cat > conftest.$ac_ext <<_LT_EOF public class foo { private int a; public void bar (void) { a = 0; } }; _LT_EOF ]) dnl Parse the compiler output and extract the necessary dnl objects, libraries and library flags. if AC_TRY_EVAL(ac_compile); then # Parse the compiler output and extract the necessary # objects, libraries and library flags. # Sentinel used to keep track of whether or not we are before # the conftest object file. pre_test_object_deps_done=no for p in `eval "$output_verbose_link_cmd"`; do case $p in -L* | -R* | -l*) # Some compilers place space between "-{L,R}" and the path. # Remove the space. if test $p = "-L" || test $p = "-R"; then prev=$p continue else prev= fi if test "$pre_test_object_deps_done" = no; then case $p in -L* | -R*) # Internal compiler library paths should come after those # provided the user. The postdeps already come after the # user supplied libs so there is no need to process them. if test -z "$_LT_TAGVAR(compiler_lib_search_path, $1)"; then _LT_TAGVAR(compiler_lib_search_path, $1)="${prev}${p}" else _LT_TAGVAR(compiler_lib_search_path, $1)="${_LT_TAGVAR(compiler_lib_search_path, $1)} ${prev}${p}" fi ;; # The "-l" case would never come before the object being # linked, so don't bother handling this case. esac else if test -z "$_LT_TAGVAR(postdeps, $1)"; then _LT_TAGVAR(postdeps, $1)="${prev}${p}" else _LT_TAGVAR(postdeps, $1)="${_LT_TAGVAR(postdeps, $1)} ${prev}${p}" fi fi ;; *.$objext) # This assumes that the test object file only shows up # once in the compiler output. if test "$p" = "conftest.$objext"; then pre_test_object_deps_done=yes continue fi if test "$pre_test_object_deps_done" = no; then if test -z "$_LT_TAGVAR(predep_objects, $1)"; then _LT_TAGVAR(predep_objects, $1)="$p" else _LT_TAGVAR(predep_objects, $1)="$_LT_TAGVAR(predep_objects, $1) $p" fi else if test -z "$_LT_TAGVAR(postdep_objects, $1)"; then _LT_TAGVAR(postdep_objects, $1)="$p" else _LT_TAGVAR(postdep_objects, $1)="$_LT_TAGVAR(postdep_objects, $1) $p" fi fi ;; *) ;; # Ignore the rest. esac done # Clean up. rm -f a.out a.exe else echo "libtool.m4: error: problem compiling $1 test program" fi $RM -f confest.$objext # PORTME: override above test on systems where it is broken m4_if([$1], [CXX], [case $host_os in interix[[3-9]]*) # Interix 3.5 installs completely hosed .la files for C++, so rather than # hack all around it, let's just trust "g++" to DTRT. _LT_TAGVAR(predep_objects,$1)= _LT_TAGVAR(postdep_objects,$1)= _LT_TAGVAR(postdeps,$1)= ;; linux*) case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C++ 5.9 # The more standards-conforming stlport4 library is # incompatible with the Cstd library. Avoid specifying # it if it's in CXXFLAGS. Ignore libCrun as # -library=stlport4 depends on it. case " $CXX $CXXFLAGS " in *" -library=stlport4 "*) solaris_use_stlport4=yes ;; esac if test "$solaris_use_stlport4" != yes; then _LT_TAGVAR(postdeps,$1)='-library=Cstd -library=Crun' fi ;; esac ;; solaris*) case $cc_basename in CC*) # The more standards-conforming stlport4 library is # incompatible with the Cstd library. Avoid specifying # it if it's in CXXFLAGS. Ignore libCrun as # -library=stlport4 depends on it. case " $CXX $CXXFLAGS " in *" -library=stlport4 "*) solaris_use_stlport4=yes ;; esac # Adding this requires a known-good setup of shared libraries for # Sun compiler versions before 5.6, else PIC objects from an old # archive will be linked into the output, leading to subtle bugs. if test "$solaris_use_stlport4" != yes; then _LT_TAGVAR(postdeps,$1)='-library=Cstd -library=Crun' fi ;; esac ;; esac ]) case " $_LT_TAGVAR(postdeps, $1) " in *" -lc "*) _LT_TAGVAR(archive_cmds_need_lc, $1)=no ;; esac _LT_TAGVAR(compiler_lib_search_dirs, $1)= if test -n "${_LT_TAGVAR(compiler_lib_search_path, $1)}"; then _LT_TAGVAR(compiler_lib_search_dirs, $1)=`echo " ${_LT_TAGVAR(compiler_lib_search_path, $1)}" | ${SED} -e 's! -L! !g' -e 's!^ !!'` fi _LT_TAGDECL([], [compiler_lib_search_dirs], [1], [The directories searched by this compiler when creating a shared library]) _LT_TAGDECL([], [predep_objects], [1], [Dependencies to place before and after the objects being linked to create a shared library]) _LT_TAGDECL([], [postdep_objects], [1]) _LT_TAGDECL([], [predeps], [1]) _LT_TAGDECL([], [postdeps], [1]) _LT_TAGDECL([], [compiler_lib_search_path], [1], [The library search path used internally by the compiler when linking a shared library]) ])# _LT_SYS_HIDDEN_LIBDEPS # _LT_PROG_F77 # ------------ # Since AC_PROG_F77 is broken, in that it returns the empty string # if there is no fortran compiler, we have our own version here. m4_defun([_LT_PROG_F77], [ pushdef([AC_MSG_ERROR], [_lt_disable_F77=yes]) AC_PROG_F77 if test -z "$F77" || test "X$F77" = "Xno"; then _lt_disable_F77=yes fi popdef([AC_MSG_ERROR]) ])# _LT_PROG_F77 dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([_LT_PROG_F77], []) # _LT_LANG_F77_CONFIG([TAG]) # -------------------------- # Ensure that the configuration variables for a Fortran 77 compiler are # suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to `libtool'. m4_defun([_LT_LANG_F77_CONFIG], [AC_REQUIRE([_LT_PROG_F77])dnl AC_LANG_PUSH(Fortran 77) _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(allow_undefined_flag, $1)= _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(archive_expsym_cmds, $1)= _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)= _LT_TAGVAR(hardcode_libdir_separator, $1)= _LT_TAGVAR(hardcode_minus_L, $1)=no _LT_TAGVAR(hardcode_automatic, $1)=no _LT_TAGVAR(inherit_rpath, $1)=no _LT_TAGVAR(module_cmds, $1)= _LT_TAGVAR(module_expsym_cmds, $1)= _LT_TAGVAR(link_all_deplibs, $1)=unknown _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(no_undefined_flag, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no # Source file extension for f77 test sources. ac_ext=f # Object file extension for compiled f77 test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # No sense in running all these tests if we already determined that # the F77 compiler isn't working. Some variables (like enable_shared) # are currently assumed to apply to all compilers on this platform, # and will be corrupted by setting them based on a non-working compiler. if test "$_lt_disable_F77" != yes; then # Code to be used in simple compile tests lt_simple_compile_test_code="\ subroutine t return end " # Code to be used in simple link tests lt_simple_link_test_code="\ program t end " # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC="$CC" lt_save_GCC=$GCC CC=${F77-"f77"} compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_CC_BASENAME([$compiler]) GCC=$G77 if test -n "$compiler"; then AC_MSG_CHECKING([if libtool supports shared libraries]) AC_MSG_RESULT([$can_build_shared]) AC_MSG_CHECKING([whether to build shared libraries]) test "$can_build_shared" = "no" && enable_shared=no # On AIX, shared libraries and static libraries use the same namespace, and # are all built from PIC. case $host_os in aix3*) test "$enable_shared" = yes && enable_static=no if test -n "$RANLIB"; then archive_cmds="$archive_cmds~\$RANLIB \$lib" postinstall_cmds='$RANLIB $lib' fi ;; aix[[4-9]]*) if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then test "$enable_shared" = yes && enable_static=no fi ;; esac AC_MSG_RESULT([$enable_shared]) AC_MSG_CHECKING([whether to build static libraries]) # Make sure either enable_shared or enable_static is yes. test "$enable_shared" = yes || enable_static=yes AC_MSG_RESULT([$enable_static]) _LT_TAGVAR(GCC, $1)="$G77" _LT_TAGVAR(LD, $1)="$LD" ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_SYS_DYNAMIC_LINKER($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi # test -n "$compiler" GCC=$lt_save_GCC CC="$lt_save_CC" fi # test "$_lt_disable_F77" != yes AC_LANG_POP ])# _LT_LANG_F77_CONFIG # _LT_PROG_FC # ----------- # Since AC_PROG_FC is broken, in that it returns the empty string # if there is no fortran compiler, we have our own version here. m4_defun([_LT_PROG_FC], [ pushdef([AC_MSG_ERROR], [_lt_disable_FC=yes]) AC_PROG_FC if test -z "$FC" || test "X$FC" = "Xno"; then _lt_disable_FC=yes fi popdef([AC_MSG_ERROR]) ])# _LT_PROG_FC dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([_LT_PROG_FC], []) # _LT_LANG_FC_CONFIG([TAG]) # ------------------------- # Ensure that the configuration variables for a Fortran compiler are # suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to `libtool'. m4_defun([_LT_LANG_FC_CONFIG], [AC_REQUIRE([_LT_PROG_FC])dnl AC_LANG_PUSH(Fortran) _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(allow_undefined_flag, $1)= _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(archive_expsym_cmds, $1)= _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)= _LT_TAGVAR(hardcode_libdir_separator, $1)= _LT_TAGVAR(hardcode_minus_L, $1)=no _LT_TAGVAR(hardcode_automatic, $1)=no _LT_TAGVAR(inherit_rpath, $1)=no _LT_TAGVAR(module_cmds, $1)= _LT_TAGVAR(module_expsym_cmds, $1)= _LT_TAGVAR(link_all_deplibs, $1)=unknown _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(no_undefined_flag, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no # Source file extension for fc test sources. ac_ext=${ac_fc_srcext-f} # Object file extension for compiled fc test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # No sense in running all these tests if we already determined that # the FC compiler isn't working. Some variables (like enable_shared) # are currently assumed to apply to all compilers on this platform, # and will be corrupted by setting them based on a non-working compiler. if test "$_lt_disable_FC" != yes; then # Code to be used in simple compile tests lt_simple_compile_test_code="\ subroutine t return end " # Code to be used in simple link tests lt_simple_link_test_code="\ program t end " # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC="$CC" lt_save_GCC=$GCC CC=${FC-"f95"} compiler=$CC GCC=$ac_cv_fc_compiler_gnu _LT_TAGVAR(compiler, $1)=$CC _LT_CC_BASENAME([$compiler]) if test -n "$compiler"; then AC_MSG_CHECKING([if libtool supports shared libraries]) AC_MSG_RESULT([$can_build_shared]) AC_MSG_CHECKING([whether to build shared libraries]) test "$can_build_shared" = "no" && enable_shared=no # On AIX, shared libraries and static libraries use the same namespace, and # are all built from PIC. case $host_os in aix3*) test "$enable_shared" = yes && enable_static=no if test -n "$RANLIB"; then archive_cmds="$archive_cmds~\$RANLIB \$lib" postinstall_cmds='$RANLIB $lib' fi ;; aix[[4-9]]*) if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then test "$enable_shared" = yes && enable_static=no fi ;; esac AC_MSG_RESULT([$enable_shared]) AC_MSG_CHECKING([whether to build static libraries]) # Make sure either enable_shared or enable_static is yes. test "$enable_shared" = yes || enable_static=yes AC_MSG_RESULT([$enable_static]) _LT_TAGVAR(GCC, $1)="$ac_cv_fc_compiler_gnu" _LT_TAGVAR(LD, $1)="$LD" ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... _LT_SYS_HIDDEN_LIBDEPS($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_SYS_DYNAMIC_LINKER($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi # test -n "$compiler" GCC=$lt_save_GCC CC="$lt_save_CC" fi # test "$_lt_disable_FC" != yes AC_LANG_POP ])# _LT_LANG_FC_CONFIG # _LT_LANG_GCJ_CONFIG([TAG]) # -------------------------- # Ensure that the configuration variables for the GNU Java Compiler compiler # are suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to `libtool'. m4_defun([_LT_LANG_GCJ_CONFIG], [AC_REQUIRE([LT_PROG_GCJ])dnl AC_LANG_SAVE # Source file extension for Java test sources. ac_ext=java # Object file extension for compiled Java test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # Code to be used in simple compile tests lt_simple_compile_test_code="class foo {}" # Code to be used in simple link tests lt_simple_link_test_code='public class conftest { public static void main(String[[]] argv) {}; }' # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC="$CC" lt_save_GCC=$GCC GCC=yes CC=${GCJ-"gcj"} compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_TAGVAR(LD, $1)="$LD" _LT_CC_BASENAME([$compiler]) # GCJ did not exist at the time GCC didn't implicitly link libc in. _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... if test -n "$compiler"; then _LT_COMPILER_NO_RTTI($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi AC_LANG_RESTORE GCC=$lt_save_GCC CC="$lt_save_CC" ])# _LT_LANG_GCJ_CONFIG # _LT_LANG_RC_CONFIG([TAG]) # ------------------------- # Ensure that the configuration variables for the Windows resource compiler # are suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to `libtool'. m4_defun([_LT_LANG_RC_CONFIG], [AC_REQUIRE([LT_PROG_RC])dnl AC_LANG_SAVE # Source file extension for RC test sources. ac_ext=rc # Object file extension for compiled RC test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # Code to be used in simple compile tests lt_simple_compile_test_code='sample MENU { MENUITEM "&Soup", 100, CHECKED }' # Code to be used in simple link tests lt_simple_link_test_code="$lt_simple_compile_test_code" # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC="$CC" lt_save_GCC=$GCC GCC= CC=${RC-"windres"} compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_CC_BASENAME([$compiler]) _LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes if test -n "$compiler"; then : _LT_CONFIG($1) fi GCC=$lt_save_GCC AC_LANG_RESTORE CC="$lt_save_CC" ])# _LT_LANG_RC_CONFIG # LT_PROG_GCJ # ----------- AC_DEFUN([LT_PROG_GCJ], [m4_ifdef([AC_PROG_GCJ], [AC_PROG_GCJ], [m4_ifdef([A][M_PROG_GCJ], [A][M_PROG_GCJ], [AC_CHECK_TOOL(GCJ, gcj,) test "x${GCJFLAGS+set}" = xset || GCJFLAGS="-g -O2" AC_SUBST(GCJFLAGS)])])[]dnl ]) # Old name: AU_ALIAS([LT_AC_PROG_GCJ], [LT_PROG_GCJ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([LT_AC_PROG_GCJ], []) # LT_PROG_RC # ---------- AC_DEFUN([LT_PROG_RC], [AC_CHECK_TOOL(RC, windres,) ]) # Old name: AU_ALIAS([LT_AC_PROG_RC], [LT_PROG_RC]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([LT_AC_PROG_RC], []) # _LT_DECL_EGREP # -------------- # If we don't have a new enough Autoconf to choose the best grep # available, choose the one first in the user's PATH. m4_defun([_LT_DECL_EGREP], [AC_REQUIRE([AC_PROG_EGREP])dnl AC_REQUIRE([AC_PROG_FGREP])dnl test -z "$GREP" && GREP=grep _LT_DECL([], [GREP], [1], [A grep program that handles long lines]) _LT_DECL([], [EGREP], [1], [An ERE matcher]) _LT_DECL([], [FGREP], [1], [A literal string matcher]) dnl Non-bleeding-edge autoconf doesn't subst GREP, so do it here too AC_SUBST([GREP]) ]) # _LT_DECL_OBJDUMP # -------------- # If we don't have a new enough Autoconf to choose the best objdump # available, choose the one first in the user's PATH. m4_defun([_LT_DECL_OBJDUMP], [AC_CHECK_TOOL(OBJDUMP, objdump, false) test -z "$OBJDUMP" && OBJDUMP=objdump _LT_DECL([], [OBJDUMP], [1], [An object symbol dumper]) AC_SUBST([OBJDUMP]) ]) # _LT_DECL_SED # ------------ # Check for a fully-functional sed program, that truncates # as few characters as possible. Prefer GNU sed if found. m4_defun([_LT_DECL_SED], [AC_PROG_SED test -z "$SED" && SED=sed Xsed="$SED -e 1s/^X//" _LT_DECL([], [SED], [1], [A sed program that does not truncate output]) _LT_DECL([], [Xsed], ["\$SED -e 1s/^X//"], [Sed that helps us avoid accidentally triggering echo(1) options like -n]) ])# _LT_DECL_SED m4_ifndef([AC_PROG_SED], [ ############################################################ # NOTE: This macro has been submitted for inclusion into # # GNU Autoconf as AC_PROG_SED. When it is available in # # a released version of Autoconf we should remove this # # macro and use it instead. # ############################################################ m4_defun([AC_PROG_SED], [AC_MSG_CHECKING([for a sed that does not truncate output]) AC_CACHE_VAL(lt_cv_path_SED, [# Loop through the user's path and test for sed and gsed. # Then use that list of sed's as ones to test for truncation. as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for lt_ac_prog in sed gsed; do for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$lt_ac_prog$ac_exec_ext"; then lt_ac_sed_list="$lt_ac_sed_list $as_dir/$lt_ac_prog$ac_exec_ext" fi done done done IFS=$as_save_IFS lt_ac_max=0 lt_ac_count=0 # Add /usr/xpg4/bin/sed as it is typically found on Solaris # along with /bin/sed that truncates output. for lt_ac_sed in $lt_ac_sed_list /usr/xpg4/bin/sed; do test ! -f $lt_ac_sed && continue cat /dev/null > conftest.in lt_ac_count=0 echo $ECHO_N "0123456789$ECHO_C" >conftest.in # Check for GNU sed and select it if it is found. if "$lt_ac_sed" --version 2>&1 < /dev/null | grep 'GNU' > /dev/null; then lt_cv_path_SED=$lt_ac_sed break fi while true; do cat conftest.in conftest.in >conftest.tmp mv conftest.tmp conftest.in cp conftest.in conftest.nl echo >>conftest.nl $lt_ac_sed -e 's/a$//' < conftest.nl >conftest.out || break cmp -s conftest.out conftest.nl || break # 10000 chars as input seems more than enough test $lt_ac_count -gt 10 && break lt_ac_count=`expr $lt_ac_count + 1` if test $lt_ac_count -gt $lt_ac_max; then lt_ac_max=$lt_ac_count lt_cv_path_SED=$lt_ac_sed fi done done ]) SED=$lt_cv_path_SED AC_SUBST([SED]) AC_MSG_RESULT([$SED]) ])#AC_PROG_SED ])#m4_ifndef # Old name: AU_ALIAS([LT_AC_PROG_SED], [AC_PROG_SED]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([LT_AC_PROG_SED], []) # _LT_CHECK_SHELL_FEATURES # ------------------------ # Find out whether the shell is Bourne or XSI compatible, # or has some other useful features. m4_defun([_LT_CHECK_SHELL_FEATURES], [AC_MSG_CHECKING([whether the shell understands some XSI constructs]) # Try some XSI features xsi_shell=no ( _lt_dummy="a/b/c" test "${_lt_dummy##*/},${_lt_dummy%/*},"${_lt_dummy%"$_lt_dummy"}, \ = c,a/b,, \ && eval 'test $(( 1 + 1 )) -eq 2 \ && test "${#_lt_dummy}" -eq 5' ) >/dev/null 2>&1 \ && xsi_shell=yes AC_MSG_RESULT([$xsi_shell]) _LT_CONFIG_LIBTOOL_INIT([xsi_shell='$xsi_shell']) AC_MSG_CHECKING([whether the shell understands "+="]) lt_shell_append=no ( foo=bar; set foo baz; eval "$[1]+=\$[2]" && test "$foo" = barbaz ) \ >/dev/null 2>&1 \ && lt_shell_append=yes AC_MSG_RESULT([$lt_shell_append]) _LT_CONFIG_LIBTOOL_INIT([lt_shell_append='$lt_shell_append']) if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then lt_unset=unset else lt_unset=false fi _LT_DECL([], [lt_unset], [0], [whether the shell understands "unset"])dnl # test EBCDIC or ASCII case `echo X|tr X '\101'` in A) # ASCII based system # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr lt_SP2NL='tr \040 \012' lt_NL2SP='tr \015\012 \040\040' ;; *) # EBCDIC based system lt_SP2NL='tr \100 \n' lt_NL2SP='tr \r\n \100\100' ;; esac _LT_DECL([SP2NL], [lt_SP2NL], [1], [turn spaces into newlines])dnl _LT_DECL([NL2SP], [lt_NL2SP], [1], [turn newlines into spaces])dnl ])# _LT_CHECK_SHELL_FEATURES # _LT_PROG_XSI_SHELLFNS # --------------------- # Bourne and XSI compatible variants of some useful shell functions. m4_defun([_LT_PROG_XSI_SHELLFNS], [case $xsi_shell in yes) cat << \_LT_EOF >> "$cfgfile" # func_dirname file append nondir_replacement # Compute the dirname of FILE. If nonempty, add APPEND to the result, # otherwise set result to NONDIR_REPLACEMENT. func_dirname () { case ${1} in */*) func_dirname_result="${1%/*}${2}" ;; * ) func_dirname_result="${3}" ;; esac } # func_basename file func_basename () { func_basename_result="${1##*/}" } # func_dirname_and_basename file append nondir_replacement # perform func_basename and func_dirname in a single function # call: # dirname: Compute the dirname of FILE. If nonempty, # add APPEND to the result, otherwise set result # to NONDIR_REPLACEMENT. # value returned in "$func_dirname_result" # basename: Compute filename of FILE. # value retuned in "$func_basename_result" # Implementation must be kept synchronized with func_dirname # and func_basename. For efficiency, we do not delegate to # those functions but instead duplicate the functionality here. func_dirname_and_basename () { case ${1} in */*) func_dirname_result="${1%/*}${2}" ;; * ) func_dirname_result="${3}" ;; esac func_basename_result="${1##*/}" } # func_stripname prefix suffix name # strip PREFIX and SUFFIX off of NAME. # PREFIX and SUFFIX must not contain globbing or regex special # characters, hashes, percent signs, but SUFFIX may contain a leading # dot (in which case that matches only a dot). func_stripname () { # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are # positional parameters, so assign one to ordinary parameter first. func_stripname_result=${3} func_stripname_result=${func_stripname_result#"${1}"} func_stripname_result=${func_stripname_result%"${2}"} } # func_opt_split func_opt_split () { func_opt_split_opt=${1%%=*} func_opt_split_arg=${1#*=} } # func_lo2o object func_lo2o () { case ${1} in *.lo) func_lo2o_result=${1%.lo}.${objext} ;; *) func_lo2o_result=${1} ;; esac } # func_xform libobj-or-source func_xform () { func_xform_result=${1%.*}.lo } # func_arith arithmetic-term... func_arith () { func_arith_result=$(( $[*] )) } # func_len string # STRING may not start with a hyphen. func_len () { func_len_result=${#1} } _LT_EOF ;; *) # Bourne compatible functions. cat << \_LT_EOF >> "$cfgfile" # func_dirname file append nondir_replacement # Compute the dirname of FILE. If nonempty, add APPEND to the result, # otherwise set result to NONDIR_REPLACEMENT. func_dirname () { # Extract subdirectory from the argument. func_dirname_result=`$ECHO "X${1}" | $Xsed -e "$dirname"` if test "X$func_dirname_result" = "X${1}"; then func_dirname_result="${3}" else func_dirname_result="$func_dirname_result${2}" fi } # func_basename file func_basename () { func_basename_result=`$ECHO "X${1}" | $Xsed -e "$basename"` } dnl func_dirname_and_basename dnl A portable version of this function is already defined in general.m4sh dnl so there is no need for it here. # func_stripname prefix suffix name # strip PREFIX and SUFFIX off of NAME. # PREFIX and SUFFIX must not contain globbing or regex special # characters, hashes, percent signs, but SUFFIX may contain a leading # dot (in which case that matches only a dot). # func_strip_suffix prefix name func_stripname () { case ${2} in .*) func_stripname_result=`$ECHO "X${3}" \ | $Xsed -e "s%^${1}%%" -e "s%\\\\${2}\$%%"`;; *) func_stripname_result=`$ECHO "X${3}" \ | $Xsed -e "s%^${1}%%" -e "s%${2}\$%%"`;; esac } # sed scripts: my_sed_long_opt='1s/^\(-[[^=]]*\)=.*/\1/;q' my_sed_long_arg='1s/^-[[^=]]*=//' # func_opt_split func_opt_split () { func_opt_split_opt=`$ECHO "X${1}" | $Xsed -e "$my_sed_long_opt"` func_opt_split_arg=`$ECHO "X${1}" | $Xsed -e "$my_sed_long_arg"` } # func_lo2o object func_lo2o () { func_lo2o_result=`$ECHO "X${1}" | $Xsed -e "$lo2o"` } # func_xform libobj-or-source func_xform () { func_xform_result=`$ECHO "X${1}" | $Xsed -e 's/\.[[^.]]*$/.lo/'` } # func_arith arithmetic-term... func_arith () { func_arith_result=`expr "$[@]"` } # func_len string # STRING may not start with a hyphen. func_len () { func_len_result=`expr "$[1]" : ".*" 2>/dev/null || echo $max_cmd_len` } _LT_EOF esac case $lt_shell_append in yes) cat << \_LT_EOF >> "$cfgfile" # func_append var value # Append VALUE to the end of shell variable VAR. func_append () { eval "$[1]+=\$[2]" } _LT_EOF ;; *) cat << \_LT_EOF >> "$cfgfile" # func_append var value # Append VALUE to the end of shell variable VAR. func_append () { eval "$[1]=\$$[1]\$[2]" } _LT_EOF ;; esac ]) haildb-2.3.2/m4/pandora_have_libgtest.m40000644000175000017500000000253211513177357020753 0ustar00pcrewspcrews00000000000000dnl Copyright (C) 2010 Monty Taylor dnl This file is free software; Monty Taylor dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. AC_DEFUN([_PANDORA_SEARCH_LIBGTEST],[ AC_REQUIRE([AC_LIB_PREFIX]) dnl -------------------------------------------------------------------- dnl Check for libgtest dnl -------------------------------------------------------------------- AC_ARG_ENABLE([libgtest], [AS_HELP_STRING([--disable-libgtest], [Build with libgtest support @<:@default=on@:>@])], [ac_enable_libgtest="$enableval"], [ac_enable_libgtest="yes"]) AS_IF([test "x$ac_enable_libgtest" = "xyes"],[ AC_LANG_PUSH(C++) save_CXXFLAGS="${CXXFLAGS}" CXXFLAGS="${AM_CXXFLAGS} ${CXXFLAGS}" AC_LIB_HAVE_LINKFLAGS(gtest,,[ #include TEST(pandora_test_libgtest, PandoraTest) { ASSERT_EQ(1, 1); } ],[]) CXXFLAGS="${save_CXXFLAGS}" AC_LANG_POP() ],[ ac_cv_libgtest="no" ]) AM_CONDITIONAL(HAVE_LIBGTEST, [test "x${ac_cv_libgtest}" = "xyes"]) ]) AC_DEFUN([PANDORA_HAVE_LIBGTEST],[ AC_REQUIRE([_PANDORA_SEARCH_LIBGTEST]) ]) AC_DEFUN([PANDORA_REQUIRE_LIBGTEST],[ AC_REQUIRE([_PANDORA_SEARCH_LIBGTEST]) AS_IF([test "x${ac_cv_libgtest}" = "xno"], AC_MSG_ERROR([libgtest is required for ${PACKAGE}])) ]) haildb-2.3.2/m4/pandora_have_libbdb.m40000644000175000017500000000224411513177357020354 0ustar00pcrewspcrews00000000000000dnl Copyright (C) 2009 Sun Microsystems, Inc. dnl This file is free software; Sun Microsystems, Inc. dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. AC_DEFUN([_PANDORA_SEARCH_LIBBDB],[ AC_REQUIRE([AC_LIB_PREFIX]) dnl -------------------------------------------------------------------- dnl Check for bekerely db dnl -------------------------------------------------------------------- AC_ARG_ENABLE([libbdb], [AS_HELP_STRING([--disable-libbdb], [Build with libbdb support @<:@default=on@:>@])], [ac_enable_libbdb="$enableval"], [ac_enable_libbdb="yes"]) AS_IF([test "x$ac_enable_libbdb" = "xyes"],[ AC_LIB_HAVE_LINKFLAGS(db,,[ #include ],[ const char *test= DB_VERSION_STRING; ]) ],[ ac_cv_libbdb="no" ]) AM_CONDITIONAL(HAVE_LIBBDB, [test "x${ac_cv_libbdb}" = "xyes"]) ]) AC_DEFUN([PANDORA_HAVE_LIBBDB],[ AC_REQUIRE([_PANDORA_SEARCH_LIBBDB]) ]) AC_DEFUN([PANDORA_REQUIRE_LIBBDB],[ AC_REQUIRE([_PANDORA_SEARCH_LIBBDB]) AS_IF([test "x${ac_cv_libbdb}" = "xno"], AC_MSG_ERROR([libbdb is required for ${PACKAGE}])) ]) haildb-2.3.2/m4/pandora_have_libvbucket.m40000644000175000017500000000236411513177357021273 0ustar00pcrewspcrews00000000000000dnl Copyright (C) 2010 NorthScale dnl This file is free software; NorthScale dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. AC_DEFUN([_PANDORA_SEARCH_LIBVBUCKET],[ AC_REQUIRE([AC_LIB_PREFIX]) dnl -------------------------------------------------------------------- dnl Check for libvbucket dnl -------------------------------------------------------------------- AC_ARG_ENABLE([libvbucket], [AS_HELP_STRING([--disable-libvbucket], [Build with libvbucket support @<:@default=on@:>@])], [ac_enable_libvbucket="$enableval"], [ac_enable_libvbucket="yes"]) AS_IF([test "x$ac_enable_libvbucket" = "xyes"],[ AC_LIB_HAVE_LINKFLAGS(vbucket,,[ #include ],[ VBUCKET_CONFIG_HANDLE config = vbucket_config_parse_file(NULL); ]) ],[ ac_cv_libvbucket="no" ]) AM_CONDITIONAL(HAVE_LIBVBUCKET, [test "x${ac_cv_libvbucket}" = "xyes"]) ]) AC_DEFUN([PANDORA_HAVE_LIBVBUCKET],[ AC_REQUIRE([_PANDORA_SEARCH_LIBVBUCKET]) ]) AC_DEFUN([PANDORA_REQUIRE_LIBVBUCKET],[ AC_REQUIRE([PANDORA_HAVE_LIBVBUCKET]) AS_IF([test x$ac_cv_libvbucket = xno], AC_MSG_ERROR([libvbucket is required for ${PACKAGE}])) ]) haildb-2.3.2/m4/pandora_have_libboost_filesystem.m40000644000175000017500000000303611513177357023217 0ustar00pcrewspcrews00000000000000dnl Copyright (C) 2010 Monty Taylor dnl This file is free software; Monty Taylor dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. AC_DEFUN([_PANDORA_SEARCH_BOOST_FILESYSTEM],[ AC_REQUIRE([AC_LIB_PREFIX]) dnl -------------------------------------------------------------------- dnl Check for Boost.Filesystem dnl -------------------------------------------------------------------- AC_LANG_PUSH(C++) AC_LIB_HAVE_LINKFLAGS(boost_filesystem-mt,boost_system-mt,[ #include ],[ boost::filesystem::path my_path("some_dir/file.txt"); ]) AS_IF([test "x${ac_cv_libboost_filesystem_mt}" = "xno"],[ AC_LIB_HAVE_LINKFLAGS(boost_filesystem,boost_system,[ #include ],[ boost::filesystem::path my_path("some_dir/file.txt"); ]) ]) AC_LANG_POP() AM_CONDITIONAL(HAVE_BOOST_FILESYSTEM, [test "x${ac_cv_libboost_filesystem}" = "xyes" -o "x${ac_cv_libboost_filesystem_mt}" = "xyes"]) BOOST_LIBS="${BOOST_LIBS} ${LTLIBBOOST_FILESYSTEM_MT} ${LTLIBBOOST_FILESYSTEM}" AC_SUBST(BOOST_LIBS) ]) AC_DEFUN([PANDORA_HAVE_BOOST_FILESYSTEM],[ PANDORA_HAVE_BOOST($1) _PANDORA_SEARCH_BOOST_FILESYSTEM($1) ]) AC_DEFUN([PANDORA_REQUIRE_BOOST_FILESYSTEM],[ PANDORA_REQUIRE_BOOST($1) _PANDORA_SEARCH_BOOST_FILESYSTEM($1) AS_IF([test "x${ac_cv_libboost_filesystem}" = "xno" -a "x${ac_cv_libboost_filesystem_mt}" = "xno"], AC_MSG_ERROR([Boost.Filesystem is required for ${PACKAGE}])) ]) haildb-2.3.2/m4/pandora_with_python3.m40000644000175000017500000000264611513177357020600 0ustar00pcrewspcrews00000000000000dnl -*- mode: m4; c-basic-offset: 2; indent-tabs-mode: nil; -*- dnl vim:expandtab:shiftwidth=2:tabstop=2:smarttab: dnl dnl pandora-build: A pedantic build system dnl Copyright (C) 2009 Sun Microsystems, Inc. dnl This file is free software; Sun Microsystems dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl dnl From Monty Taylor AC_DEFUN([PANDORA_WITH_PYTHON3], [ AC_REQUIRE([PANDORA_SWIG]) AC_ARG_WITH([python3], [AS_HELP_STRING([--with-python3], [Build Python3 Bindings @<:@default=yes@:>@])],[ with_python3=$withval python3_requested=$withval ],[ with_python3=yes python3_requested=no ]) AS_IF([test "x$ac_cv_swig_has_python3_" != "xyes"],[ with_python3=no ],[ AS_IF([test "x$with_python3" != "xno"],[ AS_IF([test "x$with_python3" != "xyes"], [PYTHON3=$with_python3],[ AC_PATH_PROG([PYTHON3],[python3],[no]) PANDORA_PYTHON3_DEVEL() AS_IF([test "x$python3exists" = "xno"],[with_python="no"]) ]) ]) ]) AS_IF([test "x$with_python3" = "xno" -a "$python3_requested" = "yes"],[ AC_MSG_ERROR([Python3 support was explicity requested, but Python3 support was not found. Please correct your build environment and try again]) ]) AM_CONDITIONAL(BUILD_PYTHON3, [test "$with_python3" = "yes"]) ]) haildb-2.3.2/m4/pandora_visibility.m40000644000175000017500000000477511513177357020335 0ustar00pcrewspcrews00000000000000dnl Copyright (C) 2005, 2008 Free Software Foundation, Inc. dnl Copyright (C) 2009 Monty Taylor dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl From Bruno Haible. dnl Tests whether the compiler supports the command-line option dnl -fvisibility=hidden and the function and variable attributes dnl __attribute__((__visibility__("hidden"))) and dnl __attribute__((__visibility__("default"))). dnl Does *not* test for __visibility__("protected") - which has tricky dnl semantics (see the 'vismain' test in glibc) and does not exist e.g. on dnl MacOS X. dnl Does *not* test for __visibility__("internal") - which has processor dnl dependent semantics. dnl Does *not* test for #pragma GCC visibility push(hidden) - which is dnl "really only recommended for legacy code". dnl Set the variable CFLAG_VISIBILITY. dnl Defines and sets the variable HAVE_VISIBILITY. AC_DEFUN([PANDORA_CHECK_VISIBILITY], [ AC_REQUIRE([AC_PROG_CC]) CFLAG_VISIBILITY= HAVE_VISIBILITY=0 AS_IF([test -n "$GCC"],[ AC_MSG_CHECKING([for simple visibility declarations]) AC_CACHE_VAL([gl_cv_cc_visibility], [ gl_save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -fvisibility=hidden -Werror" AC_TRY_COMPILE( [extern __attribute__((__visibility__("hidden"))) int hiddenvar; extern __attribute__((__visibility__("default"))) int exportedvar; extern __attribute__((__visibility__("hidden"))) int hiddenfunc (void); extern __attribute__((__visibility__("default"))) int exportedfunc (void);], [], [gl_cv_cc_visibility=yes], [gl_cv_cc_visibility=no]) CFLAGS="$gl_save_CFLAGS"]) AC_MSG_RESULT([$gl_cv_cc_visibility]) if test $gl_cv_cc_visibility = yes; then CFLAG_VISIBILITY="-fvisibility=hidden" NO_VISIBILITY="-fvisibility=default" HAVE_VISIBILITY=1 fi ]) AS_IF([test "x$SUNCC" = "xyes"],[ CFLAG_VISIBILITY="-xldscope=hidden" NO_VISIBILITY="-xldscope=global" HAVE_VISIBILITY=1 ]) AC_SUBST([CFLAG_VISIBILITY]) AC_SUBST([NO_VISIBILITY]) AC_SUBST([HAVE_VISIBILITY]) AC_DEFINE_UNQUOTED([HAVE_VISIBILITY], [$HAVE_VISIBILITY], [Define to 1 or 0, depending whether the compiler supports simple visibility declarations.]) ]) AC_DEFUN([PANDORA_ENABLE_VISIBILITY],[ AC_REQUIRE([PANDORA_CHECK_VISIBILITY]) AM_CFLAGS="${AM_CFLAGS} ${CFLAG_VISIBILITY}" AM_CXXFLAGS="${AM_CXXFLAGS} ${CFLAG_VISIBILITY}" ]) haildb-2.3.2/m4/pandora_with_r.m40000644000175000017500000000151211513177357017424 0ustar00pcrewspcrews00000000000000dnl -*- mode: m4; c-basic-offset: 2; indent-tabs-mode: nil; -*- dnl vim:expandtab:shiftwidth=2:tabstop=2:smarttab: dnl dnl pandora-build: A pedantic build system dnl Copyright (C) 2009 Sun Microsystems, Inc. dnl This file is free software; Sun Microsystems dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl dnl From Monty Taylor AC_DEFUN([PANDORA_WITH_R],[ dnl Check for GNU R AC_ARG_WITH([r], [AS_HELP_STRING([--with-r], [Build R Bindings @<:@default=yes@:>@])], [with_r=$withval], [with_r=yes]) AS_IF([test "x$with_r" != "xno"],[ PKG_CHECK_MODULES([R], [libR], [ with_r=yes ],[ with_r=no ]) AC_SUBST(R_CFLAGS) AC_SUBST(R_LIBS) ]) AM_CONDITIONAL(BUILD_R, test "$with_r" = "yes") ]) haildb-2.3.2/m4/pandora_ensure_gcc_version.m40000644000175000017500000000457111513177357022022 0ustar00pcrewspcrews00000000000000dnl Copyright (C) 2009 Sun Microsystems, Inc. dnl This file is free software; Sun Microsystems, Inc. dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. AC_DEFUN([_PANDORA_TRY_GCC],[ pushdef([Name],[translit([$1],[./-], [___])]) pushdef([NAME],[translit([$1],[abcdefghijklmnopqrstuvwxyz./-], [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])]) AC_CHECK_PROGS([CC]NAME,[gcc$1]) AC_CHECK_PROGS([CXX]NAME,[g++$1]) AS_IF([test "x${CC]NAME[}" != "x" -a "x${ac_cv_env_CC_set}" = "x"], [CC="${CC]NAME[}"]) AS_IF([test "x${CXX]NAME[}" != "x" -a "x${ac_cv_env_CCX_set}" = "x"], [CXX="${CXX]NAME[}"]) AS_IF([test "x${CC]NAME[}" != "x" -a "x${ac_cv_env_CPP_set}" = "x"], [CPP="${CC]NAME[} -E"]) ]) dnl If the user is on a Mac and didn't ask for a specific compiler dnl You're gonna get 4.2. AC_DEFUN([PANDORA_MAC_GCC42], [AS_IF([test "$GCC" = "yes"],[ AS_IF([test "$host_vendor" = "apple" -a "x${ac_cv_env_CC_set}" = "x"],[ host_os_version=`echo ${host_os} | perl -ple 's/^\D+//g;s,\..*,,'` AS_IF([test "$host_os_version" -lt 10],[ _PANDORA_TRY_GCC([-4.2]) ]) ]) ]) ]) dnl If the user is on CentOS or RHEL and didn't ask for a specific compiler dnl You're gonna get 4.4 (forward compatible with 4.5) AC_DEFUN([PANDORA_RH_GCC44], [AS_IF([test "$GCC" = "yes"],[ AS_IF([test "x${ac_cv_env_CC_set}" = "x"],[ _PANDORA_TRY_GCC([44]) _PANDORA_TRY_GCC([45]) ]) ]) ]) dnl AC_DEFUN([PANDORA_ENSURE_GCC_VERSION],[ AC_REQUIRE([PANDORA_MAC_GCC42]) AC_REQUIRE([PANDORA_RH_GCC44]) AS_IF([test "$GCC" = "yes"],[ AC_CACHE_CHECK([if GCC is recent enough], [ac_cv_gcc_recent], [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #if !defined(__GNUC__) || (__GNUC__ < 4) || ((__GNUC__ >= 4) && (__GNUC_MINOR__ < 2)) # error GCC is Too Old! #endif ]])], [ac_cv_gcc_recent=yes], [ac_cv_gcc_recent=no])]) AS_IF([test "$ac_cv_gcc_recent" = "no" -a "$host_vendor" = "apple"], AC_MSG_ERROR([Your version of GCC is too old. At least version 4.2 is required on OSX. You may need to install a version of XCode >= 3.1.2])) AS_IF([test "$ac_cv_gcc_recent" = "no"], AC_MSG_ERROR([Your version of GCC is too old. At least version 4.2 is required. On RHEL/CentOS systems this is found in the gcc44 and gcc44-c++ packages.])) ]) ]) haildb-2.3.2/m4/pandora_extensions.m40000644000175000017500000000072611513177357020335 0ustar00pcrewspcrews00000000000000dnl Copyright (C) 2009 Sun Microsystems, Inc. dnl This file is free software; Sun Microsystems, Inc. dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. AC_DEFUN([PANDORA_EXTENSIONS],[ m4_ifdef([AC_USE_SYSTEM_EXTENSIONS], [AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS])], [AC_REQUIRE([AC_GNU_SOURCE])]) ]) AC_DEFUN([gl_USE_SYSTEM_EXTENSIONS],[ AC_REQUIRE([PANDORA_EXTENSIONS]) ]) haildb-2.3.2/m4/pandora_have_libsqlite3.m40000644000175000017500000000241511513177357021211 0ustar00pcrewspcrews00000000000000dnl Copyright (C) 2009 Sun Microsystems, Inc. dnl This file is free software; Sun Microsystems, Inc. dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. AC_DEFUN([_PANDORA_SEARCH_LIBSQLITE3],[ AC_REQUIRE([AC_LIB_PREFIX]) dnl -------------------------------------------------------------------- dnl Check for libsqlite3 dnl -------------------------------------------------------------------- AC_ARG_ENABLE([libsqlite3], [AS_HELP_STRING([--disable-libsqlite3], [Build with libsqlite3 support @<:@default=on@:>@])], [ac_enable_libsqlite3="$enableval"], [ac_enable_libsqlite3="yes"]) AS_IF([test "x$ac_enable_libsqlite3" = "xyes"],[ AC_LIB_HAVE_LINKFLAGS(sqlite3,,[ #include #include ],[ sqlite3 *db; sqlite3_open(NULL, &db); ]) ],[ ac_cv_libsqlite3="no" ]) AM_CONDITIONAL(HAVE_LIBSQLITE3, [test "x${ac_cv_libsqlite3}" = "xyes"]) ]) AC_DEFUN([PANDORA_HAVE_LIBSQLITE3],[ AC_REQUIRE([_PANDORA_SEARCH_LIBSQLITE3]) ]) AC_DEFUN([PANDORA_REQUIRE_LIBSQLITE3],[ AC_REQUIRE([_PANDORA_SEARCH_LIBSQLITE3]) AS_IF([test "x${ac_cv_libsqlite3}" = "xno"], AC_MSG_ERROR([libsqlite3 is required for ${PACKAGE}])) ]) haildb-2.3.2/m4/pandora_flex.m40000644000175000017500000000224511513177357017072 0ustar00pcrewspcrews00000000000000dnl Copyright (C) 2010 Monty Taylor dnl Copyright (C) 2010 Hartmut Holzgraefe dnl This file is free software; Monty Taylor and Hartmut Holzgraefe dnl give unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. AC_DEFUN([_PANDORA_SEARCH_FLEX],[ dnl -------------------------------------------------------------------- dnl Check for flex dnl -------------------------------------------------------------------- AC_CHECK_PROGS([LEX], ['flex'], [:]) AS_IF([test "x$LEX" = "x:"],[ pandora_have_flex=no LEX='if test -f "$@"; then echo "WARNING: no proper flex binary found, ignoring changes to $<"; exit 0; else echo "ERROR: no proper flex binary found"; exit 1; fi;' ],[ pandora_have_flex=yes ]) AM_CONDITIONAL(HAVE_FLEX, [test "x${pandora_have_flex}" = "xyes"]) ]) AC_DEFUN([PANDORA_HAVE_FLEX],[ AC_REQUIRE([_PANDORA_SEARCH_FLEX]) ]) AC_DEFUN([PANDORA_REQUIRE_FLEX],[ AC_REQUIRE([PANDORA_HAVE_FLEX]) AS_IF([test "x${pandora_have_flex}" = "xno" -a "$pandora_building_from_bzr" = "yes"], AC_MSG_ERROR(["flex is required for ${PACKAGE} to build from a bzr branch"]) ) ]) haildb-2.3.2/m4/lt~obsolete.m40000644000175000017500000001311311513177375016776 0ustar00pcrewspcrews00000000000000# lt~obsolete.m4 -- aclocal satisfying obsolete definitions. -*-Autoconf-*- # # Copyright (C) 2004, 2005, 2007 Free Software Foundation, Inc. # Written by Scott James Remnant, 2004. # # This file is free software; the Free Software Foundation gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. # serial 4 lt~obsolete.m4 # These exist entirely to fool aclocal when bootstrapping libtool. # # In the past libtool.m4 has provided macros via AC_DEFUN (or AU_DEFUN) # which have later been changed to m4_define as they aren't part of the # exported API, or moved to Autoconf or Automake where they belong. # # The trouble is, aclocal is a bit thick. It'll see the old AC_DEFUN # in /usr/share/aclocal/libtool.m4 and remember it, then when it sees us # using a macro with the same name in our local m4/libtool.m4 it'll # pull the old libtool.m4 in (it doesn't see our shiny new m4_define # and doesn't know about Autoconf macros at all.) # # So we provide this file, which has a silly filename so it's always # included after everything else. This provides aclocal with the # AC_DEFUNs it wants, but when m4 processes it, it doesn't do anything # because those macros already exist, or will be overwritten later. # We use AC_DEFUN over AU_DEFUN for compatibility with aclocal-1.6. # # Anytime we withdraw an AC_DEFUN or AU_DEFUN, remember to add it here. # Yes, that means every name once taken will need to remain here until # we give up compatibility with versions before 1.7, at which point # we need to keep only those names which we still refer to. # This is to help aclocal find these macros, as it can't see m4_define. AC_DEFUN([LTOBSOLETE_VERSION], [m4_if([1])]) m4_ifndef([AC_LIBTOOL_LINKER_OPTION], [AC_DEFUN([AC_LIBTOOL_LINKER_OPTION])]) m4_ifndef([AC_PROG_EGREP], [AC_DEFUN([AC_PROG_EGREP])]) m4_ifndef([_LT_AC_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH])]) m4_ifndef([_LT_AC_SHELL_INIT], [AC_DEFUN([_LT_AC_SHELL_INIT])]) m4_ifndef([_LT_AC_SYS_LIBPATH_AIX], [AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX])]) m4_ifndef([_LT_PROG_LTMAIN], [AC_DEFUN([_LT_PROG_LTMAIN])]) m4_ifndef([_LT_AC_TAGVAR], [AC_DEFUN([_LT_AC_TAGVAR])]) m4_ifndef([AC_LTDL_ENABLE_INSTALL], [AC_DEFUN([AC_LTDL_ENABLE_INSTALL])]) m4_ifndef([AC_LTDL_PREOPEN], [AC_DEFUN([AC_LTDL_PREOPEN])]) m4_ifndef([_LT_AC_SYS_COMPILER], [AC_DEFUN([_LT_AC_SYS_COMPILER])]) m4_ifndef([_LT_AC_LOCK], [AC_DEFUN([_LT_AC_LOCK])]) m4_ifndef([AC_LIBTOOL_SYS_OLD_ARCHIVE], [AC_DEFUN([AC_LIBTOOL_SYS_OLD_ARCHIVE])]) m4_ifndef([_LT_AC_TRY_DLOPEN_SELF], [AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF])]) m4_ifndef([AC_LIBTOOL_PROG_CC_C_O], [AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O])]) m4_ifndef([AC_LIBTOOL_SYS_HARD_LINK_LOCKS], [AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS])]) m4_ifndef([AC_LIBTOOL_OBJDIR], [AC_DEFUN([AC_LIBTOOL_OBJDIR])]) m4_ifndef([AC_LTDL_OBJDIR], [AC_DEFUN([AC_LTDL_OBJDIR])]) m4_ifndef([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH], [AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH])]) m4_ifndef([AC_LIBTOOL_SYS_LIB_STRIP], [AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP])]) m4_ifndef([AC_PATH_MAGIC], [AC_DEFUN([AC_PATH_MAGIC])]) m4_ifndef([AC_PROG_LD_GNU], [AC_DEFUN([AC_PROG_LD_GNU])]) m4_ifndef([AC_PROG_LD_RELOAD_FLAG], [AC_DEFUN([AC_PROG_LD_RELOAD_FLAG])]) m4_ifndef([AC_DEPLIBS_CHECK_METHOD], [AC_DEFUN([AC_DEPLIBS_CHECK_METHOD])]) m4_ifndef([AC_LIBTOOL_PROG_COMPILER_NO_RTTI], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI])]) m4_ifndef([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE], [AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE])]) m4_ifndef([AC_LIBTOOL_PROG_COMPILER_PIC], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC])]) m4_ifndef([AC_LIBTOOL_PROG_LD_SHLIBS], [AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS])]) m4_ifndef([AC_LIBTOOL_POSTDEP_PREDEP], [AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP])]) m4_ifndef([LT_AC_PROG_EGREP], [AC_DEFUN([LT_AC_PROG_EGREP])]) m4_ifndef([LT_AC_PROG_SED], [AC_DEFUN([LT_AC_PROG_SED])]) m4_ifndef([_LT_CC_BASENAME], [AC_DEFUN([_LT_CC_BASENAME])]) m4_ifndef([_LT_COMPILER_BOILERPLATE], [AC_DEFUN([_LT_COMPILER_BOILERPLATE])]) m4_ifndef([_LT_LINKER_BOILERPLATE], [AC_DEFUN([_LT_LINKER_BOILERPLATE])]) m4_ifndef([_AC_PROG_LIBTOOL], [AC_DEFUN([_AC_PROG_LIBTOOL])]) m4_ifndef([AC_LIBTOOL_SETUP], [AC_DEFUN([AC_LIBTOOL_SETUP])]) m4_ifndef([_LT_AC_CHECK_DLFCN], [AC_DEFUN([_LT_AC_CHECK_DLFCN])]) m4_ifndef([AC_LIBTOOL_SYS_DYNAMIC_LINKER], [AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER])]) m4_ifndef([_LT_AC_TAGCONFIG], [AC_DEFUN([_LT_AC_TAGCONFIG])]) m4_ifndef([AC_DISABLE_FAST_INSTALL], [AC_DEFUN([AC_DISABLE_FAST_INSTALL])]) m4_ifndef([_LT_AC_LANG_CXX], [AC_DEFUN([_LT_AC_LANG_CXX])]) m4_ifndef([_LT_AC_LANG_F77], [AC_DEFUN([_LT_AC_LANG_F77])]) m4_ifndef([_LT_AC_LANG_GCJ], [AC_DEFUN([_LT_AC_LANG_GCJ])]) m4_ifndef([AC_LIBTOOL_RC], [AC_DEFUN([AC_LIBTOOL_RC])]) m4_ifndef([AC_LIBTOOL_LANG_C_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG])]) m4_ifndef([_LT_AC_LANG_C_CONFIG], [AC_DEFUN([_LT_AC_LANG_C_CONFIG])]) m4_ifndef([AC_LIBTOOL_LANG_CXX_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG])]) m4_ifndef([_LT_AC_LANG_CXX_CONFIG], [AC_DEFUN([_LT_AC_LANG_CXX_CONFIG])]) m4_ifndef([AC_LIBTOOL_LANG_F77_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_F77_CONFIG])]) m4_ifndef([_LT_AC_LANG_F77_CONFIG], [AC_DEFUN([_LT_AC_LANG_F77_CONFIG])]) m4_ifndef([AC_LIBTOOL_LANG_GCJ_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_GCJ_CONFIG])]) m4_ifndef([_LT_AC_LANG_GCJ_CONFIG], [AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG])]) m4_ifndef([AC_LIBTOOL_LANG_RC_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_RC_CONFIG])]) m4_ifndef([_LT_AC_LANG_RC_CONFIG], [AC_DEFUN([_LT_AC_LANG_RC_CONFIG])]) m4_ifndef([AC_LIBTOOL_CONFIG], [AC_DEFUN([AC_LIBTOOL_CONFIG])]) m4_ifndef([_LT_AC_FILE_LTDLL_C], [AC_DEFUN([_LT_AC_FILE_LTDLL_C])]) haildb-2.3.2/m4/pandora_fdatasync.m40000644000175000017500000000153211513177357020106 0ustar00pcrewspcrews00000000000000dnl Copyright (C) 2009 Sun Microsystems, Inc. dnl This file is free software; Sun Microsystems, Inc. dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. #-------------------------------------------------------------------- # Check for a working fdatasync call #-------------------------------------------------------------------- AC_DEFUN([PANDORA_WORKING_FDATASYNC],[ AC_CACHE_CHECK([working fdatasync],[ac_cv_func_fdatasync],[ AC_LANG_PUSH(C++) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]],[[ fdatasync(4); ]])], [ac_cv_func_fdatasync=yes], [ac_cv_func_fdatasync=no]) AC_LANG_POP() ]) AS_IF([test "x${ac_cv_func_fdatasync}" = "xyes"], [AC_DEFINE([HAVE_FDATASYNC],[1],[If the system has a working fdatasync])]) ]) haildb-2.3.2/m4/ac_cxx_header_stdcxx_98.m40000644000175000017500000000403711513177357021123 0ustar00pcrewspcrews00000000000000# =========================================================================== # http://autoconf-archive.cryp.to/ac_cxx_header_stdcxx_98.html # =========================================================================== # # SYNOPSIS # # AC_CXX_HEADER_STDCXX_98 # # DESCRIPTION # # Check for complete library coverage of the C++1998/2003 standard. # # LICENSE # # Copyright (c) 2008 Benjamin Kosnik # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. AC_DEFUN([AC_CXX_HEADER_STDCXX_98], [ AC_CACHE_CHECK(for ISO C++ 98 include files, ac_cv_cxx_stdcxx_98, [AC_LANG_SAVE AC_LANG_CPLUSPLUS AC_TRY_COMPILE([ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include ],, ac_cv_cxx_stdcxx_98=yes, ac_cv_cxx_stdcxx_98=no) AC_LANG_RESTORE ]) if test "$ac_cv_cxx_stdcxx_98" = yes; then AC_DEFINE(STDCXX_98_HEADERS,,[Define if ISO C++ 1998 header files are present. ]) fi ]) haildb-2.3.2/m4/ltversion.m40000644000175000017500000000127711513177375016461 0ustar00pcrewspcrews00000000000000# ltversion.m4 -- version numbers -*- Autoconf -*- # # Copyright (C) 2004 Free Software Foundation, Inc. # Written by Scott James Remnant, 2004 # # This file is free software; the Free Software Foundation gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. # Generated from ltversion.in. # serial 3017 ltversion.m4 # This file is part of GNU Libtool m4_define([LT_PACKAGE_VERSION], [2.2.6b]) m4_define([LT_PACKAGE_REVISION], [1.3017]) AC_DEFUN([LTVERSION_VERSION], [macro_version='2.2.6b' macro_revision='1.3017' _LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?]) _LT_DECL(, macro_revision, 0) ]) haildb-2.3.2/m4/pandora_have_libboost_options.m40000644000175000017500000000325011513177357022524 0ustar00pcrewspcrews00000000000000dnl Copyright (C) 2010 Monty Taylor dnl This file is free software; Monty Taylor dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. AC_DEFUN([_PANDORA_SEARCH_BOOST_PROGRAM_OPTIONS],[ AC_REQUIRE([AC_LIB_PREFIX]) dnl -------------------------------------------------------------------- dnl Check for boost::program_options dnl -------------------------------------------------------------------- AC_LANG_PUSH(C++) AC_LIB_HAVE_LINKFLAGS(boost_program_options-mt,,[ #include ],[ boost::program_options::options_description d; d.add_options()("a","some option"); ]) AS_IF([test "x${ac_cv_libboost_program_options_mt}" = "xno"],[ AC_LIB_HAVE_LINKFLAGS(boost_program_options,,[ #include ],[ boost::program_options::options_description d; d.add_options()("a","some option"); ]) ]) AC_LANG_POP() AM_CONDITIONAL(HAVE_BOOST_PROGRAM_OPTIONS, [test "x${ac_cv_libboost_program_options}" = "xyes" -o "x${ac_cv_libboost_program_options_mt}" = "xyes"]) BOOST_LIBS="${BOOST_LIBS} ${LTLIBBOOST_PROGRAM_OPTIONS} ${LTLIBBOOST_PROGRAM_OPTIONS_MT}" AC_SUBST(BOOST_LIBS) ]) AC_DEFUN([PANDORA_HAVE_BOOST_PROGRAM_OPTIONS],[ PANDORA_HAVE_BOOST($1) _PANDORA_SEARCH_BOOST_PROGRAM_OPTIONS($1) ]) AC_DEFUN([PANDORA_REQUIRE_BOOST_PROGRAM_OPTIONS],[ PANDORA_REQUIRE_BOOST($1) _PANDORA_SEARCH_BOOST_PROGRAM_OPTIONS($1) AS_IF([test "x${ac_cv_libboost_program_options}" = "xno" -a "x${ac_cv_libboost_program_options_mt}" = "xno"], AC_MSG_ERROR([boost::program_options is required for ${PACKAGE}])) ]) haildb-2.3.2/m4/pandora_compile_stdcxx_0x.m40000644000175000017500000000522411513177357021570 0ustar00pcrewspcrews00000000000000# =========================================================================== # http://autoconf-archive.cryp.to/ac_cxx_compile_stdcxx_0x.html # =========================================================================== # # SYNOPSIS # # AC_CXX_COMPILE_STDCXX_0X # # DESCRIPTION # # Check for baseline language coverage in the compiler for the C++0x # standard. # # LICENSE # # Copyright (C) 2008 Benjamin Kosnik # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. AC_DEFUN([AC_CXX_COMPILE_STDCXX_0X], [ AC_CACHE_CHECK(if g++ supports C++0x features without additional flags, ac_cv_cxx_compile_cxx0x_native, [AC_LANG_SAVE AC_LANG_CPLUSPLUS AC_TRY_COMPILE([ template struct check { static_assert(sizeof(int) <= sizeof(T), "not big enough"); }; typedef check> right_angle_brackets; int a; decltype(a) b; typedef check check_type; check_type c; check_type&& cr = c;],, ac_cv_cxx_compile_cxx0x_native=yes, ac_cv_cxx_compile_cxx0x_native=no) AC_LANG_RESTORE ]) AC_CACHE_CHECK(if g++ supports C++0x features with -std=c++0x, ac_cv_cxx_compile_cxx0x_cxx, [AC_LANG_SAVE AC_LANG_CPLUSPLUS ac_save_CXXFLAGS="$CXXFLAGS" CXXFLAGS="$CXXFLAGS -std=c++0x" AC_TRY_COMPILE([ template struct check { static_assert(sizeof(int) <= sizeof(T), "not big enough"); }; typedef check> right_angle_brackets; int a; decltype(a) b; typedef check check_type; check_type c; check_type&& cr = c;],, ac_cv_cxx_compile_cxx0x_cxx=yes, ac_cv_cxx_compile_cxx0x_cxx=no) CXXFLAGS="$ac_save_CXXFLAGS" AC_LANG_RESTORE ]) AC_CACHE_CHECK(if g++ supports C++0x features with -std=gnu++0x, ac_cv_cxx_compile_cxx0x_gxx, [AC_LANG_SAVE AC_LANG_CPLUSPLUS ac_save_CXXFLAGS="$CXXFLAGS" CXXFLAGS="$CXXFLAGS -std=gnu++0x" AC_TRY_COMPILE([ template struct check { static_assert(sizeof(int) <= sizeof(T), "not big enough"); }; typedef check> right_angle_brackets; int a; decltype(a) b; typedef check check_type; check_type c; check_type&& cr = c;],, ac_cv_cxx_compile_cxx0x_gxx=yes, ac_cv_cxx_compile_cxx0x_gxx=no) CXXFLAGS="$ac_save_CXXFLAGS" AC_LANG_RESTORE ]) if test "$ac_cv_cxx_compile_cxx0x_native" = yes || test "$ac_cv_cxx_compile_cxx0x_cxx" = yes || test "$ac_cv_cxx_compile_cxx0x_gxx" = yes; then AC_DEFINE(HAVE_STDCXX_0X,,[Define if g++ supports C++0x features. ]) fi ]) haildb-2.3.2/m4/pandora_clock_gettime.m40000644000175000017500000000113311513177357020740 0ustar00pcrewspcrews00000000000000dnl Copyright (C) 2010 Monty Taylor dnl This file is free software; Monty Taylor dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. #-------------------------------------------------------------------- # Check for clock_gettime #-------------------------------------------------------------------- AC_DEFUN([PANDORA_CLOCK_GETTIME],[ AC_SEARCH_LIBS([clock_gettime],[rt]) AS_IF([test "x${ac_cv_search_clock_gettime}" != "xno"],[ AC_DEFINE([HAVE_CLOCK_GETTIME],[1],[Have a working clock_gettime function]) ]) ]) haildb-2.3.2/m4/pandora_intltool.m40000644000175000017500000002475311513177357020010 0ustar00pcrewspcrews00000000000000## intltool.m4 - Configure intltool for the target system. -*-Shell-script-*- ## Copyright (C) 2001 Eazel, Inc. ## Author: Maciej Stachowiak ## Kenneth Christiansen ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program 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 ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 51 Franklin Place - Suite 330, Boston, MA 02110-1301, USA. ## ## As a special exception to the GNU General Public License, if you ## distribute this file as part of a program that contains a ## configuration script generated by Autoconf, you may include it under ## the same distribution terms that you use for the rest of that program. dnl IT_PROG_INTLTOOL([MINIMUM-VERSION], [no-xml]) # serial 40 IT_PROG_INTLTOOL AC_DEFUN([IT_PROG_INTLTOOL], [ AC_PREREQ([2.50])dnl AC_REQUIRE([AM_NLS])dnl case "$am__api_version" in 1.[01234]) AC_MSG_ERROR([Automake 1.5 or newer is required to use intltool]) ;; *) ;; esac if test -n "$1"; then AC_MSG_CHECKING([for intltool >= $1]) INTLTOOL_REQUIRED_VERSION_AS_INT=`echo $1 | awk -F. '{ print $ 1 * 1000 + $ 2 * 100 + $ 3; }'` INTLTOOL_APPLIED_VERSION=`intltool-update --version | head -1 | cut -d" " -f3` [INTLTOOL_APPLIED_VERSION_AS_INT=`echo $INTLTOOL_APPLIED_VERSION | awk -F. '{ print $ 1 * 1000 + $ 2 * 100 + $ 3; }'` ] AC_MSG_RESULT([$INTLTOOL_APPLIED_VERSION found]) AS_IF([test "$INTLTOOL_APPLIED_VERSION_AS_INT" -ge "$INTLTOOL_REQUIRED_VERSION_AS_INT"],[ pandora_have_intltool=yes ],[ pandora_have_intltool=no AC_MSG_WARN([Your intltool is too old. You need intltool $1 or later.]) ]) fi AC_CHECK_HEADERS([libintl.h]) AS_IF([test "x${ac_cv_header_libintl_h}" = "xfalse" -o "x${ac_cv_header_libintl_h}" = "xno"],[ pandora_have_intltool=no ]) AC_PATH_PROG(INTLTOOL_UPDATE, [intltool-update]) AC_PATH_PROG(INTLTOOL_MERGE, [intltool-merge]) AC_PATH_PROG(INTLTOOL_EXTRACT, [intltool-extract]) if test -z "$INTLTOOL_UPDATE" -o -z "$INTLTOOL_MERGE" -o -z "$INTLTOOL_EXTRACT"; then AC_MSG_WARN([The intltool scripts were not found. Please install intltool.]) AC_MSG_WARN([On Debian: apt-get install intltool. On Redhat: yum install intltool]) fi INTLTOOL_DESKTOP_RULE='%.desktop: %.desktop.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) -d -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' INTLTOOL_DIRECTORY_RULE='%.directory: %.directory.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) -d -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' INTLTOOL_KEYS_RULE='%.keys: %.keys.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) -k -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' INTLTOOL_PROP_RULE='%.prop: %.prop.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) -d -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' INTLTOOL_OAF_RULE='%.oaf: %.oaf.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) -o -p $(top_srcdir)/po $< [$]@' INTLTOOL_PONG_RULE='%.pong: %.pong.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) -x -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' INTLTOOL_SERVER_RULE='%.server: %.server.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) -o -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' INTLTOOL_SHEET_RULE='%.sheet: %.sheet.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) -x -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' INTLTOOL_SOUNDLIST_RULE='%.soundlist: %.soundlist.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) -d -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' INTLTOOL_UI_RULE='%.ui: %.ui.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) -x -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' INTLTOOL_XML_RULE='%.xml: %.xml.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) -x -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' INTLTOOL_XML_NOMERGE_RULE='%.xml: %.xml.in $(INTLTOOL_MERGE) ; LC_ALL=C $(INTLTOOL_MERGE) -x -u /tmp $< [$]@' INTLTOOL_XAM_RULE='%.xam: %.xml.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) -x -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' INTLTOOL_KBD_RULE='%.kbd: %.kbd.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) -x -u -m -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' INTLTOOL_CAVES_RULE='%.caves: %.caves.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) -d -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' INTLTOOL_SCHEMAS_RULE='%.schemas: %.schemas.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) -s -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' INTLTOOL_THEME_RULE='%.theme: %.theme.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) -d -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' INTLTOOL_SERVICE_RULE='%.service: %.service.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) -d -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' INTLTOOL_POLICY_RULE='%.policy: %.policy.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) -x -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' _IT_SUBST(INTLTOOL_DESKTOP_RULE) _IT_SUBST(INTLTOOL_DIRECTORY_RULE) _IT_SUBST(INTLTOOL_KEYS_RULE) _IT_SUBST(INTLTOOL_PROP_RULE) _IT_SUBST(INTLTOOL_OAF_RULE) _IT_SUBST(INTLTOOL_PONG_RULE) _IT_SUBST(INTLTOOL_SERVER_RULE) _IT_SUBST(INTLTOOL_SHEET_RULE) _IT_SUBST(INTLTOOL_SOUNDLIST_RULE) _IT_SUBST(INTLTOOL_UI_RULE) _IT_SUBST(INTLTOOL_XAM_RULE) _IT_SUBST(INTLTOOL_KBD_RULE) _IT_SUBST(INTLTOOL_XML_RULE) _IT_SUBST(INTLTOOL_XML_NOMERGE_RULE) _IT_SUBST(INTLTOOL_CAVES_RULE) _IT_SUBST(INTLTOOL_SCHEMAS_RULE) _IT_SUBST(INTLTOOL_THEME_RULE) _IT_SUBST(INTLTOOL_SERVICE_RULE) _IT_SUBST(INTLTOOL_POLICY_RULE) # Check the gettext tools to make sure they are GNU AC_PATH_PROG(XGETTEXT, xgettext) AC_PATH_PROG(MSGMERGE, msgmerge) AC_PATH_PROG(MSGFMT, msgfmt) AC_PATH_PROG(GMSGFMT, gmsgfmt, $MSGFMT) if test -z "$XGETTEXT" -o -z "$MSGMERGE" -o -z "$MSGFMT"; then AC_MSG_WARN([GNU gettext tools not found; required for intltool]) fi xgversion="`$XGETTEXT --version|grep '(GNU ' 2> /dev/null`" mmversion="`$MSGMERGE --version|grep '(GNU ' 2> /dev/null`" mfversion="`$MSGFMT --version|grep '(GNU ' 2> /dev/null`" if test -z "$xgversion" -o -z "$mmversion" -o -z "$mfversion"; then AC_MSG_WARN([GNU gettext tools not found; required for intltool]) fi AC_PATH_PROG(INTLTOOL_PERL, perl) if test -z "$INTLTOOL_PERL"; then AC_MSG_WARN([perl not found]) fi AC_MSG_CHECKING([for perl >= 5.8.1]) $INTLTOOL_PERL -e "use 5.8.1;" > /dev/null 2>&1 if test $? -ne 0; then AC_MSG_WARN([perl 5.8.1 is required for intltool]) else IT_PERL_VERSION="`$INTLTOOL_PERL -e \"printf '%vd', $^V\"`" AC_MSG_RESULT([$IT_PERL_VERSION]) fi if test "x$2" != "xno-xml"; then AC_MSG_CHECKING([for XML::Parser]) if `$INTLTOOL_PERL -e "require XML::Parser" 2>/dev/null`; then AC_MSG_RESULT([ok]) else AC_MSG_WARN([XML::Parser perl module is required for intltool]) fi fi # Substitute ALL_LINGUAS so we can use it in po/Makefile AC_SUBST(ALL_LINGUAS) # Set DATADIRNAME correctly if it is not set yet # (copied from glib-gettext.m4) if test -z "$DATADIRNAME"; then AC_LINK_IFELSE( [AC_LANG_PROGRAM([[]], [[extern int _nl_msg_cat_cntr; return _nl_msg_cat_cntr]])], [DATADIRNAME=share], [case $host in *-*-solaris*) dnl On Solaris, if bind_textdomain_codeset is in libc, dnl GNU format message catalog is always supported, dnl since both are added to the libc all together. dnl Hence, we'd like to go with DATADIRNAME=share dnl in this case. AC_CHECK_FUNC(bind_textdomain_codeset, [DATADIRNAME=share], [DATADIRNAME=lib]) ;; *) [DATADIRNAME=lib] ;; esac]) fi AC_SUBST(DATADIRNAME) IT_PO_SUBDIR([po]) ]) # IT_PO_SUBDIR(DIRNAME) # --------------------- # All po subdirs have to be declared with this macro; the subdir "po" is # declared by IT_PROG_INTLTOOL. # AC_DEFUN([IT_PO_SUBDIR], [AC_PREREQ([2.53])dnl We use ac_top_srcdir inside AC_CONFIG_COMMANDS. dnl dnl The following CONFIG_COMMANDS should be exetuted at the very end dnl of config.status. AC_CONFIG_COMMANDS_PRE([ AC_CONFIG_COMMANDS([$1/stamp-it], [ if [ ! grep "^# INTLTOOL_MAKEFILE$" "$1/Makefile.in" > /dev/null ]; then AC_MSG_WARN([$1/Makefile.in.in was not created by intltoolize.]) else rm -f "$1/stamp-it" "$1/stamp-it.tmp" "$1/POTFILES" "$1/Makefile.tmp" >"$1/stamp-it.tmp" [sed '/^#/d s/^[[].*] *// /^[ ]*$/d '"s|^| $ac_top_srcdir/|" \ "$srcdir/$1/POTFILES.in" | sed '$!s/$/ \\/' >"$1/POTFILES" ] [sed '/^POTFILES =/,/[^\\]$/ { /^POTFILES =/!d r $1/POTFILES } ' "$1/Makefile.in" >"$1/Makefile"] rm -f "$1/Makefile.tmp" mv "$1/stamp-it.tmp" "$1/stamp-it" fi ]) ])dnl ]) # _IT_SUBST(VARIABLE) # ------------------- # Abstract macro to do either _AM_SUBST_NOTMAKE or AC_SUBST # AC_DEFUN([_IT_SUBST], [ AC_SUBST([$1]) m4_ifdef([_AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE([$1])]) ] ) # deprecated macros AU_ALIAS([AC_PROG_INTLTOOL], [IT_PROG_INTLTOOL]) # A hint is needed for aclocal from Automake <= 1.9.4: # AC_DEFUN([AC_PROG_INTLTOOL], ...) haildb-2.3.2/m4/pandora_have_libcurl.m40000644000175000017500000000374411513177357020600 0ustar00pcrewspcrews00000000000000dnl Copyright (C) 2010 Monty Taylor dnl This file is free software; Monty Taylor dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. AC_DEFUN([_PANDORA_SEARCH_LIBCURL],[ AC_REQUIRE([AC_LIB_PREFIX]) dnl -------------------------------------------------------------------- dnl Check for libcurl dnl -------------------------------------------------------------------- AC_ARG_ENABLE([libcurl], [AS_HELP_STRING([--disable-libcurl], [Build with libcurl support @<:@default=on@:>@])], [ac_enable_libcurl="$enableval"], [ac_enable_libcurl="yes"]) AS_IF([test "x$ac_enable_libcurl" = "xyes"],[ AC_LIB_HAVE_LINKFLAGS(curl,, [#include ], [ CURL *handle; handle=curl_easy_init(); ]) ],[ ac_cv_libcurl="no" ]) AC_CACHE_CHECK([if libcurl has CURLOPT_USERNAME], [pandora_cv_curl_have_username],[ AC_COMPILE_IFELSE([ AC_LANG_PROGRAM( [[ CURL *handle; handle=curl_easy_init(); rv= curl_easy_setopt(curl_handle, CURLOPT_USERNAME, "foo"); ]])], [pandora_cv_curl_have_username=yes], [pandora_cv_curl_have_username=no]) ]) AM_CONDITIONAL(HAVE_LIBCURL,[test "x${ac_cv_libcurl}" = "xyes"]) AS_IF([test "x$pandora_cv_curl_have_username" = "xyes"], AC_DEFINE([HAVE_CURLOPT_USERNAME],[1], [Does libcurl provide the CURLOPT_USERNAME constant])) ]) AC_DEFUN([PANDORA_HAVE_LIBCURL],[ AC_REQUIRE([_PANDORA_SEARCH_LIBCURL]) AS_IF([test "x${ac_cv_libcurl}" = "xno"],[ AC_MSG_WARN([libcurl development lib not found. On Debian this is found in libcurl4-gnutls-dev. On RHEL5/Fedora11 it's in curl-devel. On RHEL6/Fedora12 it's in libcurl-devel.]) ]) ]) AC_DEFUN([PANDORA_REQUIRE_LIBCURL],[ PANDORA_HAVE_LIBCURL($1) AS_IF([test "x${ac_cv_libcurl}" = "xno"],[ AC_MSG_ERROR([libcurl is required for ${PACKAGE}]) ]) ]) haildb-2.3.2/m4/pandora_have_libboost_thread.m40000644000175000017500000000312111513177357022275 0ustar00pcrewspcrews00000000000000dnl Copyright (C) 2010 Monty Taylor dnl This file is free software; Monty Taylor dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. AC_DEFUN([_PANDORA_SEARCH_BOOST_THREAD],[ AC_REQUIRE([AC_LIB_PREFIX]) AC_REQUIRE([ACX_PTHREAD]) dnl -------------------------------------------------------------------- dnl Check for boost::thread dnl -------------------------------------------------------------------- save_CFLAGS="${CFLAGS}" save_CXXFLAGS="${CXXFLAGS}" CFLAGS="${PTHREAD_CFLAGS} ${CFLAGS}" CXXFLAGS="${PTHREAD_CFLAGS} ${CXXFLAGS}" AC_LANG_PUSH(C++) AC_LIB_HAVE_LINKFLAGS(boost_thread-mt,,[ #include ],[ boost::thread id; ]) AS_IF([test "x${ac_cv_libboost_thread_mt}" = "xno"],[ AC_LIB_HAVE_LINKFLAGS(boost_thread,,[ #include ],[ boost::thread id; ]) ]) AC_LANG_POP() CFLAGS="${save_CFLAGS}" CXXFLAGS="${save_CXXFLAGS}" AM_CONDITIONAL(HAVE_BOOST_THREAD, [test "x${ac_cv_libboost_thread}" = "xyes" -o "x${ac_cv_libboost_thread_mt}" = "xyes"]) BOOST_LIBS="${BOOST_LIBS} ${LTLIBBOOST_THREAD_MT} ${LTLIBBOOST_THREAD}" AC_SUBST(BOOST_LIBS) ]) AC_DEFUN([PANDORA_HAVE_BOOST_THREAD],[ PANDORA_HAVE_BOOST($1) _PANDORA_SEARCH_BOOST_THREAD($1) ]) AC_DEFUN([PANDORA_REQUIRE_BOOST_THREAD],[ PANDORA_REQUIRE_BOOST($1) _PANDORA_SEARCH_BOOST_THREAD($1) AS_IF([test "x${ac_cv_libboost_thread}" = "xno" -a "x${ac_cv_libboost_thread_mt}" = "xno"], AC_MSG_ERROR([boost::thread is required for ${PACKAGE}])) ]) haildb-2.3.2/m4/pandora_have_libhaildb.m40000644000175000017500000000252511513177357021052 0ustar00pcrewspcrews00000000000000dnl Copyright (C) 2009 Sun Microsystems, Inc. dnl This file is free software; Sun Microsystems, Inc. dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. AC_DEFUN([_PANDORA_SEARCH_LIBHAILDB],[ AC_REQUIRE([AC_LIB_PREFIX]) dnl -------------------------------------------------------------------- dnl Check for libhaildb dnl -------------------------------------------------------------------- AC_ARG_ENABLE([libhaildb], [AS_HELP_STRING([--disable-libhaildb], [Build with libhaildb support @<:@default=on@:>@])], [ac_enable_libhaildb="$enableval"], [ac_enable_libhaildb="yes"]) AS_IF([test "x$ac_enable_libhaildb" = "xyes"],[ AC_LIB_HAVE_LINKFLAGS(haildb,,[ #include ],[ ib_set_panic_handler(NULL); ]) AS_IF([test "x${ac_cv_libhaildb}" = "xyes"],[ AC_DEFINE([HAVE_HAILDB_H],[1],[Do we have haildb.h]) ]) ],[ ac_cv_libhaildb="no" ]) AM_CONDITIONAL(HAVE_LIBHAILDB, [test "x${ac_cv_libhaildb}" = "xyes"]) ]) AC_DEFUN([PANDORA_HAVE_LIBHAILDB],[ AC_REQUIRE([_PANDORA_SEARCH_LIBHAILDB]) ]) AC_DEFUN([PANDORA_REQUIRE_LIBHAILDB],[ AC_REQUIRE([PANDORA_HAVE_LIBHAILDB]) AS_IF([test "x${ac_cv_libhaildb}" = "xno"], AC_MSG_ERROR([libhaildb 2.2.0 or later is required for ${PACKAGE}])) ]) haildb-2.3.2/m4/lib-link.m40000644000175000017500000007677411513177357016153 0ustar00pcrewspcrews00000000000000# lib-link.m4 serial 18 (gettext-0.18) dnl Copyright (C) 2001-2009 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl From Bruno Haible. AC_PREREQ([2.54]) dnl AC_LIB_LINKFLAGS(name [, dependencies]) searches for libname and dnl the libraries corresponding to explicit and implicit dependencies. dnl Sets and AC_SUBSTs the LIB${NAME} and LTLIB${NAME} variables and dnl augments the CPPFLAGS variable. dnl Sets and AC_SUBSTs the LIB${NAME}_PREFIX variable to nonempty if libname dnl was found in ${LIB${NAME}_PREFIX}/$acl_libdirstem. AC_DEFUN([AC_LIB_LINKFLAGS], [ AC_REQUIRE([AC_LIB_PREPARE_PREFIX]) AC_REQUIRE([AC_LIB_RPATH]) pushdef([Name],[translit([$1],[./-], [___])]) pushdef([NAME],[translit([$1],[abcdefghijklmnopqrstuvwxyz./-], [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])]) AC_CACHE_CHECK([how to link with lib[]$1], [ac_cv_lib[]Name[]_libs], [ AC_LIB_LINKFLAGS_BODY([$1], [$2]) ac_cv_lib[]Name[]_libs="$LIB[]NAME" ac_cv_lib[]Name[]_ltlibs="$LTLIB[]NAME" ac_cv_lib[]Name[]_cppflags="$INC[]NAME" ac_cv_lib[]Name[]_prefix="$LIB[]NAME[]_PREFIX" ]) LIB[]NAME="$ac_cv_lib[]Name[]_libs" LTLIB[]NAME="$ac_cv_lib[]Name[]_ltlibs" INC[]NAME="$ac_cv_lib[]Name[]_cppflags" LIB[]NAME[]_PREFIX="$ac_cv_lib[]Name[]_prefix" AC_LIB_APPENDTOVAR([CPPFLAGS], [$INC]NAME) AC_SUBST([LIB]NAME) AC_SUBST([LTLIB]NAME) AC_SUBST([LIB]NAME[_PREFIX]) dnl Also set HAVE_LIB[]NAME so that AC_LIB_HAVE_LINKFLAGS can reuse the dnl results of this search when this library appears as a dependency. HAVE_LIB[]NAME=yes popdef([NAME]) popdef([Name]) ]) dnl AC_LIB_HAVE_LINKFLAGS(name, dependencies, includes, testcode, [system]) dnl searches for libname and the libraries corresponding to explicit and dnl implicit dependencies, together with the specified include files and dnl the ability to compile and link the specified testcode. If found, it dnl sets and AC_SUBSTs HAVE_LIB${NAME}=yes and the LIB${NAME} and dnl LTLIB${NAME} variables and augments the CPPFLAGS variable, and dnl #defines HAVE_LIB${NAME} to 1. Otherwise, it sets and AC_SUBSTs dnl HAVE_LIB${NAME}=no and LIB${NAME} and LTLIB${NAME} to empty. dnl Sets and AC_SUBSTs the LIB${NAME}_PREFIX variable to nonempty if libname dnl was found in ${LIB${NAME}_PREFIX}/$acl_libdirstem. AC_DEFUN([AC_LIB_HAVE_LINKFLAGS], [ AC_REQUIRE([AC_LIB_PREPARE_PREFIX]) AC_REQUIRE([AC_LIB_RPATH]) pushdef([Name],[translit([$1],[./-], [___])]) pushdef([NAME],[translit([$1],[abcdefghijklmnopqrstuvwxyz./-], [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])]) dnl Search for lib[]Name and define LIB[]NAME, LTLIB[]NAME and INC[]NAME dnl accordingly. AC_LIB_LINKFLAGS_BODY([$1], [$2], [$5]) dnl Add $INC[]NAME to CPPFLAGS before performing the following checks, dnl because if the user has installed lib[]Name and not disabled its use dnl via --without-lib[]Name-prefix, he wants to use it. ac_save_CPPFLAGS="$CPPFLAGS" AC_LIB_APPENDTOVAR([CPPFLAGS], [$INC]NAME) AC_CACHE_CHECK([for lib[]$1], [ac_cv_lib[]Name], [ ac_save_LIBS="$LIBS" LIBS="$LIBS $LIB[]NAME" AC_TRY_LINK([$3], [$4], [ac_cv_lib[]Name=yes], [ac_cv_lib[]Name=no]) LIBS="$ac_save_LIBS" ]) if test "$ac_cv_lib[]Name" = yes; then HAVE_LIB[]NAME=yes AC_DEFINE([HAVE_LIB]NAME, 1, [Define if you have the $1 library.]) AC_MSG_CHECKING([how to link with lib[]$1]) AC_MSG_RESULT([$LIB[]NAME]) else HAVE_LIB[]NAME=no dnl If $LIB[]NAME didn't lead to a usable library, we don't need dnl $INC[]NAME either. CPPFLAGS="$ac_save_CPPFLAGS" LIB[]NAME= LTLIB[]NAME= LIB[]NAME[]_PREFIX= fi AC_SUBST([HAVE_LIB]NAME) AC_SUBST([LIB]NAME) AC_SUBST([LTLIB]NAME) AC_SUBST([LIB]NAME[_PREFIX]) popdef([NAME]) popdef([Name]) ]) dnl Determine the platform dependent parameters needed to use rpath: dnl acl_libext, dnl acl_shlibext, dnl acl_hardcode_libdir_flag_spec, dnl acl_hardcode_libdir_separator, dnl acl_hardcode_direct, dnl acl_hardcode_minus_L. AC_DEFUN([AC_LIB_RPATH], [ dnl Tell automake >= 1.10 to complain if config.rpath is missing. m4_ifdef([AC_REQUIRE_AUX_FILE], [AC_REQUIRE_AUX_FILE([config.rpath])]) AC_REQUIRE([AC_PROG_CC]) dnl we use $CC, $GCC, $LDFLAGS AC_REQUIRE([AC_LIB_PROG_LD]) dnl we use $LD, $with_gnu_ld AC_REQUIRE([AC_CANONICAL_HOST]) dnl we use $host AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT]) dnl we use $ac_aux_dir AC_CACHE_CHECK([for shared library run path origin], [acl_cv_rpath], [ CC="$CC" GCC="$GCC" LDFLAGS="$LDFLAGS" LD="$LD" with_gnu_ld="$with_gnu_ld" \ ${CONFIG_SHELL-/bin/sh} "$ac_aux_dir/config.rpath" "$host" > conftest.sh . ./conftest.sh rm -f ./conftest.sh acl_cv_rpath=done ]) wl="$acl_cv_wl" acl_libext="$acl_cv_libext" acl_shlibext="$acl_cv_shlibext" acl_libname_spec="$acl_cv_libname_spec" acl_library_names_spec="$acl_cv_library_names_spec" acl_hardcode_libdir_flag_spec="$acl_cv_hardcode_libdir_flag_spec" acl_hardcode_libdir_separator="$acl_cv_hardcode_libdir_separator" acl_hardcode_direct="$acl_cv_hardcode_direct" acl_hardcode_minus_L="$acl_cv_hardcode_minus_L" dnl Determine whether the user wants rpath handling at all. AC_ARG_ENABLE([rpath], [ --disable-rpath do not hardcode runtime library paths], :, enable_rpath=yes) ]) dnl AC_LIB_FROMPACKAGE(name, package) dnl declares that libname comes from the given package. The configure file dnl will then not have a --with-libname-prefix option but a dnl --with-package-prefix option. Several libraries can come from the same dnl package. This declaration must occur before an AC_LIB_LINKFLAGS or similar dnl macro call that searches for libname. AC_DEFUN([AC_LIB_FROMPACKAGE], [ pushdef([NAME],[translit([$1],[abcdefghijklmnopqrstuvwxyz./-], [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])]) define([acl_frompackage_]NAME, [$2]) popdef([NAME]) pushdef([PACK],[$2]) pushdef([PACKUP],[translit(PACK,[abcdefghijklmnopqrstuvwxyz./-], [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])]) define([acl_libsinpackage_]PACKUP, m4_ifdef([acl_libsinpackage_]PACKUP, [acl_libsinpackage_]PACKUP[[, ]],)[lib$1]) popdef([PACKUP]) popdef([PACK]) ]) dnl AC_LIB_LINKFLAGS_BODY(name [, dependencies, system]) searches for dnl libname and the libraries corresponding to explicit and implicit dnl dependencies. dnl Sets the LIB${NAME}, LTLIB${NAME} and INC${NAME} variables. dnl Also, sets the LIB${NAME}_PREFIX variable to nonempty if libname was found dnl in ${LIB${NAME}_PREFIX}/$acl_libdirstem. dnl If system==system, -isystem is used instead of -I AC_DEFUN([AC_LIB_LINKFLAGS_BODY], [ AC_REQUIRE([AC_LIB_PREPARE_MULTILIB]) pushdef([NAME],[translit([$1],[abcdefghijklmnopqrstuvwxyz./-], [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])]) pushdef([PACK],[m4_ifdef([acl_frompackage_]NAME, [acl_frompackage_]NAME, lib[$1])]) pushdef([PACKUP],[translit(PACK,[abcdefghijklmnopqrstuvwxyz./-], [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])]) pushdef([PACKLIBS],[m4_ifdef([acl_frompackage_]NAME, [acl_libsinpackage_]PACKUP, lib[$1])]) dnl Autoconf >= 2.61 supports dots in --with options. pushdef([P_A_C_K],[m4_if(m4_version_compare(m4_defn([m4_PACKAGE_VERSION]),[2.61]),[-1],[translit(PACK,[.],[_])],PACK)]) dnl By default, look in $includedir and $libdir. use_additional=yes if test "x$GCC" = "xyes" -a "x$3" = "xsystem" then i_system="-isystem " else i_system="-I" fi AC_LIB_WITH_FINAL_PREFIX([ eval additional_includedir=\"$includedir\" eval additional_libdir=\"$libdir\" ]) AC_ARG_WITH(P_A_C_K[-prefix], [[ --with-]]P_A_C_K[[-prefix[=DIR] search for ]PACKLIBS[ in DIR/include and DIR/lib --without-]]P_A_C_K[[-prefix don't search for ]PACKLIBS[ in includedir and libdir]], [ if test "X$withval" = "Xno"; then use_additional=no else if test "X$withval" = "X"; then AC_LIB_WITH_FINAL_PREFIX([ eval additional_includedir=\"$includedir\" eval additional_libdir=\"$libdir\" ]) else additional_includedir="$withval/include" additional_libdir="$withval/$acl_libdirstem" if test "$acl_libdirstem2" != "$acl_libdirstem" \ && ! test -d "$withval/$acl_libdirstem"; then additional_libdir="$withval/$acl_libdirstem2" fi fi fi ]) dnl Search the library and its dependencies in $additional_libdir and dnl $LDFLAGS. Using breadth-first-seach. LIB[]NAME= LTLIB[]NAME= INC[]NAME= LIB[]NAME[]_PREFIX= rpathdirs= ltrpathdirs= names_already_handled= names_next_round='$1 $2' while test -n "$names_next_round"; do names_this_round="$names_next_round" names_next_round= for name in $names_this_round; do already_handled= for n in $names_already_handled; do if test "$n" = "$name"; then already_handled=yes break fi done if test -z "$already_handled"; then names_already_handled="$names_already_handled $name" dnl See if it was already located by an earlier AC_LIB_LINKFLAGS dnl or AC_LIB_HAVE_LINKFLAGS call. uppername=`echo "$name" | sed -e 'y|abcdefghijklmnopqrstuvwxyz./-|ABCDEFGHIJKLMNOPQRSTUVWXYZ___|'` eval value=\"\$HAVE_LIB$uppername\" if test -n "$value"; then if test "$value" = yes; then eval value=\"\$LIB$uppername\" test -z "$value" || LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$value" eval value=\"\$LTLIB$uppername\" test -z "$value" || LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }$value" else dnl An earlier call to AC_LIB_HAVE_LINKFLAGS has determined dnl that this library doesn't exist. So just drop it. : fi else dnl Search the library lib$name in $additional_libdir and $LDFLAGS dnl and the already constructed $LIBNAME/$LTLIBNAME. found_dir= found_la= found_so= found_a= eval libname=\"$acl_libname_spec\" # typically: libname=lib$name if test -n "$acl_shlibext"; then shrext=".$acl_shlibext" # typically: shrext=.so else shrext= fi if test $use_additional = yes; then dir="$additional_libdir" dnl The same code as in the loop below: dnl First look for a shared library. if test -n "$acl_shlibext"; then if test -f "$dir/$libname$shrext"; then found_dir="$dir" found_so="$dir/$libname$shrext" else if test "$acl_library_names_spec" = '$libname$shrext$versuffix'; then ver=`(cd "$dir" && \ for f in "$libname$shrext".*; do echo "$f"; done \ | sed -e "s,^$libname$shrext\\\\.,," \ | sort -t '.' -n -r -k1,1 -k2,2 -k3,3 -k4,4 -k5,5 \ | sed 1q ) 2>/dev/null` if test -n "$ver" && test -f "$dir/$libname$shrext.$ver"; then found_dir="$dir" found_so="$dir/$libname$shrext.$ver" fi else eval library_names=\"$acl_library_names_spec\" for f in $library_names; do if test -f "$dir/$f"; then found_dir="$dir" found_so="$dir/$f" break fi done fi fi fi dnl Then look for a static library. if test "X$found_dir" = "X"; then if test -f "$dir/$libname.$acl_libext"; then found_dir="$dir" found_a="$dir/$libname.$acl_libext" fi fi if test "X$found_dir" != "X"; then if test -f "$dir/$libname.la"; then found_la="$dir/$libname.la" fi fi fi if test "X$found_dir" = "X"; then for x in $LDFLAGS $LTLIB[]NAME; do AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) case "$x" in -L*) dir=`echo "X$x" | sed -e 's/^X-L//'` dnl First look for a shared library. if test -n "$acl_shlibext"; then if test -f "$dir/$libname$shrext"; then found_dir="$dir" found_so="$dir/$libname$shrext" else if test "$acl_library_names_spec" = '$libname$shrext$versuffix'; then ver=`(cd "$dir" && \ for f in "$libname$shrext".*; do echo "$f"; done \ | sed -e "s,^$libname$shrext\\\\.,," \ | sort -t '.' -n -r -k1,1 -k2,2 -k3,3 -k4,4 -k5,5 \ | sed 1q ) 2>/dev/null` if test -n "$ver" && test -f "$dir/$libname$shrext.$ver"; then found_dir="$dir" found_so="$dir/$libname$shrext.$ver" fi else eval library_names=\"$acl_library_names_spec\" for f in $library_names; do if test -f "$dir/$f"; then found_dir="$dir" found_so="$dir/$f" break fi done fi fi fi dnl Then look for a static library. if test "X$found_dir" = "X"; then if test -f "$dir/$libname.$acl_libext"; then found_dir="$dir" found_a="$dir/$libname.$acl_libext" fi fi if test "X$found_dir" != "X"; then if test -f "$dir/$libname.la"; then found_la="$dir/$libname.la" fi fi ;; esac if test "X$found_dir" != "X"; then break fi done fi if test "X$found_dir" != "X"; then dnl Found the library. LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-L$found_dir -l$name" if test "X$found_so" != "X"; then dnl Linking with a shared library. We attempt to hardcode its dnl directory into the executable's runpath, unless it's the dnl standard /usr/lib. if test "$enable_rpath" = no \ || test "X$found_dir" = "X/usr/$acl_libdirstem" \ || test "X$found_dir" = "X/usr/$acl_libdirstem2"; then dnl No hardcoding is needed. LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so" else dnl Use an explicit option to hardcode DIR into the resulting dnl binary. dnl Potentially add DIR to ltrpathdirs. dnl The ltrpathdirs will be appended to $LTLIBNAME at the end. haveit= for x in $ltrpathdirs; do if test "X$x" = "X$found_dir"; then haveit=yes break fi done if test -z "$haveit"; then ltrpathdirs="$ltrpathdirs $found_dir" fi dnl The hardcoding into $LIBNAME is system dependent. if test "$acl_hardcode_direct" = yes; then dnl Using DIR/libNAME.so during linking hardcodes DIR into the dnl resulting binary. LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so" else if test -n "$acl_hardcode_libdir_flag_spec" && test "$acl_hardcode_minus_L" = no; then dnl Use an explicit option to hardcode DIR into the resulting dnl binary. LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so" dnl Potentially add DIR to rpathdirs. dnl The rpathdirs will be appended to $LIBNAME at the end. haveit= for x in $rpathdirs; do if test "X$x" = "X$found_dir"; then haveit=yes break fi done if test -z "$haveit"; then rpathdirs="$rpathdirs $found_dir" fi else dnl Rely on "-L$found_dir". dnl But don't add it if it's already contained in the LDFLAGS dnl or the already constructed $LIBNAME haveit= for x in $LDFLAGS $LIB[]NAME; do AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) if test "X$x" = "X-L$found_dir"; then haveit=yes break fi done if test -z "$haveit"; then LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-L$found_dir" fi if test "$acl_hardcode_minus_L" != no; then dnl FIXME: Not sure whether we should use dnl "-L$found_dir -l$name" or "-L$found_dir $found_so" dnl here. LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so" else dnl We cannot use $acl_hardcode_runpath_var and LD_RUN_PATH dnl here, because this doesn't fit in flags passed to the dnl compiler. So give up. No hardcoding. This affects only dnl very old systems. dnl FIXME: Not sure whether we should use dnl "-L$found_dir -l$name" or "-L$found_dir $found_so" dnl here. LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-l$name" fi fi fi fi else if test "X$found_a" != "X"; then dnl Linking with a static library. LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_a" else dnl We shouldn't come here, but anyway it's good to have a dnl fallback. LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-L$found_dir -l$name" fi fi dnl Assume the include files are nearby. additional_includedir= case "$found_dir" in */$acl_libdirstem | */$acl_libdirstem/) basedir=`echo "X$found_dir" | sed -e 's,^X,,' -e "s,/$acl_libdirstem/"'*$,,'` if test "$name" = '$1'; then LIB[]NAME[]_PREFIX="$basedir" fi additional_includedir="$basedir/include" ;; */$acl_libdirstem2 | */$acl_libdirstem2/) basedir=`echo "X$found_dir" | sed -e 's,^X,,' -e "s,/$acl_libdirstem2/"'*$,,'` if test "$name" = '$1'; then LIB[]NAME[]_PREFIX="$basedir" fi additional_includedir="$basedir/include" ;; esac if test "X$additional_includedir" != "X"; then dnl Potentially add $additional_includedir to $INCNAME. dnl But don't add it dnl 1. if it's the standard /usr/include, dnl 2. if it's /usr/local/include and we are using GCC on Linux, dnl 3. if it's already present in $CPPFLAGS or the already dnl constructed $INCNAME, dnl 4. if it doesn't exist as a directory. if test "X$additional_includedir" != "X/usr/include"; then haveit= if test "X$additional_includedir" = "X/usr/local/include"; then if test -n "$GCC"; then case $host_os in linux* | gnu* | k*bsd*-gnu) haveit=yes;; esac fi fi if test -z "$haveit"; then for x in $CPPFLAGS $INC[]NAME; do AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) if test "X$x" = "X${i_system}$additional_includedir"; then haveit=yes break fi done if test -z "$haveit"; then if test -d "$additional_includedir"; then dnl Really add $additional_includedir to $INCNAME. INC[]NAME="${INC[]NAME}${INC[]NAME:+ }${i_system}$additional_includedir" fi fi fi fi fi dnl Look for dependencies. if test -n "$found_la"; then dnl Read the .la file. It defines the variables dnl dlname, library_names, old_library, dependency_libs, current, dnl age, revision, installed, dlopen, dlpreopen, libdir. save_libdir="$libdir" case "$found_la" in */* | *\\*) . "$found_la" ;; *) . "./$found_la" ;; esac libdir="$save_libdir" dnl We use only dependency_libs. for dep in $dependency_libs; do case "$dep" in -L*) additional_libdir=`echo "X$dep" | sed -e 's/^X-L//'` dnl Potentially add $additional_libdir to $LIBNAME and $LTLIBNAME. dnl But don't add it dnl 1. if it's the standard /usr/lib, dnl 2. if it's /usr/local/lib and we are using GCC on Linux, dnl 3. if it's already present in $LDFLAGS or the already dnl constructed $LIBNAME, dnl 4. if it doesn't exist as a directory. if test "X$additional_libdir" != "X/usr/$acl_libdirstem" \ && test "X$additional_libdir" != "X/usr/$acl_libdirstem2"; then haveit= if test "X$additional_libdir" = "X/usr/local/$acl_libdirstem" \ || test "X$additional_libdir" = "X/usr/local/$acl_libdirstem2"; then if test -n "$GCC"; then case $host_os in linux* | gnu* | k*bsd*-gnu) haveit=yes;; esac fi fi if test -z "$haveit"; then haveit= for x in $LDFLAGS $LIB[]NAME; do AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) if test "X$x" = "X-L$additional_libdir"; then haveit=yes break fi done if test -z "$haveit"; then if test -d "$additional_libdir"; then dnl Really add $additional_libdir to $LIBNAME. LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-L$additional_libdir" fi fi haveit= for x in $LDFLAGS $LTLIB[]NAME; do AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) if test "X$x" = "X-L$additional_libdir"; then haveit=yes break fi done if test -z "$haveit"; then if test -d "$additional_libdir"; then dnl Really add $additional_libdir to $LTLIBNAME. LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-L$additional_libdir" fi fi fi fi ;; -R*) dir=`echo "X$dep" | sed -e 's/^X-R//'` if test "$enable_rpath" != no; then dnl Potentially add DIR to rpathdirs. dnl The rpathdirs will be appended to $LIBNAME at the end. haveit= for x in $rpathdirs; do if test "X$x" = "X$dir"; then haveit=yes break fi done if test -z "$haveit"; then rpathdirs="$rpathdirs $dir" fi dnl Potentially add DIR to ltrpathdirs. dnl The ltrpathdirs will be appended to $LTLIBNAME at the end. haveit= for x in $ltrpathdirs; do if test "X$x" = "X$dir"; then haveit=yes break fi done if test -z "$haveit"; then ltrpathdirs="$ltrpathdirs $dir" fi fi ;; -l*) dnl Handle this in the next round. names_next_round="$names_next_round "`echo "X$dep" | sed -e 's/^X-l//'` ;; *.la) dnl Handle this in the next round. Throw away the .la's dnl directory; it is already contained in a preceding -L dnl option. names_next_round="$names_next_round "`echo "X$dep" | sed -e 's,^X.*/,,' -e 's,^lib,,' -e 's,\.la$,,'` ;; *) dnl Most likely an immediate library name. LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$dep" LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }$dep" ;; esac done fi else dnl Didn't find the library; assume it is in the system directories dnl known to the linker and runtime loader. (All the system dnl directories known to the linker should also be known to the dnl runtime loader, otherwise the system is severely misconfigured.) LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-l$name" LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-l$name" fi fi fi done done if test "X$rpathdirs" != "X"; then if test -n "$acl_hardcode_libdir_separator"; then dnl Weird platform: only the last -rpath option counts, the user must dnl pass all path elements in one option. We can arrange that for a dnl single library, but not when more than one $LIBNAMEs are used. alldirs= for found_dir in $rpathdirs; do alldirs="${alldirs}${alldirs:+$acl_hardcode_libdir_separator}$found_dir" done dnl Note: acl_hardcode_libdir_flag_spec uses $libdir and $wl. acl_save_libdir="$libdir" libdir="$alldirs" eval flag=\"$acl_hardcode_libdir_flag_spec\" libdir="$acl_save_libdir" LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$flag" else dnl The -rpath options are cumulative. for found_dir in $rpathdirs; do acl_save_libdir="$libdir" libdir="$found_dir" eval flag=\"$acl_hardcode_libdir_flag_spec\" libdir="$acl_save_libdir" LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$flag" done fi fi if test "X$ltrpathdirs" != "X"; then dnl When using libtool, the option that works for both libraries and dnl executables is -R. The -R options are cumulative. for found_dir in $ltrpathdirs; do LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-R$found_dir" done fi popdef([P_A_C_K]) popdef([PACKLIBS]) popdef([PACKUP]) popdef([PACK]) popdef([NAME]) ]) dnl AC_LIB_APPENDTOVAR(VAR, CONTENTS) appends the elements of CONTENTS to VAR, dnl unless already present in VAR. dnl Works only for CPPFLAGS, not for LIB* variables because that sometimes dnl contains two or three consecutive elements that belong together. AC_DEFUN([AC_LIB_APPENDTOVAR], [ for element in [$2]; do haveit= for x in $[$1]; do AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) if test "X$x" = "X$element"; then haveit=yes break fi done if test -z "$haveit"; then [$1]="${[$1]}${[$1]:+ }$element" fi done ]) dnl For those cases where a variable contains several -L and -l options dnl referring to unknown libraries and directories, this macro determines the dnl necessary additional linker options for the runtime path. dnl AC_LIB_LINKFLAGS_FROM_LIBS([LDADDVAR], [LIBSVALUE], [USE-LIBTOOL]) dnl sets LDADDVAR to linker options needed together with LIBSVALUE. dnl If USE-LIBTOOL evaluates to non-empty, linking with libtool is assumed, dnl otherwise linking without libtool is assumed. AC_DEFUN([AC_LIB_LINKFLAGS_FROM_LIBS], [ AC_REQUIRE([AC_LIB_RPATH]) AC_REQUIRE([AC_LIB_PREPARE_MULTILIB]) $1= if test "$enable_rpath" != no; then if test -n "$acl_hardcode_libdir_flag_spec" && test "$acl_hardcode_minus_L" = no; then dnl Use an explicit option to hardcode directories into the resulting dnl binary. rpathdirs= next= for opt in $2; do if test -n "$next"; then dir="$next" dnl No need to hardcode the standard /usr/lib. if test "X$dir" != "X/usr/$acl_libdirstem" \ && test "X$dir" != "X/usr/$acl_libdirstem2"; then rpathdirs="$rpathdirs $dir" fi next= else case $opt in -L) next=yes ;; -L*) dir=`echo "X$opt" | sed -e 's,^X-L,,'` dnl No need to hardcode the standard /usr/lib. if test "X$dir" != "X/usr/$acl_libdirstem" \ && test "X$dir" != "X/usr/$acl_libdirstem2"; then rpathdirs="$rpathdirs $dir" fi next= ;; *) next= ;; esac fi done if test "X$rpathdirs" != "X"; then if test -n ""$3""; then dnl libtool is used for linking. Use -R options. for dir in $rpathdirs; do $1="${$1}${$1:+ }-R$dir" done else dnl The linker is used for linking directly. if test -n "$acl_hardcode_libdir_separator"; then dnl Weird platform: only the last -rpath option counts, the user dnl must pass all path elements in one option. alldirs= for dir in $rpathdirs; do alldirs="${alldirs}${alldirs:+$acl_hardcode_libdir_separator}$dir" done acl_save_libdir="$libdir" libdir="$alldirs" eval flag=\"$acl_hardcode_libdir_flag_spec\" libdir="$acl_save_libdir" $1="$flag" else dnl The -rpath options are cumulative. for dir in $rpathdirs; do acl_save_libdir="$libdir" libdir="$dir" eval flag=\"$acl_hardcode_libdir_flag_spec\" libdir="$acl_save_libdir" $1="${$1}${$1:+ }$flag" done fi fi fi fi fi AC_SUBST([$1]) ]) haildb-2.3.2/m4/pandora_with_php.m40000644000175000017500000000321711513177357017756 0ustar00pcrewspcrews00000000000000dnl -*- mode: m4; c-basic-offset: 2; indent-tabs-mode: nil; -*- dnl vim:expandtab:shiftwidth=2:tabstop=2:smarttab: dnl dnl pandora-build: A pedantic build system dnl Copyright (C) 2009 Sun Microsystems, Inc. dnl This file is free software; Sun Microsystems dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl dnl From Monty Taylor AC_DEFUN([PANDORA_WITH_PHP],[ AC_ARG_WITH([php], [AS_HELP_STRING([--with-php], [Build NDB/PHP @<:@default=no@:>@])], [with_php=$withval], [with_php=no]) AS_IF([test "x$with_php" != "xno"],[ dnl We explicitly requested PHP build. Fail on too-young SWIG. AS_IF([test "x$SWIG_CAN_BUILD_PHP" != "xyes"], [AC_MSG_ERROR("Your version of SWIG is too young to build NDB/PHP. >=1.3.33 is required!")]) AS_IF([test "x$with_php" != "xyes"], [ac_check_php_config=$with_php], [ac_check_php_config="php-config php-config5"]) AC_CHECK_PROGS(PHP_CONFIG, [$ac_check_php_config]) ]) AS_IF([test "x$PHP_CONFIG" != "x"],[ PHP_CFLAGS=`$PHP_CONFIG --includes` PHP_CPPFLAGS=`$PHP_CONFIG --includes` PHP_LDFLAGS=`$PHP_CONFIG --ldflags` PHP_EXTDIR=`$PHP_CONFIG --extension-dir` strip_php_prefix=`$PHP_CONFIG --prefix | sed 's/\//./g'` PHP_ARCH_DIR=`echo $PHP_EXTDIR | sed "s/$strip_php_prefix//"` ],[ PHP_CFLAGS= PHP_CPPFLAGS= PHP_LDFLAGS= PHP_EXTDIR= PHP_ARCH_DIR= with_php=no ]) AC_SUBST(PHP_CFLAGS) AC_SUBST(PHP_CPPFLAGS) AC_SUBST(PHP_LDFLAGS) AC_SUBST(PHP_EXTDIR) AC_SUBST(PHP_ARCH_DIR) AM_CONDITIONAL(BUILD_PHP, test "$with_php" = "yes") ]) haildb-2.3.2/m4/pandora_optimize.m40000644000175000017500000000452211513177357017774 0ustar00pcrewspcrews00000000000000dnl Copyright (C) 2009 Sun Microsystems, Inc. dnl This file is free software; Sun Microsystems, Inc. dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. AC_DEFUN([PANDORA_OPTIMIZE],[ dnl Build optimized or debug version ? dnl First check for gcc and g++ AS_IF([test "$GCC" = "yes" -a "$INTELCC" = "no"],[ dnl The following is required for portable results of floating point dnl calculations on PowerPC. The same must also be done for IA-64, but dnl this options is missing in the IA-64 gcc backend. case "$target_cpu" in *ppc* | *powerpc*) AM_CFLAGS="-mno-fused-madd ${AM_CFLAGS}" AM_CXXFLAGS="-mno-fused-madd ${AM_CXXFLAGS}" ;; esac dnl Once we can use a modern autoconf, we can replace the std=gnu99 here dnl with using AC_CC_STD_C99 above CC="${CC} -std=gnu99" AM_CPPFLAGS="-g ${AM_CPPFLAGS}" DEBUG_CFLAGS="-O0" DEBUG_CXXFLAGS="-O0" OPTIMIZE_CFLAGS="-O2" OPTIMIZE_CXXFLAGS="-O2" ]) AS_IF([test "$INTELCC" = "yes"],[ AM_CPPFLAGS="-g ${AM_CPPFLAGS}" DEBUG_CFLAGS="-O0" DEBUG_CXXFLAGS="-O0" OPTIMIZE_CFLAGS="-xHOST -O2 -no-prec-div -static" OPTIMIZE_CXXFLAGS="${OPTIMIZE_CFLAGS}" ]) AS_IF([test "$SUNCC" = "yes"],[ dnl Once we can use a modern autoconf, we can replace the -xc99=all here dnl with using AC_CC_STD_C99 above CC="${CC} -xc99=all" CXX="${CXX} -xlang=c99" AM_CFLAGS="-g -mt -xstrconst -Xa ${AM_CFLAGS}" AM_CXXFLAGS="-mt -compat=5 -library=stlport4 -library=Crun -template=no%extdef ${AM_CXXFLAGS}" DEBUG_CXXFLAGS="-g" dnl TODO: Make a test for -xO4 usability here OPTIMIZE_FLAGS="-xO3 -xlibmil -xdepend -xbuiltin" OPTIMIZE_CFLAGS="${OPTIMIZE_FLAGS}" OPTIMIZE_CXXFLAGS="-g0 ${OPTIMIZE_FLAGS}" ]) AC_ARG_WITH([debug], [AS_HELP_STRING([--with-debug], [Add debug code/turns off optimizations (yes|no) @<:@default=no@:>@])], [with_debug=$withval], [with_debug=no]) AS_IF([test "$with_debug" = "yes"],[ # Debugging. No optimization. AM_CFLAGS="${AM_CFLAGS} ${DEBUG_CFLAGS} -DDEBUG" AM_CXXFLAGS="${AM_CXXFLAGS} ${DEBUG_CXXFLAGS} -DDEBUG" ],[ # Optimized version. No debug AM_CFLAGS="${AM_CFLAGS} ${OPTIMIZE_CFLAGS}" AM_CXXFLAGS="${AM_CXXFLAGS} ${OPTIMIZE_CXXFLAGS}" ]) ]) haildb-2.3.2/m4/pandora_stl_hash.m40000644000175000017500000000647411513177357017751 0ustar00pcrewspcrews00000000000000# We check two things: where the include file is for unordered_map, and # what namespace unordered_map lives in within that include file. We # include AC_COMPILE_IFELSE for all the combinations we've seen in the # wild. We define HAVE_UNORDERED_MAP and HAVE_UNORDERED_SET if we have # them, UNORDERED_MAP_H and UNORDERED_SET_H to their location and # UNORDERED_NAMESPACE to be the namespace unordered_map is defined in. AC_DEFUN([PANDORA_CXX_STL_UNORDERED],[ save_CXXFLAGS="${CXXFLAGS}" CXXFLAGS="${AM_CXXFLAGS} ${CXXFLAGS}" AC_LANG_PUSH(C++) AC_CACHE_CHECK([for STL unordered_map], [pandora_cv_stl_unordered],[ AC_COMPILE_IFELSE( [AC_LANG_PROGRAM([[#include ]], [[std::unordered_map t]])], [pandora_cv_stl_unordered="yes"], [pandora_cv_stl_unordered="no"])]) AS_IF([test "x${pandora_cv_stl_unordered}" != "xyes"],[ AC_CACHE_CHECK([for tr1 unordered_map], [pandora_cv_tr1_unordered],[ AC_COMPILE_IFELSE( [AC_LANG_PROGRAM([[ /* We put in this define because of a YACC symbol clash in Drizzle. Seriously... I cannot believe the GCC guys defined a piece of the internals of this named IF - and I can't believe that YACC generates a token define called IF. Really? */ #define IF 21; #include ]],[[ std::tr1::unordered_map t ]])], [pandora_cv_tr1_unordered="yes"], [pandora_cv_tr1_unordered="no"])]) ]) AS_IF([test "x${pandora_cv_stl_unordered}" != "xyes" -a "x${pandora_cv_tr1_unordered}" != "xyes"],[ AC_CACHE_CHECK([for boost unordered_map], [pandora_cv_boost_unordered],[ AC_COMPILE_IFELSE( [AC_LANG_PROGRAM([[#include ]], [[boost::unordered_map t]])], [pandora_cv_boost_unordered="yes"], [pandora_cv_boost_unordered="no"])]) ]) CXXFLAGS="${save_CXXFLAGS}" AC_LANG_POP() AS_IF([test "x${pandora_cv_stl_unordered}" = "xyes"],[ AC_DEFINE(HAVE_STD_UNORDERED_MAP, 1, [if the compiler has std::unordered_map]) AC_DEFINE(HAVE_STD_UNORDERED_SET, 1, [if the compiler has std::unordered_set]) pandora_has_unordered=yes ]) AS_IF([test "x${pandora_cv_tr1_unordered}" = "xyes"],[ AC_DEFINE(HAVE_TR1_UNORDERED_MAP, 1, [if the compiler has std::tr1::unordered_map]) AC_DEFINE(HAVE_TR1_UNORDERED_SET, 1, [if the compiler has std::tr1::unordered_set]) pandora_has_unordered=yes ]) AS_IF([test "x${pandora_cv_boost_unordered}" = "xyes"],[ AC_DEFINE(HAVE_BOOST_UNORDERED_MAP, 1, [if the compiler has boost::unordered_map]) AC_DEFINE(HAVE_BOOST_UNORDERED_SET, 1, [if the compiler has boost::unordered_set]) pandora_has_unordered=yes ]) AS_IF([test "x${pandora_has_unordered}" != "xyes"],[ AC_MSG_WARN([could not find an STL unordered_map]) ]) ]) AC_DEFUN([PANDORA_HAVE_CXX_UNORDERED],[ AC_REQUIRE([PANDORA_CXX_STL_UNORDERED]) ]) AC_DEFUN([PANDORA_REQUIRE_CXX_UNORDERED],[ AC_REQUIRE([PANDORA_HAVE_CXX_UNORDERED]) AS_IF([test "x${pandora_has_unordered}" != "xyes"],[ AC_MSG_ERROR([An STL compliant unordered_map is required for ${PACKAGE}. Implementations can be found in Recent versions of gcc and in boost]) ]) ]) haildb-2.3.2/m4/po.m40000644000175000017500000004461611513177357015056 0ustar00pcrewspcrews00000000000000# po.m4 serial 17 (gettext-0.18) dnl Copyright (C) 1995-2010 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl dnl This file can can be used in projects which are not available under dnl the GNU General Public License or the GNU Library General Public dnl License but which still want to provide support for the GNU gettext dnl functionality. dnl Please note that the actual code of the GNU gettext library is covered dnl by the GNU Library General Public License, and the rest of the GNU dnl gettext package package is covered by the GNU General Public License. dnl They are *not* in the public domain. dnl Authors: dnl Ulrich Drepper , 1995-2000. dnl Bruno Haible , 2000-2003. AC_PREREQ([2.50]) dnl Checks for all prerequisites of the po subdirectory. AC_DEFUN([AM_PO_SUBDIRS], [ AC_REQUIRE([AC_PROG_MAKE_SET])dnl AC_REQUIRE([AC_PROG_INSTALL])dnl AC_REQUIRE([AM_PROG_MKDIR_P])dnl defined by automake AC_REQUIRE([AM_NLS])dnl dnl Release version of the gettext macros. This is used to ensure that dnl the gettext macros and po/Makefile.in.in are in sync. AC_SUBST([GETTEXT_MACRO_VERSION], [0.18]) dnl Perform the following tests also if --disable-nls has been given, dnl because they are needed for "make dist" to work. dnl Search for GNU msgfmt in the PATH. dnl The first test excludes Solaris msgfmt and early GNU msgfmt versions. dnl The second test excludes FreeBSD msgfmt. AM_PATH_PROG_WITH_TEST(MSGFMT, msgfmt, [$ac_dir/$ac_word --statistics /dev/null >&]AS_MESSAGE_LOG_FD[ 2>&1 && (if $ac_dir/$ac_word --statistics /dev/null 2>&1 >/dev/null | grep usage >/dev/null; then exit 1; else exit 0; fi)], :) AC_PATH_PROG([GMSGFMT], [gmsgfmt], [$MSGFMT]) dnl Test whether it is GNU msgfmt >= 0.15. changequote(,)dnl case `$MSGFMT --version | sed 1q | sed -e 's,^[^0-9]*,,'` in '' | 0.[0-9] | 0.[0-9].* | 0.1[0-4] | 0.1[0-4].*) MSGFMT_015=: ;; *) MSGFMT_015=$MSGFMT ;; esac changequote([,])dnl AC_SUBST([MSGFMT_015]) changequote(,)dnl case `$GMSGFMT --version | sed 1q | sed -e 's,^[^0-9]*,,'` in '' | 0.[0-9] | 0.[0-9].* | 0.1[0-4] | 0.1[0-4].*) GMSGFMT_015=: ;; *) GMSGFMT_015=$GMSGFMT ;; esac changequote([,])dnl AC_SUBST([GMSGFMT_015]) dnl Search for GNU xgettext 0.12 or newer in the PATH. dnl The first test excludes Solaris xgettext and early GNU xgettext versions. dnl The second test excludes FreeBSD xgettext. AM_PATH_PROG_WITH_TEST(XGETTEXT, xgettext, [$ac_dir/$ac_word --omit-header --copyright-holder= --msgid-bugs-address= /dev/null >&]AS_MESSAGE_LOG_FD[ 2>&1 && (if $ac_dir/$ac_word --omit-header --copyright-holder= --msgid-bugs-address= /dev/null 2>&1 >/dev/null | grep usage >/dev/null; then exit 1; else exit 0; fi)], :) dnl Remove leftover from FreeBSD xgettext call. rm -f messages.po dnl Test whether it is GNU xgettext >= 0.15. changequote(,)dnl case `$XGETTEXT --version | sed 1q | sed -e 's,^[^0-9]*,,'` in '' | 0.[0-9] | 0.[0-9].* | 0.1[0-4] | 0.1[0-4].*) XGETTEXT_015=: ;; *) XGETTEXT_015=$XGETTEXT ;; esac changequote([,])dnl AC_SUBST([XGETTEXT_015]) dnl Search for GNU msgmerge 0.11 or newer in the PATH. AM_PATH_PROG_WITH_TEST(MSGMERGE, msgmerge, [$ac_dir/$ac_word --update -q /dev/null /dev/null >&]AS_MESSAGE_LOG_FD[ 2>&1], :) dnl Installation directories. dnl Autoconf >= 2.60 defines localedir. For older versions of autoconf, we dnl have to define it here, so that it can be used in po/Makefile. test -n "$localedir" || localedir='${datadir}/locale' AC_SUBST([localedir]) dnl Support for AM_XGETTEXT_OPTION. test -n "${XGETTEXT_EXTRA_OPTIONS+set}" || XGETTEXT_EXTRA_OPTIONS= AC_SUBST([XGETTEXT_EXTRA_OPTIONS]) AC_CONFIG_COMMANDS([po-directories], [[ for ac_file in $CONFIG_FILES; do # Support "outfile[:infile[:infile...]]" case "$ac_file" in *:*) ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;; esac # PO directories have a Makefile.in generated from Makefile.in.in. case "$ac_file" in */Makefile.in) # Adjust a relative srcdir. ac_dir=`echo "$ac_file"|sed 's%/[^/][^/]*$%%'` ac_dir_suffix="/`echo "$ac_dir"|sed 's%^\./%%'`" ac_dots=`echo "$ac_dir_suffix"|sed 's%/[^/]*%../%g'` # In autoconf-2.13 it is called $ac_given_srcdir. # In autoconf-2.50 it is called $srcdir. test -n "$ac_given_srcdir" || ac_given_srcdir="$srcdir" case "$ac_given_srcdir" in .) top_srcdir=`echo $ac_dots|sed 's%/$%%'` ;; /*) top_srcdir="$ac_given_srcdir" ;; *) top_srcdir="$ac_dots$ac_given_srcdir" ;; esac # Treat a directory as a PO directory if and only if it has a # POTFILES.in file. This allows packages to have multiple PO # directories under different names or in different locations. if test -f "$ac_given_srcdir/$ac_dir/POTFILES.in"; then rm -f "$ac_dir/POTFILES" test -n "$as_me" && echo "$as_me: creating $ac_dir/POTFILES" || echo "creating $ac_dir/POTFILES" cat "$ac_given_srcdir/$ac_dir/POTFILES.in" | sed -e "/^#/d" -e "/^[ ]*\$/d" -e "s,.*, $top_srcdir/& \\\\," | sed -e "\$s/\(.*\) \\\\/\1/" > "$ac_dir/POTFILES" POMAKEFILEDEPS="POTFILES.in" # ALL_LINGUAS, POFILES, UPDATEPOFILES, DUMMYPOFILES, GMOFILES depend # on $ac_dir but don't depend on user-specified configuration # parameters. if test -f "$ac_given_srcdir/$ac_dir/LINGUAS"; then # The LINGUAS file contains the set of available languages. if test -n "$OBSOLETE_ALL_LINGUAS"; then test -n "$as_me" && echo "$as_me: setting ALL_LINGUAS in configure.in is obsolete" || echo "setting ALL_LINGUAS in configure.in is obsolete" fi ALL_LINGUAS_=`sed -e "/^#/d" -e "s/#.*//" "$ac_given_srcdir/$ac_dir/LINGUAS"` # Hide the ALL_LINGUAS assigment from automake < 1.5. eval 'ALL_LINGUAS''=$ALL_LINGUAS_' POMAKEFILEDEPS="$POMAKEFILEDEPS LINGUAS" else # The set of available languages was given in configure.in. # Hide the ALL_LINGUAS assigment from automake < 1.5. eval 'ALL_LINGUAS''=$OBSOLETE_ALL_LINGUAS' fi # Compute POFILES # as $(foreach lang, $(ALL_LINGUAS), $(srcdir)/$(lang).po) # Compute UPDATEPOFILES # as $(foreach lang, $(ALL_LINGUAS), $(lang).po-update) # Compute DUMMYPOFILES # as $(foreach lang, $(ALL_LINGUAS), $(lang).nop) # Compute GMOFILES # as $(foreach lang, $(ALL_LINGUAS), $(srcdir)/$(lang).gmo) case "$ac_given_srcdir" in .) srcdirpre= ;; *) srcdirpre='$(srcdir)/' ;; esac POFILES= UPDATEPOFILES= DUMMYPOFILES= GMOFILES= for lang in $ALL_LINGUAS; do POFILES="$POFILES $srcdirpre$lang.po" UPDATEPOFILES="$UPDATEPOFILES $lang.po-update" DUMMYPOFILES="$DUMMYPOFILES $lang.nop" GMOFILES="$GMOFILES $srcdirpre$lang.gmo" done # CATALOGS depends on both $ac_dir and the user's LINGUAS # environment variable. INST_LINGUAS= if test -n "$ALL_LINGUAS"; then for presentlang in $ALL_LINGUAS; do useit=no if test "%UNSET%" != "$LINGUAS"; then desiredlanguages="$LINGUAS" else desiredlanguages="$ALL_LINGUAS" fi for desiredlang in $desiredlanguages; do # Use the presentlang catalog if desiredlang is # a. equal to presentlang, or # b. a variant of presentlang (because in this case, # presentlang can be used as a fallback for messages # which are not translated in the desiredlang catalog). case "$desiredlang" in "$presentlang"*) useit=yes;; esac done if test $useit = yes; then INST_LINGUAS="$INST_LINGUAS $presentlang" fi done fi CATALOGS= if test -n "$INST_LINGUAS"; then for lang in $INST_LINGUAS; do CATALOGS="$CATALOGS $lang.gmo" done fi test -n "$as_me" && echo "$as_me: creating $ac_dir/Makefile" || echo "creating $ac_dir/Makefile" sed -e "/^POTFILES =/r $ac_dir/POTFILES" -e "/^# Makevars/r $ac_given_srcdir/$ac_dir/Makevars" -e "s|@POFILES@|$POFILES|g" -e "s|@UPDATEPOFILES@|$UPDATEPOFILES|g" -e "s|@DUMMYPOFILES@|$DUMMYPOFILES|g" -e "s|@GMOFILES@|$GMOFILES|g" -e "s|@CATALOGS@|$CATALOGS|g" -e "s|@POMAKEFILEDEPS@|$POMAKEFILEDEPS|g" "$ac_dir/Makefile.in" > "$ac_dir/Makefile" for f in "$ac_given_srcdir/$ac_dir"/Rules-*; do if test -f "$f"; then case "$f" in *.orig | *.bak | *~) ;; *) cat "$f" >> "$ac_dir/Makefile" ;; esac fi done fi ;; esac done]], [# Capture the value of obsolete ALL_LINGUAS because we need it to compute # POFILES, UPDATEPOFILES, DUMMYPOFILES, GMOFILES, CATALOGS. But hide it # from automake < 1.5. eval 'OBSOLETE_ALL_LINGUAS''="$ALL_LINGUAS"' # Capture the value of LINGUAS because we need it to compute CATALOGS. LINGUAS="${LINGUAS-%UNSET%}" ]) ]) dnl Postprocesses a Makefile in a directory containing PO files. AC_DEFUN([AM_POSTPROCESS_PO_MAKEFILE], [ # When this code is run, in config.status, two variables have already been # set: # - OBSOLETE_ALL_LINGUAS is the value of LINGUAS set in configure.in, # - LINGUAS is the value of the environment variable LINGUAS at configure # time. changequote(,)dnl # Adjust a relative srcdir. ac_dir=`echo "$ac_file"|sed 's%/[^/][^/]*$%%'` ac_dir_suffix="/`echo "$ac_dir"|sed 's%^\./%%'`" ac_dots=`echo "$ac_dir_suffix"|sed 's%/[^/]*%../%g'` # In autoconf-2.13 it is called $ac_given_srcdir. # In autoconf-2.50 it is called $srcdir. test -n "$ac_given_srcdir" || ac_given_srcdir="$srcdir" case "$ac_given_srcdir" in .) top_srcdir=`echo $ac_dots|sed 's%/$%%'` ;; /*) top_srcdir="$ac_given_srcdir" ;; *) top_srcdir="$ac_dots$ac_given_srcdir" ;; esac # Find a way to echo strings without interpreting backslash. if test "X`(echo '\t') 2>/dev/null`" = 'X\t'; then gt_echo='echo' else if test "X`(printf '%s\n' '\t') 2>/dev/null`" = 'X\t'; then gt_echo='printf %s\n' else echo_func () { cat < "$ac_file.tmp" if grep -l '@TCLCATALOGS@' "$ac_file" > /dev/null; then # Add dependencies that cannot be formulated as a simple suffix rule. for lang in $ALL_LINGUAS; do frobbedlang=`echo $lang | sed -e 's/\..*$//' -e 'y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/'` cat >> "$ac_file.tmp" < /dev/null; then # Add dependencies that cannot be formulated as a simple suffix rule. for lang in $ALL_LINGUAS; do frobbedlang=`echo $lang | sed -e 's/_/-/g' -e 's/^sr-CS/sr-SP/' -e 's/@latin$/-Latn/' -e 's/@cyrillic$/-Cyrl/' -e 's/^sr-SP$/sr-SP-Latn/' -e 's/^uz-UZ$/uz-UZ-Latn/'` cat >> "$ac_file.tmp" <> "$ac_file.tmp" <]], [[ char *foo= 0; int bar= 0; foo= abi::__cxa_demangle(foo, foo, 0, &bar); ]])],[pandora_cv_cxa_demangle=yes],[pandora_cv_cxa_demangle=no])]) CXXFLAGS="${save_CXXFLAGS}" AC_LANG_POP() AS_IF([test "x$pandora_cv_cxa_demangle" = xyes],[ AC_DEFINE(HAVE_ABI_CXA_DEMANGLE, 1, [Define to 1 if you have the `abi::__cxa_demangle' function.]) ]) ]) haildb-2.3.2/m4/pandora_plugins.m40000644000175000017500000000600711513177357017615 0ustar00pcrewspcrews00000000000000dnl Copyright (C) 2009 Sun Microsystems, Inc. dnl This file is free software; Sun Microsystems, Inc. dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl-------------------------------------------------------------------- dnl PANDORA_PLUGINS dnl Declare our plugin modules dnl-------------------------------------------------------------------- AC_DEFUN([PANDORA_PLUGINS],[ dnl We do this to prime the files from a fresh checkout. Normally we want dnl these commands to be executed by make. Perhaps we should split them into dnl a few shell script snippets in config and make Make call them... we're dnl going to get there... dnl ANYWAY - syscmd gets called during aclocal - so before automake. It will dnl get called probably during autoconf too, so it's important to protect dnl with test -f ... if the files exist, we don't have the chicken/egg dnl problem and therefore don't need to do anything here m4_syscmd([python config/pandora-plugin > /dev/null]) m4_syscmd([test -f config/plugin.stamp || touch config/plugin.stamp aclocal.m4]) m4_sinclude(config/pandora-plugin.ac) dnl Add code here to read set plugin lists and set drizzled_default_plugin_list pandora_builtin_list=`echo $pandora_builtin_list | sed 's/, *$//'` pandora_builtin_symbols_list=`echo $pandora_builtin_symbols_list | sed 's/, *$//'` pandora_builtin_load_list=`echo $pandora_builtin_load_list | sed 's/, *$//'` pandora_builtin_load_symbols_list=`echo $pandora_builtin_load_symbols_list | sed 's/, *$//'` AS_IF([test "x$pandora_builtin_symbols_list" = "x"], pandora_builtin_symbols_list="NULL") AS_IF([test "x$pandora_builtin_load_symbols_list" = "x"], pandora_builtin_load_symbols_list="NULL") AC_SUBST([PANDORA_BUILTIN_LIST],[$pandora_builtin_list]) AC_SUBST([PANDORA_BUILTIN_SYMBOLS_LIST],[$pandora_builtin_symbols_list]) AC_SUBST([PANDORA_BUILTIN_LOAD_LIST],[$pandora_builtin_load_list]) AC_SUBST([PANDORA_BUILTIN_LOAD_SYMBOLS_LIST],[$pandora_builtin_load_symbols_list]) AC_SUBST([PANDORA_PLUGIN_LIST],[$pandora_default_plugin_list]) m4_ifval(m4_normalize([$1]),[ AC_CONFIG_FILES($*) ],[ AC_DEFINE_UNQUOTED([PANDORA_BUILTIN_LIST],["$pandora_builtin_list"], [List of plugins to be built in]) AC_DEFINE_UNQUOTED([PANDORA_BUILTIN_SYMBOLS_LIST],["$pandora_builtin_symbols_list"], [List of builtin plugin symbols to be built in]) AC_DEFINE_UNQUOTED([PANDORA_PLUGIN_LIST],["$pandora_default_plugin_list"], [List of plugins that should be loaded on startup if no value is given for --plugin-load]) ]) AC_SUBST(pandora_plugin_test_list) AC_SUBST(pandora_plugin_libs) pandora_plugin_defs=`echo $pandora_plugin_defs | sed 's/, *$//'` AC_SUBST(pandora_plugin_defs) AC_SUBST(PANDORA_PLUGIN_DEP_LIBS) AC_SUBST(pkgplugindir,"\$(pkglibdir)") ]) AC_DEFUN([PANDORA_ADD_PLUGIN_DEP_LIB],[ PANDORA_PLUGIN_DEP_LIBS="${PANDORA_PLUGIN_DEP_LIBS} $*" ]) haildb-2.3.2/m4/iconv.m40000644000175000017500000001653711513177357015557 0ustar00pcrewspcrews00000000000000# iconv.m4 serial 11 (gettext-0.18.1) dnl Copyright (C) 2000-2002, 2007-2010 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl From Bruno Haible. AC_DEFUN([AM_ICONV_LINKFLAGS_BODY], [ dnl Prerequisites of AC_LIB_LINKFLAGS_BODY. AC_REQUIRE([AC_LIB_PREPARE_PREFIX]) AC_REQUIRE([AC_LIB_RPATH]) dnl Search for libiconv and define LIBICONV, LTLIBICONV and INCICONV dnl accordingly. AC_LIB_LINKFLAGS_BODY([iconv]) ]) AC_DEFUN([AM_ICONV_LINK], [ dnl Some systems have iconv in libc, some have it in libiconv (OSF/1 and dnl those with the standalone portable GNU libiconv installed). AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles dnl Search for libiconv and define LIBICONV, LTLIBICONV and INCICONV dnl accordingly. AC_REQUIRE([AM_ICONV_LINKFLAGS_BODY]) dnl Add $INCICONV to CPPFLAGS before performing the following checks, dnl because if the user has installed libiconv and not disabled its use dnl via --without-libiconv-prefix, he wants to use it. The first dnl AC_TRY_LINK will then fail, the second AC_TRY_LINK will succeed. am_save_CPPFLAGS="$CPPFLAGS" AC_LIB_APPENDTOVAR([CPPFLAGS], [$INCICONV]) AC_CACHE_CHECK([for iconv], [am_cv_func_iconv], [ am_cv_func_iconv="no, consider installing GNU libiconv" am_cv_lib_iconv=no AC_TRY_LINK([#include #include ], [iconv_t cd = iconv_open("",""); iconv(cd,NULL,NULL,NULL,NULL); iconv_close(cd);], [am_cv_func_iconv=yes]) if test "$am_cv_func_iconv" != yes; then am_save_LIBS="$LIBS" LIBS="$LIBS $LIBICONV" AC_TRY_LINK([#include #include ], [iconv_t cd = iconv_open("",""); iconv(cd,NULL,NULL,NULL,NULL); iconv_close(cd);], [am_cv_lib_iconv=yes] [am_cv_func_iconv=yes]) LIBS="$am_save_LIBS" fi ]) if test "$am_cv_func_iconv" = yes; then AC_CACHE_CHECK([for working iconv], [am_cv_func_iconv_works], [ dnl This tests against bugs in AIX 5.1, HP-UX 11.11, Solaris 10. am_save_LIBS="$LIBS" if test $am_cv_lib_iconv = yes; then LIBS="$LIBS $LIBICONV" fi AC_TRY_RUN([ #include #include int main () { /* Test against AIX 5.1 bug: Failures are not distinguishable from successful returns. */ { iconv_t cd_utf8_to_88591 = iconv_open ("ISO8859-1", "UTF-8"); if (cd_utf8_to_88591 != (iconv_t)(-1)) { static const char input[] = "\342\202\254"; /* EURO SIGN */ char buf[10]; const char *inptr = input; size_t inbytesleft = strlen (input); char *outptr = buf; size_t outbytesleft = sizeof (buf); size_t res = iconv (cd_utf8_to_88591, (char **) &inptr, &inbytesleft, &outptr, &outbytesleft); if (res == 0) return 1; } } /* Test against Solaris 10 bug: Failures are not distinguishable from successful returns. */ { iconv_t cd_ascii_to_88591 = iconv_open ("ISO8859-1", "646"); if (cd_ascii_to_88591 != (iconv_t)(-1)) { static const char input[] = "\263"; char buf[10]; const char *inptr = input; size_t inbytesleft = strlen (input); char *outptr = buf; size_t outbytesleft = sizeof (buf); size_t res = iconv (cd_ascii_to_88591, (char **) &inptr, &inbytesleft, &outptr, &outbytesleft); if (res == 0) return 1; } } #if 0 /* This bug could be worked around by the caller. */ /* Test against HP-UX 11.11 bug: Positive return value instead of 0. */ { iconv_t cd_88591_to_utf8 = iconv_open ("utf8", "iso88591"); if (cd_88591_to_utf8 != (iconv_t)(-1)) { static const char input[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337"; char buf[50]; const char *inptr = input; size_t inbytesleft = strlen (input); char *outptr = buf; size_t outbytesleft = sizeof (buf); size_t res = iconv (cd_88591_to_utf8, (char **) &inptr, &inbytesleft, &outptr, &outbytesleft); if ((int)res > 0) return 1; } } #endif /* Test against HP-UX 11.11 bug: No converter from EUC-JP to UTF-8 is provided. */ if (/* Try standardized names. */ iconv_open ("UTF-8", "EUC-JP") == (iconv_t)(-1) /* Try IRIX, OSF/1 names. */ && iconv_open ("UTF-8", "eucJP") == (iconv_t)(-1) /* Try AIX names. */ && iconv_open ("UTF-8", "IBM-eucJP") == (iconv_t)(-1) /* Try HP-UX names. */ && iconv_open ("utf8", "eucJP") == (iconv_t)(-1)) return 1; return 0; }], [am_cv_func_iconv_works=yes], [am_cv_func_iconv_works=no], [case "$host_os" in aix* | hpux*) am_cv_func_iconv_works="guessing no" ;; *) am_cv_func_iconv_works="guessing yes" ;; esac]) LIBS="$am_save_LIBS" ]) case "$am_cv_func_iconv_works" in *no) am_func_iconv=no am_cv_lib_iconv=no ;; *) am_func_iconv=yes ;; esac else am_func_iconv=no am_cv_lib_iconv=no fi if test "$am_func_iconv" = yes; then AC_DEFINE([HAVE_ICONV], [1], [Define if you have the iconv() function and it works.]) fi if test "$am_cv_lib_iconv" = yes; then AC_MSG_CHECKING([how to link with libiconv]) AC_MSG_RESULT([$LIBICONV]) else dnl If $LIBICONV didn't lead to a usable library, we don't need $INCICONV dnl either. CPPFLAGS="$am_save_CPPFLAGS" LIBICONV= LTLIBICONV= fi AC_SUBST([LIBICONV]) AC_SUBST([LTLIBICONV]) ]) dnl Define AM_ICONV using AC_DEFUN_ONCE for Autoconf >= 2.64, in order to dnl avoid warnings like dnl "warning: AC_REQUIRE: `AM_ICONV' was expanded before it was required". dnl This is tricky because of the way 'aclocal' is implemented: dnl - It requires defining an auxiliary macro whose name ends in AC_DEFUN. dnl Otherwise aclocal's initial scan pass would miss the macro definition. dnl - It requires a line break inside the AC_DEFUN_ONCE and AC_DEFUN expansions. dnl Otherwise aclocal would emit many "Use of uninitialized value $1" dnl warnings. m4_define([gl_iconv_AC_DEFUN], m4_version_prereq([2.64], [[AC_DEFUN_ONCE( [$1], [$2])]], [[AC_DEFUN( [$1], [$2])]])) gl_iconv_AC_DEFUN([AM_ICONV], [ AM_ICONV_LINK if test "$am_cv_func_iconv" = yes; then AC_MSG_CHECKING([for iconv declaration]) AC_CACHE_VAL([am_cv_proto_iconv], [ AC_TRY_COMPILE([ #include #include extern #ifdef __cplusplus "C" #endif #if defined(__STDC__) || defined(__cplusplus) size_t iconv (iconv_t cd, char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft); #else size_t iconv(); #endif ], [], [am_cv_proto_iconv_arg1=""], [am_cv_proto_iconv_arg1="const"]) am_cv_proto_iconv="extern size_t iconv (iconv_t cd, $am_cv_proto_iconv_arg1 char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft);"]) am_cv_proto_iconv=`echo "[$]am_cv_proto_iconv" | tr -s ' ' | sed -e 's/( /(/'` AC_MSG_RESULT([ $am_cv_proto_iconv]) AC_DEFINE_UNQUOTED([ICONV_CONST], [$am_cv_proto_iconv_arg1], [Define as const if the declaration of iconv() needs const.]) fi ]) haildb-2.3.2/m4/pandora_print_callstack.m40000644000175000017500000000354311513177357021313 0ustar00pcrewspcrews00000000000000dnl Copyright (C) 2009 Sun Microsystems, Inc. dnl This file is free software; Sun Microsystems, Inc. dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl Try to define a macro to dump the current callstack. AC_DEFUN([PANDORA_PRINT_CALLSTACK],[ AC_CHECK_HEADERS([ucontext.h]) AS_IF([test "x$ac_cv_header_ucontext_h" = "xyes"], [ AC_CHECK_FUNCS([printstack]) ]) AS_IF([ test "x$ac_cv_func_printstack" != "xyes"], [ AC_CHECK_HEADERS([dlfcn.h]) AC_CHECK_HEADERS([execinfo.h]) AC_CHECK_FUNCS([backtrace]) AC_CHECK_FUNCS([backtrace_symbols_fd]) ]) AH_BOTTOM([ #ifdef __cplusplus #include #define PANDORA_PRINTSTACK_STD_PREFIX std:: #else #include #define PANDORA_PRINTSTACK_STD_PREFIX #endif #if defined(HAVE_UCONTEXT_H) && defined(HAVE_PRINTSTACK) #include #define pandora_print_callstack(a) \ printstack(PANDORA_PRINTSTACK_STD_PREFIX fileno(a)) #elif defined(HAVE_EXECINFO_H) && defined(HAVE_BACKTRACE) && defined(HAVE_BACKTRACE_SYMBOLS_FD) #include #define pandora_print_callstack(a) \ { \ void *stack[100]; \ int depth = backtrace(stack, 100); \ backtrace_symbols_fd(stack, depth, PANDORA_PRINTSTACK_STD_PREFIX fileno(a)); \ } #elif defined(HAVE_EXECINFO_H) && defined(HAVE_BACKTRACE) && defined(HAVE_BACKTRACE_SYMBOLS) && !defined(HAVE_BACKTRACE_SYMBOLS_FD) #include #define pandora_print_callstack(a) \ { \ void *stack[100]; \ int depth= backtrace(stack, 100); \ char **symbol= backtrace_symbols(stack, depth); \ for (int x= 0; x < size; ++x) \ PANDORA_PRINTSTACK_STD_PREFIX fprintf(a, "%s\n", symbol[x]); \ } #else #define pandora_print_callstack(a) \ PANDORA_PRINTSTACK_STD_PREFIX fprintf(a, \ "Stackdump not supported for this platform\n"); #endif ]) ]) haildb-2.3.2/m4/pandora_cstdint.m40000644000175000017500000000227511513177357017607 0ustar00pcrewspcrews00000000000000# We check two things: where the include file is for cstdint. We # include AC_TRY_COMPILE for all the combinations we've seen in the # wild. We define one of HAVE_CSTDINT or HAVE_TR1_CSTDINT or # HAVE_BOOST_CSTDINT depending # on location. AC_DEFUN([PANDORA_CXX_CSTDINT], [AC_MSG_CHECKING(the location of cstdint) AC_LANG_PUSH(C++) save_CXXFLAGS="${CXXFLAGS}" CXXFLAGS="${CXX_STANDARD} ${CXXFLAGS}" ac_cv_cxx_cstdint="" for location in tr1/cstdint boost/cstdint cstdint; do if test -z "$ac_cv_cxx_cstdint"; then AC_TRY_COMPILE([#include <$location>], [uint32_t t], [ac_cv_cxx_cstdint="<$location>";]) fi done AC_LANG_POP() CXXFLAGS="${save_CXXFLAGS}" if test -n "$ac_cv_cxx_cstdint"; then AC_MSG_RESULT([$ac_cv_cxx_cstdint]) else AC_DEFINE([__STDC_CONSTANT_MACROS],[1],[Use STDC Constant Macros in C++]) AC_DEFINE([__STDC_FORMAT_MACROS],[1],[Use STDC Format Macros in C++]) ac_cv_cxx_cstdint="" AC_MSG_RESULT() AC_MSG_WARN([Could not find a cstdint header.]) fi AC_DEFINE_UNQUOTED(CSTDINT_H,$ac_cv_cxx_cstdint, [the location of ]) ]) haildb-2.3.2/m4/pandora_have_libpq.m40000644000175000017500000000243711513177357020251 0ustar00pcrewspcrews00000000000000dnl Copyright (C) 2009 Sun Microsystems, Inc. dnl This file is free software; Sun Microsystems, Inc. dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. AC_DEFUN([_PANDORA_SEARCH_LIBPQ],[ AC_REQUIRE([AC_LIB_PREFIX]) dnl -------------------------------------------------------------------- dnl Check for libpq dnl -------------------------------------------------------------------- AC_ARG_ENABLE([libpq], [AS_HELP_STRING([--disable-libpq], [Build with libpq support @<:@default=on@:>@])], [ac_enable_libpq="$enableval"], [ac_enable_libpq="yes"]) AS_IF([test "x$ac_enable_libpq" = "xyes"],[ AC_CHECK_HEADERS([libpq-fe.h]) AC_LIB_HAVE_LINKFLAGS(pq,,[ #ifdef HAVE_LIBPQ_FE_H # include #else # include #endif ], [ PGconn *conn; conn = PQconnectdb(NULL); ]) ],[ ac_cv_libpq="no" ]) AM_CONDITIONAL(HAVE_LIBPQ, [test "x${ac_cv_libpq}" = "xyes"]) ]) AC_DEFUN([PANDORA_HAVE_LIBPQ],[ AC_REQUIRE([_PANDORA_SEARCH_LIBPQ]) ]) AC_DEFUN([PANDORA_REQUIRE_LIBPQ],[ AC_REQUIRE([PANDORA_HAVE_LIBPQ]) AS_IF([test "x${ac_cv_libpq}" = "xno"], AC_MSG_ERROR([libpq is required for ${PACKAGE}])) ]) haildb-2.3.2/m4/pandora_canonical.m40000644000175000017500000002676211513177357020075 0ustar00pcrewspcrews00000000000000dnl Copyright (C) 2009 Sun Microsystems, Inc. dnl This file is free software; Sun Microsystems, Inc. dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl Which version of the canonical setup we're using AC_DEFUN([PANDORA_CANONICAL_VERSION],[0.171]) AC_DEFUN([PANDORA_FORCE_DEPEND_TRACKING],[ AC_ARG_ENABLE([fat-binaries], [AS_HELP_STRING([--enable-fat-binaries], [Enable fat binary support on OSX @<:@default=off@:>@])], [ac_enable_fat_binaries="$enableval"], [ac_enable_fat_binaries="no"]) dnl Force dependency tracking on for Sun Studio builds AS_IF([test "x${enable_dependency_tracking}" = "x"],[ enable_dependency_tracking=yes ]) dnl If we're building OSX Fat Binaries, we have to turn off -M options AS_IF([test "x${ac_enable_fat_binaries}" = "xyes"],[ enable_dependency_tracking=no ]) ]) AC_DEFUN([PANDORA_BLOCK_BAD_OPTIONS],[ AS_IF([test "x${prefix}" = "x"],[ AC_MSG_ERROR([--prefix requires an argument]) ]) ]) dnl The standard setup for how we build Pandora projects AC_DEFUN([PANDORA_CANONICAL_TARGET],[ AC_REQUIRE([PANDORA_FORCE_DEPEND_TRACKING]) ifdef([m4_define],,[define([m4_define], defn([define]))]) ifdef([m4_undefine],,[define([m4_undefine], defn([undefine]))]) m4_define([PCT_ALL_ARGS],[$*]) m4_define([PCT_REQUIRE_CXX],[no]) m4_define([PCT_FORCE_GCC42],[no]) m4_define([PCT_DONT_SUPPRESS_INCLUDE],[no]) m4_define([PCT_NO_VC_CHANGELOG],[no]) m4_define([PCT_VERSION_FROM_VC],[no]) m4_define([PCT_USE_VISIBILITY],[yes]) m4_foreach([pct_arg],[$*],[ m4_case(pct_arg, [require-cxx], [ m4_undefine([PCT_REQUIRE_CXX]) m4_define([PCT_REQUIRE_CXX],[yes]) ], [force-gcc42], [ m4_undefine([PCT_FORCE_GCC42]) m4_define([PCT_FORCE_GCC42],[yes]) ], [skip-visibility], [ m4_undefine([PCT_USE_VISIBILITY]) m4_define([PCT_USE_VISIBILITY],[no]) ], [dont-suppress-include], [ m4_undefine([PCT_DONT_SUPPRESS_INCLUDE]) m4_define([PCT_DONT_SUPPRESS_INCLUDE],[yes]) ], [no-vc-changelog], [ m4_undefine([PCT_NO_VC_CHANGELOG]) m4_define([PCT_NO_VC_CHANGELOG],[yes]) ], [version-from-vc], [ m4_undefine([PCT_VERSION_FROM_VC]) m4_define([PCT_VERSION_FROM_VC],[yes]) ]) ]) AC_CONFIG_MACRO_DIR([m4]) m4_if(m4_substr(m4_esyscmd(test -d src && echo 0),0,1),0,[ AC_CONFIG_HEADERS([src/config.h]) ],[ AC_CONFIG_HEADERS([config.h]) ]) PANDORA_BLOCK_BAD_OPTIONS # We need to prevent canonical target # from injecting -O2 into CFLAGS - but we won't modify anything if we have # set CFLAGS on the command line, since that should take ultimate precedence AS_IF([test "x${ac_cv_env_CFLAGS_set}" = "x"], [CFLAGS=""]) AS_IF([test "x${ac_cv_env_CXXFLAGS_set}" = "x"], [CXXFLAGS=""]) AC_CANONICAL_TARGET m4_if(PCT_DONT_SUPRESS_INCLUDE,yes,[ AM_INIT_AUTOMAKE(-Wall -Werror -Wno-portability subdir-objects foreign tar-ustar) ],[ AM_INIT_AUTOMAKE(-Wall -Werror -Wno-portability nostdinc subdir-objects foreign tar-ustar) ]) m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])]) m4_if(m4_substr(m4_esyscmd(test -d gnulib && echo 0),0,1),0,[ gl_EARLY ],[ PANDORA_EXTENSIONS ]) AC_REQUIRE([AC_PROG_CC]) m4_if(PCT_FORCE_GCC42, [yes], [ AC_REQUIRE([PANDORA_ENSURE_GCC_VERSION]) ]) AC_REQUIRE([PANDORA_64BIT]) m4_if(PCT_NO_VC_CHANGELOG,yes,[ vc_changelog=no ],[ vc_changelog=yes ]) m4_if(PCT_VERSION_FROM_VC,yes,[ PANDORA_VC_VERSION ],[ PANDORA_TEST_VC_DIR changequote(<<, >>)dnl PANDORA_RELEASE_ID=`echo $VERSION | sed 's/[^0-9]//g'` changequote([, ])dnl PANDORA_RELEASE_COMMENT="" AC_DEFINE_UNQUOTED([PANDORA_RELEASE_VERSION],["$VERSION"], [Version of the software]) AC_SUBST(PANDORA_RELEASE_COMMENT) AC_SUBST(PANDORA_RELEASE_VERSION) AC_SUBST(PANDORA_RELEASE_ID) ]) PANDORA_VERSION dnl Once we can use a modern autoconf, we can use this dnl AC_PROG_CC_C99 AC_REQUIRE([AC_PROG_CXX]) PANDORA_EXTENSIONS AM_PROG_CC_C_O PANDORA_PLATFORM PANDORA_LIBTOOL dnl autoconf doesn't automatically provide a fail-if-no-C++ macro dnl so we check c++98 features and fail if we don't have them, mainly dnl for that reason PANDORA_CHECK_CXX_STANDARD m4_if(PCT_REQUIRE_CXX, [yes], [ AS_IF([test "$ac_cv_cxx_stdcxx_98" = "no"],[ AC_MSG_ERROR([No working C++ Compiler has been found. ${PACKAGE} requires a C++ compiler that can handle C++98]) ]) ]) PANDORA_CXX_CSTDINT PANDORA_CXX_CINTTYPES m4_if(m4_substr(m4_esyscmd(test -d gnulib && echo 0),0,1),0,[ gl_INIT AC_CONFIG_LIBOBJ_DIR([gnulib]) ]) PANDORA_CHECK_C_VERSION PANDORA_CHECK_CXX_VERSION AC_C_BIGENDIAN AC_C_CONST AC_C_INLINE AC_C_VOLATILE AC_C_RESTRICT AC_HEADER_TIME AC_STRUCT_TM AC_TYPE_SIZE_T AC_SYS_LARGEFILE PANDORA_CLOCK_GETTIME AC_CHECK_HEADERS(sys/socket.h) # off_t is not a builtin type AC_CHECK_SIZEOF(off_t, 4) AS_IF([test "$ac_cv_sizeof_off_t" -eq 0],[ AC_MSG_ERROR("${PACKAGE} needs an off_t type.") ]) AC_CHECK_SIZEOF(size_t) AS_IF([test "$ac_cv_sizeof_size_t" -eq 0],[ AC_MSG_ERROR("${PACKAGE} needs an size_t type.") ]) AC_DEFINE_UNQUOTED([SIZEOF_SIZE_T],[$ac_cv_sizeof_size_t],[Size of size_t as computed by sizeof()]) AC_CHECK_SIZEOF(long long) AC_DEFINE_UNQUOTED([SIZEOF_LONG_LONG],[$ac_cv_sizeof_long_long],[Size of long long as computed by sizeof()]) AC_CACHE_CHECK([if time_t is unsigned], [ac_cv_time_t_unsigned],[ AC_COMPILE_IFELSE([AC_LANG_PROGRAM( [[ #include ]], [[ int array[(((time_t)-1) > 0) ? 1 : -1]; ]]) ],[ ac_cv_time_t_unsigned=yes ],[ ac_cv_time_t_unsigned=no ]) ]) AS_IF([test "$ac_cv_time_t_unsigned" = "yes"],[ AC_DEFINE([TIME_T_UNSIGNED], 1, [Define to 1 if time_t is unsigned]) ]) AC_CACHE_CHECK([if system defines RUSAGE_THREAD], [ac_cv_rusage_thread],[ AC_COMPILE_IFELSE([AC_LANG_PROGRAM( [[ #include #include ]],[[ int x= RUSAGE_THREAD; ]]) ],[ ac_cv_rusage_thread=yes ],[ ac_cv_rusage_thread=no ]) ]) AS_IF([test "$ac_cv_rusage_thread" = "no"],[ AC_DEFINE([RUSAGE_THREAD], [RUSAGE_SELF], [Define if system doesn't define]) ]) AC_CHECK_LIBM dnl Bug on FreeBSD - LIBM check doesn't set the damn variable AC_SUBST([LIBM]) AC_CHECK_FUNC(setsockopt, [], [AC_CHECK_LIB(socket, setsockopt)]) AC_CHECK_FUNC(bind, [], [AC_CHECK_LIB(bind, bind)]) PANDORA_OPTIMIZE AC_LANG_PUSH(C++) # Test whether madvise() is declared in C++ code -- it is not on some # systems, such as Solaris AC_CHECK_DECLS([madvise], [], [], [AC_INCLUDES_DEFAULT[ #if HAVE_SYS_MMAN_H #include #include #endif ]]) AC_LANG_POP() PANDORA_HAVE_GCC_ATOMICS m4_if(PCT_USE_VISIBILITY,[yes],[ PANDORA_ENABLE_VISIBILITY ],[ PANDORA_CHECK_VISIBILITY ]) PANDORA_HEADER_ASSERT PANDORA_WARNINGS(PCT_ALL_ARGS) PANDORA_ENABLE_DTRACE AC_LIB_PREFIX PANDORA_HAVE_BETTER_MALLOC AC_CHECK_PROGS([DOXYGEN], [doxygen]) AC_CHECK_PROGS([PERL], [perl]) AC_CHECK_PROGS([DPKG_GENSYMBOLS], [dpkg-gensymbols], [:]) AC_CHECK_PROGS([LCOV], [lcov], [echo lcov not found]) AC_CHECK_PROGS([LCOV_GENHTML], [genhtml], [echo genhtml not found]) AC_CHECK_PROGS([SPHINXBUILD], [sphinx-build], [:]) AS_IF([test "x${SPHINXBUILD}" != "x:"],[ AC_CACHE_CHECK([if sphinx is new enough],[ac_cv_recent_sphinx],[ ${SPHINXBUILD} -Q -C -b man -d conftest.d . . >/dev/null 2>&1 AS_IF([test $? -eq 0],[ac_cv_recent_sphinx=yes], [ac_cv_recent_sphinx=no]) rm -rf conftest.d ]) ]) AM_CONDITIONAL(HAVE_DPKG_GENSYMBOLS,[test "x${DPKG_GENSYMBOLS}" != "x:"]) AM_CONDITIONAL(HAVE_SPHINX,[test "x${SPHINXBUILD}" != "x:"]) AM_CONDITIONAL(HAVE_RECENT_SPHINX,[test "x${ac_cv_recent_sphinx}" = "xyes"]) m4_if(m4_substr(m4_esyscmd(test -d po && echo 0),0,1),0, [ AM_PO_SUBDIRS IT_PROG_INTLTOOL([0.35],[no-xml]) GETTEXT_PACKAGE=$PACKAGE AC_CHECK_LIB(intl, libintl_gettext) AC_SUBST([GETTEXT_PACKAGE]) AS_IF([test "x${USE_NLS}" = "xyes" -a "x${pandora_have_intltool}" = "xyes"], [AC_DEFINE([ENABLE_NLS],[1],[Turn on language support]) AC_CONFIG_FILES([po/Makefile.in]) ]) ]) AM_CONDITIONAL(BUILD_PO,[test "x${USE_NLS}" = "xyes" -a "x${pandora_have_intltool}" = "xyes"]) AS_IF([test "x${gl_LIBOBJS}" != "x"],[ AS_IF([test "$GCC" = "yes"],[ AM_CPPFLAGS="-isystem \${top_srcdir}/gnulib -isystem \${top_builddir}/gnulib ${AM_CPPFLAGS}" ],[ AM_CPPFLAGS="-I\${top_srcdir}/gnulib -I\${top_builddir}/gnulib ${AM_CPPFLAGS}" ]) ]) m4_if(m4_substr(m4_esyscmd(test -d src && echo 0),0,1),0,[ AM_CPPFLAGS="-I\$(top_srcdir)/src -I\$(top_builddir)/src ${AM_CPPFLAGS}" ],[ AM_CPPFLAGS="-I\$(top_srcdir) -I\$(top_builddir) ${AM_CPPFLAGS}" ]) PANDORA_USE_PIPE AH_TOP([ #ifndef __CONFIG_H__ #define __CONFIG_H__ /* _SYS_FEATURE_TESTS_H is Solaris, _FEATURES_H is GCC */ #if defined( _SYS_FEATURE_TESTS_H) || defined(_FEATURES_H) #error "You should include config.h as your first include file" #endif #include "config/top.h" ]) mkdir -p config cat > config/top.h.stamp </dev/null 2>&1 || mv config/top.h.stamp config/top.h rm -f config/top.h.stamp AH_BOTTOM([ #if defined(__cplusplus) # include CSTDINT_H # include CINTTYPES_H #else # include # include #endif #if !defined(HAVE_ULONG) && !defined(__USE_MISC) # define HAVE_ULONG 1 typedef unsigned long int ulong; #endif /* To hide the platform differences between MS Windows and Unix, I am * going to use the Microsoft way and #define the Microsoft-specific * functions to the unix way. Microsoft use a separate subsystem for sockets, * but Unix normally just use a filedescriptor on the same functions. It is * a lot easier to map back to the unix way with macros than going the other * way without side effect ;-) */ #ifdef TARGET_OS_WINDOWS #define random() rand() #define srandom(a) srand(a) #define get_socket_errno() WSAGetLastError() #else #define INVALID_SOCKET -1 #define SOCKET_ERROR -1 #define closesocket(a) close(a) #define get_socket_errno() errno #endif #if defined(__cplusplus) # if defined(DEBUG) # include # include # endif template inline To implicit_cast(From const &f) { return f; } template // use like this: down_cast(foo); inline To down_cast(From* f) { // so we only accept pointers // Ensures that To is a sub-type of From *. This test is here only // for compile-time type checking, and has no overhead in an // optimized build at run-time, as it will be optimized away // completely. if (false) { implicit_cast(0); } #if defined(DEBUG) assert(f == NULL || dynamic_cast(f) != NULL); // RTTI: debug mode only! #endif return static_cast(f); } #endif /* defined(__cplusplus) */ #endif /* __CONFIG_H__ */ ]) AM_CFLAGS="${AM_CFLAGS} ${CC_WARNINGS} ${CC_PROFILING} ${CC_COVERAGE}" AM_CXXFLAGS="${AM_CXXFLAGS} ${CXX_WARNINGS} ${CC_PROFILING} ${CC_COVERAGE}" AC_SUBST([AM_CFLAGS]) AC_SUBST([AM_CXXFLAGS]) AC_SUBST([AM_CPPFLAGS]) AC_SUBST([AM_LDFLAGS]) ]) haildb-2.3.2/m4/pandora_have_thrift.m40000644000175000017500000000231611513177357020436 0ustar00pcrewspcrews00000000000000dnl Copyright (C) 2010 Padraig O'Sullivan dnl This file is free software; Padraig O'Sullivan dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. AC_DEFUN([_PANDORA_SEARCH_THRIFT],[ AC_REQUIRE([AC_LIB_PREFIX]) dnl -------------------------------------------------------------------- dnl Check for thrift dnl -------------------------------------------------------------------- AC_ARG_ENABLE([thrift], [AS_HELP_STRING([--disable-thrift], [Build with thrift support @<:@default=on@:>@])], [ac_enable_thrift="$enableval"], [ac_enable_thrift="yes"]) AS_IF([test "x$ac_enable_thrift" = "xyes"],[ AC_LANG_PUSH(C++) AC_LIB_HAVE_LINKFLAGS(thrift,,[ #include ],[ apache::thrift::TOutput test_output; ]) AC_LANG_POP() ],[ ac_cv_thrift="no" ]) AM_CONDITIONAL(HAVE_THRIFT, [test "x${ac_cv_thrift}" = "xyes"]) ]) AC_DEFUN([PANDORA_HAVE_THRIFT],[ AC_REQUIRE([_PANDORA_SEARCH_THRIFT]) ]) AC_DEFUN([PANDORA_REQUIRE_THRIFT],[ AC_REQUIRE([PANDORA_HAVE_THRIFT]) AS_IF([test x$ac_cv_thrift= xno],[ AC_MSG_ERROR([thrift required for ${PACKAGE}]) ]) ]) haildb-2.3.2/m4/pandora_drizzle_build.m40000644000175000017500000000422111513177357020772 0ustar00pcrewspcrews00000000000000dnl Copyright (C) 2009 Sun Microsystems, Inc. dnl This file is free software; Sun Microsystems, Inc. dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl Check for all of the headers and libs that Drizzle needs. We check all dnl of these for plugins too, to ensure that all of the appropriate defines dnl are set. AC_DEFUN([PANDORA_DRIZZLE_BUILD],[ AC_STRUCT_TM AC_FUNC_ALLOCA AC_FUNC_UTIME_NULL AC_FUNC_VPRINTF PANDORA_WORKING_FDATASYNC AC_CHECK_FUNCS(\ gethrtime \ setupterm \ backtrace \ backtrace_symbols \ backtrace_symbols_fd) AC_HEADER_STAT AC_HEADER_DIRENT AC_HEADER_STDC AC_HEADER_SYS_WAIT AC_HEADER_STDBOOL AC_CHECK_HEADERS(sys/types.h sys/fpu.h fpu_control.h ieeefp.h) AC_CHECK_HEADERS(select.h sys/select.h) AC_CHECK_HEADERS(utime.h sys/utime.h ) AC_CHECK_HEADERS(synch.h sys/mman.h) AC_CHECK_HEADERS(sched.h) AC_CHECK_HEADERS(sys/prctl.h) AC_CHECK_HEADERS(execinfo.h) AC_CHECK_HEADERS(locale.h) AC_CHECK_HEADERS(termcap.h termio.h termios.h asm/termbits.h) AC_CHECK_HEADERS(paths.h) #-------------------------------------------------------------------- # Check for system libraries. Adds the library to $LIBS # and defines HAVE_LIBM etc #-------------------------------------------------------------------- # For the sched_yield() function on Solaris AC_CHECK_FUNC(sched_yield, [], [AC_CHECK_LIB(posix4, [sched_yield], [AC_DEFINE(HAVE_SCHED_YIELD, 1, [Have sched_yield function]) LIBS="$LIBS -lposix4"])]) AS_IF([test "$ac_cv_header_termio_h" = "no" -a "$ac_cv_header_termios_h" = "no"],[ AC_CHECK_FUNC(gtty, [], [AC_CHECK_LIB(compat, gtty)]) ]) AC_CHECK_HEADERS([curses.h term.h],[],[],[[ #ifdef HAVE_CURSES_H # include #endif ]]) AC_CHECK_TYPES([uint, ulong]) PANDORA_REQUIRE_BISON PANDORA_CXX_DEMANGLE PANDORA_REQUIRE_BOOST([1.38]) PANDORA_REQUIRE_BOOST_PROGRAM_OPTIONS PANDORA_REQUIRE_BOOST_THREAD PANDORA_REQUIRE_BOOST_REGEX PANDORA_REQUIRE_BOOST_DATE_TIME PANDORA_REQUIRE_BOOST_FILESYSTEM PANDORA_REQUIRE_BOOST_IOSTREAMS ]) haildb-2.3.2/m4/pandora_have_sasl.m40000644000175000017500000000750211513177357020102 0ustar00pcrewspcrews00000000000000dnl Copyright (C) 2009 Sun Microsystems, Inc. dnl This file is free software; Sun Microsystems, Inc. dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. AC_DEFUN([_PANDORA_SEARCH_SASL],[ AC_REQUIRE([AC_LIB_PREFIX]) dnl -------------------------------------------------------------------- dnl Check for sasl dnl -------------------------------------------------------------------- AC_ARG_ENABLE([sasl], [AS_HELP_STRING([--disable-sasl], [Build with sasl support @<:@default=on@:>@])], [ac_enable_sasl="$enableval"], [ac_enable_sasl="yes"]) AS_IF([test "x$ac_enable_sasl" = "xyes"], [ AC_LIB_HAVE_LINKFLAGS(sasl,,[ #include #include ],[ sasl_server_init(NULL, NULL); ]) AS_IF([test "x${ac_cv_libsasl}" != "xyes" ], [ AC_LIB_HAVE_LINKFLAGS(sasl2,,[ #include #include ],[ sasl_server_init(NULL, NULL); ]) HAVE_LIBSASL="$HAVE_LIBSASL2" LIBSASL="$LIBSASL2" LIBSASL_PREFIX="$LIBSASL2_PREFIX" LTLIBSASL="$LT_LIBSASL2" ]) ]) AS_IF([test "x${ac_cv_libsasl}" = "xyes" -o "x${ac_cv_libsasl2}" = "xyes"], [ac_cv_sasl=yes], [ac_cv_sasl=no]) AM_CONDITIONAL(HAVE_LIBSASL, [test "x${ac_cv_libsasl}" = "xyes"]) AM_CONDITIONAL(HAVE_LIBSASL2, [test "x${ac_cv_libsasl2}" = "xyes"]) AM_CONDITIONAL(HAVE_SASL, [test "x${ac_cv_sasl}" = "xyes"]) ]) AC_DEFUN([PANDORA_HAVE_SASL],[ AC_REQUIRE([_PANDORA_SEARCH_SASL]) ]) AC_DEFUN([PANDORA_REQUIRE_SASL],[ AC_REQUIRE([_PANDORA_SEARCH_SASL]) AS_IF([test "x${ac_cv_sasl}" = "xno"], AC_MSG_ERROR([SASL (libsasl or libsasl2) is required for ${PACKAGE}])) ]) AC_DEFUN([_PANDORA_SEARCH_LIBSASL],[ AC_REQUIRE([AC_LIB_PREFIX]) dnl -------------------------------------------------------------------- dnl Check for libsasl dnl -------------------------------------------------------------------- AC_ARG_ENABLE([libsasl], [AS_HELP_STRING([--disable-libsasl], [Build with libsasl support @<:@default=on@:>@])], [ac_enable_libsasl="$enableval"], [ac_enable_libsasl="yes"]) AS_IF([test "x$ac_enable_libsasl" = "xyes"],[ AC_LIB_HAVE_LINKFLAGS(sasl,,[ #include #include ],[ sasl_server_init(NULL, NULL); ]) ],[ ac_cv_libsasl="no" ]) AM_CONDITIONAL(HAVE_LIBSASL, [test "x${ac_cv_libsasl}" = "xyes"]) ]) AC_DEFUN([PANDORA_HAVE_LIBSASL],[ AC_REQUIRE([_PANDORA_SEARCH_LIBSASL]) ]) AC_DEFUN([PANDORA_REQUIRE_LIBSASL],[ AC_REQUIRE([_PANDORA_SEARCH_LIBSASL]) AS_IF([test "x${ac_cv_libsasl}" = "xno"], AC_MSG_ERROR([libsasl is required for ${PACKAGE}])) ]) AC_DEFUN([_PANDORA_SEARCH_LIBSASL2],[ AC_REQUIRE([AC_LIB_PREFIX]) dnl -------------------------------------------------------------------- dnl Check for libsasl2 dnl -------------------------------------------------------------------- AC_ARG_ENABLE([libsasl2], [AS_HELP_STRING([--disable-libsasl2], [Build with libsasl2 support @<:@default=on@:>@])], [ac_enable_libsasl2="$enableval"], [ac_enable_libsasl2="yes"]) AS_IF([test "x$ac_enable_libsasl2" = "xyes"],[ AC_LIB_HAVE_LINKFLAGS(sasl2,,[ #include #include ],[ sasl2_server_init(NULL, NULL); ]) ],[ ac_cv_libsasl2="no" ]) AM_CONDITIONAL(HAVE_LIBSASL2, [test "x${ac_cv_libsasl2}" = "xyes"]) ]) AC_DEFUN([PANDORA_HAVE_LIBSASL2],[ AC_REQUIRE([_PANDORA_SEARCH_LIBSASL2]) ]) AC_DEFUN([PANDORA_REQUIRE_LIBSASL2],[ AC_REQUIRE([_PANDORA_SEARCH_LIBSASL2]) AS_IF([test "x${ac_cv_libsasl2}" = "xno"], AC_MSG_ERROR([libsasl2 is required for ${PACKAGE}])) ]) haildb-2.3.2/m4/pandora_with_gettext.m40000644000175000017500000000302111513177357020644 0ustar00pcrewspcrews00000000000000dnl -*- mode: m4; c-basic-offset: 2; indent-tabs-mode: nil; -*- dnl vim:expandtab:shiftwidth=2:tabstop=2:smarttab: dnl dnl pandora-build: A pedantic build system dnl Copyright (C) 2009 Sun Microsystems, Inc. dnl This file is free software; Sun Microsystems dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl dnl From Monty Taylor AC_DEFUN([PANDORA_WITH_GETTEXT],[ m4_syscmd([if test -d po ; then echo "# This file is auto-generated from configure. Do not edit directly" > po/POTFILES.in.stamp PACKAGE=$(grep ^AC_INIT configure.ac | cut -f2-3 -d[ | cut -f1 -d]) for f in $(find . | grep -v "${PACKAGE}-" | egrep '\.(cc|c|h|yy)$' | cut -c3- | sort) do if grep gettext.h "$f" | grep include >/dev/null 2>&1 then echo "$f" >> po/POTFILES.in.stamp fi done if diff po/POTFILES.in.stamp po/POTFILES.in >/dev/null 2>&1 then rm po/POTFILES.in.stamp else mv po/POTFILES.in.stamp po/POTFILES.in fi fi]) m4_if(m4_substr(m4_esyscmd(test -d po && echo 0),0,1),0, [ AM_GNU_GETTEXT(external, need-formatstring-macros) AM_GNU_GETTEXT_VERSION([0.17]) AS_IF([test "x$MSGMERGE" = "x" -o "x$MSGMERGE" = "x:"],[ AM_PATH_PROG_WITH_TEST([GMSGMERGE], gmsgmerge, [$ac_dir/$ac_word --update -q /dev/null /dev/null >&]AS_MESSAGE_LOG_FD[ 2>&1], :) MSGMERGE="${GMSGMERGE}" ]) AM_CONDITIONAL([BUILD_GETTEXT],[test "x$MSGMERGE" != "x" -a "x$MSGMERGE" != "x:"]) ]) ]) haildb-2.3.2/m4/pandora_warnings.m40000644000175000017500000003757311513177357020000 0ustar00pcrewspcrews00000000000000dnl Copyright (C) 2009 Sun Microsystems, Inc. dnl This file is free software; Sun Microsystems, Inc. dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl AC_PANDORA_WARNINGS([less-warnings|warnings-always-on]) dnl less-warnings turn on a limited set of warnings dnl warnings-always-on always set warnings=error regardless of tarball/vc dnl @TODO: remove less-warnings option as soon as Drizzle is clean enough to dnl allow it AC_DEFUN([PANDORA_WARNINGS],[ m4_define([PW_LESS_WARNINGS],[no]) m4_define([PW_WARN_ALWAYS_ON],[no]) ifdef([m4_define],,[define([m4_define], defn([define]))]) ifdef([m4_undefine],,[define([m4_undefine], defn([undefine]))]) m4_foreach([pw_arg],[$*],[ m4_case(pw_arg, [less-warnings],[ m4_undefine([PW_LESS_WARNINGS]) m4_define([PW_LESS_WARNINGS],[yes]) ], [warnings-always-on],[ m4_undefine([PW_WARN_ALWAYS_ON]) m4_define([PW_WARN_ALWAYS_ON],[yes]) ]) ]) AC_REQUIRE([PANDORA_BUILDING_FROM_VC]) m4_if(PW_WARN_ALWAYS_ON, [yes], [ac_cv_warnings_as_errors=yes], AS_IF([test "$pandora_building_from_vc" = "yes"], [ac_cv_warnings_as_errors=yes], [ac_cv_warnings_as_errors=no])) AC_ARG_ENABLE([gcc-profile-mode], [AS_HELP_STRING([--enable-gcc-profile-mode], [Toggle gcc profile mode @<:@default=off@:>@])], [ac_gcc_profile_mode="$enableval"], [ac_gcc_profile_mode="no"]) AC_ARG_ENABLE([profiling], [AS_HELP_STRING([--enable-profiling], [Toggle profiling @<:@default=off@:>@])], [ac_profiling="$enableval"], [ac_profiling="no"]) AC_ARG_ENABLE([coverage], [AS_HELP_STRING([--enable-coverage], [Toggle coverage @<:@default=off@:>@])], [ac_coverage="$enableval"], [ac_coverage="no"]) AS_IF([test "$GCC" = "yes"],[ AS_IF([test "$ac_profiling" = "yes"],[ CC_PROFILING="-pg" GCOV_LIBS="-pg -lgcov" save_LIBS="${LIBS}" LIBS="" AC_CHECK_LIB(c_p, read) LIBC_P="${LIBS}" LIBS="${save_LIBS}" AC_SUBST(LIBC_P) ],[ CC_PROFILING=" " ]) AS_IF([test "$ac_coverage" = "yes"], [ CC_COVERAGE="--coverage" GCOV_LIBS="-lgcov" ]) AS_IF([test "$ac_cv_warnings_as_errors" = "yes"], [W_FAIL="-Werror"]) AC_CACHE_CHECK([whether it is safe to use -fdiagnostics-show-option], [ac_cv_safe_to_use_fdiagnostics_show_option_], [save_CFLAGS="$CFLAGS" CFLAGS="-fdiagnostics-show-option ${AM_CFLAGS} ${CFLAGS}" AC_COMPILE_IFELSE( [AC_LANG_PROGRAM([],[])], [ac_cv_safe_to_use_fdiagnostics_show_option_=yes], [ac_cv_safe_to_use_fdiagnostics_show_option_=no]) CFLAGS="$save_CFLAGS"]) AS_IF([test "$ac_cv_safe_to_use_fdiagnostics_show_option_" = "yes"], [ F_DIAGNOSTICS_SHOW_OPTION="-fdiagnostics-show-option" ]) AC_CACHE_CHECK([whether it is safe to use -floop-parallelize-all], [ac_cv_safe_to_use_floop_parallelize_all_], [save_CFLAGS="$CFLAGS" CFLAGS="-floop-parallelize-all ${AM_CFLAGS} ${CFLAGS}" AC_COMPILE_IFELSE( [AC_LANG_PROGRAM([],[])], [ac_cv_safe_to_use_floop_parallelize_all_=yes], [ac_cv_safe_to_use_floop_parallelize_all_=no]) CFLAGS="$save_CFLAGS"]) AS_IF([test "$ac_cv_safe_to_use_floop_parallelize_all_" = "yes"], [ F_LOOP_PARALLELIZE_ALL="-floop-parallelize-all" ]) NO_STRICT_ALIASING="-fno-strict-aliasing -Wno-strict-aliasing" NO_SHADOW="-Wno-shadow" AS_IF([test "$INTELCC" = "yes"],[ m4_if(PW_LESS_WARNINGS,[no],[ BASE_WARNINGS="-w1 -Werror -Wcheck -Wp64 -Woverloaded-virtual -Wcast-qual -diag-disable 188" ],[ dnl 2203 is like old-style-cast dnl 1684 is like strict-aliasing dnl 188 is about using enums as bitfields dnl 1683 is a warning about _EXPLICIT_ casting, which we want BASE_WARNINGS="-w1 -Werror -Wcheck -Wp64 -Woverloaded-virtual -Wcast-qual -diag-disable 188,981,2259,2203,1683,1684" ]) CC_WARNINGS="${BASE_WARNINGS}" CXX_WARNINGS="${BASE_WARNINGS}" PROTOSKIP_WARNINGS="-diag-disable 188,981,967,2259,1683,1684,2203" ],[ m4_if(PW_LESS_WARNINGS,[no],[ BASE_WARNINGS_FULL="${W_CONVERSION} -Wstrict-aliasing" CC_WARNINGS_FULL="-Wswitch-default -Wswitch-enum -Wwrite-strings" CXX_WARNINGS_FULL="-Weffc++ -Wold-style-cast" NO_OLD_STYLE_CAST="-Wno-old-style-cast" NO_EFF_CXX="-Wno-effc++" ],[ BASE_WARNINGS_FULL="${NO_STRICT_ALIASING}" ]) AS_IF([test "${ac_cv_assert}" = "no"], [NO_UNUSED="-Wno-unused-variable -Wno-unused-parameter"]) AC_CACHE_CHECK([whether it is safe to use -Wextra], [ac_cv_safe_to_use_Wextra_], [save_CFLAGS="$CFLAGS" CFLAGS="${W_FAIL} -pedantic -Wextra ${AM_CFLAGS} ${CFLAGS}" AC_COMPILE_IFELSE([ AC_LANG_PROGRAM( [[ #include ]], [[]]) ], [ac_cv_safe_to_use_Wextra_=yes], [ac_cv_safe_to_use_Wextra_=no]) CFLAGS="$save_CFLAGS"]) BASE_WARNINGS="${W_FAIL} -pedantic -Wall -Wundef -Wshadow ${NO_UNUSED} ${F_DIAGNOSTICS_SHOW_OPTION} ${F_LOOP_PARALLELIZE_ALL} ${BASE_WARNINGS_FULL}" AS_IF([test "$ac_cv_safe_to_use_Wextra_" = "yes"], [BASE_WARNINGS="${BASE_WARNINGS} -Wextra"], [BASE_WARNINGS="${BASE_WARNINGS} -W"]) AC_CACHE_CHECK([whether it is safe to use -Wformat], [ac_cv_safe_to_use_wformat_], [save_CFLAGS="$CFLAGS" dnl Use -Werror here instead of ${W_FAIL} so that we don't spew dnl conversion warnings to all the tarball folks CFLAGS="-Wformat -Werror -pedantic ${AM_CFLAGS} ${CFLAGS}" AC_COMPILE_IFELSE( [AC_LANG_PROGRAM([[ #include #include #include void foo(); void foo() { uint64_t test_u= 0; printf("This is a %" PRIu64 "test\n", test_u); } ]],[[ foo(); ]])], [ac_cv_safe_to_use_wformat_=yes], [ac_cv_safe_to_use_wformat_=no]) CFLAGS="$save_CFLAGS"]) AS_IF([test "$ac_cv_safe_to_use_wformat_" = "yes"],[ BASE_WARNINGS="${BASE_WARNINGS} -Wformat -Wno-format-nonliteral -Wno-format-security" BASE_WARNINGS_FULL="${BASE_WARNINGS_FULL} -Wformat=2 -Wno-format-nonliteral -Wno-format-security" ],[ BASE_WARNINGS="${BASE_WARNINGS} -Wno-format" BASE_WARNINGS_FULL="${BASE_WARNINGS_FULL} -Wno-format" ]) AC_CACHE_CHECK([whether it is safe to use -Wconversion], [ac_cv_safe_to_use_wconversion_], [save_CFLAGS="$CFLAGS" dnl Use -Werror here instead of ${W_FAIL} so that we don't spew dnl conversion warnings to all the tarball folks CFLAGS="-Wconversion -Werror -pedantic ${AM_CFLAGS} ${CFLAGS}" AC_COMPILE_IFELSE( [AC_LANG_PROGRAM([[ #include void foo(bool a) { (void)a; } ]],[[ foo(0); ]])], [ac_cv_safe_to_use_wconversion_=yes], [ac_cv_safe_to_use_wconversion_=no]) CFLAGS="$save_CFLAGS"]) AS_IF([test "$ac_cv_safe_to_use_wconversion_" = "yes"], [W_CONVERSION="-Wconversion" AC_CACHE_CHECK([whether it is safe to use -Wconversion with htons], [ac_cv_safe_to_use_Wconversion_], [save_CFLAGS="$CFLAGS" dnl Use -Werror here instead of ${W_FAIL} so that we don't spew dnl conversion warnings to all the tarball folks CFLAGS="-Wconversion -Werror -pedantic ${AM_CFLAGS} ${CFLAGS}" AC_COMPILE_IFELSE( [AC_LANG_PROGRAM( [[ #include ]],[[ uint16_t x= htons(80); ]])], [ac_cv_safe_to_use_Wconversion_=yes], [ac_cv_safe_to_use_Wconversion_=no]) CFLAGS="$save_CFLAGS"]) AS_IF([test "$ac_cv_safe_to_use_Wconversion_" = "no"], [NO_CONVERSION="-Wno-conversion"]) ]) CC_WARNINGS="${BASE_WARNINGS} -Wstrict-prototypes -Wmissing-prototypes -Wredundant-decls -Wmissing-declarations -Wcast-align ${CC_WARNINGS_FULL}" CXX_WARNINGS="${BASE_WARNINGS} -Woverloaded-virtual -Wnon-virtual-dtor -Wctor-dtor-privacy -Wno-long-long ${CXX_WARNINGS_FULL}" AC_CACHE_CHECK([whether it is safe to use -Wmissing-declarations from C++], [ac_cv_safe_to_use_Wmissing_declarations_], [AC_LANG_PUSH(C++) save_CXXFLAGS="$CXXFLAGS" CXXFLAGS="-Werror -pedantic -Wmissing-declarations ${AM_CXXFLAGS}" AC_COMPILE_IFELSE([ AC_LANG_PROGRAM( [[ #include ]], [[]]) ], [ac_cv_safe_to_use_Wmissing_declarations_=yes], [ac_cv_safe_to_use_Wmissing_declarations_=no]) CXXFLAGS="$save_CXXFLAGS" AC_LANG_POP() ]) AS_IF([test "$ac_cv_safe_to_use_Wmissing_declarations_" = "yes"], [CXX_WARNINGS="${CXX_WARNINGS} -Wmissing-declarations"]) AC_CACHE_CHECK([whether it is safe to use -Wframe-larger-than], [ac_cv_safe_to_use_Wframe_larger_than_], [AC_LANG_PUSH(C++) save_CXXFLAGS="$CXXFLAGS" CXXFLAGS="-Werror -pedantic -Wframe-larger-than=32768 ${AM_CXXFLAGS}" AC_COMPILE_IFELSE([ AC_LANG_PROGRAM( [[ #include ]], [[]]) ], [ac_cv_safe_to_use_Wframe_larger_than_=yes], [ac_cv_safe_to_use_Wframe_larger_than_=no]) CXXFLAGS="$save_CXXFLAGS" AC_LANG_POP() ]) AS_IF([test "$ac_cv_safe_to_use_Wframe_larger_than_" = "yes"], [CXX_WARNINGS="${CXX_WARNINGS} -Wframe-larger-than=32768"]) AC_CACHE_CHECK([whether it is safe to use -Wlogical-op], [ac_cv_safe_to_use_Wlogical_op_], [save_CFLAGS="$CFLAGS" CFLAGS="${W_FAIL} -pedantic -Wlogical-op ${AM_CFLAGS} ${CFLAGS}" AC_COMPILE_IFELSE([ AC_LANG_PROGRAM( [[ #include ]], [[]]) ], [ac_cv_safe_to_use_Wlogical_op_=yes], [ac_cv_safe_to_use_Wlogical_op_=no]) CFLAGS="$save_CFLAGS"]) AS_IF([test "$ac_cv_safe_to_use_Wlogical_op_" = "yes"], [CC_WARNINGS="${CC_WARNINGS} -Wlogical-op"]) AC_CACHE_CHECK([whether it is safe to use -Wredundant-decls from C++], [ac_cv_safe_to_use_Wredundant_decls_], [AC_LANG_PUSH(C++) save_CXXFLAGS="${CXXFLAGS}" CXXFLAGS="${W_FAIL} -pedantic -Wredundant-decls ${AM_CXXFLAGS}" AC_COMPILE_IFELSE( [AC_LANG_PROGRAM([ template struct C { void foo(); }; template void C::foo() { } template <> void C::foo(); AC_INCLUDES_DEFAULT])], [ac_cv_safe_to_use_Wredundant_decls_=yes], [ac_cv_safe_to_use_Wredundant_decls_=no]) CXXFLAGS="${save_CXXFLAGS}" AC_LANG_POP()]) AS_IF([test "$ac_cv_safe_to_use_Wredundant_decls_" = "yes"], [CXX_WARNINGS="${CXX_WARNINGS} -Wredundant-decls"], [CXX_WARNINGS="${CXX_WARNINGS} -Wno-redundant-decls"]) AC_CACHE_CHECK([whether it is safe to use -Wattributes from C++], [ac_cv_safe_to_use_Wattributes_], [AC_LANG_PUSH(C++) save_CXXFLAGS="${CXXFLAGS}" CXXFLAGS="${W_FAIL} -pedantic -Wattributes -fvisibility=hidden ${AM_CXXFLAGS}" AC_COMPILE_IFELSE( [AC_LANG_PROGRAM([ #include #include const ::google::protobuf::EnumDescriptor* Table_TableOptions_RowType_descriptor(); enum Table_TableOptions_RowType { Table_TableOptions_RowType_ROW_TYPE_DEFAULT = 0, Table_TableOptions_RowType_ROW_TYPE_FIXED = 1, Table_TableOptions_RowType_ROW_TYPE_DYNAMIC = 2, Table_TableOptions_RowType_ROW_TYPE_COMPRESSED = 3, Table_TableOptions_RowType_ROW_TYPE_REDUNDANT = 4, Table_TableOptions_RowType_ROW_TYPE_COMPACT = 5, Table_TableOptions_RowType_ROW_TYPE_PAGE = 6 }; namespace google { namespace protobuf { template <> inline const EnumDescriptor* GetEnumDescriptor() { return Table_TableOptions_RowType_descriptor(); } } } ])], [ac_cv_safe_to_use_Wattributes_=yes], [ac_cv_safe_to_use_Wattributes_=no]) CXXFLAGS="${save_CXXFLAGS}" AC_LANG_POP()]) AC_CACHE_CHECK([whether it is safe to use -Wno-attributes], [ac_cv_safe_to_use_Wno_attributes_], [save_CFLAGS="$CFLAGS" CFLAGS="${W_FAIL} -pedantic -Wno_attributes_ ${AM_CFLAGS} ${CFLAGS}" AC_COMPILE_IFELSE([ AC_LANG_PROGRAM( [[ #include ]], [[]]) ], [ac_cv_safe_to_use_Wno_attributes_=yes], [ac_cv_safe_to_use_Wno_attributes_=no]) CFLAGS="$save_CFLAGS"]) dnl GCC 3.4 doesn't have -Wno-attributes, so we can't turn them off dnl by using that. AS_IF([test "$ac_cv_safe_to_use_Wattributes_" != "yes"],[ AS_IF([test "$ac_cv_safe_to_use_Wno_attributes_" = "yes"],[ CC_WARNINGS="${CC_WARNINGS} -Wno-attributes" NO_ATTRIBUTES="-Wno-attributes"])]) NO_REDUNDANT_DECLS="-Wno-redundant-decls" dnl TODO: Figure out a better way to deal with this: PROTOSKIP_WARNINGS="-Wno-effc++ -Wno-shadow -Wno-missing-braces ${NO_ATTRIBUTES}" NO_WERROR="-Wno-error" PERMISSIVE_WARNINGS="-Wno-error -Wno-unused-function -fpermissive" AS_IF([test "$host_vendor" = "apple"],[ BOOSTSKIP_WARNINGS="-Wno-uninitialized" ]) ]) ]) AS_IF([test "$SUNCC" = "yes"],[ AS_IF([test "$ac_profiling" = "yes"], [CC_PROFILING="-xinstrument=datarace"]) AS_IF([test "$ac_cv_warnings_as_errors" = "yes"], [W_FAIL="-errwarn=%all"]) AC_CACHE_CHECK([whether E_PASTE_RESULT_NOT_TOKEN is usable], [ac_cv_paste_result], [ save_CFLAGS="${CFLAGS}" CFLAGS="-errwarn=%all -erroff=E_PASTE_RESULT_NOT_TOKEN ${CFLAGS}" AC_COMPILE_IFELSE( [AC_LANG_PROGRAM([ AC_INCLUDES_DEFAULT ],[ int x= 0;])], [ac_cv_paste_result=yes], [ac_cv_paste_result=no]) CFLAGS="${save_CFLAGS}" ]) AS_IF([test $ac_cv_paste_result = yes], [W_PASTE_RESULT=",E_PASTE_RESULT_NOT_TOKEN"]) m4_if(PW_LESS_WARNINGS, [no],[ CC_WARNINGS_FULL="-erroff=E_STATEMENT_NOT_REACHED,E_INTEGER_OVERFLOW_DETECTED${W_PASTE_RESULT}" CXX_WARNINGS_FULL="-erroff=inllargeuse" ],[ CC_WARNINGS_FULL="-erroff=E_ATTRIBUTE_NOT_VAR,E_STATEMENT_NOT_REACHED" CXX_WARNINGS_FULL="-erroff=attrskipunsup,doubunder,reftotemp,inllargeuse,truncwarn1,signextwarn,inllargeint" ]) CC_WARNINGS="-v -errtags=yes ${W_FAIL} ${CC_WARNINGS_FULL}" CXX_WARNINGS="+w +w2 -xwe -xport64 -errtags=yes ${CXX_WARNINGS_FULL} ${W_FAIL}" PROTOSKIP_WARNINGS="-erroff=attrskipunsup,doubunder,reftotemp,wbadinitl,identexpected,inllargeuse,truncwarn1,signextwarn,partinit,notused,badargtype2w,wbadinit" BOOSTSKIP_WARNINGS="-erroff=attrskipunsup,doubunder,reftotemp,inllargeuse,truncwarn1,signextwarn,inllargeint,hidef,wvarhidenmem" PERMISSIVE_WARNINGS="-erroff=attrskipunsup,doubunder,reftotemp,inllargeuse,truncwarn1,signextwarn,inllargeint,hidef,wvarhidenmem,notused,badargtype2w,wunreachable" INNOBASE_SKIP_WARNINGS="-erroff=attrskipunsup,doubunder,reftotemp,wbadinitl,identexpected,inllargeuse,truncwarn1,signextwarn,partinit,notused,badargtype2w,wbadinit,wunreachable" NO_UNREACHED="-erroff=E_STATEMENT_NOT_REACHED" NO_WERROR="-errwarn=%none" ]) AC_SUBST(NO_CONVERSION) AC_SUBST(NO_REDUNDANT_DECLS) AC_SUBST(NO_UNREACHED) AC_SUBST(NO_SHADOW) AC_SUBST(NO_STRICT_ALIASING) AC_SUBST(NO_EFF_CXX) AC_SUBST(NO_OLD_STYLE_CAST) AC_SUBST(PROTOSKIP_WARNINGS) AC_SUBST(INNOBASE_SKIP_WARNINGS) AC_SUBST(BOOSTSKIP_WARNINGS) AC_SUBST(PERMISSIVE_WARNINGS) AC_SUBST(NO_WERROR) AC_SUBST([GCOV_LIBS]) ]) haildb-2.3.2/m4/pandora_use_pipe.m40000644000175000017500000000201511513177357017740 0ustar00pcrewspcrews00000000000000dnl -*- mode: m4; c-basic-offset: 2; indent-tabs-mode: nil; -*- dnl vim:expandtab:shiftwidth=2:tabstop=2:smarttab: dnl dnl pandora-build: A pedantic build system dnl Copyright (C) 2009 Sun Microsystems, Inc. dnl This file is free software; Sun Microsystem dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl dnl From Monty Taylor dnl dnl Test if we can Use -pipe to avoid making temp files during the compile. dnl Should speed up compile on slower disks AC_DEFUN([PANDORA_USE_PIPE],[ AS_IF([test "$GCC" = "yes"],[ AC_CACHE_CHECK([for working -pipe], [pandora_cv_use_pipe], [ AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ #include int main(int argc, char** argv) { (void) argc; (void) argv; return 0; } ]])], [pandora_cv_use_pipe=yes], [pandora_cv_use_pipe=no]) ]) AS_IF([test "$pandora_cv_use_pipe" = "yes"],[ AM_CFLAGS="-pipe ${AM_CFLAGS}" AM_CXXFLAGS="-pipe ${AM_CXXFLAGS}" ]) ]) ]) haildb-2.3.2/m4/pandora_header_stdcxx_98.m40000644000175000017500000000403711513177357021302 0ustar00pcrewspcrews00000000000000# =========================================================================== # http://autoconf-archive.cryp.to/ac_cxx_header_stdcxx_98.html # =========================================================================== # # SYNOPSIS # # AC_CXX_HEADER_STDCXX_98 # # DESCRIPTION # # Check for complete library coverage of the C++1998/2003 standard. # # LICENSE # # Copyright (C) 2008 Benjamin Kosnik # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. AC_DEFUN([AC_CXX_HEADER_STDCXX_98], [ AC_CACHE_CHECK(for ISO C++ 98 include files, ac_cv_cxx_stdcxx_98, [AC_LANG_SAVE AC_LANG_CPLUSPLUS AC_TRY_COMPILE([ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include ],, ac_cv_cxx_stdcxx_98=yes, ac_cv_cxx_stdcxx_98=no) AC_LANG_RESTORE ]) if test "$ac_cv_cxx_stdcxx_98" = yes; then AC_DEFINE(STDCXX_98_HEADERS,,[Define if ISO C++ 1998 header files are present. ]) fi ]) haildb-2.3.2/m4/pandora_have_libpqxx.m40000644000175000017500000000232011513177357020620 0ustar00pcrewspcrews00000000000000dnl Copyright (C) 2010 Padraig O'Sullivan dnl This file is free software; dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. AC_DEFUN([_PANDORA_SEARCH_LIBPQXX],[ AC_REQUIRE([AC_LIB_PREFIX]) dnl -------------------------------------------------------------------- dnl Check for libpqxx dnl -------------------------------------------------------------------- AC_ARG_ENABLE([libpqxx], [AS_HELP_STRING([--disable-libpqxx], [Build with libpqxx support @<:@default=on@:>@])], [ac_enable_libpqxx="$enableval"], [ac_enable_libpqxx="yes"]) AS_IF([test "x$ac_enable_libpqxx" = "xyes"],[ AC_LANG_PUSH([C++]) AC_LIB_HAVE_LINKFLAGS(pqxx,,[ #include ],[ pqxx::connection conn("dbname=test"); ]) AC_LANG_POP() ],[ ac_cv_libpqxx="no" ]) AM_CONDITIONAL(HAVE_LIBPQXX, [test "x${ac_cv_libpqxx}" = "xyes"]) ]) AC_DEFUN([PANDORA_HAVE_LIBPQXX],[ AC_REQUIRE([_PANDORA_SEARCH_LIBPQXX]) ]) AC_DEFUN([PANDORA_REQUIRE_LIBPQXX],[ AC_REQUIRE([PANDORA_HAVE_LIBPQXX]) AS_IF([test "x$ac_cv_libpqxx" = "xno"],[ AC_MSG_ERROR([libpqxx is required for ${PACKAGE}]) ]) ]) haildb-2.3.2/m4/pandora_cinttypes.m40000644000175000017500000000237011513177357020155 0ustar00pcrewspcrews00000000000000# We check two things: where the include file is for cinttypes. We # include AC_TRY_COMPILE for all the combinations we've seen in the # wild. We define one of HAVE_CINTTYPES or HAVE_TR1_CINTTYPES or # HAVE_BOOST_CINTTYPES depending # on location. AC_DEFUN([PANDORA_CXX_CINTTYPES], [AC_REQUIRE([PANDORA_CXX_CSTDINT]) AC_MSG_CHECKING(the location of cinttypes) AC_LANG_PUSH(C++) save_CXXFLAGS="${CXXFLAGS}" CXXFLAGS="${CXX_STANDARD} ${CXXFLAGS}" ac_cv_cxx_cinttypes="" for location in tr1/cinttypes boost/cinttypes cinttypes; do if test -z "$ac_cv_cxx_cinttypes"; then AC_TRY_COMPILE([#include $ac_cv_cxx_cstdint; #include <$location>], [uint32_t foo= UINT32_C(1)], [ac_cv_cxx_cinttypes="<$location>";]) fi done AC_LANG_POP() CXXFLAGS="${save_CXXFLAGS}" if test -n "$ac_cv_cxx_cinttypes"; then AC_MSG_RESULT([$ac_cv_cxx_cinttypes]) else ac_cv_cxx_cinttypes="" AC_MSG_RESULT() AC_MSG_WARN([Could not find a cinttypes header.]) fi AC_DEFINE([__STDC_LIMIT_MACROS],[1],[Use STDC Limit Macros in C++]) AC_DEFINE_UNQUOTED(CINTTYPES_H,$ac_cv_cxx_cinttypes, [the location of ]) ]) haildb-2.3.2/m4/pandora_have_libdrizzle.m40000644000175000017500000000366611513177357021321 0ustar00pcrewspcrews00000000000000dnl Copyright (C) 2009 Sun Microsystems, Inc. dnl This file is free software; Sun Microsystems, Inc. dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. AC_DEFUN([_PANDORA_SEARCH_LIBDRIZZLE],[ AC_REQUIRE([AC_LIB_PREFIX]) dnl -------------------------------------------------------------------- dnl Check for libdrizzle dnl -------------------------------------------------------------------- AC_ARG_ENABLE([libdrizzle], [AS_HELP_STRING([--disable-libdrizzle], [Build with libdrizzle support @<:@default=on@:>@])], [ac_enable_libdrizzle="$enableval"], [ac_enable_libdrizzle="yes"]) AS_IF([test "x$ac_enable_libdrizzle" = "xyes"],[ AC_LIB_HAVE_LINKFLAGS(drizzle,,[ #include ],[ drizzle_st drizzle; drizzle_version(); ]) ],[ ac_cv_libdrizzle="no" ]) AM_CONDITIONAL(HAVE_LIBDRIZZLE, [test "x${ac_cv_libdrizzle}" = "xyes"]) ]) AC_DEFUN([PANDORA_HAVE_LIBDRIZZLE],[ AC_REQUIRE([_PANDORA_SEARCH_LIBDRIZZLE]) ]) AC_DEFUN([PANDORA_REQUIRE_LIBDRIZZLE],[ AC_REQUIRE([PANDORA_HAVE_LIBDRIZZLE]) AS_IF([test "x${ac_cv_libdrizzle}" = "xno"],[ AC_MSG_ERROR([libdrizzle is required for ${PACKAGE}]) ],[ dnl We need at least 0.8 on Solaris non-sparc AS_IF([test "$target_cpu" != "sparc" -a "x${TARGET_SOLARIS}" = "xtrue"],[ PANDORA_LIBDRIZZLE_RECENT ]) ]) ]) AC_DEFUN([PANDORA_LIBDRIZZLE_RECENT],[ AC_CACHE_CHECK([if libdrizzle is recent enough], [pandora_cv_libdrizzle_recent], [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include drizzle_con_options_t foo= DRIZZLE_CON_EXPERIMENTAL; ]])], [pandora_cv_libdrizzle_recent=yes], [pandora_cv_libdrizzle_recent=no])]) AS_IF([test "$pandora_cv_libdrizzle_recent" = "no"],[ AC_MSG_ERROR([Your version of libdrizzle is too old. ${PACKAGE} requires at least version 0.8]) ]) ]) haildb-2.3.2/m4/lib-prefix.m40000644000175000017500000002030711513177357016470 0ustar00pcrewspcrews00000000000000# lib-prefix.m4 serial 6 (gettext-0.18) dnl Copyright (C) 2001-2005, 2008 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl From Bruno Haible. dnl AC_LIB_ARG_WITH is synonymous to AC_ARG_WITH in autoconf-2.13, and dnl similar to AC_ARG_WITH in autoconf 2.52...2.57 except that is doesn't dnl require excessive bracketing. ifdef([AC_HELP_STRING], [AC_DEFUN([AC_LIB_ARG_WITH], [AC_ARG_WITH([$1],[[$2]],[$3],[$4])])], [AC_DEFUN([AC_][LIB_ARG_WITH], [AC_ARG_WITH([$1],[$2],[$3],[$4])])]) dnl AC_LIB_PREFIX adds to the CPPFLAGS and LDFLAGS the flags that are needed dnl to access previously installed libraries. The basic assumption is that dnl a user will want packages to use other packages he previously installed dnl with the same --prefix option. dnl This macro is not needed if only AC_LIB_LINKFLAGS is used to locate dnl libraries, but is otherwise very convenient. AC_DEFUN([AC_LIB_PREFIX], [ AC_BEFORE([$0], [AC_LIB_LINKFLAGS]) AC_REQUIRE([AC_PROG_CC]) AC_REQUIRE([AC_CANONICAL_HOST]) AC_REQUIRE([AC_LIB_PREPARE_MULTILIB]) AC_REQUIRE([AC_LIB_PREPARE_PREFIX]) dnl By default, look in $includedir and $libdir. use_additional=yes AC_LIB_WITH_FINAL_PREFIX([ eval additional_includedir=\"$includedir\" eval additional_libdir=\"$libdir\" ]) AC_LIB_ARG_WITH([lib-prefix], [ --with-lib-prefix[=DIR] search for libraries in DIR/include and DIR/lib --without-lib-prefix don't search for libraries in includedir and libdir], [ if test "X$withval" = "Xno"; then use_additional=no else if test "X$withval" = "X"; then AC_LIB_WITH_FINAL_PREFIX([ eval additional_includedir=\"$includedir\" eval additional_libdir=\"$libdir\" ]) else additional_includedir="$withval/include" additional_libdir="$withval/$acl_libdirstem" fi fi ]) if test $use_additional = yes; then dnl Potentially add $additional_includedir to $CPPFLAGS. dnl But don't add it dnl 1. if it's the standard /usr/include, dnl 2. if it's already present in $CPPFLAGS, dnl 3. if it's /usr/local/include and we are using GCC on Linux, dnl 4. if it doesn't exist as a directory. if test "X$additional_includedir" != "X/usr/include"; then haveit= for x in $CPPFLAGS; do AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) if test "X$x" = "X-I$additional_includedir"; then haveit=yes break fi done if test -z "$haveit"; then if test "X$additional_includedir" = "X/usr/local/include"; then if test -n "$GCC"; then case $host_os in linux* | gnu* | k*bsd*-gnu) haveit=yes;; esac fi fi if test -z "$haveit"; then if test -d "$additional_includedir"; then dnl Really add $additional_includedir to $CPPFLAGS. CPPFLAGS="${CPPFLAGS}${CPPFLAGS:+ }-I$additional_includedir" fi fi fi fi dnl Potentially add $additional_libdir to $LDFLAGS. dnl But don't add it dnl 1. if it's the standard /usr/lib, dnl 2. if it's already present in $LDFLAGS, dnl 3. if it's /usr/local/lib and we are using GCC on Linux, dnl 4. if it doesn't exist as a directory. if test "X$additional_libdir" != "X/usr/$acl_libdirstem"; then haveit= for x in $LDFLAGS; do AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) if test "X$x" = "X-L$additional_libdir"; then haveit=yes break fi done if test -z "$haveit"; then if test "X$additional_libdir" = "X/usr/local/$acl_libdirstem"; then if test -n "$GCC"; then case $host_os in linux*) haveit=yes;; esac fi fi if test -z "$haveit"; then if test -d "$additional_libdir"; then dnl Really add $additional_libdir to $LDFLAGS. LDFLAGS="${LDFLAGS}${LDFLAGS:+ }-L$additional_libdir" fi fi fi fi fi ]) dnl AC_LIB_PREPARE_PREFIX creates variables acl_final_prefix, dnl acl_final_exec_prefix, containing the values to which $prefix and dnl $exec_prefix will expand at the end of the configure script. AC_DEFUN([AC_LIB_PREPARE_PREFIX], [ dnl Unfortunately, prefix and exec_prefix get only finally determined dnl at the end of configure. if test "X$prefix" = "XNONE"; then acl_final_prefix="$ac_default_prefix" else acl_final_prefix="$prefix" fi if test "X$exec_prefix" = "XNONE"; then acl_final_exec_prefix='${prefix}' else acl_final_exec_prefix="$exec_prefix" fi acl_save_prefix="$prefix" prefix="$acl_final_prefix" eval acl_final_exec_prefix=\"$acl_final_exec_prefix\" prefix="$acl_save_prefix" ]) dnl AC_LIB_WITH_FINAL_PREFIX([statement]) evaluates statement, with the dnl variables prefix and exec_prefix bound to the values they will have dnl at the end of the configure script. AC_DEFUN([AC_LIB_WITH_FINAL_PREFIX], [ acl_save_prefix="$prefix" prefix="$acl_final_prefix" acl_save_exec_prefix="$exec_prefix" exec_prefix="$acl_final_exec_prefix" $1 exec_prefix="$acl_save_exec_prefix" prefix="$acl_save_prefix" ]) dnl AC_LIB_PREPARE_MULTILIB creates dnl - a variable acl_libdirstem, containing the basename of the libdir, either dnl "lib" or "lib64" or "lib/64", dnl - a variable acl_libdirstem2, as a secondary possible value for dnl acl_libdirstem, either the same as acl_libdirstem or "lib/sparcv9" or dnl "lib/amd64". AC_DEFUN([AC_LIB_PREPARE_MULTILIB], [ dnl There is no formal standard regarding lib and lib64. dnl On glibc systems, the current practice is that on a system supporting dnl 32-bit and 64-bit instruction sets or ABIs, 64-bit libraries go under dnl $prefix/lib64 and 32-bit libraries go under $prefix/lib. We determine dnl the compiler's default mode by looking at the compiler's library search dnl path. If at least one of its elements ends in /lib64 or points to a dnl directory whose absolute pathname ends in /lib64, we assume a 64-bit ABI. dnl Otherwise we use the default, namely "lib". dnl On Solaris systems, the current practice is that on a system supporting dnl 32-bit and 64-bit instruction sets or ABIs, 64-bit libraries go under dnl $prefix/lib/64 (which is a symlink to either $prefix/lib/sparcv9 or dnl $prefix/lib/amd64) and 32-bit libraries go under $prefix/lib. AC_REQUIRE([AC_CANONICAL_HOST]) acl_libdirstem=lib acl_libdirstem2= case "$host_os" in solaris*) dnl See Solaris 10 Software Developer Collection > Solaris 64-bit Developer's Guide > The Development Environment dnl . dnl "Portable Makefiles should refer to any library directories using the 64 symbolic link." dnl But we want to recognize the sparcv9 or amd64 subdirectory also if the dnl symlink is missing, so we set acl_libdirstem2 too. AC_CACHE_CHECK([for 64-bit host], [gl_cv_solaris_64bit], [AC_RUN_IFELSE([ AC_LANG_PROGRAM([], [[ return sizeof(void*) == 8 ? 0 : 1; ]]) ], [gl_cv_solaris_64bit=yes], [gl_cv_solaris_64bit=no]) ]) if test $gl_cv_solaris_64bit = yes; then acl_libdirstem=lib/64 case "$host_cpu" in sparc*) acl_libdirstem2=lib/sparcv9 ;; i*86 | x86_64) acl_libdirstem2=lib/amd64 ;; esac fi ;; *) searchpath=`(LC_ALL=C $CC -print-search-dirs) 2>/dev/null | sed -n -e 's,^libraries: ,,p' | sed -e 's,^=,,'` if test -n "$searchpath"; then acl_save_IFS="${IFS= }"; IFS=":" for searchdir in $searchpath; do if test -d "$searchdir"; then case "$searchdir" in */lib64/ | */lib64 ) acl_libdirstem=lib64 ;; *) searchdir=`cd "$searchdir" && pwd` case "$searchdir" in */lib64 ) acl_libdirstem=lib64 ;; esac ;; esac fi done IFS="$acl_save_IFS" fi ;; esac test -n "$acl_libdirstem2" || acl_libdirstem2="$acl_libdirstem" ]) haildb-2.3.2/m4/pandora_sasl.m40000644000175000017500000000750211513177357017077 0ustar00pcrewspcrews00000000000000dnl Copyright (C) 2009 Sun Microsystems, Inc. dnl This file is free software; Sun Microsystems, Inc. dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. AC_DEFUN([_PANDORA_SEARCH_SASL],[ AC_REQUIRE([AC_LIB_PREFIX]) dnl -------------------------------------------------------------------- dnl Check for sasl dnl -------------------------------------------------------------------- AC_ARG_ENABLE([sasl], [AS_HELP_STRING([--disable-sasl], [Build with sasl support @<:@default=on@:>@])], [ac_enable_sasl="$enableval"], [ac_enable_sasl="yes"]) AS_IF([test "x$ac_enable_sasl" = "xyes"], [ AC_LIB_HAVE_LINKFLAGS(sasl,,[ #include #include ],[ sasl_server_init(NULL, NULL); ]) AS_IF([test "x${ac_cv_libsasl}" != "xyes" ], [ AC_LIB_HAVE_LINKFLAGS(sasl2,,[ #include #include ],[ sasl_server_init(NULL, NULL); ]) HAVE_LIBSASL="$HAVE_LIBSASL2" LIBSASL="$LIBSASL2" LIBSASL_PREFIX="$LIBSASL2_PREFIX" LTLIBSASL="$LT_LIBSASL2" ]) ]) AS_IF([test "x${ac_cv_libsasl}" = "xyes" -o "x${ac_cv_libsasl2}" = "xyes"], [ac_cv_sasl=yes], [ac_cv_sasl=no]) AM_CONDITIONAL(HAVE_LIBSASL, [test "x${ac_cv_libsasl}" = "xyes"]) AM_CONDITIONAL(HAVE_LIBSASL2, [test "x${ac_cv_libsasl2}" = "xyes"]) AM_CONDITIONAL(HAVE_SASL, [test "x${ac_cv_sasl}" = "xyes"]) ]) AC_DEFUN([PANDORA_HAVE_SASL],[ AC_REQUIRE([_PANDORA_SEARCH_SASL]) ]) AC_DEFUN([PANDORA_REQUIRE_SASL],[ AC_REQUIRE([_PANDORA_SEARCH_SASL]) AS_IF([test "x${ac_cv_sasl}" = "xno"], AC_MSG_ERROR([SASL (libsasl or libsasl2) is required for ${PACKAGE}])) ]) AC_DEFUN([_PANDORA_SEARCH_LIBSASL],[ AC_REQUIRE([AC_LIB_PREFIX]) dnl -------------------------------------------------------------------- dnl Check for libsasl dnl -------------------------------------------------------------------- AC_ARG_ENABLE([libsasl], [AS_HELP_STRING([--disable-libsasl], [Build with libsasl support @<:@default=on@:>@])], [ac_enable_libsasl="$enableval"], [ac_enable_libsasl="yes"]) AS_IF([test "x$ac_enable_libsasl" = "xyes"],[ AC_LIB_HAVE_LINKFLAGS(sasl,,[ #include #include ],[ sasl_server_init(NULL, NULL); ]) ],[ ac_cv_libsasl="no" ]) AM_CONDITIONAL(HAVE_LIBSASL, [test "x${ac_cv_libsasl}" = "xyes"]) ]) AC_DEFUN([PANDORA_HAVE_LIBSASL],[ AC_REQUIRE([_PANDORA_SEARCH_LIBSASL]) ]) AC_DEFUN([PANDORA_REQUIRE_LIBSASL],[ AC_REQUIRE([_PANDORA_SEARCH_LIBSASL]) AS_IF([test "x${ac_cv_libsasl}" = "xno"], AC_MSG_ERROR([libsasl is required for ${PACKAGE}])) ]) AC_DEFUN([_PANDORA_SEARCH_LIBSASL2],[ AC_REQUIRE([AC_LIB_PREFIX]) dnl -------------------------------------------------------------------- dnl Check for libsasl2 dnl -------------------------------------------------------------------- AC_ARG_ENABLE([libsasl2], [AS_HELP_STRING([--disable-libsasl2], [Build with libsasl2 support @<:@default=on@:>@])], [ac_enable_libsasl2="$enableval"], [ac_enable_libsasl2="yes"]) AS_IF([test "x$ac_enable_libsasl2" = "xyes"],[ AC_LIB_HAVE_LINKFLAGS(sasl2,,[ #include #include ],[ sasl2_server_init(NULL, NULL); ]) ],[ ac_cv_libsasl2="no" ]) AM_CONDITIONAL(HAVE_LIBSASL2, [test "x${ac_cv_libsasl2}" = "xyes"]) ]) AC_DEFUN([PANDORA_HAVE_LIBSASL2],[ AC_REQUIRE([_PANDORA_SEARCH_LIBSASL2]) ]) AC_DEFUN([PANDORA_REQUIRE_LIBSASL2],[ AC_REQUIRE([_PANDORA_SEARCH_LIBSASL2]) AS_IF([test "x${ac_cv_libsasl2}" = "xno"], AC_MSG_ERROR([libsasl2 is required for ${PACKAGE}])) ]) haildb-2.3.2/m4/progtest.m40000644000175000017500000000557311513177357016306 0ustar00pcrewspcrews00000000000000# progtest.m4 serial 6 (gettext-0.18) dnl Copyright (C) 1996-2003, 2005, 2008-2010 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl dnl This file can can be used in projects which are not available under dnl the GNU General Public License or the GNU Library General Public dnl License but which still want to provide support for the GNU gettext dnl functionality. dnl Please note that the actual code of the GNU gettext library is covered dnl by the GNU Library General Public License, and the rest of the GNU dnl gettext package package is covered by the GNU General Public License. dnl They are *not* in the public domain. dnl Authors: dnl Ulrich Drepper , 1996. AC_PREREQ([2.50]) # Search path for a program which passes the given test. dnl AM_PATH_PROG_WITH_TEST(VARIABLE, PROG-TO-CHECK-FOR, dnl TEST-PERFORMED-ON-FOUND_PROGRAM [, VALUE-IF-NOT-FOUND [, PATH]]) AC_DEFUN([AM_PATH_PROG_WITH_TEST], [ # Prepare PATH_SEPARATOR. # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then echo "#! /bin/sh" >conf$$.sh echo "exit 0" >>conf$$.sh chmod +x conf$$.sh if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then PATH_SEPARATOR=';' else PATH_SEPARATOR=: fi rm -f conf$$.sh fi # Find out how to test for executable files. Don't use a zero-byte file, # as systems may use methods other than mode bits to determine executability. cat >conf$$.file <<_ASEOF #! /bin/sh exit 0 _ASEOF chmod +x conf$$.file if test -x conf$$.file >/dev/null 2>&1; then ac_executable_p="test -x" else ac_executable_p="test -f" fi rm -f conf$$.file # Extract the first word of "$2", so it can be a program name with args. set dummy $2; ac_word=[$]2 AC_MSG_CHECKING([for $ac_word]) AC_CACHE_VAL([ac_cv_path_$1], [case "[$]$1" in [[\\/]]* | ?:[[\\/]]*) ac_cv_path_$1="[$]$1" # Let the user override the test with a path. ;; *) ac_save_IFS="$IFS"; IFS=$PATH_SEPARATOR for ac_dir in ifelse([$5], , $PATH, [$5]); do IFS="$ac_save_IFS" test -z "$ac_dir" && ac_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $ac_executable_p "$ac_dir/$ac_word$ac_exec_ext"; then echo "$as_me: trying $ac_dir/$ac_word..." >&AS_MESSAGE_LOG_FD if [$3]; then ac_cv_path_$1="$ac_dir/$ac_word$ac_exec_ext" break 2 fi fi done done IFS="$ac_save_IFS" dnl If no 4th arg is given, leave the cache variable unset, dnl so AC_PATH_PROGS will keep looking. ifelse([$4], , , [ test -z "[$]ac_cv_path_$1" && ac_cv_path_$1="$4" ])dnl ;; esac])dnl $1="$ac_cv_path_$1" if test ifelse([$4], , [-n "[$]$1"], ["[$]$1" != "$4"]); then AC_MSG_RESULT([$][$1]) else AC_MSG_RESULT([no]) fi AC_SUBST([$1])dnl ]) haildb-2.3.2/m4/pandora_have_libndbclient.m40000644000175000017500000000471711513177357021576 0ustar00pcrewspcrews00000000000000dnl -*- mode: m4; c-basic-offset: 2; indent-tabs-mode: nil; -*- dnl vim:expandtab:shiftwidth=2:tabstop=2:smarttab: dnl dnl Copyright (C) 2010 Monty Taylor dnl This file is free software; Sun Microsystems dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl AC_DEFUN([_PANDORA_SEARCH_LIBNDBCLIENT],[ AC_REQUIRE([AC_LIB_PREFIX]) AC_REQUIRE([PANDORA_WITH_MYSQL]) AC_ARG_ENABLE([libndbclient], [AS_HELP_STRING([--disable-libndbclient], [Build with libndbclient support @<:@default=on@:>@])], [ac_enable_libndbclient="$enableval"], [ac_enable_libndbclient="yes"]) AC_ARG_WITH([libndbclient-prefix], [AS_HELP_STRING([--with-libndbclient-prefix], [search for libndbclient in DIR])], [ac_with_libndbclient=${withval}], [ac_with_libndbclient=${pandora_cv_mysql_base}]) save_LIBS="${LIBS}" LIBS="" save_CPPFLAGS="${CPPFLAGS}" AS_IF([test "x${ac_with_libndbclient}" != "x"],[ LIBS="-L${ac_with_libndbclient}/lib/mysql -L${ac_with_libndbclient}/lib" AS_IF([test "$GCC" = "yes"],[ ndb_include_prefix="-isystem " ],[ ndb_include_prefix="-I" ]) CPPFLAGS="${CPPFLAGS} ${ndb_include_prefix}${ac_with_libndbclient}/include ${ndb_include_prefix}${ac_with_libndbclient}/include/mysql ${ndb_include_prefix}${ac_with_libndbclient}/include/mysql/storage/ndb ${ndb_include_prefix}${ac_with_libndbclient}/include/mysql/storage/ndb/ndbapi ${ndb_include_prefix}${ac_with_libndbclient}/include/mysql/storage/ndb/mgmapi" ]) LIBS="${LIBS} -lndbclient -lmysqlclient_r" AC_CACHE_CHECK([if NdbApi works],[ac_cv_libndbclient],[ AC_LANG_PUSH(C++) AC_LINK_IFELSE([ AC_LANG_PROGRAM([[ #include ]],[[ Ndb *ndb; ndb_init(); ]]) ],[ ac_cv_libndbclient=yes ],[ ac_cv_libndbclient=no ]) ]) AC_LANG_POP() LIBNDBCLIENT="${LIBS}" LTLIBNDBCLIENT="${LIBS}" AC_SUBST([LIBNDBCLIENT]) AC_SUBST([LTLIBNDBCLIENT]) AS_IF([test "x${ac_cv_libndbclient}" = "xno"],[ CPPFLAGS="${save_CPPFLAGS}" ]) LIBS="${save_LIBS}" AM_CONDITIONAL(HAVE_LIBNDBCLIENT, [test "x${ac_cv_libndbclient}" = "xyes"]) ]) AC_DEFUN([PANDORA_HAVE_LIBNDBCLIENT],[ AC_REQUIRE([_PANDORA_SEARCH_LIBNDBCLIENT]) ]) AC_DEFUN([PANDORA_REQUIRE_LIBNDBCLIENT],[ AC_REQUIRE([PANDORA_HAVE_LIBNDBCLIENT]) AS_IF([test "x${ac_cv_libndbclient}" = "xno"], AC_MSG_ERROR([libndbclient is required for ${PACKAGE}])) ]) haildb-2.3.2/m4/pandora_have_libuuid.m40000644000175000017500000000273411513177357020577 0ustar00pcrewspcrews00000000000000dnl Copyright (C) 2009 Sun Microsystems, Inc. dnl This file is free software; Sun Microsystems, Inc. dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. #-------------------------------------------------------------------- # Check for libuuid #-------------------------------------------------------------------- AC_DEFUN([_PANDORA_SEARCH_LIBUUID],[ AC_REQUIRE([AC_LIB_PREFIX]) dnl Do this by hand. Need to check for uuid/uuid.h, but uuid may or may dnl not be a lib is weird. AC_CHECK_HEADERS(uuid/uuid.h) AC_LIB_HAVE_LINKFLAGS(uuid,, [ #include ], [ uuid_t uout; uuid_generate(uout); ]) AM_CONDITIONAL(HAVE_LIBUUID, [test "x${ac_cv_libuuid}" = "xyes"]) ]) AC_DEFUN([_PANDORA_HAVE_LIBUUID],[ AC_ARG_ENABLE([libuuid], [AS_HELP_STRING([--disable-libuuid], [Build with libuuid support @<:@default=on@:>@])], [ac_enable_libuuid="$enableval"], [ac_enable_libuuid="yes"]) _PANDORA_SEARCH_LIBUUID ]) AC_DEFUN([PANDORA_HAVE_LIBUUID],[ AC_REQUIRE([_PANDORA_HAVE_LIBUUID]) ]) AC_DEFUN([_PANDORA_REQUIRE_LIBUUID],[ ac_enable_libuuid="yes" _PANDORA_SEARCH_LIBUUID AS_IF([test "x$ac_cv_header_uuid_uuid_h" = "xno"],[ AC_MSG_ERROR([Couldn't find uuid/uuid.h. On Debian this can be found in uuid-dev. On Redhat this can be found in e2fsprogs-devel.]) ]) ]) AC_DEFUN([PANDORA_REQUIRE_LIBUUID],[ AC_REQUIRE([_PANDORA_REQUIRE_LIBUUID]) ]) haildb-2.3.2/m4/pandora_64bit.m40000644000175000017500000000400411513177357017057 0ustar00pcrewspcrews00000000000000dnl Copyright (C) 2009 Sun Microsystems, Inc. dnl This file is free software; Sun Microsystems, Inc. dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl --------------------------------------------------------------------------- dnl Macro: PANDORA_64BIT dnl --------------------------------------------------------------------------- AC_DEFUN([PANDORA_64BIT],[ AC_BEFORE([$0], [AC_LIB_PREFIX]) AC_ARG_ENABLE([64bit], [AS_HELP_STRING([--disable-64bit], [Build 64 bit binary @<:@default=on@:>@])], [ac_enable_64bit="$enableval"], [ac_enable_64bit="yes"]) AC_CHECK_PROGS(ISAINFO, [isainfo], [no]) AS_IF([test "x$ISAINFO" != "xno"], [isainfo_b=`${ISAINFO} -b`], [isainfo_b="x"]) AS_IF([test "$isainfo_b" != "x"],[ isainfo_k=`${ISAINFO} -k` DTRACEFLAGS="${DTRACEFLAGS} -${isainfo_b}" AS_IF([test "x$ac_enable_64bit" = "xyes"],[ AS_IF([test "x${ac_cv_env_LDFLAGS_set}" = "x"],[ LDFLAGS="-L/usr/local/lib/${isainfo_k} ${LDFLAGS}" ]) AS_IF([test "x$libdir" = "x\${exec_prefix}/lib"],[ dnl The user hasn't overridden the default libdir, so we'll dnl the dir suffix to match solaris 32/64-bit policy libdir="${libdir}/${isainfo_k}" ]) AS_IF([test "x${ac_cv_env_CFLAGS_set}" = "x"],[ CFLAGS="${CFLAGS} -m64" ac_cv_env_CFLAGS_set=set ac_cv_env_CFLAGS_value='-m64' ]) AS_IF([test "x${ac_cv_env_CXXFLAGS_set}" = "x"],[ CXXFLAGS="${CXXFLAGS} -m64" ac_cv_env_CXXFLAGS_set=set ac_cv_env_CXXFLAGS_value='-m64' ]) AS_IF([test "$target_cpu" = "sparc" -a "x$SUNCC" = "xyes"],[ AM_CFLAGS="-xmemalign=8s ${AM_CFLAGS}" AM_CXXFLAGS="-xmemalign=8s ${AM_CXXFLAGS}" ]) ]) ]) ]) dnl --------------------------------------------------------------------------- dnl End Macro: PANDORA_64BIT dnl --------------------------------------------------------------------------- haildb-2.3.2/m4/pandora_have_libreadline.m40000644000175000017500000001561511513177357021416 0ustar00pcrewspcrews00000000000000# # SYNOPSIS # # PANDORA_HAVE_LIBREADLINE # # DESCRIPTION # # Searches for a readline compatible library. If found, defines # `HAVE_LIBREADLINE'. If the found library has the `add_history' # function, sets also `HAVE_READLINE_HISTORY'. Also checks for the # locations of the necessary include files and sets `HAVE_READLINE_H' # or `HAVE_READLINE_READLINE_H' and `HAVE_READLINE_HISTORY_H' or # 'HAVE_HISTORY_H' if the corresponding include files exists. # # The libraries that may be readline compatible are `libedit', # `libeditline' and `libreadline'. Sometimes we need to link a # termcap library for readline to work, this macro tests these cases # too by trying to link with `libtermcap', `libcurses' or # `libncurses' before giving up. # # Here is an example of how to use the information provided by this # macro to perform the necessary includes or declarations in a C # file: # # #ifdef HAVE_LIBREADLINE # # if defined(HAVE_READLINE_READLINE_H) # # include # # elif defined(HAVE_READLINE_H) # # include # # else /* !defined(HAVE_READLINE_H) */ # extern char *readline (); # # endif /* !defined(HAVE_READLINE_H) */ # char *cmdline = NULL; # #else /* !defined(HAVE_READLINE_READLINE_H) */ # /* no readline */ # #endif /* HAVE_LIBREADLINE */ # # #ifdef HAVE_READLINE_HISTORY # # if defined(HAVE_READLINE_HISTORY_H) # # include # # elif defined(HAVE_HISTORY_H) # # include # # else /* !defined(HAVE_HISTORY_H) */ # extern void add_history (); # extern int write_history (); # extern int read_history (); # # endif /* defined(HAVE_READLINE_HISTORY_H) */ # /* no history */ # #endif /* HAVE_READLINE_HISTORY */ # # LAST MODIFICATION # # 2009-11-17 # # Based on VL_LIB_READLINE from Ville Laurikari # # COPYLEFT # # Copyright (C) 2009 Monty Taylor # Copyright (C) 2002 Ville Laurikari # # Copying and distribution of this file, with or without # modification, are permitted in any medium without royalty provided # the copyright notice and this notice are preserved. AC_DEFUN([PANDORA_CHECK_TIOCGWINSZ],[ AC_CACHE_CHECK([for TIOCGWINSZ in sys/ioctl.h], [pandora_cv_tiocgwinsz_in_ioctl],[ AC_COMPILE_IFELSE([ AC_LANG_PROGRAM([[ #include #include ]],[[ int x= TIOCGWINSZ; ]]) ],[ pandora_cv_tiocgwinsz_in_ioctl=yes ],[ pandora_cv_tiocgwinsz_in_ioctl=no ]) ]) AS_IF([test "$pandora_cv_tiocgwinsz_in_ioctl" = "yes"],[ AC_DEFINE([GWINSZ_IN_SYS_IOCTL], [1], [READLINE: your system defines TIOCGWINSZ in sys/ioctl.h.]) ]) ]) AC_DEFUN([PANDORA_CHECK_RL_COMPENTRY], [ AC_CACHE_CHECK([defined rl_compentry_func_t], [pandora_cv_rl_compentry],[ AC_COMPILE_IFELSE([ AC_LANG_PROGRAM([[ #include "stdio.h" #include "readline/readline.h" ]],[[ rl_compentry_func_t *func2= (rl_compentry_func_t*)0; ]]) ],[ pandora_cv_rl_compentry=yes ],[ pandora_cv_rl_compentry=no ]) ]) AS_IF([test "$pandora_cv_rl_compentry" = "yes"],[ AC_DEFINE([HAVE_RL_COMPENTRY], [1], [Does system provide rl_compentry_func_t]) ]) save_CXXFLAGS="${CXXFLAGS}" CXXFLAGS="${AM_CXXFLAGS} ${CXXFLAGS}" AC_LANG_PUSH(C++) AC_CACHE_CHECK([rl_compentry_func_t works], [pandora_cv_rl_compentry_works],[ AC_COMPILE_IFELSE([ AC_LANG_PROGRAM([[ #include "stdio.h" #include "readline/readline.h" ]],[[ rl_completion_entry_function= (rl_compentry_func_t*)NULL; ]]) ],[ pandora_cv_rl_compentry_works=yes ],[ pandora_cv_rl_compentry_works=no ]) ]) AS_IF([test "$pandora_cv_rl_compentry_works" = "yes"],[ AC_DEFINE([HAVE_WORKING_RL_COMPENTRY], [1], [Does system provide an rl_compentry_func_t that is usable]) ]) CXXFLAGS="${save_CXXFLAGS}" AC_LANG_POP() ]) AC_DEFUN([PANDORA_CHECK_RL_COMPLETION_FUNC], [ AC_CACHE_CHECK([defined rl_completion_func_t], [pandora_cv_rl_completion],[ AC_COMPILE_IFELSE([ AC_LANG_PROGRAM([[ #include "stdio.h" #include "readline/readline.h" ]],[[ rl_completion_func_t *func1= (rl_completion_func_t*)0; ]]) ],[ pandora_cv_rl_completion=yes ],[ pandora_cv_rl_completion=no ]) ]) AS_IF([test "$pandora_cv_rl_completion" = "yes"],[ AC_DEFINE([HAVE_RL_COMPLETION], [1], [Does system provide rl_completion_func_t]) ]) ]) AC_DEFUN([_PANDORA_SEARCH_LIBREADLINE], [ save_LIBS="${LIBS}" LIBS="" AC_CACHE_CHECK([for a readline compatible library], ac_cv_libreadline, [ ORIG_LIBS="$LIBS" for readline_lib in readline edit editline; do for termcap_lib in "" termcap curses ncurses; do if test -z "$termcap_lib"; then TRY_LIB="-l$readline_lib" else TRY_LIB="-l$readline_lib -l$termcap_lib" fi LIBS="$ORIG_LIBS $TRY_LIB" AC_TRY_LINK_FUNC(readline, ac_cv_libreadline="$TRY_LIB") if test -n "$ac_cv_libreadline"; then break fi done if test -n "$ac_cv_libreadline"; then break fi done if test -z "$ac_cv_libreadline"; then ac_cv_libreadline="no" LIBS="$ORIG_LIBS" fi ]) if test "$ac_cv_libreadline" != "no"; then AC_DEFINE(HAVE_LIBREADLINE, 1, [Define if you have a readline compatible library]) AC_CHECK_HEADERS(readline.h readline/readline.h) AC_CACHE_CHECK([whether readline supports history], ac_cv_libreadline_history, [ ac_cv_libreadline_history="no" AC_TRY_LINK_FUNC(add_history, ac_cv_libreadline_history="yes") ]) if test "$ac_cv_libreadline_history" = "yes"; then AC_DEFINE(HAVE_READLINE_HISTORY, 1, [Define if your readline library has \`add_history']) AC_CHECK_HEADERS(history.h readline/history.h) fi fi PANDORA_CHECK_RL_COMPENTRY PANDORA_CHECK_RL_COMPLETION_FUNC PANDORA_CHECK_TIOCGWINSZ READLINE_LIBS="${LIBS}" LIBS="${save_LIBS}" AC_SUBST(READLINE_LIBS) AM_CONDITIONAL(HAVE_LIBREADLINE, [test "x${ac_cv_libreadline}" = "xyes"]) ]) AC_DEFUN([_PANDORA_HAVE_LIBREADLINE],[ AC_ARG_ENABLE([libreadline], [AS_HELP_STRING([--disable-libreadline], [Build with libreadline support @<:@default=on@:>@])], [ac_enable_libreadline="$enableval"], [ac_enable_libreadline="yes"]) _PANDORA_SEARCH_LIBREADLINE ]) AC_DEFUN([PANDORA_HAVE_LIBREADLINE],[ AC_REQUIRE([_PANDORA_HAVE_LIBREADLINE]) ]) AC_DEFUN([_PANDORA_REQUIRE_LIBREADLINE],[ ac_enable_libreadline="yes" _PANDORA_SEARCH_LIBREADLINE AS_IF([test "x$ac_cv_libreadline" = "xno"], AC_MSG_ERROR([libreadline is required for ${PACKAGE}. On Debian this can be found in libreadline5-dev. On RedHat this can be found in readline-devel.])) ]) AC_DEFUN([PANDORA_REQUIRE_LIBREADLINE],[ AC_REQUIRE([_PANDORA_REQUIRE_LIBREADLINE]) ]) haildb-2.3.2/m4/pandora_have_libxml2.m40000644000175000017500000000313011513177357020502 0ustar00pcrewspcrews00000000000000dnl Copyright (C) 2009 Sun Microsystems, Inc. dnl This file is free software; Sun Microsystems, Inc. dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl Provides support for finding libxml2. dnl LIBXML2_CFLAGS will be set, in addition to LIBXML2 and LTLIBXML2 AC_DEFUN([_PANDORA_SEARCH_LIBXML2],[ AC_REQUIRE([AC_LIB_PREFIX]) dnl -------------------------------------------------------------------- dnl Check for libxml2 dnl -------------------------------------------------------------------- AC_ARG_ENABLE([libxml2], [AS_HELP_STRING([--disable-libxml2], [Build with libxml2 support @<:@default=on@:>@])], [ac_enable_libxml2="$enableval"], [ac_enable_libxml2="yes"]) AS_IF([test "x$ac_enable_libxml2" = "xyes"],[ AC_LIB_HAVE_LINKFLAGS(xml2,,[ #include ],[ const char *test= LIBXML_DOTTED_VERSION; ]) ],[ ac_cv_libxml2="no" ]) AS_IF([test "${ac_cv_libxml2}" = "no" -a "${ac_enable_libxml2}" = "yes"],[ PKG_CHECK_MODULES([LIBXML2], [libxml-2.0], [ ac_cv_libxml2=yes LTLIBXML2=${LIBXML2_LIBS} LIBXML2=${LIBXML2_LIBS} ],[]) ]) AM_CONDITIONAL(HAVE_LIBXML2, [test "${ac_cv_libxml2}" = "yes"]) ]) AC_DEFUN([PANDORA_HAVE_LIBXML2],[ AC_REQUIRE([_PANDORA_SEARCH_LIBXML2]) ]) AC_DEFUN([PANDORA_REQUIRE_LIBXML2],[ AC_REQUIRE([_PANDORA_SEARCH_LIBXML2]) AS_IF([test "x${ac_cv_libxml2}" = "xno"], AC_MSG_ERROR([libxml2 is required for ${PACKAGE}. On Debian systems this is found in libxml2-dev. On RedHat, libxml2-devel.])) ]) haildb-2.3.2/m4/pandora_bison.m40000644000175000017500000000227211513177357017246 0ustar00pcrewspcrews00000000000000dnl Copyright (C) 2010 Monty Taylor dnl Copyright (C) 2010 Hartmut Holzgraefe dnl This file is free software; Monty Taylor and Hartmut Holzgraefe dnl give unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. AC_DEFUN([_PANDORA_SEARCH_BISON],[ dnl -------------------------------------------------------------------- dnl Check for bison dnl -------------------------------------------------------------------- AC_CHECK_PROGS([YACC], ['bison -y'], [:]) AS_IF([test "x$YACC" = "x:"],[ pandora_have_bison=no YACC='if test -f "$@"; then echo "WARNING: no proper bison binary found, ignoring changes to $<"; exit 0; else echo "ERROR: no proper bison binary found"; exit 1; fi;' ],[ pandora_have_bison=yes ]) AM_CONDITIONAL(HAVE_BISON, [test "x${pandora_have_bison}" = "xyes"]) ]) AC_DEFUN([PANDORA_HAVE_BISON],[ AC_REQUIRE([_PANDORA_SEARCH_BISON]) ]) AC_DEFUN([PANDORA_REQUIRE_BISON],[ AC_REQUIRE([PANDORA_HAVE_BISON]) AS_IF([test "x${pandora_have_bison}" = "xno" -a "$pandora_building_from_bzr" = "yes"], AC_MSG_ERROR(["bison is required for ${PACKAGE} to build from a bzr branch"]) ) ]) haildb-2.3.2/m4/pandora_have_libpcre.m40000644000175000017500000000352711513177357020563 0ustar00pcrewspcrews00000000000000dnl Copyright (C) 2009 Sun Microsystems, Inc. dnl This file is free software; Sun Microsystems, Inc. dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. #-------------------------------------------------------------------- # Check for libpcre #-------------------------------------------------------------------- AC_DEFUN([_PANDORA_SEARCH_LIBPCRE],[ AC_REQUIRE([AC_LIB_PREFIX]) AC_LIB_HAVE_LINKFLAGS(pcre,, [#include ], [ pcre *re= NULL; pcre_version(); ]) AS_IF([test "x$ac_cv_libpcre" = "xno"], [ unset ac_cv_libpcre unset HAVE_LIBPCRE unset LIBPCRE unset LIBPCRE_PREFIX unset LTLIBPCRE AC_LIB_HAVE_LINKFLAGS(pcre,, [#include ], [ pcre *re= NULL; pcre_version(); ]) AS_IF([test "x$ac_cv_libpcre" = "xyes"], [ ac_cv_pcre_location="" ]) ],[ ac_cv_pcre_location="" ]) AM_CONDITIONAL(HAVE_LIBPCRE, [test "x${ac_cv_libpcre}" = "xyes"]) ]) AC_DEFUN([_PANDORA_HAVE_LIBPCRE],[ AC_ARG_ENABLE([libpcre], [AS_HELP_STRING([--disable-libpcre], [Build with libpcre support @<:@default=on@:>@])], [ac_enable_libpcre="$enableval"], [ac_enable_libpcre="yes"]) _PANDORA_SEARCH_LIBPCRE ]) AC_DEFUN([PANDORA_HAVE_LIBPCRE],[ AC_REQUIRE([_PANDORA_HAVE_LIBPCRE]) ]) AC_DEFUN([_PANDORA_REQUIRE_LIBPCRE],[ ac_enable_libpcre="yes" _PANDORA_SEARCH_LIBPCRE AS_IF([test x$ac_cv_libpcre = xno],[ AC_MSG_ERROR([libpcre is required for ${PACKAGE}. On Debian this can be found in libpcre3-dev. On RedHat this can be found in pcre-devel.]) ],[ AC_DEFINE_UNQUOTED(PCRE_HEADER,[${ac_cv_pcre_location}], [Location of pcre header]) ]) ]) AC_DEFUN([PANDORA_REQUIRE_LIBPCRE],[ AC_REQUIRE([_PANDORA_REQUIRE_LIBPCRE]) ]) haildb-2.3.2/m4/pandora_check_cxx_standard.m40000644000175000017500000000150011513177357021744 0ustar00pcrewspcrews00000000000000dnl Copyright (C) 2009 Sun Microsystems, Inc. dnl This file is free software; Sun Microsystems, Inc. dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. AC_DEFUN([PANDORA_CHECK_CXX_STANDARD],[ dnl AC_REQUIRE([AC_CXX_COMPILE_STDCXX_0X]) AS_IF([test "$GCC" = "yes"], [AS_IF([test "$ac_cv_cxx_compile_cxx0x_native" = "yes"],[], [AS_IF([test "$ac_cv_cxx_compile_cxx0x_gxx" = "yes"], [CXX_STANDARD="-std=gnu++0x"], [CXX_STANDARD="-std=gnu++98"]) ]) ]) AM_CXXFLAGS="${CXX_STANDARD} ${AM_CXXFLAGS}" save_CXXFLAGS="${CXXFLAGS}" CXXFLAGS="${CXXFLAGS} ${CXX_STANDARD}" AC_CXX_HEADER_STDCXX_98 CXXFLAGS="${save_CXXFLAGS}" AC_SUBST([CXX_STANDARD]) ]) haildb-2.3.2/m4/pandora_have_libboost_regex.m40000644000175000017500000000315211513177357022144 0ustar00pcrewspcrews00000000000000dnl Copyright (C) 2010 Andrew Hutchings dnl This file is free software; Andrew Hutchings dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. AC_DEFUN([_PANDORA_SEARCH_BOOST_REGEX],[ AC_REQUIRE([AC_LIB_PREFIX]) AC_REQUIRE([ACX_PTHREAD]) dnl -------------------------------------------------------------------- dnl Check for boost::regex dnl -------------------------------------------------------------------- save_CFLAGS="${CFLAGS}" save_CXXFLAGS="${CXXFLAGS}" CFLAGS="${PTHREAD_CFLAGS} ${CFLAGS}" CXXFLAGS="${PTHREAD_CFLAGS} ${CXXFLAGS}" AC_LANG_PUSH(C++) AC_LIB_HAVE_LINKFLAGS(boost_regex-mt,,[ #include ],[ boost::regex test_regex("drizzle"); ]) AS_IF([test "x${ac_cv_libboost_regex_mt}" = "xno"],[ AC_LIB_HAVE_LINKFLAGS(boost_regex,,[ #include ],[ boost::regex test_regex("drizzle"); ]) ]) AC_LANG_POP() CFLAGS="${save_CFLAGS}" CXXFLAGS="${save_CXXFLAGS}" AM_CONDITIONAL(HAVE_BOOST_REGEX, [test "x${ac_cv_libboost_regex}" = "xyes" -o "x${ac_cv_libboost_regex_mt}" = "xyes"]) BOOST_LIBS="${BOOST_LIBS} ${LTLIBBOOST_REGEX_MT} ${LTLIBBOOST_REGEX}" AC_SUBST(BOOST_LIBS) ]) AC_DEFUN([PANDORA_HAVE_BOOST_REGEX],[ PANDORA_HAVE_BOOST($1) _PANDORA_SEARCH_BOOST_REGEX($1) ]) AC_DEFUN([PANDORA_REQUIRE_BOOST_REGEX],[ PANDORA_REQUIRE_BOOST($1) _PANDORA_SEARCH_BOOST_REGEX($1) AS_IF([test "x${ac_cv_libboost_regex}" = "xno" -a "x${ac_cv_libboost_regex_mt}" = "xno"], AC_MSG_ERROR([boost::regex is required for ${PACKAGE}])) ]) haildb-2.3.2/m4/pandora_have_libmemcached.m40000644000175000017500000000660211513177357021535 0ustar00pcrewspcrews00000000000000dnl Copyright (C) 2009 Sun Microsystems, Inc. dnl This file is free software; Sun Microsystems, Inc. dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. AC_DEFUN([_PANDORA_SEARCH_LIBMEMCACHED],[ AC_REQUIRE([AC_LIB_PREFIX]) dnl -------------------------------------------------------------------- dnl Check for libmemcached dnl -------------------------------------------------------------------- AC_ARG_ENABLE([libmemcached], [AS_HELP_STRING([--disable-libmemcached], [Build with libmemcached support @<:@default=on@:>@])], [ac_enable_libmemcached="$enableval"], [ac_enable_libmemcached="yes"]) AS_IF([test "x$ac_enable_libmemcached" = "xyes"],[ AC_LIB_HAVE_LINKFLAGS(memcached,,[ #include ],[ memcached_st memc; memcached_dump_func *df; memcached_lib_version(); ]) ],[ ac_cv_libmemcached="no" ]) AS_IF([test "x$ac_enable_libmemcached" = "xyes"],[ AC_LIB_HAVE_LINKFLAGS(memcachedprotocol,,[ #include ],[ struct memcached_protocol_st *protocol_handle; protocol_handle= memcached_protocol_create_instance(); ]) ],[ ac_cv_libmemcachedprotocol="no" ]) AC_CACHE_CHECK([if libmemcached has memcached_server_fn], [pandora_cv_libmemcached_server_fn], [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include memcached_server_fn callbacks[1]; ]])], [pandora_cv_libmemcached_server_fn=yes], [pandora_cv_libmemcached_server_fn=no])]) AS_IF([test "x$pandora_cv_libmemcached_server_fn" = "xyes"],[ AC_DEFINE([HAVE_MEMCACHED_SERVER_FN],[1],[If we have the new memcached_server_fn typedef]) ]) ]) AC_DEFUN([_PANDORA_RECENT_LIBMEMCACHED],[ AC_CACHE_CHECK([if libmemcached is recent enough], [pandora_cv_recent_libmemcached],[ AS_IF([test "x${ac_cv_libmemcached}" = "xno"],[ pandora_cv_recent_libmemcached=no ],[ AS_IF([test "x$1" != "x"],[ pandora_need_libmemcached_version=`echo "$1" | perl -nle '/(\d+)\.(\d+)/; printf "%d%0.3d000", $[]1, $[]2 ;'` AS_IF([test "x${pandora_need_libmemcached_version}" = "x0000000"],[ pandora_cv_recent_libmemcached=yes ],[ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #if !defined(LIBMEMCACHED_VERSION_HEX) || LIBMEMCACHED_VERSION_HEX < 0x]]${pandora_need_libmemcached_version}[[ # error libmemcached too old! #endif ]],[[]]) ],[ pandora_cv_recent_libmemcached=yes ],[ pandora_cv_recent_libmemcached=no ]) ]) ],[ pandora_cv_recent_libmemcached=yes ]) ]) ]) AM_CONDITIONAL(HAVE_LIBMEMCACHED,[test "x${ac_cv_libmemcached}" = "xyes" -a "x${pandora_cv_recent_libmemcached}" = "xyes"]) ]) AC_DEFUN([PANDORA_HAVE_LIBMEMCACHED],[ AC_REQUIRE([_PANDORA_SEARCH_LIBMEMCACHED]) _PANDORA_RECENT_LIBMEMCACHED($1) ]) AC_DEFUN([PANDORA_REQUIRE_LIBMEMCACHED],[ PANDORA_HAVE_LIBMEMCACHED($1) AS_IF([test "x{$pandora_cv_recent_libmemcached}" = "xno"], AC_MSG_ERROR([libmemcached is required for ${PACKAGE}])) ]) AC_DEFUN([PANDORA_REQUIRE_LIBMEMCACHEDPROTOCOL],[ PANDORA_HAVE_LIBMEMCACHED($1) AS_IF([test x$ac_cv_libmemcachedprotocol = xno], AC_MSG_ERROR([libmemcachedprotocol is required for ${PACKAGE}])) ]) haildb-2.3.2/m4/intltool.m40000644000175000017500000002421511513177357016275 0ustar00pcrewspcrews00000000000000## intltool.m4 - Configure intltool for the target system. -*-Shell-script-*- ## Copyright (C) 2001 Eazel, Inc. ## Author: Maciej Stachowiak ## Kenneth Christiansen ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program 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 ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ## ## As a special exception to the GNU General Public License, if you ## distribute this file as part of a program that contains a ## configuration script generated by Autoconf, you may include it under ## the same distribution terms that you use for the rest of that program. dnl IT_PROG_INTLTOOL([MINIMUM-VERSION], [no-xml]) # serial 40 IT_PROG_INTLTOOL AC_DEFUN([IT_PROG_INTLTOOL], [ AC_PREREQ([2.50])dnl AC_REQUIRE([AM_NLS])dnl case "$am__api_version" in 1.[01234]) AC_MSG_ERROR([Automake 1.5 or newer is required to use intltool]) ;; *) ;; esac if test -n "$1"; then AC_MSG_CHECKING([for intltool >= $1]) INTLTOOL_REQUIRED_VERSION_AS_INT=`echo $1 | awk -F. '{ print $ 1 * 1000 + $ 2 * 100 + $ 3; }'` INTLTOOL_APPLIED_VERSION=`intltool-update --version | head -1 | cut -d" " -f3` [INTLTOOL_APPLIED_VERSION_AS_INT=`echo $INTLTOOL_APPLIED_VERSION | awk -F. '{ print $ 1 * 1000 + $ 2 * 100 + $ 3; }'` ] AC_MSG_RESULT([$INTLTOOL_APPLIED_VERSION found]) test "$INTLTOOL_APPLIED_VERSION_AS_INT" -ge "$INTLTOOL_REQUIRED_VERSION_AS_INT" || AC_MSG_ERROR([Your intltool is too old. You need intltool $1 or later.]) fi AC_PATH_PROG(INTLTOOL_UPDATE, [intltool-update]) AC_PATH_PROG(INTLTOOL_MERGE, [intltool-merge]) AC_PATH_PROG(INTLTOOL_EXTRACT, [intltool-extract]) if test -z "$INTLTOOL_UPDATE" -o -z "$INTLTOOL_MERGE" -o -z "$INTLTOOL_EXTRACT"; then AC_MSG_ERROR([The intltool scripts were not found. Please install intltool.]) fi INTLTOOL_DESKTOP_RULE='%.desktop: %.desktop.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) -d -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' INTLTOOL_DIRECTORY_RULE='%.directory: %.directory.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) -d -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' INTLTOOL_KEYS_RULE='%.keys: %.keys.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) -k -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' INTLTOOL_PROP_RULE='%.prop: %.prop.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) -d -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' INTLTOOL_OAF_RULE='%.oaf: %.oaf.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) -o -p $(top_srcdir)/po $< [$]@' INTLTOOL_PONG_RULE='%.pong: %.pong.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) -x -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' INTLTOOL_SERVER_RULE='%.server: %.server.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) -o -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' INTLTOOL_SHEET_RULE='%.sheet: %.sheet.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) -x -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' INTLTOOL_SOUNDLIST_RULE='%.soundlist: %.soundlist.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) -d -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' INTLTOOL_UI_RULE='%.ui: %.ui.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) -x -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' INTLTOOL_XML_RULE='%.xml: %.xml.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) -x -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' INTLTOOL_XML_NOMERGE_RULE='%.xml: %.xml.in $(INTLTOOL_MERGE) ; LC_ALL=C $(INTLTOOL_MERGE) -x -u /tmp $< [$]@' INTLTOOL_XAM_RULE='%.xam: %.xml.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) -x -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' INTLTOOL_KBD_RULE='%.kbd: %.kbd.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) -x -u -m -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' INTLTOOL_CAVES_RULE='%.caves: %.caves.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) -d -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' INTLTOOL_SCHEMAS_RULE='%.schemas: %.schemas.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) -s -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' INTLTOOL_THEME_RULE='%.theme: %.theme.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) -d -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' INTLTOOL_SERVICE_RULE='%.service: %.service.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) -d -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' INTLTOOL_POLICY_RULE='%.policy: %.policy.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) -x -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< [$]@' _IT_SUBST(INTLTOOL_DESKTOP_RULE) _IT_SUBST(INTLTOOL_DIRECTORY_RULE) _IT_SUBST(INTLTOOL_KEYS_RULE) _IT_SUBST(INTLTOOL_PROP_RULE) _IT_SUBST(INTLTOOL_OAF_RULE) _IT_SUBST(INTLTOOL_PONG_RULE) _IT_SUBST(INTLTOOL_SERVER_RULE) _IT_SUBST(INTLTOOL_SHEET_RULE) _IT_SUBST(INTLTOOL_SOUNDLIST_RULE) _IT_SUBST(INTLTOOL_UI_RULE) _IT_SUBST(INTLTOOL_XAM_RULE) _IT_SUBST(INTLTOOL_KBD_RULE) _IT_SUBST(INTLTOOL_XML_RULE) _IT_SUBST(INTLTOOL_XML_NOMERGE_RULE) _IT_SUBST(INTLTOOL_CAVES_RULE) _IT_SUBST(INTLTOOL_SCHEMAS_RULE) _IT_SUBST(INTLTOOL_THEME_RULE) _IT_SUBST(INTLTOOL_SERVICE_RULE) _IT_SUBST(INTLTOOL_POLICY_RULE) # Check the gettext tools to make sure they are GNU AC_PATH_PROG(XGETTEXT, xgettext) AC_PATH_PROG(MSGMERGE, msgmerge) AC_PATH_PROG(MSGFMT, msgfmt) AC_PATH_PROG(GMSGFMT, gmsgfmt, $MSGFMT) if test -z "$XGETTEXT" -o -z "$MSGMERGE" -o -z "$MSGFMT"; then AC_MSG_ERROR([GNU gettext tools not found; required for intltool]) fi xgversion="`$XGETTEXT --version|grep '(GNU ' 2> /dev/null`" mmversion="`$MSGMERGE --version|grep '(GNU ' 2> /dev/null`" mfversion="`$MSGFMT --version|grep '(GNU ' 2> /dev/null`" if test -z "$xgversion" -o -z "$mmversion" -o -z "$mfversion"; then AC_MSG_ERROR([GNU gettext tools not found; required for intltool]) fi AC_PATH_PROG(INTLTOOL_PERL, perl) if test -z "$INTLTOOL_PERL"; then AC_MSG_ERROR([perl not found]) fi AC_MSG_CHECKING([for perl >= 5.8.1]) $INTLTOOL_PERL -e "use 5.8.1;" > /dev/null 2>&1 if test $? -ne 0; then AC_MSG_ERROR([perl 5.8.1 is required for intltool]) else IT_PERL_VERSION="`$INTLTOOL_PERL -e \"printf '%vd', $^V\"`" AC_MSG_RESULT([$IT_PERL_VERSION]) fi if test "x$2" != "xno-xml"; then AC_MSG_CHECKING([for XML::Parser]) if `$INTLTOOL_PERL -e "require XML::Parser" 2>/dev/null`; then AC_MSG_RESULT([ok]) else AC_MSG_ERROR([XML::Parser perl module is required for intltool]) fi fi # Substitute ALL_LINGUAS so we can use it in po/Makefile AC_SUBST(ALL_LINGUAS) # Set DATADIRNAME correctly if it is not set yet # (copied from glib-gettext.m4) if test -z "$DATADIRNAME"; then AC_LINK_IFELSE( [AC_LANG_PROGRAM([[]], [[extern int _nl_msg_cat_cntr; return _nl_msg_cat_cntr]])], [DATADIRNAME=share], [case $host in *-*-solaris*) dnl On Solaris, if bind_textdomain_codeset is in libc, dnl GNU format message catalog is always supported, dnl since both are added to the libc all together. dnl Hence, we'd like to go with DATADIRNAME=share dnl in this case. AC_CHECK_FUNC(bind_textdomain_codeset, [DATADIRNAME=share], [DATADIRNAME=lib]) ;; *) [DATADIRNAME=lib] ;; esac]) fi AC_SUBST(DATADIRNAME) IT_PO_SUBDIR([po]) ]) # IT_PO_SUBDIR(DIRNAME) # --------------------- # All po subdirs have to be declared with this macro; the subdir "po" is # declared by IT_PROG_INTLTOOL. # AC_DEFUN([IT_PO_SUBDIR], [AC_PREREQ([2.53])dnl We use ac_top_srcdir inside AC_CONFIG_COMMANDS. dnl dnl The following CONFIG_COMMANDS should be exetuted at the very end dnl of config.status. AC_CONFIG_COMMANDS_PRE([ AC_CONFIG_COMMANDS([$1/stamp-it], [ if [ ! grep "^# INTLTOOL_MAKEFILE$" "$1/Makefile.in" > /dev/null ]; then AC_MSG_ERROR([$1/Makefile.in.in was not created by intltoolize.]) fi rm -f "$1/stamp-it" "$1/stamp-it.tmp" "$1/POTFILES" "$1/Makefile.tmp" >"$1/stamp-it.tmp" [sed '/^#/d s/^[[].*] *// /^[ ]*$/d '"s|^| $ac_top_srcdir/|" \ "$srcdir/$1/POTFILES.in" | sed '$!s/$/ \\/' >"$1/POTFILES" ] [sed '/^POTFILES =/,/[^\\]$/ { /^POTFILES =/!d r $1/POTFILES } ' "$1/Makefile.in" >"$1/Makefile"] rm -f "$1/Makefile.tmp" mv "$1/stamp-it.tmp" "$1/stamp-it" ]) ])dnl ]) # _IT_SUBST(VARIABLE) # ------------------- # Abstract macro to do either _AM_SUBST_NOTMAKE or AC_SUBST # AC_DEFUN([_IT_SUBST], [ AC_SUBST([$1]) m4_ifdef([_AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE([$1])]) ] ) # deprecated macros AU_ALIAS([AC_PROG_INTLTOOL], [IT_PROG_INTLTOOL]) # A hint is needed for aclocal from Automake <= 1.9.4: # AC_DEFUN([AC_PROG_INTLTOOL], ...) haildb-2.3.2/m4/pandora_with_lua.m40000644000175000017500000000332111513177357017744 0ustar00pcrewspcrews00000000000000dnl -*- mode: m4; c-basic-offset: 2; indent-tabs-mode: nil; -*- dnl vim:expandtab:shiftwidth=2:tabstop=2:smarttab: dnl dnl pandora-build: A pedantic build system dnl Copyright (C) 2009 Sun Microsystems, Inc. dnl This file is free software; Sun Microsystems dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl dnl From Monty Taylor AC_DEFUN([PANDORA_WITH_LUA],[ dnl Check for lua AC_ARG_WITH([lua], [AS_HELP_STRING([--with-lua], [Build Lua Bindings @<:@default=yes@:>@])], [with_lua=$withval], [with_lua=yes]) AS_IF([test "x$with_lua" != "xno"],[ AS_IF([test "x$with_lua" = "xyes"], [LUAPC=lua], [LUAPC=$with_lua]) PKG_CHECK_MODULES([LUA], $LUAPC >= 5.1, [ AC_DEFINE([HAVE_LUA], [1], [liblua]) AC_DEFINE([HAVE_LUA_H], [1], [lua.h]) with_lua=yes ],[ LUAPC=lua5.1 PKG_CHECK_MODULES([LUA], $LUAPC >= 5.1, [ AC_DEFINE([HAVE_LUA], [1], [liblua]) AC_DEFINE([HAVE_LUA_H], [1], [lua.h]) with_lua=yes ],[ AC_DEFINE([HAVE_LUA],["x"],["x"]) with_lua=no ]) ]) AC_CACHE_CHECK([for LUA installation location],[pandora_cv_lua_archdir],[ AS_IF([test "$prefix" = "NONE"],[ pandora_cv_lua_archdir=`${PKG_CONFIG} --define-variable=prefix=${ac_default_prefix} --variable=INSTALL_CMOD ${LUAPC}` ],[ pandora_cv_lua_archdir=`${PKG_CONFIG} --define-variable=prefix=${prefix} --variable=INSTALL_CMOD ${LUAPC}` ]) ]) LUA_ARCHDIR="${pandora_cv_lua_archdir}" AC_SUBST(LUA_ARCHDIR) AC_SUBST(LUA_CFLAGS) AC_SUBST(LUA_LIBS) ]) AM_CONDITIONAL(BUILD_LUA, test "$with_lua" = "yes") ]) haildb-2.3.2/m4/pandora_platform.m40000644000175000017500000000761611513177357017767 0ustar00pcrewspcrews00000000000000dnl -*- mode: m4; c-basic-offset: 2; indent-tabs-mode: nil; -*- dnl vim:expandtab:shiftwidth=2:tabstop=2:smarttab: dnl dnl pandora-build: A pedantic build system dnl Copyright (C) 2009 Sun Microsystems, Inc. dnl This file is free software; Sun Microsystems dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl dnl From Monty Taylor AC_DEFUN([PANDORA_PLATFORM],[ dnl Canonicalize the configuration name. AC_DEFINE_UNQUOTED([HOST_VENDOR], ["$host_vendor"],[Vendor of Build System]) AC_DEFINE_UNQUOTED([HOST_OS], ["$host_os"], [OS of Build System]) AC_DEFINE_UNQUOTED([HOST_CPU], ["$host_cpu"], [CPU of Build System]) AC_DEFINE_UNQUOTED([TARGET_VENDOR], ["$target_vendor"],[Vendor of Target System]) AC_DEFINE_UNQUOTED([TARGET_OS], ["$target_os"], [OS of Target System]) AC_DEFINE_UNQUOTED([TARGET_CPU], ["$target_cpu"], [CPU of Target System]) case "$host_os" in *solaris*) AS_IF([test "x${ac_cv_env_CPPFLAGS_set}" = "x"],[ CPPFLAGS="${CPPFLAGS} -I/usr/local/include" ]) AS_IF([test "x${ac_cv_env_LDFLAGS_set}" = "x"],[ LDFLAGS="${LDFLAGS} -L/usr/local/lib" ]) ;; *freebsd*) AS_IF([test "x${ac_cv_env_CPPFLAGS_set}" = "x"],[ CPPFLAGS="${CPPFLAGS} -isystem /usr/local/include" ]) AS_IF([test "x${ac_cv_env_LDFLAGS_set}" = "x"],[ LDFLAGS="${LDFLAGS} -L/usr/local/lib" ]) ;; esac PANDORA_OPTIMIZE_BITFIELD=1 case "$target_os" in *linux*) TARGET_LINUX="true" AC_SUBST(TARGET_LINUX) AC_DEFINE([TARGET_OS_LINUX], [1], [Whether we build for Linux]) ;; *darwin*) TARGET_OSX="true" AC_SUBST(TARGET_OSX) AC_DEFINE([TARGET_OS_OSX], [1], [Whether we build for OSX]) ;; *solaris*) TARGET_SOLARIS="true" PANDORA_OPTIMIZE_BITFIELD=0 AS_IF([test "x${USE_NLS}" = "xyes"],[LIBS="${LIBS} -lintl"]) AC_SUBST(TARGET_SOLARIS) AC_DEFINE([TARGET_OS_SOLARIS], [1], [Whether we are building for Solaris]) ;; *freebsd*) TARGET_FREEBSD="true" AC_SUBST(TARGET_FREEBSD) AC_DEFINE([TARGET_OS_FREEBSD], [1], [Whether we are building for FreeBSD]) AC_DEFINE([__APPLE_CC__],[1],[Workaround for bug in FreeBSD headers]) ;; *mingw32*) TARGET_WINDOWS="true" AC_SUBST(TARGET_WINDOWS) AC_DEFINE([TARGET_OS_WINDOWS], [1], [Whether we are building for Windows]) AC_DEFINE([WINVER], [WindowsXP], [Version of Windows]) AC_DEFINE([_WIN32_WINNT], [0x0501], [Magical number to make things work]) AC_DEFINE([EAI_SYSTEM], [11], [Another magical number]) AH_BOTTOM([ #ifndef HAVE_SYS_SOCKET_H # define SHUT_RD SD_RECEIVE # define SHUT_WR SD_SEND # define SHUT_RDWR SD_BOTH #endif ]) LIBS="$LIBS -lwsock32 -lws2_32" AM_CFLAGS="${AM_CFLAGS} -I\${top_srcdir}/win32/mingw -I\${top_builddir}/win32/mingw -I\${top_srcdir}/win32 -I\${top_builddir}/win32" ;; esac AM_CONDITIONAL(BUILD_WIN32, [test "x${TARGET_WINDOWS}" = "xtrue"]) AC_SUBST(PANDORA_OPTIMIZE_BITFIELD) AC_CHECK_DECL([__SUNPRO_C], [SUNCC="yes"], [SUNCC="no"]) AC_CHECK_DECL([__ICC], [INTELCC="yes"], [INTELCC="no"]) AS_IF([test "$INTELCC" = "yes"], [enable_rpath=no]) dnl By default, Sun Studio grabs special versions of limits.h and string.h dnl when you use and . By setting this define, we can dnl disable that and cause those to wrap the standard headers instead. dnl http://www.stlport.com/doc/configure.html AS_IF([test "$SUNCC" = "yes"],[ AC_DEFINE([_STLP_NO_NEW_C_HEADERS],[1], [Cause Sun Studio to not be quite so strict with standards conflicts]) ]) AS_IF([test "x$TARGET_OSX" = "xtrue"],[ AS_IF([test "x$ac_enable_fat_binaries" = "xyes"],[ AM_CFLAGS="-arch i386 -arch x86_64 -arch ppc" AM_CXXFLAGS="-arch i386 -arch x86_64 -arch ppc" AM_LDFLAGS="-arch i386 -arch x86_64 -arch ppc" ]) ]) ]) haildb-2.3.2/m4/pandora_with_perl.m40000644000175000017500000000505411513177357020132 0ustar00pcrewspcrews00000000000000dnl -*- mode: m4; c-basic-offset: 2; indent-tabs-mode: nil; -*- dnl vim:expandtab:shiftwidth=2:tabstop=2:smarttab: dnl dnl pandora-build: A pedantic build system dnl Copyright (C) 2009 Sun Microsystems, Inc. dnl This file is free software; Sun Microsystems dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl dnl From Monty Taylor AC_DEFUN([PANDORA_WITH_PERL], [ AC_ARG_WITH([perl], [AS_HELP_STRING([--with-perl], [Build Perl Bindings @<:@default=yes@:>@])], [with_perl=$withval], [with_perl=yes]) AC_ARG_WITH([perl-arch], [AS_HELP_STRING([--with-perl-arch], [Install Perl bindings into system location @<:@default=no@:>@])], [with_perl_arch=$withval], [with_perl_arch=no]) AS_IF([test "x$with_perl" != "xno"],[ AS_IF([test "x$with_perl" != "xyes"], [ac_chk_perl=$with_perl], [ac_chk_perl=perl]) AC_CHECK_PROGS(PERL,$ac_chk_perl) ]) AS_IF([test "x$PERL" != "x"],[ AC_CACHE_CHECK([for Perl include path],[pandora_cv_perl_include],[ pandora_cv_perl_include=`$PERL -MConfig -e 'print $Config{archlib};'` pandora_cv_perl_include="${pandora_cv_perl_include}/CORE" ]) AC_CACHE_CHECK([for Perl CPPFLAGS],[pandora_cv_perl_cppflags],[ pandora_cv_perl_cppflags=`$PERL -MConfig -e 'print $Config{cppflags};'` pandora_cv_perl_cppflags="${pandora_cv_perl_cppflags}" ]) PERL_CPPFLAGS="-I${pandora_cv_perl_include} ${pandora_cv_perl_cppflags}" AC_CACHE_CHECK([for Perl development headers], [pandora_cv_perl_dev],[ save_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${PERL_CPPFLAGS}" AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include #include "EXTERN.h" #include "perl.h" #include "XSUB.h" ]])], [pandora_cv_perl_dev=yes], [pandora_cv_perl_dev=no]) CPPFLAGS="${save_CPPFLAGS}" ]) AS_IF([test "${pandora_cv_perl_dev}" = "no"], [with_perl=no]) AC_CACHE_CHECK([for Perl install location], [pandora_cv_perl_archdir],[ AS_IF([test "${with_perl_arch}" = "no"],[ pandora_cv_perl_archdir=`$PERL -MConfig -e 'print $Config{sitearch}'` ],[ pandora_cv_perl_archdir=`$PERL -MConfig -e 'print $Config{archlib}'` ]) pandora_cv_perl_archdir="${pandora_cv_perl_archdir}" ]) PERL_ARCHDIR="${pandora_cv_perl_archdir}" ]) AC_SUBST([PERL_CPPFLAGS]) AC_SUBST([PERL_ARCHDIR]) AM_CONDITIONAL(BUILD_PERL, [test "$with_perl" != "no"]) ]) haildb-2.3.2/m4/pandora_have_libinnodb.m40000644000175000017500000000345311513177357021101 0ustar00pcrewspcrews00000000000000dnl Copyright (C) 2009 Sun Microsystems dnl This file is free software; Sun Microsystems dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. AC_DEFUN([_PANDORA_SEARCH_LIBINNODB],[ AC_REQUIRE([AC_LIB_PREFIX]) dnl -------------------------------------------------------------------- dnl Check for libhaildb or libinnodb dnl -------------------------------------------------------------------- AC_ARG_ENABLE([libinnodb], [AS_HELP_STRING([--disable-libinnodb], [Build with libinnodb support @<:@default=on@:>@])], [ac_enable_libinnodb="$enableval"], [ac_enable_libinnodb="yes"]) AS_IF([test "x$ac_enable_libinnodb" = "xyes"],[ AC_LIB_HAVE_LINKFLAGS(haildb,,[ #include ],[ ib_u64_t ib_api_version(void); ]) AS_IF([test "x${ac_cv_libhaildb}" = "xyes"],[ AC_DEFINE([HAVE_HAILDB_H],[1],[Do we have haildb.h]) INNODB_LIBS="${LTLIBHAILDB}" ac_cv_have_innodb=yes ],[ AC_LIB_HAVE_LINKFLAGS(innodb,,[ #include ],[ ib_u64_t ib_api_version(void); ]) AS_IF([test "x{ac_cv_libinnodb}" = "xyes"],[ AC_DEFINE([HAVE_INNODB_H],[1],[Do we have innodb.h]) INNODB_LIBS="${LTLIBINNODB}" ac_cv_have_innodb=yes ]) ]) ],[ ac_cv_libhaildb="no" ac_cv_libinnodb="no" ]) AC_SUBST([INNODB_LIBS]) AM_CONDITIONAL(HAVE_LIBINNODB, [test "x${ac_cv_have_innodb}" = "xyes"]) ]) AC_DEFUN([PANDORA_HAVE_LIBINNODB],[ AC_REQUIRE([_PANDORA_SEARCH_LIBINNODB]) ]) AC_DEFUN([PANDORA_REQUIRE_LIBINNODB],[ AC_REQUIRE([PANDORA_HAVE_LIBINNODB]) AS_IF([test "x${ac_cv_libinnodb}" = "xno"], AC_MSG_ERROR([libhaildb or libinnodb is required for ${PACKAGE}])) ]) haildb-2.3.2/m4/pandora_with_python.m40000644000175000017500000000235111513177357020506 0ustar00pcrewspcrews00000000000000dnl -*- mode: m4; c-basic-offset: 2; indent-tabs-mode: nil; -*- dnl vim:expandtab:shiftwidth=2:tabstop=2:smarttab: dnl dnl pandora-build: A pedantic build system dnl Copyright (C) 2009 Sun Microsystems, Inc. dnl This file is free software; Sun Microsystems dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl dnl From Monty Taylor AC_DEFUN([PANDORA_WITH_PYTHON], [ AC_ARG_WITH([python], [AS_HELP_STRING([--with-python], [Build Python Bindings @<:@default=yes@:>@])], [with_python=$withval python_requested=$withval ], [with_python=yes python_requested=no ]) AS_IF([test "x$with_python" != "xno"],[ AS_IF([test "x$with_python" != "xyes"],[PYTHON=$with_python]) AM_PATH_PYTHON([2.4],,[with_python="no"]) AC_PYTHON_DEVEL() AS_IF([test "x$pythonexists" = "xno"],[with_python="no"]) ]) AS_IF([test "x$with_python" = "xno" -a "$python_requested" = "yes"],[ AC_MSG_ERROR([Python support was explicity requested, but Python support was not found. Please correct your build environment and try again]) ]) AM_CONDITIONAL(BUILD_PYTHON, [test "$with_python" = "yes"]) ]) haildb-2.3.2/m4/pandora_python3_devel.m40000644000175000017500000002002311513177357020711 0ustar00pcrewspcrews00000000000000dnl -*- mode: m4; c-basic-offset: 2; indent-tabs-mode: nil; -*- dnl vim:expandtab:shiftwidth=2:tabstop=2:smarttab: dnl dnl pandora-build: A pedantic build system dnl dnl Copyright (C) 2009 Sun Microsystems, Inc. dnl Copyright (C) 2008 Sebastian Huber dnl Copyright (C) 2008 Alan W. Irwin dnl Copyright (C) 2008 Rafael Laboissiere dnl Copyright (C) 2008 Andrew Collier dnl Copyright (C) 2008 Matteo Settenvini dnl Copyright (C) 2008 Horst Knorr dnl dnl This program is free software: you can redistribute it and/or modify it dnl under the terms of the GNU General Public License as published by the dnl Free Software Foundation, either version 3 of the License, or (at your dnl option) any later version. dnl dnl This program is distributed in the hope that it will be useful, but dnl WITHOUT ANY WARRANTY; without even the implied warranty of dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General dnl Public License for more details. dnl dnl You should have received a copy of the GNU General Public License along dnl with this program. If not, see . dnl dnl As a special exception, the respective Autoconf Macro's copyright owner dnl gives unlimited permission to copy, distribute and modify the configure dnl scripts that are the output of Autoconf when processing the Macro. You dnl need not follow the terms of the GNU General Public License when using dnl or distributing such scripts, even though portions of the text of the dnl Macro appear in them. The GNU General Public License (GPL) does govern dnl all other use of the material that constitutes the Autoconf Macro. dnl dnl This special exception to the GPL applies to versions of the Autoconf dnl Macro released by the Autoconf Macro Archive. When you make and dnl distribute a modified version of the Autoconf Macro, you may extend this dnl special exception to the GPL to apply to your modified version as well. dnl SYNOPSIS dnl dnl PANDORA_PYTHON3_DEVEL([version]) dnl dnl DESCRIPTION dnl dnl Note: Defines as a precious variable "PYTHON3_VERSION". Don't override it dnl in your configure.ac. dnl dnl This macro checks for Python and tries to get the include path to dnl 'Python.h'. It provides the $(PYTHON3_CPPFLAGS) and $(PYTHON3_LDFLAGS) dnl output variables. It also exports $(PYTHON3_EXTRA_LIBS) and dnl $(PYTHON3_EXTRA_LDFLAGS) for embedding Python in your code. dnl dnl You can search for some particular version of Python by passing a dnl parameter to this macro, for example ">= '2.3.1'", or "== '2.4'". Please dnl note that you *have* to pass also an operator along with the version to dnl match, and pay special attention to the single quotes surrounding the dnl version number. Don't use "PYTHON3_VERSION" for this: that environment dnl variable is declared as precious and thus reserved for the end-user. dnl dnl LAST MODIFICATION dnl dnl 2009-08-23 AC_DEFUN([PANDORA_PYTHON3_DEVEL],[ # # Allow the use of a (user set) custom python version # AC_ARG_VAR([PYTHON3_VERSION],[The installed Python version to use, for example '3.0'. This string will be appended to the Python interpreter canonical name.]) AS_IF([test -z "$PYTHON3"],[ AC_PATH_PROG([PYTHON3],[python[$PYTHON3_VERSION]]) ]) AS_IF([test -z "$PYTHON3"],[ AC_MSG_ERROR([Cannot find python$PYTHON3_VERSION in your system path]) PYTHON3_VERSION="" ]) # # if the macro parameter ``version'' is set, honour it # if test -n "$1"; then AC_MSG_CHECKING([for a version of Python $1]) ac_supports_python3_ver=`$PYTHON3 -c "import sys, string; \ ver = string.split(sys.version)[[0]]; \ print(ver $1)"` if test "$ac_supports_python3_ver" = "True"; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) AC_MSG_ERROR([this package requires Python $1. If you have it installed, but it isn't the default Python interpreter in your system path, please pass the PYTHON3_VERSION variable to configure. See ``configure --help'' for reference. ]) PYTHON_VERSION="" fi fi # # Check if you have distutils, else fail # AC_MSG_CHECKING([for Python3 distutils package]) ac_python3_distutils_result=`$PYTHON3 -c "import distutils" 2>&1` if test -z "$ac_python3_distutils_result"; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) AC_MSG_ERROR([cannot import Python3 module "distutils". Please check your Python3 installation. The error was: $ac_python3_distutils_result]) PYTHON3_VERSION="" fi # # Check for Python include path # AC_MSG_CHECKING([for Python3 include path]) if test -z "$PYTHON3_CPPFLAGS"; then python3_path=`$PYTHON3 -c "import distutils.sysconfig; \ print(distutils.sysconfig.get_python_inc());"` if test -n "${python3_path}"; then python3_path="-I$python3_path" fi PYTHON3_CPPFLAGS=$python3_path fi AC_MSG_RESULT([$PYTHON3_CPPFLAGS]) AC_SUBST([PYTHON3_CPPFLAGS]) # # Check for Python library path # AC_MSG_CHECKING([for Python3 library path]) if test -z "$PYTHON3_LDFLAGS"; then # (makes two attempts to ensure we've got a version number # from the interpreter) py3_version=`$PYTHON3 -c "from distutils.sysconfig import *; \ print(' '.join(get_config_vars('VERSION')))"` if test "$py3_version" == "[None]"; then if test -n "$PYTHON3_VERSION"; then py3_version=$PYTHON3_VERSION else py3_version=`$PYTHON3 -c "import sys; \ print(sys.version[[:3]])"` fi fi PYTHON3_LDFLAGS=`$PYTHON3 -c "from distutils.sysconfig import *; \ print('-L' + get_python_lib(0,1), \ '-lpython');"`$py3_version fi AC_MSG_RESULT([$PYTHON3_LDFLAGS]) AC_SUBST([PYTHON3_LDFLAGS]) # # Check for site packages # AC_MSG_CHECKING([for Python3 site-packages path]) if test -z "$PYTHON3_SITE_PKG"; then PYTHON3_SITE_PKG=`$PYTHON3 -c "import distutils.sysconfig; \ print(distutils.sysconfig.get_python_lib(0,0));"` fi AC_MSG_RESULT([$PYTHON3_SITE_PKG]) AC_SUBST([PYTHON3_SITE_PKG]) # # libraries which must be linked in when embedding # AC_MSG_CHECKING(for Python3 embedding libraries) if test -z "$PYTHON3_EMBED_LIBS"; then PYTHON3_EMBED_LIBS=`$PYTHON3 -c "import distutils.sysconfig; \ conf = distutils.sysconfig.get_config_var; \ print(conf('LOCALMODLIBS'), conf('LIBS'))"` fi AC_MSG_RESULT([$PYTHON3_EMBED_LIBS]) AC_SUBST(PYTHON3_EMBED_LIBS) # # linking flags needed when embedding # AC_MSG_CHECKING(for Python3 embedding linking flags) if test -z "$PYTHON3_EMBED_LDFLAGS"; then PYTHON3_EMBED_LDFLAGS=`$PYTHON3 -c "import distutils.sysconfig; \ conf = distutils.sysconfig.get_config_var; \ print(conf('LINKFORSHARED'))"` fi AC_MSG_RESULT([$PYTHON3_EMBED_LDFLAGS]) AC_SUBST(PYTHON3_EMBED_LDFLAGS) # # final check to see if everything compiles alright # AC_MSG_CHECKING([for Python3 development environment consistency]) AC_LANG_PUSH([C]) # save current global flags ac_save_LIBS="$LIBS" ac_save_CPPFLAGS="$CPPFLAGS" LIBS="$ac_save_LIBS $PYTHON3_LDFLAGS" CPPFLAGS="$ac_save_CPPFLAGS $PYTHON3_CPPFLAGS" AC_TRY_LINK([ #include ],[ Py_Initialize(); ],[python3exists=yes],[python3exists=no]) AC_MSG_RESULT([$python3exists]) if test ! "$python3exists" = "yes"; then AC_MSG_WARN([ Could not link test program to Python3. Maybe the main Python3 library has been installed in some non-standard library path. If so, pass it to configure, via the LDFLAGS environment variable. Example: ./configure LDFLAGS="-L/usr/non-standard-path/python3/lib" ============================================================================ ERROR! You probably have to install the development version of the Python3 package for your distribution. The exact name of this package varies among them. ============================================================================ ]) PYTHON3_VERSION="" fi AC_LANG_POP # turn back to default flags CPPFLAGS="$ac_save_CPPFLAGS" LIBS="$ac_save_LIBS" # # all done! # ]) haildb-2.3.2/m4/pandora_shared_ptr.m40000644000175000017500000000363111513177357020267 0ustar00pcrewspcrews00000000000000dnl Copyright (C) 2009 Sun Microsystems, Inc. dnl This file is free software; Sun Microsystems, Inc. dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl We check two things: where is the memory include file, and in what dnl namespace does shared_ptr reside. dnl We include AC_COMPILE_IFELSE for all the combinations we've seen in the dnl wild: dnl dnl GCC 4.3: namespace: std:: #include dnl GCC 4.2: namespace: tr1:: #include dnl GCC 4.2: namespace: boost:: #include dnl dnl We define one of HAVE_HAVE_TR1_SHARED_PTR or HAVE_BOOST_SHARED_PTR dnl depending on location, and SHARED_PTR_NAMESPACE to be the namespace in dnl which shared_ptr is defined. dnl AC_DEFUN([PANDORA_SHARED_PTR],[ AC_REQUIRE([PANDORA_CHECK_CXX_STANDARD]) AC_LANG_PUSH(C++) save_CXXFLAGS="${CXXFLAGS}" CXXFLAGS="${CXX_STANDARD} ${CXXFLAGS}" AC_CHECK_HEADERS(memory tr1/memory boost/shared_ptr.hpp) AC_CACHE_CHECK([the location of shared_ptr header file], [ac_cv_shared_ptr_h],[ for namespace in std tr1 std::tr1 boost do AC_COMPILE_IFELSE( [AC_LANG_PROGRAM([[ #if defined(HAVE_MEMORY) # include #endif #if defined(HAVE_TR1_MEMORY) # include #endif #if defined(HAVE_BOOST_SHARED_PTR_HPP) # include #endif #include using $namespace::shared_ptr; using namespace std; ]],[[ shared_ptr test_ptr(new string("test string")); ]])], [ ac_cv_shared_ptr_namespace="${namespace}" break ],[ac_cv_shared_ptr_namespace=missing]) done ]) AC_DEFINE_UNQUOTED([SHARED_PTR_NAMESPACE], ${ac_cv_shared_ptr_namespace}, [The namespace in which SHARED_PTR can be found]) CXXFLAGS="${save_CXXFLAGS}" AC_LANG_POP() ]) haildb-2.3.2/m4/pandora_have_libmysqlclient.m40000644000175000017500000001216311513177357022172 0ustar00pcrewspcrews00000000000000dnl -*- mode: m4; c-basic-offset: 2; indent-tabs-mode: nil; -*- dnl vim:expandtab:shiftwidth=2:tabstop=2:smarttab: dnl dnl Copyright (C) 2010 Monty Taylor dnl This file is free software; Sun Microsystems dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl AC_DEFUN([PANDORA_WITH_MYSQL],[ AC_ARG_WITH([mysql], [AS_HELP_STRING([--with-mysql=PATH], [path to mysql_config binary or mysql prefix dir])], [with_mysql=$withval], [with_mysql=":"]) dnl There are three possibilities: dnl 1) nothing is given: we will search for mysql_config in PATH dnl 2) the location of mysql_config is given: we'll use that to determine dnl 3) a directory argument is given: that will be mysql_base dnl option 1: nothing, we need to insert something into MYSQL_CONFIG AS_IF([test "x$with_mysql" = "x:"],[ AC_CHECK_PROGS(MYSQL_CONFIG,[mysql_config]) ],[ MYSQL_CONFIG="${with_mysql}" ]) AC_CACHE_CHECK([for MySQL Base Location],[pandora_cv_mysql_base],[ dnl option 2: something in MYSQL_CONFIG now, use that to get a base dir AS_IF([test -f "${MYSQL_CONFIG}" -a -x "${MYSQL_CONFIG}"],[ pandora_cv_mysql_base=$(dirname $(MYSQL_CONFIG --include | sed 's/-I//')) ],[ dnl option 1: a directory AS_IF([test -d $with_mysql],[pandora_cv_mysql_base=$with_mysql],[ pandora_cv_mysql_base="not found" ]) ]) ]) ]) AC_DEFUN([_PANDORA_SEARCH_LIBMYSQLCLIENT],[ AC_REQUIRE([AC_LIB_PREFIX]) AC_ARG_ENABLE([libmysqlclient], [AS_HELP_STRING([--disable-libmysqlclient], [Build with libmysqlclient support @<:@default=on@:>@])], [ac_enable_libmysqlclient="$enableval"], [ac_enable_libmysqlclient="yes"]) AS_IF([test "x$ac_enable_libmysqlclient" = "xyes"],[ AC_LIB_HAVE_LINKFLAGS(mysqlclient_r,,[ #include ],[ MYSQL mysql; ])],[ ac_cv_libmysqlclient_r="no" ]) AM_CONDITIONAL(HAVE_LIBMYSQLCLIENT, [test "x${ac_cv_libmysqlclient_r}" = "xyes"]) AC_DEFUN([PANDORA_HAVE_LIBMYSQLCLIENT],[ AC_REQUIRE([_PANDORA_SEARCH_LIBMYSQLCLIENT]) ]) AC_DEFUN([PANDORA_REQUIRE_LIBMYSQLCLIENT],[ AC_REQUIRE([PANDORA_HAVE_LIBMYSQLCLIENT]) AS_IF([test "x${ac_cv_libmysqlclient_r}" = "xno"], AC_MSG_ERROR([libmysqlclient_r is required for ${PACKAGE}])) ]) AS_IF([test "x$MYSQL_CONFIG" = "xISDIR"],[ IBASE="-I${with_mysql}" MYSQL_CONFIG="${with_mysql}/scripts/mysql_config" ADDIFLAGS="$IBASE/include " ADDIFLAGS="$ADDIFLAGS $IBASE/storage/ndb/include/ndbapi " ADDIFLAGS="$ADDIFLAGS $IBASE/storage/ndb/include/mgmapi " ADDIFLAGS="$ADDIFLAGS $IBASE/storage/ndb/include " LDFLAGS="-L${with_mysql}/storage/ndb/src/.libs -L${with_mysql}/libmysql_r/.libs/ -L${with_mysql}/mysys/.libs -L${with_mysql}/mysys -L${with_mysql}/strings/.libs -L${with_mysql}/strings " ],[ IBASE=`$MYSQL_CONFIG --include` ADDIFLAGS="" # add regular MySQL C flags ADDCFLAGS=`$MYSQL_CONFIG --cflags` # add NdbAPI specific C flags LDFLAGS="$LDFLAGS "`$MYSQL_CONFIG --libs_r | sed 's/-lmysqlclient_r//'` ]) ADDIFLAGS="$ADDIFLAGS $IBASE/storage/ndb" ADDIFLAGS="$ADDIFLAGS $IBASE/storage/ndb/ndbapi" ADDIFLAGS="$ADDIFLAGS $IBASE/storage/ndb/mgmapi" ADDIFLAGS="$ADDIFLAGS $IBASE/ndb" ADDIFLAGS="$ADDIFLAGS $IBASE/ndb/ndbapi" ADDIFLAGS="$ADDIFLAGS $IBASE/ndb/mgmapi" ADDIFLAGS="$ADDIFLAGS $IBASE" CFLAGS="$CFLAGS $ADDCFLAGS $ADDIFLAGS" CXXFLAGS="$CXXFLAGS $ADDCFLAGS $ADDIFLAGS" MYSQL_INCLUDES="$IBASE $ADDIFLAGS" dnl AC_CHECK_LIB([mysqlclient_r],[safe_mutex_init],,[AC_MSG_ERROR([Can't link against libmysqlclient_r])]) dnl First test to see if we can run with only ndbclient AC_CHECK_LIB([ndbclient],[decimal_bin_size],,[dnl else LDFLAGS="$LDFLAGS -lmysys -ldbug" AC_CHECK_LIB([mysqlclient_r],[safe_mutex_init],,) AC_CHECK_LIB([ndbclient],[ndb_init],,[ AC_MSG_ERROR([Can't link against libndbclient])]) AC_CHECK_LIB([mystrings],[decimal_bin_size],,[ AC_MSG_ERROR([Can't find decimal_bin_size])])]) AC_MSG_CHECKING(for NdbApi headers) AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], [[int attr=NdbTransaction::Commit; ]])],[ndbapi_found="yes"],[]) AS_IF([test "$ndbapi_found" = "yes"], [AC_MSG_RESULT(found)], [AC_MSG_ERROR([Couldn't find NdbApi.hpp!])]) AC_MSG_CHECKING(for NDB_LE_ThreadConfigLoop) AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], [[int attr=NDB_LE_ThreadConfigLoop; ]])],[have_cge63="yes"],[]) AS_IF([test "$have_cge63" = "yes"], [AC_MSG_RESULT(found) HAVE_CGE63="-DCGE63" AC_SUBST(HAVE_CGE63)], [AC_MSG_RESULT(missing)]) LDFLAGS="$LDFLAGS $LIBS" MYSQL_MAJOR_VERSION=`$MYSQL_CONFIG --version | sed -e 's/\.//g' -e 's/-//g' -e 's/[A-Za-z]//g' | cut -c1-2` case "$MYSQL_MAJOR_VERSION" in 50) AC_DEFINE(MYSQL_50, [1], [mysql5.0]) ;; 51) AC_DEFINE(MYSQL_51, [1], [mysql5.1]) ;; *) echo "Unsupported version of MySQL Detected!" ;; esac AC_SUBST(MYSQL_MAJOR_VERSION) AC_SUBST(MYSQL_CONFIG) ]) haildb-2.3.2/m4/acx_pthread.m40000644000175000017500000002541711513177357016720 0ustar00pcrewspcrews00000000000000# =========================================================================== # http://autoconf-archive.cryp.to/acx_pthread.html # =========================================================================== # # SYNOPSIS # # ACX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) # # DESCRIPTION # # This macro figures out how to build C programs using POSIX threads. It # sets the PTHREAD_LIBS output variable to the threads library and linker # flags, and the PTHREAD_CFLAGS output variable to any special C compiler # flags that are needed. (The user can also force certain compiler # flags/libs to be tested by setting these environment variables.) # # Also sets PTHREAD_CC to any special C compiler that is needed for # multi-threaded programs (defaults to the value of CC otherwise). (This # is necessary on AIX to use the special cc_r compiler alias.) # # NOTE: You are assumed to not only compile your program with these flags, # but also link it with them as well. e.g. you should link with # $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS # # If you are only building threads programs, you may wish to use these # variables in your default LIBS, CFLAGS, and CC: # # LIBS="$PTHREAD_LIBS $LIBS" # CFLAGS="$CFLAGS $PTHREAD_CFLAGS" # CC="$PTHREAD_CC" # # In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant # has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to that name # (e.g. PTHREAD_CREATE_UNDETACHED on AIX). # # ACTION-IF-FOUND is a list of shell commands to run if a threads library # is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it # is not found. If ACTION-IF-FOUND is not specified, the default action # will define HAVE_PTHREAD. # # Please let the authors know if this macro fails on any platform, or if # you have any other suggestions or comments. This macro was based on work # by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help # from M. Frigo), as well as ac_pthread and hb_pthread macros posted by # Alejandro Forero Cuervo to the autoconf macro repository. We are also # grateful for the helpful feedback of numerous users. # # LICENSE # # Copyright (c) 2008 Steven G. Johnson # # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation, either version 3 of the License, or (at your # option) any later version. # # This program 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 General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . # # As a special exception, the respective Autoconf Macro's copyright owner # gives unlimited permission to copy, distribute and modify the configure # scripts that are the output of Autoconf when processing the Macro. You # need not follow the terms of the GNU General Public License when using # or distributing such scripts, even though portions of the text of the # Macro appear in them. The GNU General Public License (GPL) does govern # all other use of the material that constitutes the Autoconf Macro. # # This special exception to the GPL applies to versions of the Autoconf # Macro released by the Autoconf Archive. When you make and distribute a # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. AC_DEFUN([ACX_PTHREAD], [ AC_REQUIRE([AC_CANONICAL_HOST]) AC_LANG_SAVE AC_LANG_C acx_pthread_ok=no # We used to check for pthread.h first, but this fails if pthread.h # requires special compiler flags (e.g. on True64 or Sequent). # It gets checked for in the link test anyway. # First of all, check if the user has set any of the PTHREAD_LIBS, # etcetera environment variables, and if threads linking works using # them: if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" save_LIBS="$LIBS" LIBS="$PTHREAD_LIBS $LIBS" AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS]) AC_TRY_LINK_FUNC(pthread_join, acx_pthread_ok=yes) AC_MSG_RESULT($acx_pthread_ok) if test x"$acx_pthread_ok" = xno; then PTHREAD_LIBS="" PTHREAD_CFLAGS="" fi LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" fi # We must check for the threads library under a number of different # names; the ordering is very important because some systems # (e.g. DEC) have both -lpthread and -lpthreads, where one of the # libraries is broken (non-POSIX). # Create a list of thread flags to try. Items starting with a "-" are # C compiler flags, and other items are library names, except for "none" # which indicates that we try without any flags at all, and "pthread-config" # which is a program returning the flags for the Pth emulation library. acx_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" # The ordering *is* (sometimes) important. Some notes on the # individual items follow: # pthreads: AIX (must check this before -lpthread) # none: in case threads are in libc; should be tried before -Kthread and # other compiler flags to prevent continual compiler warnings # -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) # -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) # -pthreads: Solaris/gcc # -mthreads: Mingw32/gcc, Lynx/gcc # -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it # doesn't hurt to check since this sometimes defines pthreads too; # also defines -D_REENTRANT) # ... -mt is also the pthreads flag for HP/aCC # pthread: Linux, etcetera # --thread-safe: KAI C++ # pthread-config: use pthread-config program (for GNU Pth library) case "${host_cpu}-${host_os}" in *solaris*) # On Solaris (at least, for some versions), libc contains stubbed # (non-functional) versions of the pthreads routines, so link-based # tests will erroneously succeed. (We need to link with -pthreads/-mt/ # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather # a function called by this macro, so we could check for that, but # who knows whether they'll stub that too in a future libc.) So, # we'll just look for -pthreads and -lpthread first: acx_pthread_flags="-pthreads pthread -mt -pthread $acx_pthread_flags" ;; esac if test x"$acx_pthread_ok" = xno; then for flag in $acx_pthread_flags; do case $flag in none) AC_MSG_CHECKING([whether pthreads work without any flags]) ;; -*) AC_MSG_CHECKING([whether pthreads work with $flag]) PTHREAD_CFLAGS="$flag" ;; pthread-config) AC_CHECK_PROG(acx_pthread_config, pthread-config, yes, no) if test x"$acx_pthread_config" = xno; then continue; fi PTHREAD_CFLAGS="`pthread-config --cflags`" PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" ;; *) AC_MSG_CHECKING([for the pthreads library -l$flag]) PTHREAD_LIBS="-l$flag" ;; esac save_LIBS="$LIBS" save_CFLAGS="$CFLAGS" LIBS="$PTHREAD_LIBS $LIBS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" # Check for various functions. We must include pthread.h, # since some functions may be macros. (On the Sequent, we # need a special flag -Kthread to make this header compile.) # We check for pthread_join because it is in -lpthread on IRIX # while pthread_create is in libc. We check for pthread_attr_init # due to DEC craziness with -lpthreads. We check for # pthread_cleanup_push because it is one of the few pthread # functions on Solaris that doesn't have a non-functional libc stub. # We try pthread_create on general principles. AC_TRY_LINK([#include ], [pthread_t th; pthread_join(th, 0); pthread_attr_init(0); pthread_cleanup_push(0, 0); pthread_create(0,0,0,0); pthread_cleanup_pop(0); ], [acx_pthread_ok=yes]) LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" AC_MSG_RESULT($acx_pthread_ok) if test "x$acx_pthread_ok" = xyes; then break; fi PTHREAD_LIBS="" PTHREAD_CFLAGS="" done fi # Various other checks: if test "x$acx_pthread_ok" = xyes; then save_LIBS="$LIBS" LIBS="$PTHREAD_LIBS $LIBS" save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. AC_MSG_CHECKING([for joinable pthread attribute]) attr_name=unknown for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do AC_TRY_LINK([#include ], [int attr=$attr; return attr;], [attr_name=$attr; break]) done AC_MSG_RESULT($attr_name) if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name, [Define to necessary symbol if this constant uses a non-standard name on your system.]) fi AC_MSG_CHECKING([if more special flags are required for pthreads]) flag=no case "${host_cpu}-${host_os}" in *-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";; *solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";; esac AC_MSG_RESULT(${flag}) if test "x$flag" != xno; then PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" fi LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" # More AIX lossage: must compile with xlc_r or cc_r if test x"$GCC" != xyes; then AC_CHECK_PROGS(PTHREAD_CC, xlc_r cc_r, ${CC}) else PTHREAD_CC=$CC fi else PTHREAD_CC="$CC" fi AC_SUBST(PTHREAD_LIBS) AC_SUBST(PTHREAD_CFLAGS) AC_SUBST(PTHREAD_CC) # Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: if test x"$acx_pthread_ok" = xyes; then ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1]) : else acx_pthread_ok=no $2 fi AC_LANG_RESTORE ])dnl ACX_PTHREAD haildb-2.3.2/m4/pandora_have_libboost_date_time.m40000644000175000017500000000277011513177357022772 0ustar00pcrewspcrews00000000000000dnl Copyright (C) 2010 Monty Taylor dnl This file is free software; Monty Taylor dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. AC_DEFUN([_PANDORA_SEARCH_BOOST_DATE_TIME],[ AC_REQUIRE([AC_LIB_PREFIX]) AC_REQUIRE([ACX_PTHREAD]) dnl -------------------------------------------------------------------- dnl Check for Boost.Date_Time dnl -------------------------------------------------------------------- AC_LANG_PUSH(C++) AC_LIB_HAVE_LINKFLAGS(boost_date_time-mt,,[ #include ],[ boost::gregorian::date weekstart(2002,2,1); ]) AS_IF([test "x${ac_cv_libboost_date_time_mt}" = "xno"],[ AC_LIB_HAVE_LINKFLAGS(boost_date_time,,[ #include ],[ boost::gregorian::date weekstart(2002,2,1); ]) ]) AC_LANG_POP() AM_CONDITIONAL(HAVE_BOOST_DATE_TIME, [test "x${ac_cv_libboost_date_time}" = "xyes" -o "x${ac_cv_libboost_date_time_mt}" = "xyes"]) BOOST_LIBS="${BOOST_LIBS} ${LTLIBBOOST_DATE_TIME_MT} ${LTLIBBOOST_DATE_TIME}" AC_SUBST(BOOST_LIBS) ]) AC_DEFUN([PANDORA_HAVE_BOOST_DATE_TIME],[ PANDORA_HAVE_BOOST($1) _PANDORA_SEARCH_BOOST_DATE_TIME($1) ]) AC_DEFUN([PANDORA_REQUIRE_BOOST_DATE_TIME],[ PANDORA_REQUIRE_BOOST($1) _PANDORA_SEARCH_BOOST_DATE_TIME($1) AS_IF([test "x${ac_cv_libboost_date_time}" = "xno" -a "x${ac_cv_libboost_date_time_mt}" = "xno"], AC_MSG_ERROR([Boost.Date_Time is required for ${PACKAGE}])) ]) haildb-2.3.2/m4/pandora_have_innodb.m40000644000175000017500000000234111513177357020405 0ustar00pcrewspcrews00000000000000dnl Copyright (C) 2009 Sun Microsystems dnl This file is free software; Sun Microsystems dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. AC_DEFUN([_PANDORA_SEARCH_LIBINNODB],[ AC_REQUIRE([AC_LIB_PREFIX]) dnl -------------------------------------------------------------------- dnl Check for libinnodb dnl -------------------------------------------------------------------- AC_ARG_ENABLE([libinnodb], [AS_HELP_STRING([--disable-libinnodb], [Build with libinnodb support @<:@default=on@:>@])], [ac_enable_libinnodb="$enableval"], [ac_enable_libinnodb="yes"]) AS_IF([test "x$ac_enable_libinnodb" = "xyes"],[ AC_LIB_HAVE_LINKFLAGS(innodb,,[ #include ],[ ib_u64_t ib_api_version(void); ]) ],[ ac_cv_libinnodb="no" ]) AM_CONDITIONAL(HAVE_LIBINNODB, [test "x${ac_cv_libinnodb}" = "xyes"]) ]) AC_DEFUN([PANDORA_HAVE_LIBINNODB],[ AC_REQUIRE([_PANDORA_SEARCH_LIBINNODB]) ]) AC_DEFUN([PANDORA_REQUIRE_LIBINNODB],[ AC_REQUIRE([PANDORA_HAVE_LIBINNODB]) AS_IF([test "x${ac_cv_libinnodb}" = "xno"], AC_MSG_ERROR([libinnodb is required for ${PACKAGE}])) ]) haildb-2.3.2/m4/pandora_pthread.m40000644000175000017500000001633511513177357017570 0ustar00pcrewspcrews00000000000000dnl -*- mode: m4; c-basic-offset: 2; indent-tabs-mode: nil; -*- dnl vim:expandtab:shiftwidth=2:tabstop=2:smarttab: dnl dnl pandora-build: A pedantic build system dnl Copyright (C) 2009 Sun Microsystems, Inc. dnl This file is free software; Sun Microsystems dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl dnl From Monty Taylor dnl -------------------------------------------------------------------- dnl Check for libpthread dnl -------------------------------------------------------------------- AC_DEFUN([PANDORA_PTHREAD_YIELD],[ AC_REQUIRE([ACX_PTHREAD]) save_CFLAGS="${CFLAGS}" save_CXXFLAGS="${CXXFLAGS}" CFLAGS="${PTHREAD_CFLAGS} ${CFLAGS}" CXXFLAGS="${PTHREAD_CFLAGS} ${CXXFLAGS}" dnl Some OSes like Mac OS X have that as a replacement for pthread_yield() AC_CHECK_FUNCS(pthread_yield_np) AC_CACHE_CHECK([if pthread_yield takes zero arguments], [pandora_cv_pthread_yield_zero_arg], [AC_LINK_IFELSE([ AC_LANG_PROGRAM([[ #include ]],[[ pthread_yield(); ]])], [pandora_cv_pthread_yield_zero_arg=yes], [pandora_cv_pthread_yield_zero_arg=no])]) AS_IF([test "$pandora_cv_pthread_yield_zero_arg" = "yes"],[ AC_DEFINE([HAVE_PTHREAD_YIELD_ZERO_ARG], [1], [pthread_yield that doesn't take any arguments]) ]) AC_CACHE_CHECK([if pthread_yield takes one argument], [pandora_cv_pthread_yield_one_arg], [AC_LINK_IFELSE([ AC_LANG_PROGRAM([[ #include ]],[[ pthread_yield(0); ]])], [pandora_cv_pthread_yield_one_arg=yes], [pandora_cv_pthread_yield_one_arg=no])]) AS_IF([test "$pandora_cv_pthread_yield_one_arg" = "yes"],[ AC_DEFINE([HAVE_PTHREAD_YIELD_ONE_ARG], [1], [pthread_yield function with one argument]) ]) AC_CHECK_FUNCS(pthread_attr_getstacksize pthread_attr_setprio \ pthread_attr_setschedparam \ pthread_attr_setstacksize pthread_condattr_create pthread_getsequence_np \ pthread_key_delete pthread_rwlock_rdlock pthread_setprio \ pthread_setprio_np pthread_setschedparam pthread_sigmask \ pthread_attr_create rwlock_init ) # Check definition of pthread_getspecific AC_CACHE_CHECK([args to pthread_getspecific], [pandora_cv_getspecific_args], [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #if !defined(_REENTRANT) #define _REENTRANT #endif #ifndef _POSIX_PTHREAD_SEMANTICS #define _POSIX_PTHREAD_SEMANTICS #endif #include ]], [[ void *pthread_getspecific(pthread_key_t key); pthread_getspecific((pthread_key_t) NULL); ]])], [pandora_cv_getspecific_args=POSIX], [pandora_cv_getspecific_args=other])]) if test "$pandora_cv_getspecific_args" = "other" then AC_DEFINE([HAVE_NONPOSIX_PTHREAD_GETSPECIFIC], [1], [For some non posix threads]) fi # Check definition of pthread_mutex_init AC_CACHE_CHECK([args to pthread_mutex_init], [pandora_cv_mutex_init_args], [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #ifndef _REENTRANT #define _REENTRANT #endif #ifndef _POSIX_PTHREAD_SEMANTICS #define _POSIX_PTHREAD_SEMANTICS #endif #include ]], [[ pthread_mutexattr_t attr; pthread_mutex_t mp; pthread_mutex_init(&mp,&attr); ]])], [pandora_cv_mutex_init_args=POSIX], [pandora_cv_mutex_init_args=other])]) if test "$pandora_cv_mutex_init_args" = "other" then AC_DEFINE([HAVE_NONPOSIX_PTHREAD_MUTEX_INIT], [1], [For some non posix threads]) fi #---END: #---START: Used in for client configure # Check definition of readdir_r AC_CACHE_CHECK([args to readdir_r], [pandora_cv_readdir_r], [AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #ifndef _REENTRANT #define _REENTRANT #endif #ifndef _POSIX_PTHREAD_SEMANTICS #define _POSIX_PTHREAD_SEMANTICS #endif #include #include ]], [[ int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result); readdir_r((DIR *) NULL, (struct dirent *) NULL, (struct dirent **) NULL); ]])], [pandora_cv_readdir_r=POSIX], [pandora_cv_readdir_r=other])]) if test "$pandora_cv_readdir_r" = "POSIX" then AC_DEFINE([HAVE_READDIR_R], [1], [POSIX readdir_r]) fi # Check definition of posix sigwait() AC_CACHE_CHECK([style of sigwait], [pandora_cv_sigwait], [AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #ifndef _REENTRANT #define _REENTRANT #endif #ifndef _POSIX_PTHREAD_SEMANTICS #define _POSIX_PTHREAD_SEMANTICS #endif #include #include ]], [[ #ifndef _AIX sigset_t set; int sig; sigwait(&set,&sig); #endif ]])], [pandora_cv_sigwait=POSIX], [pandora_cv_sigwait=other])]) if test "$pandora_cv_sigwait" = "POSIX" then AC_DEFINE([HAVE_SIGWAIT], [1], [POSIX sigwait]) fi if test "$pandora_cv_sigwait" != "POSIX" then unset pandora_cv_sigwait # Check definition of posix sigwait() AC_CACHE_CHECK([style of sigwait], [pandora_cv_sigwait], [AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #ifndef _REENTRANT #define _REENTRANT #endif #ifndef _POSIX_PTHREAD_SEMANTICS #define _POSIX_PTHREAD_SEMANTICS #endif #include #include ]], [[ sigset_t set; int sig; sigwait(&set); ]])], [pandora_cv_sigwait=NONPOSIX], [pandora_cv_sigwait=other])]) if test "$pandora_cv_sigwait" = "NONPOSIX" then AC_DEFINE([HAVE_NONPOSIX_SIGWAIT], [1], [sigwait with one argument]) fi fi #---END: # Check if pthread_attr_setscope() exists AC_CACHE_CHECK([for pthread_attr_setscope], [pandora_cv_pthread_attr_setscope], [AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #ifndef _REENTRANT #define _REENTRANT #endif #ifndef _POSIX_PTHREAD_SEMANTICS #define _POSIX_PTHREAD_SEMANTICS #endif #include ]], [[ pthread_attr_t thr_attr; pthread_attr_setscope(&thr_attr,0); ]])], [pandora_cv_pthread_attr_setscope=yes], [pandora_cv_pthread_attr_setscope=no])]) if test "$pandora_cv_pthread_attr_setscope" = "yes" then AC_DEFINE([HAVE_PTHREAD_ATTR_SETSCOPE], [1], [pthread_attr_setscope]) fi AC_CACHE_CHECK([if pthread_yield takes zero arguments], ac_cv_pthread_yield_zero_arg, [AC_TRY_LINK([#define _GNU_SOURCE #include #ifdef __cplusplus extern "C" #endif ], [ pthread_yield(); ], ac_cv_pthread_yield_zero_arg=yes, ac_cv_pthread_yield_zero_arg=yeso)]) if test "$ac_cv_pthread_yield_zero_arg" = "yes" then AC_DEFINE([HAVE_PTHREAD_YIELD_ZERO_ARG], [1], [pthread_yield that doesn't take any arguments]) fi AC_CACHE_CHECK([if pthread_yield takes 1 argument], ac_cv_pthread_yield_one_arg, [AC_TRY_LINK([#define _GNU_SOURCE #include #ifdef __cplusplus extern "C" #endif ], [ pthread_yield(0); ], ac_cv_pthread_yield_one_arg=yes, ac_cv_pthread_yield_one_arg=no)]) if test "$ac_cv_pthread_yield_one_arg" = "yes" then AC_DEFINE([HAVE_PTHREAD_YIELD_ONE_ARG], [1], [pthread_yield function with one argument]) fi CFLAGS="${save_CFLAGS}" CXXFLAGS="${save_CXXFLAGS}" ]) AC_DEFUN([_PANDORA_SEARCH_PTHREAD],[ AC_REQUIRE([ACX_PTHREAD]) LIBS="${PTHREAD_LIBS} ${LIBS}" AM_CFLAGS="${PTHREAD_CFLAGS} ${AM_CFLAGS}" AM_CXXFLAGS="${PTHREAD_CFLAGS} ${AM_CXXFLAGS}" PANDORA_PTHREAD_YIELD ]) AC_DEFUN([PANDORA_HAVE_PTHREAD],[ AC_REQUIRE([_PANDORA_SEARCH_PTHREAD]) ]) AC_DEFUN([PANDORA_REQUIRE_PTHREAD],[ AC_REQUIRE([PANDORA_HAVE_PTHREAD]) AS_IF([test "x$acx_pthread_ok" != "xyes"],[ AC_MSG_ERROR(could not find libpthread)]) ]) haildb-2.3.2/m4/pandora_have_boost.m40000644000175000017500000000507211513177357020266 0ustar00pcrewspcrews00000000000000dnl Copyright (C) 2010 Monty Taylor dnl This file is free software; Monty Taylor dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. AC_DEFUN([_PANDORA_SEARCH_BOOST],[ AC_REQUIRE([AC_LIB_PREFIX]) dnl -------------------------------------------------------------------- dnl Check for boost dnl -------------------------------------------------------------------- AC_ARG_ENABLE([boost], [AS_HELP_STRING([--disable-boost], [Build with boost support @<:@default=on@:>@])], [ac_enable_boost="$enableval"], [ac_enable_boost="yes"]) AS_IF([test "x$ac_enable_boost" = "xyes"],[ dnl link against libc because we're just looking for headers here AC_LANG_PUSH(C++) AC_LIB_HAVE_LINKFLAGS(c,, [#include ], [boost::pool<> test_pool(1);], [system]) AC_LANG_POP() ],[ ac_cv_boost="no" ]) AS_IF([test "x$1" != "x"],[ AC_CACHE_CHECK([if boost is recent enough], [pandora_cv_recent_boost],[ pandora_need_boost_version=`echo "$1" | perl -nle '/(\d+)\.(\d+)/; printf "%d%0.3d00", $[]1, $[]2 ;'` AS_IF([test "x${pandora_need_boost_version}" = "x000000"],[ pandora_cv_recent_boost=yes ],[ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #if BOOST_VERSION < ${pandora_need_boost_version} # error boost too old! #endif ]],[[]]) ],[ pandora_cv_recent_boost=yes ],[ pandora_cv_recent_boost=no ]) ]) ]) AS_IF([test "x${pandora_cv_recent_boost}" = "xno"],[ ac_cv_boost=no ]) ]) AS_IF([test "x${ac_gcc_profile_mode}" = "xyes"],[ AC_CACHE_CHECK([if boost is recent enough for GCC Profile Mode], [pandora_cv_boost_profile],[ pandora_need_boost_version=104300 AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #if BOOST_VERSION < ${pandora_need_boost_version} # error boost too old! #endif ]],[[]]) ],[ pandora_cv_boost_profile=yes ],[ pandora_cv_boost_profile=no ]) ]) AS_IF([test "x${pandora_cv_boost_profile}" = "xyes"],[ AC_DEFINE([BOOST_DETAIL_NO_CONTAINER_FWD],[1],[Disable forward decl of stl in boost]) ]) ]) AM_CONDITIONAL(HAVE_BOOST, [test "x${ac_cv_boost}" = "xyes"]) ]) AC_DEFUN([PANDORA_HAVE_BOOST],[ _PANDORA_SEARCH_BOOST($1) ]) AC_DEFUN([PANDORA_REQUIRE_BOOST],[ PANDORA_HAVE_BOOST($1) AS_IF([test x$ac_cv_boost = xno], AC_MSG_ERROR([boost is required for ${PACKAGE}])) ]) haildb-2.3.2/m4/pandora_check_compiler_version.m40000644000175000017500000000171411513177357022650 0ustar00pcrewspcrews00000000000000dnl Copyright (C) 2009 Sun Microsystems, Inc. dnl This file is free software; Sun Microsystems, Inc. dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. AC_DEFUN([PANDORA_CHECK_C_VERSION],[ dnl Print version of C compiler AC_MSG_CHECKING("C Compiler version--$GCC") AS_IF([test "$GCC" = "yes"],[ CC_VERSION=`$CC --version | sed 1q` ],[AS_IF([test "$SUNCC" = "yes"],[ CC_VERSION=`$CC -V 2>&1 | sed 1q` ],[ CC_VERSION="" ]) ]) AC_MSG_RESULT("$CC_VERSION") AC_SUBST(CC_VERSION) ]) AC_DEFUN([PANDORA_CHECK_CXX_VERSION], [ dnl Print version of CXX compiler AC_MSG_CHECKING("C++ Compiler version") AS_IF([test "$GCC" = "yes"],[ CXX_VERSION=`$CXX --version | sed 1q` ],[AS_IF([test "$SUNCC" = "yes"],[ CXX_VERSION=`$CXX -V 2>&1 | sed 1q` ],[ CXX_VERSION="" ]) ]) AC_MSG_RESULT("$CXX_VERSION") AC_SUBST(CXX_VERSION) ]) haildb-2.3.2/m4/pandora_have_gcc_atomics.m40000644000175000017500000000225311513177357021411 0ustar00pcrewspcrews00000000000000dnl Copyright (C) 2009 Sun Microsystems, Inc. dnl This file is free software; Sun Microsystems, Inc. dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. #-------------------------------------------------------------------- # Check for GCC Atomic Support #-------------------------------------------------------------------- AC_DEFUN([PANDORA_HAVE_GCC_ATOMICS],[ AC_CACHE_CHECK( [whether the compiler provides atomic builtins], [ac_cv_gcc_atomic_builtins], [AC_LINK_IFELSE( [AC_LANG_PROGRAM([],[[ int foo= -10; int bar= 10; if (!__sync_fetch_and_add(&foo, bar) || foo) return -1; bar= __sync_lock_test_and_set(&foo, bar); if (bar || foo != 10) return -1; bar= __sync_val_compare_and_swap(&bar, foo, 15); if (bar) return -1; return 0; ]])], [ac_cv_gcc_atomic_builtins=yes], [ac_cv_gcc_atomic_builtins=no])]) AS_IF([test "x$ac_cv_gcc_atomic_builtins" = "xyes"],[ AC_DEFINE(HAVE_GCC_ATOMIC_BUILTINS, 1, [Define to 1 if compiler provides atomic builtins.]) ]) ]) haildb-2.3.2/m4/ltsugar.m40000644000175000017500000001042411513177375016107 0ustar00pcrewspcrews00000000000000# ltsugar.m4 -- libtool m4 base layer. -*-Autoconf-*- # # Copyright (C) 2004, 2005, 2007, 2008 Free Software Foundation, Inc. # Written by Gary V. Vaughan, 2004 # # This file is free software; the Free Software Foundation gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. # serial 6 ltsugar.m4 # This is to help aclocal find these macros, as it can't see m4_define. AC_DEFUN([LTSUGAR_VERSION], [m4_if([0.1])]) # lt_join(SEP, ARG1, [ARG2...]) # ----------------------------- # Produce ARG1SEPARG2...SEPARGn, omitting [] arguments and their # associated separator. # Needed until we can rely on m4_join from Autoconf 2.62, since all earlier # versions in m4sugar had bugs. m4_define([lt_join], [m4_if([$#], [1], [], [$#], [2], [[$2]], [m4_if([$2], [], [], [[$2]_])$0([$1], m4_shift(m4_shift($@)))])]) m4_define([_lt_join], [m4_if([$#$2], [2], [], [m4_if([$2], [], [], [[$1$2]])$0([$1], m4_shift(m4_shift($@)))])]) # lt_car(LIST) # lt_cdr(LIST) # ------------ # Manipulate m4 lists. # These macros are necessary as long as will still need to support # Autoconf-2.59 which quotes differently. m4_define([lt_car], [[$1]]) m4_define([lt_cdr], [m4_if([$#], 0, [m4_fatal([$0: cannot be called without arguments])], [$#], 1, [], [m4_dquote(m4_shift($@))])]) m4_define([lt_unquote], $1) # lt_append(MACRO-NAME, STRING, [SEPARATOR]) # ------------------------------------------ # Redefine MACRO-NAME to hold its former content plus `SEPARATOR'`STRING'. # Note that neither SEPARATOR nor STRING are expanded; they are appended # to MACRO-NAME as is (leaving the expansion for when MACRO-NAME is invoked). # No SEPARATOR is output if MACRO-NAME was previously undefined (different # than defined and empty). # # This macro is needed until we can rely on Autoconf 2.62, since earlier # versions of m4sugar mistakenly expanded SEPARATOR but not STRING. m4_define([lt_append], [m4_define([$1], m4_ifdef([$1], [m4_defn([$1])[$3]])[$2])]) # lt_combine(SEP, PREFIX-LIST, INFIX, SUFFIX1, [SUFFIX2...]) # ---------------------------------------------------------- # Produce a SEP delimited list of all paired combinations of elements of # PREFIX-LIST with SUFFIX1 through SUFFIXn. Each element of the list # has the form PREFIXmINFIXSUFFIXn. # Needed until we can rely on m4_combine added in Autoconf 2.62. m4_define([lt_combine], [m4_if(m4_eval([$# > 3]), [1], [m4_pushdef([_Lt_sep], [m4_define([_Lt_sep], m4_defn([lt_car]))])]]dnl [[m4_foreach([_Lt_prefix], [$2], [m4_foreach([_Lt_suffix], ]m4_dquote(m4_dquote(m4_shift(m4_shift(m4_shift($@)))))[, [_Lt_sep([$1])[]m4_defn([_Lt_prefix])[$3]m4_defn([_Lt_suffix])])])])]) # lt_if_append_uniq(MACRO-NAME, VARNAME, [SEPARATOR], [UNIQ], [NOT-UNIQ]) # ----------------------------------------------------------------------- # Iff MACRO-NAME does not yet contain VARNAME, then append it (delimited # by SEPARATOR if supplied) and expand UNIQ, else NOT-UNIQ. m4_define([lt_if_append_uniq], [m4_ifdef([$1], [m4_if(m4_index([$3]m4_defn([$1])[$3], [$3$2$3]), [-1], [lt_append([$1], [$2], [$3])$4], [$5])], [lt_append([$1], [$2], [$3])$4])]) # lt_dict_add(DICT, KEY, VALUE) # ----------------------------- m4_define([lt_dict_add], [m4_define([$1($2)], [$3])]) # lt_dict_add_subkey(DICT, KEY, SUBKEY, VALUE) # -------------------------------------------- m4_define([lt_dict_add_subkey], [m4_define([$1($2:$3)], [$4])]) # lt_dict_fetch(DICT, KEY, [SUBKEY]) # ---------------------------------- m4_define([lt_dict_fetch], [m4_ifval([$3], m4_ifdef([$1($2:$3)], [m4_defn([$1($2:$3)])]), m4_ifdef([$1($2)], [m4_defn([$1($2)])]))]) # lt_if_dict_fetch(DICT, KEY, [SUBKEY], VALUE, IF-TRUE, [IF-FALSE]) # ----------------------------------------------------------------- m4_define([lt_if_dict_fetch], [m4_if(lt_dict_fetch([$1], [$2], [$3]), [$4], [$5], [$6])]) # lt_dict_filter(DICT, [SUBKEY], VALUE, [SEPARATOR], KEY, [...]) # -------------------------------------------------------------- m4_define([lt_dict_filter], [m4_if([$5], [], [], [lt_join(m4_quote(m4_default([$4], [[, ]])), lt_unquote(m4_split(m4_normalize(m4_foreach(_Lt_key, lt_car([m4_shiftn(4, $@)]), [lt_if_dict_fetch([$1], _Lt_key, [$2], [$3], [_Lt_key ])])))))])[]dnl ]) haildb-2.3.2/m4/pandora_have_libhashkit.m40000644000175000017500000000241311513177357021256 0ustar00pcrewspcrews00000000000000dnl Copyright (C) 2010 NorthScale dnl This file is free software; NorthScale dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. AC_DEFUN([_PANDORA_SEARCH_LIBHASHKIT],[ AC_REQUIRE([AC_LIB_PREFIX]) dnl -------------------------------------------------------------------- dnl Check for libhashkit dnl -------------------------------------------------------------------- AC_ARG_ENABLE([libhashkit], [AS_HELP_STRING([--disable-libhashkit], [Build with libhashkit support @<:@default=on@:>@])], [ac_enable_libhashkit="$enableval"], [ac_enable_libhashkit="yes"]) AS_IF([test "x$ac_enable_libhashkit" = "xyes"],[ AC_LIB_HAVE_LINKFLAGS(hashkit,,[ #include ],[ hashkit_st foo; hashkit_st *kit = hashkit_create(&foo); hashkit_free(kit); ]) ],[ ac_cv_libhashkit="no" ]) AM_CONDITIONAL(HAVE_LIBHASHKIT, [test "x${ac_cv_libhashkit}" = "xyes"]) ]) AC_DEFUN([PANDORA_HAVE_LIBHASHKIT],[ AC_REQUIRE([_PANDORA_SEARCH_LIBHASHKIT]) ]) AC_DEFUN([PANDORA_REQUIRE_LIBHASHKIT],[ AC_REQUIRE([PANDORA_HAVE_LIBHASHKIT]) AS_IF([test x$ac_cv_libhashkit = xno], AC_MSG_ERROR([libhashkit is required for ${PACKAGE}])) ]) haildb-2.3.2/m4/pandora_libtool.m40000644000175000017500000000156211513177357017601 0ustar00pcrewspcrews00000000000000dnl Copyright (C) 2009 Sun Microsystems, Inc. dnl This file is free software; Sun Microsystems, Inc. dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. AC_DEFUN([PANDORA_LIBTOOL],[ AC_REQUIRE([AC_DISABLE_STATIC]) AC_REQUIRE([AC_PROG_LIBTOOL]) m4_ifndef([LT_PREREQ],[ pandora_have_old_libtool=yes ],[ pandora_have_old_libtool=no ]) AS_IF([test "$SUNCC" = "yes" -a "${pandora_have_old_libtool}" = "yes"],[ AC_MSG_ERROR([Building ${PACKAGE} with Sun Studio requires at least libtool 2.2]) ]) dnl By requiring AC_PROG_LIBTOOL, we should force the macro system to read dnl libtool.m4, where in 2.2 AC_PROG_LIBTOOL is an alias for LT_INIT dnl Then, if we're on 2.2, we should have LT_LANG, so we'll call it. m4_ifdef([LT_LANG],[ LT_LANG(C) LT_LANG(C++) ]) ]) haildb-2.3.2/m4/pandora_have_libtokyocabinet.m40000644000175000017500000000362611513177357022325 0ustar00pcrewspcrews00000000000000dnl Copyright (C) 2009 Sun Microsystems, Inc. dnl This file is free software; Sun Microsystems, Inc. dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl Provides support for finding libtokyocabinet. dnl LIBTOKYOCABINET_CFLAGS will be set, in addition to LIBTOKYOCABINET and LTLIBTOKYOCABINET AC_DEFUN([_PANDORA_SEARCH_LIBTOKYOCABINET],[ AC_REQUIRE([AC_LIB_PREFIX]) dnl -------------------------------------------------------------------- dnl Check for libtokyocabinet dnl -------------------------------------------------------------------- AC_ARG_ENABLE([libtokyocabinet], [AS_HELP_STRING([--disable-libtokyocabinet], [Build with libtokyocabinet support @<:@default=on@:>@])], [ac_enable_libtokyocabinet="$enableval"], [ac_enable_libtokyocabinet="yes"]) AS_IF([test "x$ac_enable_libtokyocabinet" = "xyes"],[ AC_LIB_HAVE_LINKFLAGS(tokyocabinet,,[ #include #include ],[ const char *test= tcversion; bool ret= tcadboptimize(NULL, "params"); ]) ],[ ac_cv_libtokyocabinet="no" ]) AS_IF([test "${ac_cv_libtokyocabinet}" = "no" -a "${ac_enable_libtokyocabinet}" = "yes"],[ PKG_CHECK_MODULES([LIBTOKYOCABINET], [libtokyocabinet >= 1.4.15], [ ac_cv_libtokyocabinet=yes LTLIBTOKYOCABINET=${LIBTOKYOCABINET_LIBS} LIBTOKYOCABINET=${LIBTOKYOCABINET_LIBS} ],[test x = y]) ]) AM_CONDITIONAL(HAVE_LIBTOKYOCABINET, [test "${ac_cv_libtokyocabinet}" = "yes"]) ]) AC_DEFUN([PANDORA_HAVE_LIBTOKYOCABINET],[ AC_REQUIRE([_PANDORA_SEARCH_LIBTOKYOCABINET]) ]) AC_DEFUN([PANDORA_REQUIRE_LIBTOKYOCABINET],[ AC_REQUIRE([_PANDORA_SEARCH_LIBTOKYOCABINET]) AS_IF([test "x${ac_cv_libtokyocabinet}" = "xno"], AC_MSG_ERROR([libtokyocabinet is required for ${PACKAGE}. On Debian systems this is found in libtokyocabinet-dev. On RedHat, in tokyocabinet-devel.])) ]) haildb-2.3.2/m4/pandora_have_libcassandra.m40000644000175000017500000000253311513177357021565 0ustar00pcrewspcrews00000000000000dnl Copyright (C) 2010 Padraig O'Sullivan dnl This file is free software; dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. AC_DEFUN([_PANDORA_SEARCH_LIBCASSANDRA],[ AC_REQUIRE([AC_LIB_PREFIX]) dnl -------------------------------------------------------------------- dnl Check for libcassandra dnl -------------------------------------------------------------------- AC_ARG_ENABLE([libcassandra], [AS_HELP_STRING([--disable-libcassandra], [Build with libcassandra support @<:@default=on@:>@])], [ac_enable_libcassandra="$enableval"], [ac_enable_libcassandra="yes"]) AS_IF([test "x$ac_enable_libcassandra" = "xyes"],[ AC_LANG_PUSH([C++]) AC_LIB_HAVE_LINKFLAGS(cassandra,[thrift],[ #include ],[ libcassandra::CassandraFactory fact("localhost", 9306); ]) AC_LANG_POP() ],[ ac_cv_libcassandra="no" ]) AM_CONDITIONAL(HAVE_LIBCASSANDRA, [test "x${ac_cv_libcassandra}" = "xyes"]) ]) AC_DEFUN([PANDORA_HAVE_LIBCASSANDRA],[ AC_REQUIRE([_PANDORA_SEARCH_LIBCASSANDRA]) ]) AC_DEFUN([PANDORA_REQUIRE_LIBCASSANDRA],[ AC_REQUIRE([PANDORA_HAVE_LIBCASSANDRA]) AS_IF([test "x$ac_cv_libcassandra" = "xno"],[ AC_MSG_ERROR([libcassandra is required for ${PACKAGE}]) ]) ]) haildb-2.3.2/m4/pandora_have_libevent.m40000644000175000017500000000335511513177357020752 0ustar00pcrewspcrews00000000000000dnl Copyright (C) 2009 Sun Microsystems, Inc. dnl This file is free software; Sun Microsystems, Inc. dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. #-------------------------------------------------------------------- # Check for libevent #-------------------------------------------------------------------- AC_DEFUN([_PANDORA_SEARCH_LIBEVENT],[ AC_REQUIRE([AC_LIB_PREFIX]) AC_LIB_HAVE_LINKFLAGS(event,, [ #include #include #include #include ],[ struct bufferevent bev; bufferevent_settimeout(&bev, 1, 1); event_init(); event_loop(EVLOOP_ONCE); ]) AM_CONDITIONAL(HAVE_LIBEVENT, [test "x${ac_cv_libevent}" = "xyes"]) AS_IF([test "x${ac_cv_libevent}" = "xyes"],[ save_LIBS="${LIBS}" LIBS="${LIBS} ${LTLIBEVENT}" AC_CHECK_FUNCS(event_base_new) AC_CHECK_FUNCS(event_base_free) AC_CHECK_FUNCS(event_base_get_method) LIBS="$save_LIBS" ]) ]) AC_DEFUN([_PANDORA_HAVE_LIBEVENT],[ AC_ARG_ENABLE([libevent], [AS_HELP_STRING([--disable-libevent], [Build with libevent support @<:@default=on@:>@])], [ac_enable_libevent="$enableval"], [ac_enable_libevent="yes"]) _PANDORA_SEARCH_LIBEVENT ]) AC_DEFUN([PANDORA_HAVE_LIBEVENT],[ AC_REQUIRE([_PANDORA_HAVE_LIBEVENT]) ]) AC_DEFUN([_PANDORA_REQUIRE_LIBEVENT],[ ac_enable_libevent="yes" _PANDORA_SEARCH_LIBEVENT AS_IF([test x$ac_cv_libevent = xno],[ AC_MSG_ERROR([libevent is required for ${PACKAGE}. On Debian this can be found in libevent-dev. On RedHat this can be found in libevent-devel.]) ]) ]) AC_DEFUN([PANDORA_REQUIRE_LIBEVENT],[ AC_REQUIRE([_PANDORA_REQUIRE_LIBEVENT]) ]) haildb-2.3.2/m4/pandora_version.m40000644000175000017500000000065711513177357017626 0ustar00pcrewspcrews00000000000000dnl Copyright (C) 2009 Sun Microsystems, Inc. dnl This file is free software; Sun Microsystems, Inc. dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. AC_DEFUN([PANDORA_VERSION],[ PANDORA_HEX_VERSION=`echo $VERSION | sed 's|[\-a-z0-9]*$||' | \ awk -F. '{printf "0x%0.2d%0.3d%0.3d", $[]1, $[]2, $[]3}'` AC_SUBST([PANDORA_HEX_VERSION]) ]) haildb-2.3.2/m4/pandora_have_libgearman.m40000644000175000017500000000247411513177357021244 0ustar00pcrewspcrews00000000000000dnl Copyright (C) 2009 Sun Microsystems, Inc. dnl This file is free software; Sun Microsystems, Inc. dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. AC_DEFUN([_PANDORA_SEARCH_LIBGEARMAN],[ AC_REQUIRE([AC_LIB_PREFIX]) dnl -------------------------------------------------------------------- dnl Check for libgearman dnl -------------------------------------------------------------------- AC_ARG_ENABLE([libgearman], [AS_HELP_STRING([--disable-libgearman], [Build with libgearman support @<:@default=on@:>@])], [ac_enable_libgearman="$enableval"], [ac_enable_libgearman="yes"]) AS_IF([test "x$ac_enable_libgearman" = "xyes"],[ AC_LIB_HAVE_LINKFLAGS(gearman,,[ #include ],[ gearman_client_st gearman_client; gearman_client_context(&gearman_client); ]) ],[ ac_cv_libgearman="no" ]) AM_CONDITIONAL(HAVE_LIBGEARMAN, [test "x${ac_cv_libgearman}" = "xyes"]) ]) AC_DEFUN([PANDORA_HAVE_LIBGEARMAN],[ AC_REQUIRE([_PANDORA_SEARCH_LIBGEARMAN]) ]) AC_DEFUN([PANDORA_REQUIRE_LIBGEARMAN],[ AC_REQUIRE([PANDORA_HAVE_LIBGEARMAN]) AS_IF([test "x${ac_cv_libgearman}" = "xno"], AC_MSG_ERROR([At least version 0.10 of libgearman is required for ${PACKAGE}])) ]) haildb-2.3.2/m4/pandora_have_libz.m40000644000175000017500000000236511513177357020102 0ustar00pcrewspcrews00000000000000dnl Copyright (C) 2009 Sun Microsystems, Inc. dnl This file is free software; Sun Microsystems, Inc. dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. #-------------------------------------------------------------------- # Check for libz #-------------------------------------------------------------------- AC_DEFUN([_PANDORA_SEARCH_LIBZ],[ AC_REQUIRE([AC_LIB_PREFIX]) AC_LIB_HAVE_LINKFLAGS(z,, [ #include ],[ crc32(0, Z_NULL, 0); ]) AM_CONDITIONAL(HAVE_LIBZ, [test "x${ac_cv_libz}" = "xyes"]) ]) AC_DEFUN([_PANDORA_HAVE_LIBZ],[ AC_ARG_ENABLE([libz], [AS_HELP_STRING([--disable-libz], [Build with libz support @<:@default=on@:>@])], [ac_enable_libz="$enableval"], [ac_enable_libz="yes"]) _PANDORA_SEARCH_LIBZ ]) AC_DEFUN([PANDORA_HAVE_LIBZ],[ AC_REQUIRE([_PANDORA_HAVE_LIBZ]) ]) AC_DEFUN([_PANDORA_REQUIRE_LIBZ],[ ac_enable_libz="yes" _PANDORA_SEARCH_LIBZ AS_IF([test x$ac_cv_libz = xno],[ AC_MSG_ERROR([libz is required for ${PACKAGE}. On Debian this can be found in zlib1g-dev. On RedHat this can be found in zlib-devel.]) ]) ]) AC_DEFUN([PANDORA_REQUIRE_LIBZ],[ AC_REQUIRE([_PANDORA_REQUIRE_LIBZ]) ]) haildb-2.3.2/m4/pandora_run_cpplint.m40000644000175000017500000000047211513177357020471 0ustar00pcrewspcrews00000000000000dnl Copyright (C) 2009 Sun Microsystems, Inc. dnl This file is free software; Sun Microsystems, Inc. dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. AC_DEFUN([PANDORA_RUN_CPPLINT],[ m4_syscmd([python config/make-lint.py]) ]) haildb-2.3.2/m4/pandora_have_better_malloc.m40000644000175000017500000000344011513177357021751 0ustar00pcrewspcrews00000000000000dnl Copyright (C) 2009 Sun Microsystems, Inc. dnl This file is free software; Sun Microsystems, Inc. dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. AC_DEFUN([PANDORA_HAVE_BETTER_MALLOC],[ AC_REQUIRE([AC_LIB_PREFIX]) AC_ARG_ENABLE([umem], [AS_HELP_STRING([--enable-umem], [Enable linking with libumem @<:@default=off@:>@])], [ac_enable_umem="$enableval"],[ case "$target_os" in *solaris*) ac_enable_umem="yes" ;; *) ac_enable_umem="no" ;; esac ]) AC_ARG_ENABLE([tcmalloc], [AS_HELP_STRING([--enable-tcmalloc], [Enable linking with tcmalloc @<:@default=off@:>@])], [ac_enable_tcmalloc="$enableval"], [ac_enable_tcmalloc="no"]) AC_ARG_ENABLE([mtmalloc], [AS_HELP_STRING([--disable-mtmalloc], [Enable linking with mtmalloc @<:@default=on@:>@])], [ac_enable_mtmalloc="$enableval"], [ac_enable_mtmalloc="yes"]) save_LIBS="${LIBS}" LIBS= AS_IF([test "x$ac_enable_umem" = "xyes"],[ AC_CHECK_LIB(umem,malloc,[],[]) ],[ case "$target_os" in *linux*) AS_IF([test "x$ac_enable_tcmalloc" != "xno"],[ AC_CHECK_LIB(tcmalloc-minimal,malloc,[],[]) AS_IF([test "x$ac_cv_lib_tcmalloc_minimal_malloc" != "xyes"],[ AC_CHECK_LIB(tcmalloc,malloc,[],[]) ]) ]) ;; *solaris*) AS_IF([test "x$ac_enable_mtmalloc" != "xno"],[ AC_CHECK_LIB(mtmalloc,malloc,[],[]) ]) ;; esac ]) BETTER_MALLOC_LIBS="${LIBS}" LIBS="${save_LIBS}" AC_SUBST([BETTER_MALLOC_LIBS]) ]) AC_DEFUN([PANDORA_USE_BETTER_MALLOC],[ AC_REQUIRE([PANDORA_HAVE_BETTER_MALLOC]) LIBS="${LIBS} ${BETTER_MALLOC_LIBS}" ]) haildb-2.3.2/m4/pandora_vc_build.m40000644000175000017500000001172511513177357017726 0ustar00pcrewspcrews00000000000000dnl Copyright (C) 2009 Sun Microsystems, Inc. dnl This file is free software; Sun Microsystems, Inc. dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. AC_DEFUN([PANDORA_TEST_VC_DIR],[ pandora_building_from_vc=no if test -d ".bzr" ; then pandora_building_from_bzr=yes pandora_building_from_vc=yes else pandora_building_from_bzr=no fi if test -d ".svn" ; then pandora_building_from_svn=yes pandora_building_from_vc=yes else pandora_building_from_svn=no fi if test -d ".hg" ; then pandora_building_from_hg=yes pandora_building_from_vc=yes else pandora_building_from_hg=no fi if test -d ".git" ; then pandora_building_from_git=yes pandora_building_from_vc=yes else pandora_building_from_git=no fi ]) AC_DEFUN([PANDORA_BUILDING_FROM_VC],[ m4_syscmd(PANDORA_TEST_VC_DIR m4_if(PCT_NO_VC_CHANGELOG,yes,[ vc_changelog=no ],[ vc_changelog=yes ]) [ PANDORA_RELEASE_DATE=`date +%Y.%m` PANDORA_RELEASE_NODOTS_DATE=`date +%Y%m` # Set some defaults PANDORA_VC_REVNO="0" PANDORA_VC_REVID="unknown" PANDORA_VC_BRANCH="bzr-export" if test "${pandora_building_from_bzr}" = "yes"; then echo "# Grabbing changelog and version information from bzr" PANDORA_BZR_REVNO=`bzr revno` if test "x$PANDORA_BZR_REVNO" != "x${PANDORA_VC_REVNO}" ; then PANDORA_VC_REVNO="${PANDORA_BZR_REVNO}" PANDORA_VC_REVID=`bzr log -r-1 --show-ids | grep revision-id | cut -f2 -d' ' | head -1` PANDORA_VC_BRANCH=`bzr nick` if test "x${vc_changelog}" = "xyes"; then bzr log --gnu > ChangeLog fi fi fi if ! test -d config ; then mkdir -p config fi if test "${pandora_building_from_bzr}" = "yes" -o ! -f config/pandora_vc_revinfo ; then cat > config/pandora_vc_revinfo.tmp </dev/null 2>&1 ; then mv config/pandora_vc_revinfo.tmp config/pandora_vc_revinfo fi rm -f config/pandora_vc_revinfo.tmp fi ]) ]) AC_DEFUN([_PANDORA_READ_FROM_FILE],[ $1=`grep $1 $2 | cut -f2 -d=` ]) AC_DEFUN([PANDORA_VC_VERSION],[ AC_REQUIRE([PANDORA_BUILDING_FROM_VC]) PANDORA_TEST_VC_DIR AS_IF([test -f ${srcdir}/config/pandora_vc_revinfo],[ _PANDORA_READ_FROM_FILE([PANDORA_VC_REVNO],${srcdir}/config/pandora_vc_revinfo) _PANDORA_READ_FROM_FILE([PANDORA_VC_REVID],${srcdir}/config/pandora_vc_revinfo) _PANDORA_READ_FROM_FILE([PANDORA_VC_BRANCH], ${srcdir}/config/pandora_vc_revinfo) _PANDORA_READ_FROM_FILE([PANDORA_RELEASE_DATE], ${srcdir}/config/pandora_vc_revinfo) _PANDORA_READ_FROM_FILE([PANDORA_RELEASE_NODOTS_DATE], ${srcdir}/config/pandora_vc_revinfo) ]) AS_IF([test "x${PANDORA_VC_BRANCH}" != x"${PACKAGE}"],[ PANDORA_RELEASE_COMMENT="${PANDORA_VC_BRANCH}" ],[ PANDORA_RELEASE_COMMENT="trunk" ]) PANDORA_RELEASE_VERSION="${PANDORA_RELEASE_DATE}.${PANDORA_VC_REVNO}" PANDORA_RELEASE_ID="${PANDORA_RELEASE_NODOTS_DATE}${PANDORA_VC_REVNO}" VERSION="${PANDORA_RELEASE_VERSION}" AC_DEFINE_UNQUOTED([PANDORA_RELEASE_VERSION],["${PANDORA_RELEASE_VERSION}"], [The real version of the software]) AC_SUBST(PANDORA_VC_REVNO) AC_SUBST(PANDORA_VC_REVID) AC_SUBST(PANDORA_VC_BRANCH) AC_SUBST(PANDORA_RELEASE_DATE) AC_SUBST(PANDORA_RELEASE_NODOTS_DATE) AC_SUBST(PANDORA_RELEASE_COMMENT) AC_SUBST(PANDORA_RELEASE_VERSION) AC_SUBST(PANDORA_RELEASE_ID) ]) AC_DEFUN([PANDORA_VC_INFO_HEADER],[ AC_REQUIRE([PANDORA_VC_VERSION]) m4_define([PANDORA_VC_PREFIX],m4_toupper(m4_normalize(AC_PACKAGE_NAME))[_]) AC_DEFINE_UNQUOTED(PANDORA_VC_PREFIX[VC_REVNO], [$PANDORA_VC_REVNO], [Version control revision number]) AC_DEFINE_UNQUOTED(PANDORA_VC_PREFIX[VC_REVID], ["$PANDORA_VC_REVID"], [Version control revision ID]) AC_DEFINE_UNQUOTED(PANDORA_VC_PREFIX[VC_BRANCH], ["$PANDORA_VC_BRANCH"], [Version control branch name]) AC_DEFINE_UNQUOTED(PANDORA_VC_PREFIX[RELEASE_DATE], ["$PANDORA_RELEASE_DATE"], [Release date of version control checkout]) AC_DEFINE_UNQUOTED(PANDORA_VC_PREFIX[RELEASE_NODOTS_DATE], [$PANDORA_RELEASE_NODOTS_DATE], [Numeric formatted release date of checkout]) AC_DEFINE_UNQUOTED(PANDORA_VC_PREFIX[RELEASE_COMMENT], ["$PANDORA_RELEASE_COMMENT"], [Set to trunk if the branch is the main $PACKAGE branch]) AC_DEFINE_UNQUOTED(PANDORA_VC_PREFIX[RELEASE_VERSION], ["$PANDORA_RELEASE_VERSION"], [Release date and revision number of checkout]) AC_DEFINE_UNQUOTED(PANDORA_VC_PREFIX[RELEASE_ID], [$PANDORA_RELEASE_ID], [Numeric formatted release date and revision number of checkout]) ]) haildb-2.3.2/m4/pandora_swig.m40000644000175000017500000000220711513177357017103 0ustar00pcrewspcrews00000000000000dnl -*- mode: m4; c-basic-offset: 2; indent-tabs-mode: nil; -*- dnl vim:expandtab:shiftwidth=2:tabstop=2:smarttab: dnl dnl pandora-build: A pedantic build system dnl Copyright (C) 2009 Sun Microsystems, Inc. dnl This file is free software; Sun Microsystem dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl dnl From Monty Taylor AC_DEFUN([PANDORA_SWIG],[ AC_PROG_SWIG(1.3.31) AC_DEFINE_UNQUOTED([SWIG_TYPE_TABLE], [$PACKAGE], [Type Table name for SWIG symbol table]) dnl Have to hard-code /usr/local/include and /usr/include into the path. dnl I hate this. Why is swig sucking me SWIG="$SWIG \${DEFS} -I\${top_srcdir} -I\${top_builddir} -I/usr/local/include -I/usr/include" AC_SUBST([SWIG]) ]) AC_DEFUN([PANDORA_SWIG_PYTHON3],[ AC_REQUIRE([PANDORA_SWIG]) AS_IF([test "x$SWIG" != "x"],[ AC_CACHE_CHECK([if swig supports Python3], [ac_cv_swig_has_python3_], [ AS_IF([$SWIG -python -help 2>&1 | grep py3 > /dev/null], [ac_cv_swig_has_python3_=yes], [ac_cv_swig_has_python3_=no]) ]) ]) ]) haildb-2.3.2/m4/ac_cxx_compile_stdcxx_0x.m40000644000175000017500000000522411513177357021411 0ustar00pcrewspcrews00000000000000# =========================================================================== # http://autoconf-archive.cryp.to/ac_cxx_compile_stdcxx_0x.html # =========================================================================== # # SYNOPSIS # # AC_CXX_COMPILE_STDCXX_0X # # DESCRIPTION # # Check for baseline language coverage in the compiler for the C++0x # standard. # # LICENSE # # Copyright (c) 2008 Benjamin Kosnik # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. AC_DEFUN([AC_CXX_COMPILE_STDCXX_0X], [ AC_CACHE_CHECK(if g++ supports C++0x features without additional flags, ac_cv_cxx_compile_cxx0x_native, [AC_LANG_SAVE AC_LANG_CPLUSPLUS AC_TRY_COMPILE([ template struct check { static_assert(sizeof(int) <= sizeof(T), "not big enough"); }; typedef check> right_angle_brackets; int a; decltype(a) b; typedef check check_type; check_type c; check_type&& cr = c;],, ac_cv_cxx_compile_cxx0x_native=yes, ac_cv_cxx_compile_cxx0x_native=no) AC_LANG_RESTORE ]) AC_CACHE_CHECK(if g++ supports C++0x features with -std=c++0x, ac_cv_cxx_compile_cxx0x_cxx, [AC_LANG_SAVE AC_LANG_CPLUSPLUS ac_save_CXXFLAGS="$CXXFLAGS" CXXFLAGS="$CXXFLAGS -std=c++0x" AC_TRY_COMPILE([ template struct check { static_assert(sizeof(int) <= sizeof(T), "not big enough"); }; typedef check> right_angle_brackets; int a; decltype(a) b; typedef check check_type; check_type c; check_type&& cr = c;],, ac_cv_cxx_compile_cxx0x_cxx=yes, ac_cv_cxx_compile_cxx0x_cxx=no) CXXFLAGS="$ac_save_CXXFLAGS" AC_LANG_RESTORE ]) AC_CACHE_CHECK(if g++ supports C++0x features with -std=gnu++0x, ac_cv_cxx_compile_cxx0x_gxx, [AC_LANG_SAVE AC_LANG_CPLUSPLUS ac_save_CXXFLAGS="$CXXFLAGS" CXXFLAGS="$CXXFLAGS -std=gnu++0x" AC_TRY_COMPILE([ template struct check { static_assert(sizeof(int) <= sizeof(T), "not big enough"); }; typedef check> right_angle_brackets; int a; decltype(a) b; typedef check check_type; check_type c; check_type&& cr = c;],, ac_cv_cxx_compile_cxx0x_gxx=yes, ac_cv_cxx_compile_cxx0x_gxx=no) CXXFLAGS="$ac_save_CXXFLAGS" AC_LANG_RESTORE ]) if test "$ac_cv_cxx_compile_cxx0x_native" = yes || test "$ac_cv_cxx_compile_cxx0x_cxx" = yes || test "$ac_cv_cxx_compile_cxx0x_gxx" = yes; then AC_DEFINE(HAVE_STDCXX_0X,,[Define if g++ supports C++0x features. ]) fi ]) haildb-2.3.2/m4/pandora_with_ruby.m40000644000175000017500000000503711513177357020152 0ustar00pcrewspcrews00000000000000dnl -*- mode: m4; c-basic-offset: 2; indent-tabs-mode: nil; -*- dnl vim:expandtab:shiftwidth=2:tabstop=2:smarttab: dnl dnl pandora-build: A pedantic build system dnl Copyright (C) 2009 Sun Microsystems, Inc. dnl This file is free software; Sun Microsystems dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl dnl From Monty Taylor AC_DEFUN([PANDORA_WITH_RUBY], [ AC_ARG_WITH([ruby], [AS_HELP_STRING([--with-ruby], [Build Ruby Bindings @<:@default=yes@:>@])], [with_ruby=$withval], [with_ruby=ruby]) AS_IF([test "x$with_ruby" != "xno"],[ AS_IF([test "x$with_ruby" != "xyes"], [ac_chk_ruby=$with_ruby], [ac_chk_ruby=ruby1.8 ruby]) AC_CHECK_PROGS(RUBY,$ac_chk_ruby) ]) AS_IF([test "x$RUBY" != "x"],[ AC_MSG_CHECKING(for ruby devel) dnl need to change quotes to allow square brackets changequote(<<, >>)dnl ruby_prefix=`$RUBY -rrbconfig -e "print Config::CONFIG['archdir']"` strip_ruby_prefix=`$RUBY -rrbconfig -e "print Config::CONFIG['prefix']" | sed 's/\//./g'` RUBY_LIB=`$RUBY -rrbconfig -e "puts Config::CONFIG['ruby_install_name']"` LIBRUBYARG_SHARED=`$RUBY -rrbconfig -e "puts Config::CONFIG['LIBRUBYARG_SHARED']"` RUBY_DIR=`$RUBY -rrbconfig -e "puts Config::CONFIG['archdir']"` RUBY_ARCH_DIR=`echo $RUBY_DIR | sed "s/$strip_ruby_prefix//"` RUBY_LIBDIR=`$RUBY -rrbconfig -e "puts Config::CONFIG['rubylibdir']"` RUBY_INCLUDES="-I$ruby_prefix" changequote([, ])dnl ac_save_CFLAGS="$CFLAGS" ac_save_CPPFLAGS="$CPPFLAGS" ac_save_LDFLAGS="$LDFLAGS" CFLAGS="$ac_save_CFLAGS $RUBY_INCLUDES" CPPFLAGS="$ac_save_CPPFLAGS $RUBY_INCLUDES" LDFLAGS="$ac_save_LDFLAGS $LIBRUBYARG_SHARED" AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], [[VALUE rb_ac_test = rb_define_module("actest");]])],[with_ruby="yes";AC_MSG_RESULT(found)],[with_ruby="no";AC_MSG_RESULT(missing)]) CPPFLAGS="$ac_save_CPPFLAGS" CFLAGS="$ac_save_CFLAGS" LDFLAGS="$ac_save_LDFLAGS" ],[ # This allows 'make clean' in the ruby directory to work when # ruby isn't available RUBY= RUBY_INCLUDES= LIBRUBYARG_SHARED= RUBY_LIB= RUBY_DIR= RUBY_LIBDIR= RUBY_ARCH_DIR= with_ruby="no" ]) AC_SUBST(RUBY_INCLUDES) AC_SUBST(LIBRUBYARG_SHARED) AC_SUBST(RUBY_LIB) AC_SUBST(RUBY_DIR) AC_SUBST(RUBY_LIBDIR) AC_SUBST(RUBY_ARCH_DIR) AS_IF([test "x$RUBY_DIR" = "x"],[with_ruby="no"]) AM_CONDITIONAL(BUILD_RUBY, test "$with_ruby" = "yes") ]) haildb-2.3.2/m4/pandora_have_libdl.m40000644000175000017500000000247511513177357020232 0ustar00pcrewspcrews00000000000000dnl Copyright (C) 2009 Sun Microsystems, Inc. dnl This file is free software; Sun Microsystems, Inc. dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. #-------------------------------------------------------------------- # Check for libdl #-------------------------------------------------------------------- AC_DEFUN([_PANDORA_SEARCH_LIBDL],[ save_LIBS="$LIBS" LIBS="" AC_CHECK_LIB(dl,dlopen) AC_CHECK_FUNCS(dlopen) LIBDL_LIBS="$LIBS" LIBS="${save_LIBS}" AC_SUBST(LIBDL_LIBS) AM_CONDITIONAL(HAVE_LIBDL, [test "x${ac_cv_func_dlopen}" = "xyes"]) ]) AC_DEFUN([_PANDORA_HAVE_LIBDL],[ AC_ARG_ENABLE([libdl], [AS_HELP_STRING([--disable-libdl], [Build with libdl support @<:@default=on@:>@])], [ac_enable_libdl="$enableval"], [ac_enable_libdl="yes"]) _PANDORA_SEARCH_LIBDL ]) AC_DEFUN([PANDORA_HAVE_LIBDL],[ AC_REQUIRE([_PANDORA_HAVE_LIBDL]) ]) AC_DEFUN([_PANDORA_REQUIRE_LIBDL],[ ac_enable_libdl="yes" _PANDORA_SEARCH_LIBDL AS_IF([test "$ac_cv_func_dlopen" != "yes"],[ AC_MSG_ERROR([libdl/dlopen() is required for ${PACKAGE}. On Debian this can be found in libc6-dev. On RedHat this can be found in glibc-devel.]) ]) ]) AC_DEFUN([PANDORA_REQUIRE_LIBDL],[ AC_REQUIRE([_PANDORA_REQUIRE_LIBDL]) ]) haildb-2.3.2/m4/pandora_have_protobuf.m40000644000175000017500000000554711513177357021007 0ustar00pcrewspcrews00000000000000dnl -*- mode: m4; c-basic-offset: 2; indent-tabs-mode: nil; -*- dnl vim:expandtab:shiftwidth=2:tabstop=2:smarttab: dnl dnl pandora-build: A pedantic build system dnl Copyright (C) 2009 Sun Microsystems, Inc. dnl This file is free software; Sun Microsystems dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl dnl From Monty Taylor dnl -------------------------------------------------------------------- dnl Check for Google Proto Buffers dnl -------------------------------------------------------------------- AC_DEFUN([_PANDORA_SEARCH_LIBPROTOBUF],[ AC_REQUIRE([PANDORA_HAVE_PTHREAD]) AC_LANG_PUSH([C++]) save_CXXFLAGS="${CXXFLAGS}" CXXFLAGS="${PTHREAD_CFLAGS} ${CXXFLAGS}" AC_LIB_HAVE_LINKFLAGS(protobuf,, [#include ], [google::protobuf::FileDescriptor* file;], [system]) CXXFLAGS="${PTHREAD_CFLAGS} ${save_CXXFLAGS}" LIBPROTOBUF="${LIBPROTOBUF} ${PTHREAD_LIBS}" LTLIBPROTOBUF="${LTLIBPROTOBUF} ${PTHREAD_LIBS}" AC_LANG_POP() ]) AC_DEFUN([PANDORA_HAVE_LIBPROTOBUF],[ AC_REQUIRE([_PANDORA_SEARCH_LIBPROTOBUF]) ]) AC_DEFUN([PANDORA_REQUIRE_LIBPROTOBUF],[ AC_REQUIRE([PANDORA_HAVE_LIBPROTOBUF]) AS_IF([test x$ac_cv_libprotobuf = xno], AC_MSG_ERROR([libprotobuf is required for ${PACKAGE}. On Debian this can be found in libprotobuf-dev. On RedHat this can be found in protobuf-devel.])) ]) AC_DEFUN([PANDORA_PROTOBUF_REQUIRE_VERSION],[ AC_REQUIRE([_PANDORA_SEARCH_LIBPROTOBUF]) p_recent_ver=$1 p_recent_ver_major=`echo $p_recent_ver | cut -f1 -d.` p_recent_ver_minor=`echo $p_recent_ver | cut -f2 -d.` p_recent_ver_patch=`echo $p_recent_ver | cut -f3 -d.` p_recent_ver_hex=`printf "%d%03d%03d" $p_recent_ver_major $p_recent_ver_minor $p_recent_ver_patch` AC_LANG_PUSH([C++]) AC_CACHE_CHECK([for protobuf >= $p_recent_ver], [drizzle_cv_protobuf_recent], [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #if GOOGLE_PROTOBUF_VERSION < $p_recent_ver_hex # error Your version of Protobuf is too old #endif ]])], [drizzle_cv_protobuf_recent=yes], [drizzle_cv_protobuf_recent=no])]) AS_IF([test "$drizzle_cv_protobuf_recent" = "no"],[ AC_MSG_ERROR([Your version of Google Protocol Buffers is too old. ${PACKAGE} requires at least version $p_recent_ver]) ]) AC_LANG_POP() ]) AC_DEFUN([_PANDORA_SEARCH_PROTOC],[ AC_REQUIRE([_PANDORA_SEARCH_LIBPROTOBUF]) AC_PATH_PROG([PROTOC],[protoc],[no],[$LIBPROTOBUF_PREFIX/bin:$PATH]) ]) AC_DEFUN([PANDORA_HAVE_PROTOC],[ AC_REQUIRE([_PANDORA_SEARCH_PROTOC]) ]) AC_DEFUN([PANDORA_REQUIRE_PROTOC],[ AC_REQUIRE([PANDORA_HAVE_PROTOC]) AS_IF([test "x$PROTOC" = "xno"],[ AC_MSG_ERROR([Couldn't find the protoc compiler. On Debian this can be found in protobuf-compiler. On RedHat this can be found in protobuf-compiler.]) ]) ]) haildb-2.3.2/m4/pandora_enable_dtrace.m40000644000175000017500000000437011513177357020705 0ustar00pcrewspcrews00000000000000dnl Copyright (C) 2009 Sun Microsystems, Inc. dnl This file is free software; Sun Microsystems, Inc. dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl --------------------------------------------------------------------------- dnl Macro: PANDORA_ENABLE_DTRACE dnl --------------------------------------------------------------------------- AC_DEFUN([PANDORA_ENABLE_DTRACE],[ AC_ARG_ENABLE([dtrace], [AS_HELP_STRING([--disable-dtrace], [Build with support for the DTRACE. @<:@default=on@:>@])], [ac_cv_enable_dtrace="$enableval"], [ac_cv_enable_dtrace="yes"]) AS_IF([test "$ac_cv_enable_dtrace" = "yes"],[ AC_CHECK_PROGS([DTRACE], [dtrace]) AC_CHECK_HEADERS(sys/sdt.h) AS_IF([test "x$ac_cv_prog_DTRACE" = "xdtrace" -a "x${ac_cv_header_sys_sdt_h}" = "xyes"],[ AC_CACHE_CHECK([if dtrace works],[ac_cv_dtrace_works],[ cat >conftest.d <<_ACEOF provider Example { probe increment(int); }; _ACEOF $DTRACE -h -o conftest.h -s conftest.d 2>/dev/zero AS_IF([test $? -eq 0],[ac_cv_dtrace_works=yes], [ac_cv_dtrace_works=no]) rm -f conftest.h conftest.d ]) AS_IF([test "x$ac_cv_dtrace_works" = "xyes"],[ AC_DEFINE([HAVE_DTRACE], [1], [Enables DTRACE Support]) ]) AC_CACHE_CHECK([if dtrace should instrument object files], [ac_cv_dtrace_needs_objects],[ dnl DTrace on MacOSX does not use -G option cat >conftest.d <<_ACEOF provider Example { probe increment(int); }; _ACEOF $DTRACE -G -o conftest.d.o -s conftest.d 2>/dev/zero AS_IF([test $? -eq 0],[ac_cv_dtrace_needs_objects=yes], [ac_cv_dtrace_needs_objects=no]) rm -f conftest.d.o conftest.d ]) AC_SUBST(DTRACEFLAGS) dnl TODO: test for -G on OSX ac_cv_have_dtrace=yes ])]) AM_CONDITIONAL([HAVE_DTRACE], [test "x$ac_cv_dtrace_works" = "xyes"]) AM_CONDITIONAL([DTRACE_NEEDS_OBJECTS], [test "x$ac_cv_dtrace_needs_objects" = "xyes"]) ]) dnl --------------------------------------------------------------------------- dnl End Macro: PANDORA_ENABLE_DTRACE dnl --------------------------------------------------------------------------- haildb-2.3.2/m4/ltoptions.m40000644000175000017500000002724211513177375016467 0ustar00pcrewspcrews00000000000000# Helper functions for option handling. -*- Autoconf -*- # # Copyright (C) 2004, 2005, 2007, 2008 Free Software Foundation, Inc. # Written by Gary V. Vaughan, 2004 # # This file is free software; the Free Software Foundation gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. # serial 6 ltoptions.m4 # This is to help aclocal find these macros, as it can't see m4_define. AC_DEFUN([LTOPTIONS_VERSION], [m4_if([1])]) # _LT_MANGLE_OPTION(MACRO-NAME, OPTION-NAME) # ------------------------------------------ m4_define([_LT_MANGLE_OPTION], [[_LT_OPTION_]m4_bpatsubst($1__$2, [[^a-zA-Z0-9_]], [_])]) # _LT_SET_OPTION(MACRO-NAME, OPTION-NAME) # --------------------------------------- # Set option OPTION-NAME for macro MACRO-NAME, and if there is a # matching handler defined, dispatch to it. Other OPTION-NAMEs are # saved as a flag. m4_define([_LT_SET_OPTION], [m4_define(_LT_MANGLE_OPTION([$1], [$2]))dnl m4_ifdef(_LT_MANGLE_DEFUN([$1], [$2]), _LT_MANGLE_DEFUN([$1], [$2]), [m4_warning([Unknown $1 option `$2'])])[]dnl ]) # _LT_IF_OPTION(MACRO-NAME, OPTION-NAME, IF-SET, [IF-NOT-SET]) # ------------------------------------------------------------ # Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. m4_define([_LT_IF_OPTION], [m4_ifdef(_LT_MANGLE_OPTION([$1], [$2]), [$3], [$4])]) # _LT_UNLESS_OPTIONS(MACRO-NAME, OPTION-LIST, IF-NOT-SET) # ------------------------------------------------------- # Execute IF-NOT-SET unless all options in OPTION-LIST for MACRO-NAME # are set. m4_define([_LT_UNLESS_OPTIONS], [m4_foreach([_LT_Option], m4_split(m4_normalize([$2])), [m4_ifdef(_LT_MANGLE_OPTION([$1], _LT_Option), [m4_define([$0_found])])])[]dnl m4_ifdef([$0_found], [m4_undefine([$0_found])], [$3 ])[]dnl ]) # _LT_SET_OPTIONS(MACRO-NAME, OPTION-LIST) # ---------------------------------------- # OPTION-LIST is a space-separated list of Libtool options associated # with MACRO-NAME. If any OPTION has a matching handler declared with # LT_OPTION_DEFINE, dispatch to that macro; otherwise complain about # the unknown option and exit. m4_defun([_LT_SET_OPTIONS], [# Set options m4_foreach([_LT_Option], m4_split(m4_normalize([$2])), [_LT_SET_OPTION([$1], _LT_Option)]) m4_if([$1],[LT_INIT],[ dnl dnl Simply set some default values (i.e off) if boolean options were not dnl specified: _LT_UNLESS_OPTIONS([LT_INIT], [dlopen], [enable_dlopen=no ]) _LT_UNLESS_OPTIONS([LT_INIT], [win32-dll], [enable_win32_dll=no ]) dnl dnl If no reference was made to various pairs of opposing options, then dnl we run the default mode handler for the pair. For example, if neither dnl `shared' nor `disable-shared' was passed, we enable building of shared dnl archives by default: _LT_UNLESS_OPTIONS([LT_INIT], [shared disable-shared], [_LT_ENABLE_SHARED]) _LT_UNLESS_OPTIONS([LT_INIT], [static disable-static], [_LT_ENABLE_STATIC]) _LT_UNLESS_OPTIONS([LT_INIT], [pic-only no-pic], [_LT_WITH_PIC]) _LT_UNLESS_OPTIONS([LT_INIT], [fast-install disable-fast-install], [_LT_ENABLE_FAST_INSTALL]) ]) ])# _LT_SET_OPTIONS ## --------------------------------- ## ## Macros to handle LT_INIT options. ## ## --------------------------------- ## # _LT_MANGLE_DEFUN(MACRO-NAME, OPTION-NAME) # ----------------------------------------- m4_define([_LT_MANGLE_DEFUN], [[_LT_OPTION_DEFUN_]m4_bpatsubst(m4_toupper([$1__$2]), [[^A-Z0-9_]], [_])]) # LT_OPTION_DEFINE(MACRO-NAME, OPTION-NAME, CODE) # ----------------------------------------------- m4_define([LT_OPTION_DEFINE], [m4_define(_LT_MANGLE_DEFUN([$1], [$2]), [$3])[]dnl ])# LT_OPTION_DEFINE # dlopen # ------ LT_OPTION_DEFINE([LT_INIT], [dlopen], [enable_dlopen=yes ]) AU_DEFUN([AC_LIBTOOL_DLOPEN], [_LT_SET_OPTION([LT_INIT], [dlopen]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the `dlopen' option into LT_INIT's first parameter.]) ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_DLOPEN], []) # win32-dll # --------- # Declare package support for building win32 dll's. LT_OPTION_DEFINE([LT_INIT], [win32-dll], [enable_win32_dll=yes case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-cegcc*) AC_CHECK_TOOL(AS, as, false) AC_CHECK_TOOL(DLLTOOL, dlltool, false) AC_CHECK_TOOL(OBJDUMP, objdump, false) ;; esac test -z "$AS" && AS=as _LT_DECL([], [AS], [0], [Assembler program])dnl test -z "$DLLTOOL" && DLLTOOL=dlltool _LT_DECL([], [DLLTOOL], [0], [DLL creation program])dnl test -z "$OBJDUMP" && OBJDUMP=objdump _LT_DECL([], [OBJDUMP], [0], [Object dumper program])dnl ])# win32-dll AU_DEFUN([AC_LIBTOOL_WIN32_DLL], [AC_REQUIRE([AC_CANONICAL_HOST])dnl _LT_SET_OPTION([LT_INIT], [win32-dll]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the `win32-dll' option into LT_INIT's first parameter.]) ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_WIN32_DLL], []) # _LT_ENABLE_SHARED([DEFAULT]) # ---------------------------- # implement the --enable-shared flag, and supports the `shared' and # `disable-shared' LT_INIT options. # DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'. m4_define([_LT_ENABLE_SHARED], [m4_define([_LT_ENABLE_SHARED_DEFAULT], [m4_if($1, no, no, yes)])dnl AC_ARG_ENABLE([shared], [AS_HELP_STRING([--enable-shared@<:@=PKGS@:>@], [build shared libraries @<:@default=]_LT_ENABLE_SHARED_DEFAULT[@:>@])], [p=${PACKAGE-default} case $enableval in yes) enable_shared=yes ;; no) enable_shared=no ;; *) enable_shared=no # Look at the argument we got. We use all the common list separators. lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," for pkg in $enableval; do IFS="$lt_save_ifs" if test "X$pkg" = "X$p"; then enable_shared=yes fi done IFS="$lt_save_ifs" ;; esac], [enable_shared=]_LT_ENABLE_SHARED_DEFAULT) _LT_DECL([build_libtool_libs], [enable_shared], [0], [Whether or not to build shared libraries]) ])# _LT_ENABLE_SHARED LT_OPTION_DEFINE([LT_INIT], [shared], [_LT_ENABLE_SHARED([yes])]) LT_OPTION_DEFINE([LT_INIT], [disable-shared], [_LT_ENABLE_SHARED([no])]) # Old names: AC_DEFUN([AC_ENABLE_SHARED], [_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[shared]) ]) AC_DEFUN([AC_DISABLE_SHARED], [_LT_SET_OPTION([LT_INIT], [disable-shared]) ]) AU_DEFUN([AM_ENABLE_SHARED], [AC_ENABLE_SHARED($@)]) AU_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AM_ENABLE_SHARED], []) dnl AC_DEFUN([AM_DISABLE_SHARED], []) # _LT_ENABLE_STATIC([DEFAULT]) # ---------------------------- # implement the --enable-static flag, and support the `static' and # `disable-static' LT_INIT options. # DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'. m4_define([_LT_ENABLE_STATIC], [m4_define([_LT_ENABLE_STATIC_DEFAULT], [m4_if($1, no, no, yes)])dnl AC_ARG_ENABLE([static], [AS_HELP_STRING([--enable-static@<:@=PKGS@:>@], [build static libraries @<:@default=]_LT_ENABLE_STATIC_DEFAULT[@:>@])], [p=${PACKAGE-default} case $enableval in yes) enable_static=yes ;; no) enable_static=no ;; *) enable_static=no # Look at the argument we got. We use all the common list separators. lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," for pkg in $enableval; do IFS="$lt_save_ifs" if test "X$pkg" = "X$p"; then enable_static=yes fi done IFS="$lt_save_ifs" ;; esac], [enable_static=]_LT_ENABLE_STATIC_DEFAULT) _LT_DECL([build_old_libs], [enable_static], [0], [Whether or not to build static libraries]) ])# _LT_ENABLE_STATIC LT_OPTION_DEFINE([LT_INIT], [static], [_LT_ENABLE_STATIC([yes])]) LT_OPTION_DEFINE([LT_INIT], [disable-static], [_LT_ENABLE_STATIC([no])]) # Old names: AC_DEFUN([AC_ENABLE_STATIC], [_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[static]) ]) AC_DEFUN([AC_DISABLE_STATIC], [_LT_SET_OPTION([LT_INIT], [disable-static]) ]) AU_DEFUN([AM_ENABLE_STATIC], [AC_ENABLE_STATIC($@)]) AU_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AM_ENABLE_STATIC], []) dnl AC_DEFUN([AM_DISABLE_STATIC], []) # _LT_ENABLE_FAST_INSTALL([DEFAULT]) # ---------------------------------- # implement the --enable-fast-install flag, and support the `fast-install' # and `disable-fast-install' LT_INIT options. # DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'. m4_define([_LT_ENABLE_FAST_INSTALL], [m4_define([_LT_ENABLE_FAST_INSTALL_DEFAULT], [m4_if($1, no, no, yes)])dnl AC_ARG_ENABLE([fast-install], [AS_HELP_STRING([--enable-fast-install@<:@=PKGS@:>@], [optimize for fast installation @<:@default=]_LT_ENABLE_FAST_INSTALL_DEFAULT[@:>@])], [p=${PACKAGE-default} case $enableval in yes) enable_fast_install=yes ;; no) enable_fast_install=no ;; *) enable_fast_install=no # Look at the argument we got. We use all the common list separators. lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," for pkg in $enableval; do IFS="$lt_save_ifs" if test "X$pkg" = "X$p"; then enable_fast_install=yes fi done IFS="$lt_save_ifs" ;; esac], [enable_fast_install=]_LT_ENABLE_FAST_INSTALL_DEFAULT) _LT_DECL([fast_install], [enable_fast_install], [0], [Whether or not to optimize for fast installation])dnl ])# _LT_ENABLE_FAST_INSTALL LT_OPTION_DEFINE([LT_INIT], [fast-install], [_LT_ENABLE_FAST_INSTALL([yes])]) LT_OPTION_DEFINE([LT_INIT], [disable-fast-install], [_LT_ENABLE_FAST_INSTALL([no])]) # Old names: AU_DEFUN([AC_ENABLE_FAST_INSTALL], [_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[fast-install]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the `fast-install' option into LT_INIT's first parameter.]) ]) AU_DEFUN([AC_DISABLE_FAST_INSTALL], [_LT_SET_OPTION([LT_INIT], [disable-fast-install]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the `disable-fast-install' option into LT_INIT's first parameter.]) ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_ENABLE_FAST_INSTALL], []) dnl AC_DEFUN([AM_DISABLE_FAST_INSTALL], []) # _LT_WITH_PIC([MODE]) # -------------------- # implement the --with-pic flag, and support the `pic-only' and `no-pic' # LT_INIT options. # MODE is either `yes' or `no'. If omitted, it defaults to `both'. m4_define([_LT_WITH_PIC], [AC_ARG_WITH([pic], [AS_HELP_STRING([--with-pic], [try to use only PIC/non-PIC objects @<:@default=use both@:>@])], [pic_mode="$withval"], [pic_mode=default]) test -z "$pic_mode" && pic_mode=m4_default([$1], [default]) _LT_DECL([], [pic_mode], [0], [What type of objects to build])dnl ])# _LT_WITH_PIC LT_OPTION_DEFINE([LT_INIT], [pic-only], [_LT_WITH_PIC([yes])]) LT_OPTION_DEFINE([LT_INIT], [no-pic], [_LT_WITH_PIC([no])]) # Old name: AU_DEFUN([AC_LIBTOOL_PICMODE], [_LT_SET_OPTION([LT_INIT], [pic-only]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the `pic-only' option into LT_INIT's first parameter.]) ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_PICMODE], []) ## ----------------- ## ## LTDL_INIT Options ## ## ----------------- ## m4_define([_LTDL_MODE], []) LT_OPTION_DEFINE([LTDL_INIT], [nonrecursive], [m4_define([_LTDL_MODE], [nonrecursive])]) LT_OPTION_DEFINE([LTDL_INIT], [recursive], [m4_define([_LTDL_MODE], [recursive])]) LT_OPTION_DEFINE([LTDL_INIT], [subproject], [m4_define([_LTDL_MODE], [subproject])]) m4_define([_LTDL_TYPE], []) LT_OPTION_DEFINE([LTDL_INIT], [installable], [m4_define([_LTDL_TYPE], [installable])]) LT_OPTION_DEFINE([LTDL_INIT], [convenience], [m4_define([_LTDL_TYPE], [convenience])]) haildb-2.3.2/m4/pandora_have_libboost_iostreams.m40000644000175000017500000000336011513177357023041 0ustar00pcrewspcrews00000000000000dnl Copyright (C) 2010 Andrew Hutchings dnl This file is free software; Andrew Hutchings dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. AC_DEFUN([_PANDORA_SEARCH_BOOST_IOSTREAMS],[ AC_REQUIRE([AC_LIB_PREFIX]) dnl -------------------------------------------------------------------- dnl Check for Boost.Iostreams dnl -------------------------------------------------------------------- AC_LANG_PUSH(C++) AC_LIB_HAVE_LINKFLAGS(boost_iostreams-mt,,[ #include #include ],[ const char* input= "hello world"; boost::iostreams::stream in(input, strlen(input)); ]) AS_IF([test "x${ac_cv_libboost_iostreams_mt}" = "xno"],[ AC_LIB_HAVE_LINKFLAGS(boost_iostreams,,[ #include #include ],[ const char* input= "hello world"; boost::iostreams::stream in(input, strlen(input)); ]) ]) AC_LANG_POP() AM_CONDITIONAL(HAVE_BOOST_IOSTREAMS, [test "x${ac_cv_libboost_iostreams}" = "xyes" -o "x${ac_cv_libboost_iostreams_mt}" = "xyes"]) BOOST_LIBS="${BOOST_LIBS} ${LTLIBBOOST_IOSTREAMS_MT} ${LTLIBBOOST_IOSTREAMS}" AC_SUBST(BOOST_LIBS) ]) AC_DEFUN([PANDORA_HAVE_BOOST_IOSTREAMS],[ PANDORA_HAVE_BOOST($1) _PANDORA_SEARCH_BOOST_IOSTREAMS($1) ]) AC_DEFUN([PANDORA_REQUIRE_BOOST_IOSTREAMS],[ PANDORA_REQUIRE_BOOST($1) _PANDORA_SEARCH_BOOST_IOSTREAMS($1) AS_IF([test "x${ac_cv_libboost_iostreams}" = "xno" -a "x${ac_cv_libboost_iostreams_mt}" = "xno"], AC_MSG_ERROR([Boost.Iostreams is required for ${PACKAGE}])) ]) haildb-2.3.2/m4/pandora_header_assert.m40000644000175000017500000000134611513177357020746 0ustar00pcrewspcrews00000000000000dnl Copyright (C) 2009 Sun Microsystems, Inc. dnl This file is free software; Sun Microsystems, Inc. dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. dnl PANDORA_HEADER_ASSERT dnl ---------------- dnl Check whether to enable assertions. AC_DEFUN([PANDORA_HEADER_ASSERT], [ AC_CHECK_HEADERS(assert.h) AC_MSG_CHECKING([whether to enable assertions]) AC_ARG_ENABLE([assert], [AS_HELP_STRING([--disable-assert], [Turn off assertions])], [ac_cv_assert="no"], [ac_cv_assert="yes"]) AC_MSG_RESULT([$ac_cv_assert]) AS_IF([test "$ac_cv_assert" = "no"], [AC_DEFINE(NDEBUG, 1, [Define to 1 if assertions should be disabled.])]) ]) haildb-2.3.2/m4/pandora_have_libldap.m40000644000175000017500000000361511513177357020550 0ustar00pcrewspcrews00000000000000dnl Copyright (C) 2009 Sun Microsystems, Inc. dnl This file is free software; Sun Microsystems, Inc. dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. #-------------------------------------------------------------------- # Check for libldap #-------------------------------------------------------------------- AC_DEFUN([_PANDORA_SEARCH_LIBLDAP],[ AC_REQUIRE([AC_LIB_PREFIX]) AC_LIB_HAVE_LINKFLAGS(ldap,, [#include ], [ LDAP *ldap; ldap_initialize(&ldap, "ldap://localhost/"); ]) AS_IF([test "x$ac_cv_libldap" = "xno"], [ unset ac_cv_libldap unset HAVE_LIBLDAP unset LIBLDAP unset LIBLDAP_PREFIX unset LTLIBLDAP AC_LIB_HAVE_LINKFLAGS(ldap,, [#include ], [ LDAP *ldap; ldap_initialize(&ldap, "ldap://localhost/"); ]) AS_IF([test "x$ac_cv_libldap" = "xyes"], [ ac_cv_ldap_location="" ]) ],[ ac_cv_ldap_location="" ]) AM_CONDITIONAL(HAVE_LIBLDAP, [test "x${ac_cv_libldap}" = "xyes"]) ]) AC_DEFUN([_PANDORA_HAVE_LIBLDAP],[ AC_ARG_ENABLE([libldap], [AS_HELP_STRING([--disable-libldap], [Build with libldap support @<:@default=on@:>@])], [ac_enable_libldap="$enableval"], [ac_enable_libldap="yes"]) _PANDORA_SEARCH_LIBLDAP ]) AC_DEFUN([PANDORA_HAVE_LIBLDAP],[ AC_REQUIRE([_PANDORA_HAVE_LIBLDAP]) ]) AC_DEFUN([_PANDORA_REQUIRE_LIBLDAP],[ ac_enable_libldap="yes" _PANDORA_SEARCH_LIBLDAP AS_IF([test x$ac_cv_libldap = xno],[ AC_MSG_ERROR([libldap is required for ${PACKAGE}. On Debian this can be found in libldap2-dev. On RedHat this can be found in openldap-devel.]) ],[ AC_DEFINE_UNQUOTED(LDAP_HEADER,[${ac_cv_ldap_location}], [Location of ldap header]) ]) ]) AC_DEFUN([PANDORA_REQUIRE_LIBLDAP],[ AC_REQUIRE([_PANDORA_REQUIRE_LIBLDAP]) ]) haildb-2.3.2/m4/pandora_have_libavahi.m40000644000175000017500000000244711513177357020722 0ustar00pcrewspcrews00000000000000dnl Copyright (C) 2009 Sun Microsystems, Inc. dnl This file is free software; Sun Microsystems, Inc. dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. AC_DEFUN([_PANDORA_SEARCH_LIBAVAHI],[ AC_REQUIRE([AC_LIB_PREFIX]) dnl -------------------------------------------------------------------- dnl Check for libavahi dnl -------------------------------------------------------------------- AC_ARG_ENABLE([libavahi], [AS_HELP_STRING([--disable-libavahi], [Build with libavahi support @<:@default=on@:>@])], [ac_enable_libavahi="$enableval"], [ac_enable_libavahi="yes"]) AS_IF([test "x$ac_enable_libavahi" = "xyes"],[ AC_LIB_HAVE_LINKFLAGS(avahi-client,avahi-common,[ #include #include ],[ AvahiSimplePoll *simple_poll= avahi_simple_poll_new(); ]) ],[ ac_cv_libavahi="no" ]) AM_CONDITIONAL(HAVE_LIBAVAHI, [test "x${ac_cv_libavahi}" = "xyes"]) ]) AC_DEFUN([PANDORA_HAVE_LIBAVAHI],[ AC_REQUIRE([_PANDORA_SEARCH_LIBAVAHI]) ]) AC_DEFUN([PANDORA_REQUIRE_LIBAVAHI],[ AC_REQUIRE([_PANDORA_SEARCH_LIBAVAHI]) AS_IF([test "x${ac_cv_libavahi}" = "xno"], AC_MSG_ERROR([libavahi is required for ${PACKAGE}])) ]) haildb-2.3.2/fil/0000755000175000017500000000000011513177437014414 5ustar00pcrewspcrews00000000000000haildb-2.3.2/fil/fil0fil.c0000644000175000017500000040667611513177357016131 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2010, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file fil/fil0fil.c The tablespace memory cache Created 10/25/1995 Heikki Tuuri *******************************************************/ #include "fil0fil.h" #include "mem0mem.h" #include "hash0hash.h" #include "os0file.h" #include "mach0data.h" #include "buf0buf.h" #include "buf0flu.h" #include "log0recv.h" #include "fsp0fsp.h" #include "srv0srv.h" #include "srv0start.h" #include "mtr0mtr.h" #include "mtr0log.h" #include "dict0dict.h" #include "page0page.h" #include "page0zip.h" #ifndef UNIV_HOTBACKUP # include "buf0lru.h" # include "ibuf0ibuf.h" # include "sync0sync.h" # include "os0sync.h" #else /* !UNIV_HOTBACKUP */ static ulint srv_data_read, srv_data_written; #endif /* !UNIV_HOTBACKUP */ #ifdef HAVE_UNISTD_H #include #endif /* IMPLEMENTATION OF THE TABLESPACE MEMORY CACHE ============================================= The tablespace cache is responsible for providing fast read/write access to tablespaces and logs of the database. File creation and deletion is done in other modules which know more of the logic of the operation, however. A tablespace consists of a chain of files. The size of the files does not have to be divisible by the database block size, because we may just leave the last incomplete block unused. When a new file is appended to the tablespace, the maximum size of the file is also specified. At the moment, we think that it is best to extend the file to its maximum size already at the creation of the file, because then we can avoid dynamically extending the file when more space is needed for the tablespace. A block's position in the tablespace is specified with a 32-bit unsigned integer. The files in the chain are thought to be catenated, and the block corresponding to an address n is the nth block in the catenated file (where the first block is named the 0th block, and the incomplete block fragments at the end of files are not taken into account). A tablespace can be extended by appending a new file at the end of the chain. Our tablespace concept is similar to the one of Oracle. To acquire more speed in disk transfers, a technique called disk striping is sometimes used. This means that logical block addresses are divided in a round-robin fashion across several disks. Windows NT supports disk striping, so there we do not need to support it in the database. Disk striping is implemented in hardware in RAID disks. We conclude that it is not necessary to implement it in the database. Oracle 7 does not support disk striping, either. Another trick used at some database sites is replacing tablespace files by raw disks, that is, the whole physical disk drive, or a partition of it, is opened as a single file, and it is accessed through byte offsets calculated from the start of the disk or the partition. This is recommended in some books on database tuning to achieve more speed in i/o. Using raw disk certainly prevents the OS from fragmenting disk space, but it is not clear if it really adds speed. We measured on the Pentium 100 MHz + NT + NTFS file system + EIDE Conner disk only a negligible difference in speed when reading from a file, versus reading from a raw disk. To have fast access to a tablespace or a log file, we put the data structures to a hash table. Each tablespace and log file is given an unique 32-bit identifier. Some operating systems do not support many open files at the same time, though NT seems to tolerate at least 900 open files. Therefore, we put the open files in an LRU-list. If we need to open another file, we may close the file at the end of the LRU-list. When an i/o-operation is pending on a file, the file cannot be closed. We take the file nodes with pending i/o-operations out of the LRU-list and keep a count of pending operations. When an operation completes, we decrement the count and return the file node to the LRU-list if the count drops to zero. */ /** The number of fsyncs done to the log */ UNIV_INTERN ulint fil_n_log_flushes = 0; /** Number of pending redo log flushes */ UNIV_INTERN ulint fil_n_pending_log_flushes = 0; /** Number of pending tablespace flushes */ UNIV_INTERN ulint fil_n_pending_tablespace_flushes = 0; /** The null file address */ UNIV_INTERN const fil_addr_t fil_addr_null = {FIL_NULL, 0}; /** File node of a tablespace or the log data space */ struct fil_node_struct { fil_space_t* space; /*!< backpointer to the space where this node belongs */ char* name; /*!< path to the file */ ibool open; /*!< TRUE if file open */ os_file_t handle; /*!< OS handle to the file, if file open */ ibool is_raw_disk;/*!< TRUE if the 'file' is actually a raw device or a raw disk partition */ ulint size; /*!< size of the file in database pages, 0 if not known yet; the possible last incomplete megabyte may be ignored if space == 0 */ ulint n_pending; /*!< count of pending i/o's on this file; closing of the file is not allowed if this is > 0 */ ulint n_pending_flushes; /*!< count of pending flushes on this file; closing of the file is not allowed if this is > 0 */ ib_int64_t modification_counter;/*!< when we write to the file we increment this by one */ ib_int64_t flush_counter;/*!< up to what modification_counter value we have flushed the modifications to disk */ UT_LIST_NODE_T(fil_node_t) chain; /*!< link field for the file chain */ UT_LIST_NODE_T(fil_node_t) LRU; /*!< link field for the LRU list */ ulint magic_n;/*!< FIL_NODE_MAGIC_N */ }; /** Value of fil_node_struct::magic_n */ #define FIL_NODE_MAGIC_N 89389 /** Tablespace or log data space: let us call them by a common name space */ struct fil_space_struct { char* name; /*!< space name = the path to the first file in it */ ulint id; /*!< space id */ ib_int64_t tablespace_version; /*!< in DISCARD/IMPORT this timestamp is used to check if we should ignore an insert buffer merge request for a page because it actually was for the previous incarnation of the space */ ibool mark; /*!< this is set to TRUE at database startup if the space corresponds to a table in the InnoDB data dictionary; so we can print a warning of orphaned tablespaces */ ibool stop_ios;/*!< TRUE if we want to rename the .ibd file of tablespace and want to stop temporarily posting of new i/o requests on the file */ ibool stop_ibuf_merges; /*!< we set this TRUE when we start deleting a single-table tablespace */ ibool is_being_deleted; /*!< this is set to TRUE when we start deleting a single-table tablespace and its file; when this flag is set no further i/o or flush requests can be placed on this space, though there may be such requests still being processed on this space */ ulint purpose;/*!< FIL_TABLESPACE, FIL_LOG, or FIL_ARCH_LOG */ UT_LIST_BASE_NODE_T(fil_node_t) chain; /*!< base node for the file chain */ ulint size; /*!< space size in pages; 0 if a single-table tablespace whose size we do not know yet; last incomplete megabytes in data files may be ignored if space == 0 */ ulint flags; /*!< compressed page size and file format, or 0 */ ulint n_reserved_extents; /*!< number of reserved free extents for ongoing operations like B-tree page split */ ulint n_pending_flushes; /*!< this is positive when flushing the tablespace to disk; dropping of the tablespace is forbidden if this is positive */ ulint n_pending_ibuf_merges;/*!< this is positive when merging insert buffer entries to a page so that we may need to access the ibuf bitmap page in the tablespade: dropping of the tablespace is forbidden if this is positive */ hash_node_t hash; /*!< hash chain node */ hash_node_t name_hash;/*!< hash chain the name_hash table */ #ifndef UNIV_HOTBACKUP rw_lock_t latch; /*!< latch protecting the file space storage allocation */ #endif /* !UNIV_HOTBACKUP */ UT_LIST_NODE_T(fil_space_t) unflushed_spaces; /*!< list of spaces with at least one unflushed file we have written to */ ibool is_in_unflushed_spaces; /*!< TRUE if this space is currently in unflushed_spaces */ UT_LIST_NODE_T(fil_space_t) space_list; /*!< list of all spaces */ ulint magic_n;/*!< FIL_SPACE_MAGIC_N */ }; /** Value of fil_space_struct::magic_n */ #define FIL_SPACE_MAGIC_N 89472 /** The tablespace memory cache */ typedef struct fil_system_struct fil_system_t; /** The tablespace memory cache; also the totality of logs (the log data space) is stored here; below we talk about tablespaces, but also the ib_logfiles form a 'space' and it is handled here */ struct fil_system_struct { #ifndef UNIV_HOTBACKUP mutex_t mutex; /*!< The mutex protecting the cache */ #endif /* !UNIV_HOTBACKUP */ hash_table_t* spaces; /*!< The hash table of spaces in the system; they are hashed on the space id */ hash_table_t* name_hash; /*!< hash table based on the space name */ UT_LIST_BASE_NODE_T(fil_node_t) LRU; /*!< base node for the LRU list of the most recently used open files with no pending i/o's; if we start an i/o on the file, we first remove it from this list, and return it to the start of the list when the i/o ends; log files and the system tablespace are not put to this list: they are opened after the startup, and kept open until shutdown */ UT_LIST_BASE_NODE_T(fil_space_t) unflushed_spaces; /*!< base node for the list of those tablespaces whose files contain unflushed writes; those spaces have at least one file node where modification_counter > flush_counter */ ulint n_open; /*!< number of files currently open */ ulint max_n_open; /*!< n_open is not allowed to exceed this */ ib_int64_t modification_counter;/*!< when we write to a file we increment this by one */ ulint max_assigned_id;/*!< maximum space id in the existing tables, or assigned during the time the server has been up; at an InnoDB startup we scan the data dictionary and set here the maximum of the space id's of the tables there */ ib_int64_t tablespace_version; /*!< a counter which is incremented for every space object memory creation; every space mem object gets a 'timestamp' from this; in DISCARD/ IMPORT this is used to check if we should ignore an insert buffer merge request */ UT_LIST_BASE_NODE_T(fil_space_t) space_list; /*!< list of all file spaces */ }; /** The tablespace memory cache. This variable is NULL before the module is initialized. */ static fil_system_t* fil_system = NULL; /*******************************************************************//** Frees a space object from the tablespace memory cache. Closes the files in the chain but does not delete them. There must not be any pending i/o's or flushes on the files. @return TRUE if success */ static ibool fil_space_free( /*===========*/ ulint id, /*!< in: space id */ ibool own_mutex); /*!< in: TRUE if own fil_system->mutex */ /************************************************************************ Remove extraneous '.' && '\' && '/' characters from the prefix. Note: Currently it will not handle paths like: ../a/b. @return pointer to normalized path */ UNIV_INLINE const char* fil_normalize_path( /*===============*/ const char* ptr) /*!< in: path to normalize */ { if (*ptr == '.' && *(ptr + 1) == SRV_PATH_SEPARATOR) { /* Skip ./ */ ptr += 2; /* Skip any subsequent slashes. */ while (*ptr == SRV_PATH_SEPARATOR) { ++ptr; } } return(ptr); } /********************************************************************//** Compare two table names. It will compare the two table names using the canonical names. e.g., ./a/b == a/b. TODO: /path/to/a/b == a/b if both /path/to/a/b and a/b refer to the same file. @return = 0 if name1 == name2 < 0 if name1 < name2 > 0 if name1 > name2 */ UNIV_INLINE int fil_tablename_compare( /*==================*/ const char* name1, /*!< in: table name to compare */ const char* name2) /*!< in: table name to compare */ { name1 = fil_normalize_path(name1); name2 = fil_normalize_path(name2); return(strcmp(name1, name2)); } /************************************************************************ NOTE: you must call fil_mutex_enter_and_prepare_for_io() first! Prepares a file node for i/o. Opens the file if it is closed. Updates the pending i/o's field in the node and the system appropriately. Takes the node off the LRU list if it is in the LRU list. The caller must hold the fil_sys mutex. */ static void fil_node_prepare_for_io( /*====================*/ fil_node_t* node, /*!< in: file node */ fil_system_t* system, /*!< in: tablespace memory cache */ fil_space_t* space); /*!< in: space */ /********************************************************************//** Updates the data structures when an i/o operation finishes. Updates the pending i/o's field in the node appropriately. */ static void fil_node_complete_io( /*=================*/ fil_node_t* node, /*!< in: file node */ fil_system_t* system, /*!< in: tablespace memory cache */ ulint type); /*!< in: OS_FILE_WRITE or OS_FILE_READ; marks the node as modified if type == OS_FILE_WRITE */ /*******************************************************************//** Checks if a single-table tablespace for a given table name exists in the tablespace memory cache. @return space id, ULINT_UNDEFINED if not found */ static ulint fil_get_space_id_for_table( /*=======================*/ const char* name); /*!< in: table name in the standard 'databasename/tablename' format */ /*******************************************************************//** Frees a space object from the tablespace memory cache. Closes the files in the chain but does not delete them. There must not be any pending i/o's or flushes on the files. */ static ibool fil_space_free( /*===========*/ /* out: TRUE if success */ ulint id, /* in: space id */ ibool own_mutex);/* in: TRUE if own system->mutex */ /********************************************************************//** Reset variables. */ UNIV_INTERN void fil_var_init(void) /*==============*/ { fil_system = NULL; fil_n_log_flushes = 0; fil_n_pending_log_flushes = 0; fil_n_pending_tablespace_flushes = 0; } /************************************************************************ Reads data from a space to a buffer. Remember that the possible incomplete blocks at the end of file are ignored: they are not taken into account when calculating the byte offset within a space. @return DB_SUCCESS, or DB_TABLESPACE_DELETED if we are trying to do i/o on a tablespace which does not exist */ UNIV_INLINE ulint fil_read( /*=====*/ ibool sync, /*!< in: TRUE if synchronous aio is desired */ ulint space_id, /*!< in: space id */ ulint zip_size, /*!< in: compressed page size in bytes; 0 for uncompressed pages */ ulint block_offset, /*!< in: offset in number of blocks */ ulint byte_offset, /*!< in: remainder of offset in bytes; in aio this must be divisible by the OS block size */ ulint len, /*!< in: how many bytes to read; this must not cross a file boundary; in aio this must be a block size multiple */ void* buf, /*!< in/out: buffer where to store data read; in aio this must be appropriately aligned */ void* message) /*!< in: message for aio handler if non-sync aio used, else ignored */ { return(fil_io(OS_FILE_READ, sync, space_id, zip_size, block_offset, byte_offset, len, buf, message)); } /********************************************************************//** Writes data to a space from a buffer. Remember that the possible incomplete blocks at the end of file are ignored: they are not taken into account when calculating the byte offset within a space. @return DB_SUCCESS, or DB_TABLESPACE_DELETED if we are trying to do i/o on a tablespace which does not exist */ UNIV_INLINE ulint fil_write( /*======*/ ibool sync, /*!< in: TRUE if synchronous aio is desired */ ulint space_id, /*!< in: space id */ ulint zip_size, /*!< in: compressed page size in bytes; 0 for uncompressed pages */ ulint block_offset, /*!< in: offset in number of blocks */ ulint byte_offset, /*!< in: remainder of offset in bytes; in aio this must be divisible by the OS block size */ ulint len, /*!< in: how many bytes to write; this must not cross a file boundary; in aio this must be a block size multiple */ void* buf, /*!< in: buffer from which to write; in aio this must be appropriately aligned */ void* message) /*!< in: message for aio handler if non-sync aio used, else ignored */ { return(fil_io(OS_FILE_WRITE, sync, space_id, zip_size, block_offset, byte_offset, len, buf, message)); } /*******************************************************************//** Returns the table space by a given id, NULL if not found. */ UNIV_INLINE fil_space_t* fil_space_get_by_id( /*================*/ ulint id) /*!< in: space id */ { fil_space_t* space; ut_ad(mutex_own(&fil_system->mutex)); HASH_SEARCH(hash, fil_system->spaces, id, fil_space_t*, space, ut_ad(space->magic_n == FIL_SPACE_MAGIC_N), space->id == id); return(space); } /*******************************************************************//** Returns the table space by a given name, NULL if not found. */ UNIV_INLINE fil_space_t* fil_space_get_by_name( /*==================*/ const char* name) /*!< in: space name */ { fil_space_t* space; ulint fold; ut_ad(mutex_own(&fil_system->mutex)); fold = ut_fold_string(name); HASH_SEARCH(name_hash, fil_system->name_hash, fold, fil_space_t*, space, ut_ad(space->magic_n == FIL_SPACE_MAGIC_N), !fil_tablename_compare(name, space->name)); return(space); } #ifndef UNIV_HOTBACKUP /*******************************************************************//** Returns the version number of a tablespace, -1 if not found. @return version number, -1 if the tablespace does not exist in the memory cache */ UNIV_INTERN ib_int64_t fil_space_get_version( /*==================*/ ulint id) /*!< in: space id */ { fil_space_t* space; ib_int64_t version = -1; ut_ad(fil_system); mutex_enter(&fil_system->mutex); space = fil_space_get_by_id(id); if (space) { version = space->tablespace_version; } mutex_exit(&fil_system->mutex); return(version); } /*******************************************************************//** Returns the latch of a file space. @return latch protecting storage allocation */ UNIV_INTERN rw_lock_t* fil_space_get_latch( /*================*/ ulint id, /*!< in: space id */ ulint* flags) /*!< out: tablespace flags */ { fil_space_t* space; ut_ad(fil_system); mutex_enter(&fil_system->mutex); space = fil_space_get_by_id(id); ut_a(space); if (flags) { *flags = space->flags; } mutex_exit(&fil_system->mutex); return(&(space->latch)); } /*******************************************************************//** Returns the type of a file space. @return FIL_TABLESPACE or FIL_LOG */ UNIV_INTERN ulint fil_space_get_type( /*===============*/ ulint id) /*!< in: space id */ { fil_space_t* space; ut_ad(fil_system); mutex_enter(&fil_system->mutex); space = fil_space_get_by_id(id); ut_a(space); mutex_exit(&fil_system->mutex); return(space->purpose); } #endif /* !UNIV_HOTBACKUP */ /**********************************************************************//** Checks if all the file nodes in a space are flushed. The caller must hold the fil_system mutex. @return TRUE if all are flushed */ static ibool fil_space_is_flushed( /*=================*/ fil_space_t* space) /*!< in: space */ { fil_node_t* node; ut_ad(mutex_own(&fil_system->mutex)); node = UT_LIST_GET_FIRST(space->chain); while (node) { if (node->modification_counter > node->flush_counter) { return(FALSE); } node = UT_LIST_GET_NEXT(chain, node); } return(TRUE); } /*******************************************************************//** Appends a new file to the chain of files of a space. File must be closed. */ UNIV_INTERN void fil_node_create( /*============*/ const char* name, /*!< in: file name (file must be closed) */ ulint size, /*!< in: file size in database blocks, rounded downwards to an integer */ ulint id, /*!< in: space id where to append */ ibool is_raw) /*!< in: TRUE if a raw device or a raw disk partition */ { fil_node_t* node; fil_space_t* space; ut_a(fil_system); ut_a(name); mutex_enter(&fil_system->mutex); node = mem_alloc(sizeof(fil_node_t)); node->name = mem_strdup(name); node->open = FALSE; ut_a(!is_raw || srv_start_raw_disk_in_use); node->is_raw_disk = is_raw; node->size = size; node->magic_n = FIL_NODE_MAGIC_N; node->n_pending = 0; node->n_pending_flushes = 0; node->modification_counter = 0; node->flush_counter = 0; space = fil_space_get_by_id(id); if (!space) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Error: Could not find tablespace %lu for\n" "InnoDB: file ", (ulong) id); ut_print_filename(ib_stream, name); ib_logger(ib_stream, " in the tablespace memory cache.\n"); mem_free(node->name); mem_free(node); mutex_exit(&fil_system->mutex); return; } space->size += size; node->space = space; UT_LIST_ADD_LAST(chain, space->chain, node); if (id < SRV_LOG_SPACE_FIRST_ID && fil_system->max_assigned_id < id) { fil_system->max_assigned_id = id; } mutex_exit(&fil_system->mutex); } /********************************************************************//** Opens a the file of a node of a tablespace. The caller must own the fil_system mutex. */ static void fil_node_open_file( /*===============*/ fil_node_t* node, /*!< in: file node */ fil_system_t* system, /*!< in: tablespace memory cache */ fil_space_t* space) /*!< in: space */ { ib_int64_t size_bytes; ulint size_low; ulint size_high; ibool ret; ibool success; byte* buf2; byte* page; ulint space_id; ulint flags; ut_ad(mutex_own(&(system->mutex))); ut_a(node->n_pending == 0); ut_a(node->open == FALSE); if (node->size == 0) { /* It must be a single-table tablespace and we do not know the size of the file yet. First we open the file in the normal mode, no async I/O here, for simplicity. Then do some checks, and close the file again. NOTE that we could not use the simple file read function os_file_read() in Windows to read from a file opened for async I/O! */ node->handle = os_file_create_simple_no_error_handling( node->name, OS_FILE_OPEN, OS_FILE_READ_ONLY, &success); if (!success) { /* The following call prints an error message */ os_file_get_last_error(TRUE); ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Fatal error: cannot open %s\n." "InnoDB: Have you deleted .ibd files" " under a running server?\n", node->name); ut_a(0); } os_file_get_size(node->handle, &size_low, &size_high); size_bytes = (((ib_int64_t)size_high) << 32) + (ib_int64_t)size_low; #ifdef UNIV_HOTBACKUP if (space->id == 0) { node->size = (ulint) (size_bytes / UNIV_PAGE_SIZE); os_file_close(node->handle); goto add_size; } #endif /* UNIV_HOTBACKUP */ ut_a(space->purpose != FIL_LOG); ut_a(space->id != 0); if (size_bytes < FIL_IBD_FILE_INITIAL_SIZE * UNIV_PAGE_SIZE) { ib_logger(ib_stream, "InnoDB: Error: the size of single-table" " tablespace file %s\n" "InnoDB: is only %lu %lu," " should be at least %lu!\n", node->name, (ulong) size_high, (ulong) size_low, (ulong) (FIL_IBD_FILE_INITIAL_SIZE * UNIV_PAGE_SIZE)); ut_a(0); } /* Read the first page of the tablespace */ buf2 = ut_malloc(2 * UNIV_PAGE_SIZE); /* Align the memory for file i/o if we might have O_DIRECT set */ page = ut_align(buf2, UNIV_PAGE_SIZE); success = os_file_read(node->handle, page, 0, 0, UNIV_PAGE_SIZE); space_id = fsp_header_get_space_id(page); flags = fsp_header_get_flags(page); ut_free(buf2); /* Close the file now that we have read the space id from it */ os_file_close(node->handle); if (UNIV_UNLIKELY(space_id != space->id)) { ib_logger(ib_stream, "InnoDB: Error: tablespace id is %lu" " in the data dictionary\n" "InnoDB: but in file %s it is %lu!\n", space->id, node->name, space_id); ut_error; } if (UNIV_UNLIKELY(space_id == ULINT_UNDEFINED || space_id == 0)) { ib_logger(ib_stream, "InnoDB: Error: tablespace id %lu" " in file %s is not sensible\n", (ulong) space_id, node->name); ut_error; } if (UNIV_UNLIKELY(space->flags != flags)) { ib_logger(ib_stream, "InnoDB: Error: table flags are %lx" " in the data dictionary\n" "InnoDB: but the flags in file %s are %lx!\n", space->flags, node->name, flags); ut_error; } if (size_bytes >= 1024 * 1024) { /* Truncate the size to whole megabytes. */ size_bytes = ut_2pow_round(size_bytes, 1024 * 1024); } if (!(flags & DICT_TF_ZSSIZE_MASK)) { node->size = (ulint) (size_bytes / UNIV_PAGE_SIZE); } else { node->size = (ulint) (size_bytes / dict_table_flags_to_zip_size(flags)); } #ifdef UNIV_HOTBACKUP add_size: #endif /* UNIV_HOTBACKUP */ space->size += node->size; } /* printf("Opening file %s\n", node->name); */ /* Open the file for reading and writing, in Windows normally in the unbuffered async I/O mode, though global variables may make os_file_create() to fall back to the normal file I/O mode. */ if (space->purpose == FIL_LOG) { node->handle = os_file_create(node->name, OS_FILE_OPEN, OS_FILE_AIO, OS_LOG_FILE, &ret); } else if (node->is_raw_disk) { node->handle = os_file_create(node->name, OS_FILE_OPEN_RAW, OS_FILE_AIO, OS_DATA_FILE, &ret); } else { node->handle = os_file_create(node->name, OS_FILE_OPEN, OS_FILE_AIO, OS_DATA_FILE, &ret); } ut_a(ret); node->open = TRUE; system->n_open++; if (space->purpose == FIL_TABLESPACE && space->id != 0) { /* Put the node to the LRU list */ UT_LIST_ADD_FIRST(LRU, system->LRU, node); } } /**********************************************************************//** Closes a file. */ static void fil_node_close_file( /*================*/ fil_node_t* node, /*!< in: file node */ fil_system_t* system) /*!< in: tablespace memory cache */ { ibool ret; ut_ad(node && system); ut_ad(mutex_own(&(system->mutex))); ut_a(node->open); ut_a(node->n_pending == 0); ut_a(node->n_pending_flushes == 0); ut_a(node->modification_counter == node->flush_counter); ret = os_file_close(node->handle); ut_a(ret); /* printf("Closing file %s\n", node->name); */ node->open = FALSE; ut_a(system->n_open > 0); system->n_open--; if (node->space->purpose == FIL_TABLESPACE && node->space->id != 0) { ut_a(UT_LIST_GET_LEN(system->LRU) > 0); /* The node is in the LRU list, remove it */ UT_LIST_REMOVE(LRU, system->LRU, node); } } /********************************************************************//** Tries to close a file in the LRU list. The caller must hold the fil_sys mutex. @return TRUE if success, FALSE if should retry later; since i/o's generally complete in < 100 ms, and as InnoDB writes at most 128 pages from the buffer pool in a batch, and then immediately flushes the files, there is a good chance that the next time we find a suitable node from the LRU list */ static ibool fil_try_to_close_file_in_LRU( /*=========================*/ ibool print_info) /*!< in: if TRUE, prints information why it cannot close a file */ { fil_node_t* node; ut_ad(mutex_own(&fil_system->mutex)); node = UT_LIST_GET_LAST(fil_system->LRU); if (print_info) { ib_logger(ib_stream, "InnoDB: fil_sys open file LRU len %lu\n", (ulong) UT_LIST_GET_LEN(fil_system->LRU)); } while (node != NULL) { if (node->modification_counter == node->flush_counter && node->n_pending_flushes == 0) { fil_node_close_file(node, fil_system); return(TRUE); } if (print_info && node->n_pending_flushes > 0) { ib_logger(ib_stream, "InnoDB: cannot close file "); ut_print_filename(ib_stream, node->name); ib_logger(ib_stream, ", because n_pending_flushes %lu\n", (ulong) node->n_pending_flushes); } if (print_info && node->modification_counter != node->flush_counter) { ib_logger(ib_stream, "InnoDB: cannot close file "); ut_print_filename(ib_stream, node->name); ib_logger(ib_stream, ", because mod_count %ld != fl_count %ld\n", (long) node->modification_counter, (long) node->flush_counter); } node = UT_LIST_GET_PREV(LRU, node); } return(FALSE); } /*******************************************************************//** Reserves the fil_system mutex and tries to make sure we can open at least one file while holding it. This should be called before calling fil_node_prepare_for_io(), because that function may need to open a file. */ static void fil_mutex_enter_and_prepare_for_io( /*===============================*/ ulint space_id) /*!< in: space id */ { fil_space_t* space; ibool success; ibool print_info = FALSE; ulint count = 0; ulint count2 = 0; retry: mutex_enter(&fil_system->mutex); if (space_id == 0 || space_id >= SRV_LOG_SPACE_FIRST_ID) { /* We keep log files and system tablespace files always open; this is important in preventing deadlocks in this module, as a page read completion often performs another read from the insert buffer. The insert buffer is in tablespace 0, and we cannot end up waiting in this function. */ return; } if (fil_system->n_open < fil_system->max_n_open) { return; } space = fil_space_get_by_id(space_id); if (space != NULL && space->stop_ios) { /* We are going to do a rename file and want to stop new i/o's for a while */ if (count2 > 20000) { ib_logger(ib_stream, "InnoDB: Warning: tablespace "); ut_print_filename(ib_stream, space->name); ib_logger(ib_stream, " has i/o ops stopped for a long time %lu\n", (ulong) count2); } mutex_exit(&fil_system->mutex); os_thread_sleep(20000); count2++; goto retry; } /* If the file is already open, no need to do anything; if the space does not exist, we handle the situation in the function which called this function */ if (!space || UT_LIST_GET_FIRST(space->chain)->open) { return; } if (count > 1) { print_info = TRUE; } /* Too many files are open, try to close some */ close_more: success = fil_try_to_close_file_in_LRU(print_info); if (success && fil_system->n_open >= fil_system->max_n_open) { goto close_more; } if (fil_system->n_open < fil_system->max_n_open) { /* Ok */ return; } if (count >= 2) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Warning: too many (%lu) files stay open" " while the maximum\n" "InnoDB: allowed value would be %lu.\n" "InnoDB: You may need to raise the value of" " max_files_open\n", (ulong) fil_system->n_open, (ulong) fil_system->max_n_open); return; } mutex_exit(&fil_system->mutex); #ifndef UNIV_HOTBACKUP /* Wake the i/o-handler threads to make sure pending i/o's are performed */ os_aio_simulated_wake_handler_threads(); os_thread_sleep(20000); #endif /* Flush tablespaces so that we can close modified files in the LRU list */ fil_flush_file_spaces(FIL_TABLESPACE); count++; goto retry; } /*******************************************************************//** Frees a file node object from a tablespace memory cache. */ static void fil_node_free( /*==========*/ fil_node_t* node, /*!< in, own: file node */ fil_system_t* system, /*!< in: tablespace memory cache */ fil_space_t* space) /*!< in: space where the file node is chained */ { ut_ad(node && system && space); ut_ad(mutex_own(&(system->mutex))); ut_a(node->magic_n == FIL_NODE_MAGIC_N); ut_a(node->n_pending == 0); if (node->open) { /* We fool the assertion in fil_node_close_file() to think there are no unflushed modifications in the file */ node->modification_counter = node->flush_counter; if (space->is_in_unflushed_spaces && fil_space_is_flushed(space)) { space->is_in_unflushed_spaces = FALSE; UT_LIST_REMOVE(unflushed_spaces, system->unflushed_spaces, space); } fil_node_close_file(node, system); } space->size -= node->size; UT_LIST_REMOVE(chain, space->chain, node); mem_free(node->name); mem_free(node); } #ifdef UNIV_LOG_ARCHIVE /****************************************************************//** Drops files from the start of a file space, so that its size is cut by the amount given. */ UNIV_INTERN void fil_space_truncate_start( /*=====================*/ ulint id, /*!< in: space id */ ulint trunc_len) /*!< in: truncate by this much; it is an error if this does not equal to the combined size of some initial files in the space */ { fil_node_t* node; fil_space_t* space; mutex_enter(&fil_system->mutex); space = fil_space_get_by_id(id); ut_a(space); while (trunc_len > 0) { node = UT_LIST_GET_FIRST(space->chain); ut_a(node->size * UNIV_PAGE_SIZE <= trunc_len); trunc_len -= node->size * UNIV_PAGE_SIZE; fil_node_free(node, fil_system, space); } mutex_exit(&fil_system->mutex); } #endif /* UNIV_LOG_ARCHIVE */ /*******************************************************************//** Creates a space memory object and puts it to the tablespace memory cache. If there is an error, prints an error message to the .err log. @return TRUE if success */ UNIV_INTERN ibool fil_space_create( /*=============*/ const char* name, /*!< in: space name */ ulint id, /*!< in: space id */ ulint flags, /*!< in: compressed page size and file format, or 0 */ ulint purpose)/*!< in: FIL_TABLESPACE, or FIL_LOG if log */ { fil_space_t* space; /* The tablespace flags (FSP_SPACE_FLAGS) should be 0 for ROW_FORMAT=COMPACT ((table->flags & ~(~0 << DICT_TF_BITS)) == DICT_TF_COMPACT) and ROW_FORMAT=REDUNDANT (table->flags == 0). For any other format, the tablespace flags should equal (table->flags & ~(~0 << DICT_TF_BITS)). */ ut_a(flags != DICT_TF_COMPACT); ut_a(!(flags & (~0UL << DICT_TF_BITS))); try_again: /*printf( "InnoDB: Adding tablespace %lu of name %s, purpose %lu\n", id, name, purpose);*/ ut_a(fil_system); ut_a(name); mutex_enter(&fil_system->mutex); space = fil_space_get_by_name(name); if (UNIV_LIKELY_NULL(space)) { ulint namesake_id; ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Warning: trying to init to the" " tablespace memory cache\n" "InnoDB: a tablespace %lu of name ", (ulong) id); ut_print_filename(ib_stream, name); ib_logger(ib_stream, ",\n" "InnoDB: but a tablespace %lu of the same name\n" "InnoDB: already exists in the" " tablespace memory cache!\n", (ulong) space->id); if (id == 0 || purpose != FIL_TABLESPACE) { mutex_exit(&fil_system->mutex); return(FALSE); } ib_logger(ib_stream, "InnoDB: We assume that InnoDB did a crash recovery," " and you had\n" "InnoDB: an .ibd file for which the table" " did not exist in the\n" "InnoDB: InnoDB internal data dictionary in the" " ibdata files.\n" "InnoDB: We assume that you later removed the" " .ibd file,\n" "InnoDB: and are now trying to recreate the table." " We now remove the\n" "InnoDB: conflicting tablespace object" " from the memory cache and try\n" "InnoDB: the init again.\n"); namesake_id = space->id; mutex_exit(&fil_system->mutex); fil_space_free(namesake_id, FALSE); goto try_again; } space = fil_space_get_by_id(id); if (UNIV_LIKELY_NULL(space)) { ib_logger(ib_stream, "InnoDB: Error: trying to add tablespace %lu" " of name ", (ulong) id); ut_print_filename(ib_stream, name); ib_logger(ib_stream, "\n" "InnoDB: to the tablespace memory cache," " but tablespace\n" "InnoDB: %lu of name ", (ulong) space->id); ut_print_filename(ib_stream, space->name); ib_logger(ib_stream, " already exists in the tablespace\n" "InnoDB: memory cache!\n"); mutex_exit(&fil_system->mutex); return(FALSE); } space = mem_alloc(sizeof(fil_space_t)); space->name = mem_strdup(name); space->id = id; fil_system->tablespace_version++; space->tablespace_version = fil_system->tablespace_version; space->mark = FALSE; if (purpose == FIL_TABLESPACE && id > fil_system->max_assigned_id) { fil_system->max_assigned_id = id; } space->stop_ios = FALSE; space->stop_ibuf_merges = FALSE; space->is_being_deleted = FALSE; space->purpose = purpose; space->size = 0; space->flags = flags; space->n_reserved_extents = 0; space->n_pending_flushes = 0; space->n_pending_ibuf_merges = 0; UT_LIST_INIT(space->chain); space->magic_n = FIL_SPACE_MAGIC_N; rw_lock_create(&space->latch, SYNC_FSP); HASH_INSERT(fil_space_t, hash, fil_system->spaces, id, space); HASH_INSERT(fil_space_t, name_hash, fil_system->name_hash, ut_fold_string(name), space); space->is_in_unflushed_spaces = FALSE; UT_LIST_ADD_LAST(space_list, fil_system->space_list, space); mutex_exit(&fil_system->mutex); return(TRUE); } /*******************************************************************//** Assigns a new space id for a new single-table tablespace. This works simply by incrementing the global counter. If 4 billion id's is not enough, we may need to recycle id's. @return new tablespace id; ULINT_UNDEFINED if could not assign an id */ static ulint fil_assign_new_space_id(void) /*=========================*/ { ulint id; mutex_enter(&fil_system->mutex); fil_system->max_assigned_id++; id = fil_system->max_assigned_id; if (id > (SRV_LOG_SPACE_FIRST_ID / 2) && (id % 1000000UL == 0)) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, "InnoDB: Warning: you are running out of new" " single-table tablespace id's.\n" "InnoDB: Current counter is %lu and it" " must not exceed %lu!\n" "InnoDB: To reset the counter to zero" " you have to dump all your tables and\n" "InnoDB: recreate the whole InnoDB installation.\n", (ulong) id, (ulong) SRV_LOG_SPACE_FIRST_ID); } if (id >= SRV_LOG_SPACE_FIRST_ID) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, "InnoDB: You have run out of single-table" " tablespace id's!\n" "InnoDB: Current counter is %lu.\n" "InnoDB: To reset the counter to zero you" " have to dump all your tables and\n" "InnoDB: recreate the whole InnoDB installation.\n", (ulong) id); fil_system->max_assigned_id--; id = ULINT_UNDEFINED; } mutex_exit(&fil_system->mutex); return(id); } /*******************************************************************//** Frees a space object from the tablespace memory cache. Closes the files in the chain but does not delete them. There must not be any pending i/o's or flushes on the files. @return TRUE if success */ static ibool fil_space_free( /*===========*/ /* out: TRUE if success */ ulint id, /* in: space id */ ibool own_mutex) /* in: TRUE if own system->mutex */ { fil_space_t* space; fil_space_t* namespace; fil_node_t* fil_node; if (!own_mutex) { mutex_enter(&fil_system->mutex); } space = fil_space_get_by_id(id); if (!space) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Error: trying to remove tablespace %lu" " from the cache but\n" "InnoDB: it is not there.\n", (ulong) id); mutex_exit(&fil_system->mutex); return(FALSE); } HASH_DELETE(fil_space_t, hash, fil_system->spaces, id, space); namespace = fil_space_get_by_name(space->name); ut_a(namespace); ut_a(space == namespace); HASH_DELETE(fil_space_t, name_hash, fil_system->name_hash, ut_fold_string(space->name), space); if (space->is_in_unflushed_spaces) { space->is_in_unflushed_spaces = FALSE; UT_LIST_REMOVE(unflushed_spaces, fil_system->unflushed_spaces, space); } UT_LIST_REMOVE(space_list, fil_system->space_list, space); ut_a(space->magic_n == FIL_SPACE_MAGIC_N); ut_a(0 == space->n_pending_flushes); fil_node = UT_LIST_GET_FIRST(space->chain); while (fil_node != NULL) { fil_node_free(fil_node, fil_system, space); fil_node = UT_LIST_GET_FIRST(space->chain); } ut_a(0 == UT_LIST_GET_LEN(space->chain)); if (!own_mutex) { mutex_exit(&fil_system->mutex); } rw_lock_free(&(space->latch)); mem_free(space->name); mem_free(space); return(TRUE); } /*******************************************************************//** Returns the size of the space in pages. The tablespace must be cached in the memory cache. @return space size, 0 if space not found */ UNIV_INTERN ulint fil_space_get_size( /*===============*/ ulint id) /*!< in: space id */ { fil_node_t* node; fil_space_t* space; ulint size; ut_ad(fil_system); fil_mutex_enter_and_prepare_for_io(id); space = fil_space_get_by_id(id); if (space == NULL) { mutex_exit(&fil_system->mutex); return(0); } if (space->size == 0 && space->purpose == FIL_TABLESPACE) { ut_a(id != 0); ut_a(1 == UT_LIST_GET_LEN(space->chain)); node = UT_LIST_GET_FIRST(space->chain); /* It must be a single-table tablespace and we have not opened the file yet; the following calls will open it and update the size fields */ fil_node_prepare_for_io(node, fil_system, space); fil_node_complete_io(node, fil_system, OS_FILE_READ); } size = space->size; mutex_exit(&fil_system->mutex); return(size); } /*******************************************************************//** Returns the flags of the space. The tablespace must be cached in the memory cache. @return flags, ULINT_UNDEFINED if space not found */ UNIV_INTERN ulint fil_space_get_flags( /*================*/ ulint id) /*!< in: space id */ { fil_node_t* node; fil_space_t* space; ulint flags; ut_ad(fil_system); if (UNIV_UNLIKELY(!id)) { return(0); } fil_mutex_enter_and_prepare_for_io(id); space = fil_space_get_by_id(id); if (space == NULL) { mutex_exit(&fil_system->mutex); return(ULINT_UNDEFINED); } if (space->size == 0 && space->purpose == FIL_TABLESPACE) { ut_a(id != 0); ut_a(1 == UT_LIST_GET_LEN(space->chain)); node = UT_LIST_GET_FIRST(space->chain); /* It must be a single-table tablespace and we have not opened the file yet; the following calls will open it and update the size fields */ fil_node_prepare_for_io(node, fil_system, space); fil_node_complete_io(node, fil_system, OS_FILE_READ); } flags = space->flags; mutex_exit(&fil_system->mutex); return(flags); } /*******************************************************************//** Returns the compressed page size of the space, or 0 if the space is not compressed. The tablespace must be cached in the memory cache. @return compressed page size, ULINT_UNDEFINED if space not found */ UNIV_INTERN ulint fil_space_get_zip_size( /*===================*/ ulint id) /*!< in: space id */ { ulint flags; flags = fil_space_get_flags(id); if (flags && flags != ULINT_UNDEFINED) { return(dict_table_flags_to_zip_size(flags)); } return(flags); } /*******************************************************************//** Checks if the pair space, page_no refers to an existing page in a tablespace file space. The tablespace must be cached in the memory cache. @return TRUE if the address is meaningful */ UNIV_INTERN ibool fil_check_adress_in_tablespace( /*===========================*/ ulint id, /*!< in: space id */ ulint page_no)/*!< in: page number */ { if (fil_space_get_size(id) > page_no) { return(TRUE); } return(FALSE); } /****************************************************************//** Initializes the tablespace memory cache. */ UNIV_INTERN void fil_init( /*=====*/ ulint hash_size, /*!< in: hash table size */ ulint max_n_open) /*!< in: max number of open files */ { ut_a(fil_system == NULL); ut_a(hash_size > 0); ut_a(max_n_open > 0); fil_system = mem_alloc(sizeof(fil_system_t)); mutex_create(&fil_system->mutex, SYNC_ANY_LATCH); fil_system->spaces = hash_create(hash_size); fil_system->name_hash = hash_create(hash_size); UT_LIST_INIT(fil_system->LRU); fil_system->n_open = 0; fil_system->max_n_open = max_n_open; fil_system->modification_counter = 0; fil_system->max_assigned_id = 0; fil_system->tablespace_version = 0; UT_LIST_INIT(fil_system->unflushed_spaces); UT_LIST_INIT(fil_system->space_list); } /*******************************************************************//** Opens all log files and system tablespace data files. They stay open until the database server shutdown. This should be called at a server startup after the space objects for the log and the system tablespace have been created. The purpose of this operation is to make sure we never run out of file descriptors if we need to read from the insert buffer or to write to the log. */ UNIV_INTERN void fil_open_log_and_system_tablespace_files(void) /*==========================================*/ { fil_space_t* space; fil_node_t* node; mutex_enter(&fil_system->mutex); space = UT_LIST_GET_FIRST(fil_system->space_list); while (space != NULL) { if (space->purpose != FIL_TABLESPACE || space->id == 0) { node = UT_LIST_GET_FIRST(space->chain); while (node != NULL) { if (!node->open) { fil_node_open_file(node, fil_system, space); } if (fil_system->max_n_open < 10 + fil_system->n_open) { ib_logger(ib_stream, "InnoDB: Warning: you must" " raise the value of" " max_open_files!\n" "InnoDB: Remember that" " InnoDB keeps all log files" " and all system\n" "InnoDB: tablespace files open" " for the whole time server is" " running, and\n" "InnoDB: needs to open also" " some .ibd files if the" " file-per-table storage\n" "InnoDB: model is used." " Current open files %lu," " max allowed" " open files %lu.\n", (ulong) fil_system->n_open, (ulong) fil_system->max_n_open); } node = UT_LIST_GET_NEXT(chain, node); } } space = UT_LIST_GET_NEXT(space_list, space); } mutex_exit(&fil_system->mutex); } /*******************************************************************//** Closes all open files. There must not be any pending i/o's or not flushed modifications in the files. */ UNIV_INTERN void fil_close_all_files(void) /*=====================*/ { fil_space_t* space; fil_node_t* node; /* If we decide to abort before this module has been initialized then we simply ignore the request. */ if (fil_system == NULL) { return; } mutex_enter(&fil_system->mutex); space = UT_LIST_GET_FIRST(fil_system->space_list); while (space != NULL) { fil_space_t* prev_space = space; node = UT_LIST_GET_FIRST(space->chain); while (node != NULL) { if (node->open) { fil_node_close_file(node, fil_system); } node = UT_LIST_GET_NEXT(chain, node); } space = UT_LIST_GET_NEXT(space_list, space); fil_space_free(prev_space->id, TRUE); } mutex_exit(&fil_system->mutex); } /*******************************************************************//** Sets the max tablespace id counter if the given number is bigger than the previous value. */ UNIV_INTERN void fil_set_max_space_id_if_bigger( /*===========================*/ ulint max_id) /*!< in: maximum known id */ { if (max_id >= SRV_LOG_SPACE_FIRST_ID) { ib_logger(ib_stream, "InnoDB: Fatal error: max tablespace id" " is too high, %lu\n", (ulong) max_id); ut_error; } mutex_enter(&fil_system->mutex); if (fil_system->max_assigned_id < max_id) { fil_system->max_assigned_id = max_id; } mutex_exit(&fil_system->mutex); } /****************************************************************//** Writes the flushed lsn and the latest archived log number to the page header of the first page of a data file of the system tablespace (space 0), which is uncompressed. */ static ulint fil_write_lsn_and_arch_no_to_file( /*==============================*/ ulint sum_of_sizes, /*!< in: combined size of previous files in space, in database pages */ ib_uint64_t lsn, /*!< in: lsn to write */ ulint arch_log_no __attribute__((unused))) /*!< in: archived log number to write */ { byte* buf1; byte* buf; buf1 = mem_alloc(2 * UNIV_PAGE_SIZE); buf = ut_align(buf1, UNIV_PAGE_SIZE); fil_read(TRUE, 0, 0, sum_of_sizes, 0, UNIV_PAGE_SIZE, buf, NULL); mach_write_ull(buf + FIL_PAGE_FILE_FLUSH_LSN, lsn); #ifdef UNIV_LOG_ARCHIVE // FIXME: ARCHIVE: We still haven't decided where this will go //mach_write_to_4(buf + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID, arch_log_no); #endif fil_write(TRUE, 0, 0, sum_of_sizes, 0, UNIV_PAGE_SIZE, buf, NULL); mem_free(buf1); return(DB_SUCCESS); } /****************************************************************//** Writes the flushed lsn and the latest archived log number to the page header of the first page of each data file in the system tablespace. @return DB_SUCCESS or error number */ UNIV_INTERN ulint fil_write_flushed_lsn_to_data_files( /*================================*/ ib_uint64_t lsn, /*!< in: lsn to write */ ulint arch_log_no) /*!< in: latest archived log file number */ { fil_space_t* space; fil_node_t* node; ulint sum_of_sizes; ulint err; mutex_enter(&fil_system->mutex); space = UT_LIST_GET_FIRST(fil_system->space_list); while (space) { /* We only write the lsn to all existing data files which have been open during the lifetime of the server process; they are represented by the space objects in the tablespace memory cache. Note that all data files in the system tablespace 0 are always open. */ if (space->purpose == FIL_TABLESPACE && space->id == 0) { sum_of_sizes = 0; node = UT_LIST_GET_FIRST(space->chain); while (node) { mutex_exit(&fil_system->mutex); err = fil_write_lsn_and_arch_no_to_file( sum_of_sizes, lsn, arch_log_no); if (err != DB_SUCCESS) { return(err); } mutex_enter(&fil_system->mutex); sum_of_sizes += node->size; node = UT_LIST_GET_NEXT(chain, node); } } space = UT_LIST_GET_NEXT(space_list, space); } mutex_exit(&fil_system->mutex); return(DB_SUCCESS); } /*******************************************************************//** Reads the flushed lsn and arch no fields from a data file at database startup. */ UNIV_INTERN void fil_read_flushed_lsn_and_arch_log_no( /*=================================*/ os_file_t data_file, /*!< in: open data file */ ibool one_read_already, /*!< in: TRUE if min and max parameters below already contain sensible data */ #ifdef UNIV_LOG_ARCHIVE ulint* min_arch_log_no, /*!< in/out: */ ulint* max_arch_log_no, /*!< in/out: */ #endif /* UNIV_LOG_ARCHIVE */ ib_uint64_t* min_flushed_lsn, /*!< in/out: */ ib_uint64_t* max_flushed_lsn) /*!< in/out: */ { byte* buf; byte* buf2; ib_uint64_t flushed_lsn; #ifdef UNIV_LOG_ARCHIVE ulint arch_log_no = 0; #endif buf2 = ut_malloc(2 * UNIV_PAGE_SIZE); /* Align the memory for a possible read from a raw device */ buf = ut_align(buf2, UNIV_PAGE_SIZE); os_file_read(data_file, buf, 0, 0, UNIV_PAGE_SIZE); flushed_lsn = mach_read_ull(buf + FIL_PAGE_FILE_FLUSH_LSN); #ifdef UNIV_LOG_ARCHIVE // FIXME: ARCHIVE: We still haven't decided where this will go //arch_log_no = mach_read_from_4(buf + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID); #endif ut_free(buf2); if (!one_read_already) { *min_flushed_lsn = flushed_lsn; *max_flushed_lsn = flushed_lsn; #ifdef UNIV_LOG_ARCHIVE *min_arch_log_no = arch_log_no; *max_arch_log_no = arch_log_no; #endif /* UNIV_LOG_ARCHIVE */ return; } if (*min_flushed_lsn > flushed_lsn) { *min_flushed_lsn = flushed_lsn; } if (*max_flushed_lsn < flushed_lsn) { *max_flushed_lsn = flushed_lsn; } #ifdef UNIV_LOG_ARCHIVE if (*min_arch_log_no > arch_log_no) { *min_arch_log_no = arch_log_no; } if (*max_arch_log_no < arch_log_no) { *max_arch_log_no = arch_log_no; } #endif /* UNIV_LOG_ARCHIVE */ } /*================ SINGLE-TABLE TABLESPACES ==========================*/ #ifndef UNIV_HOTBACKUP /*******************************************************************//** Increments the count of pending insert buffer page merges, if space is not being deleted. @return TRUE if being deleted, and ibuf merges should be skipped */ UNIV_INTERN ibool fil_inc_pending_ibuf_merges( /*========================*/ ulint id) /*!< in: space id */ { fil_space_t* space; mutex_enter(&fil_system->mutex); space = fil_space_get_by_id(id); if (space == NULL) { ib_logger(ib_stream, "InnoDB: Error: trying to do ibuf merge to a" " dropped tablespace %lu\n", (ulong) id); } if (space == NULL || space->stop_ibuf_merges) { mutex_exit(&fil_system->mutex); return(TRUE); } space->n_pending_ibuf_merges++; mutex_exit(&fil_system->mutex); return(FALSE); } /*******************************************************************//** Decrements the count of pending insert buffer page merges. */ UNIV_INTERN void fil_decr_pending_ibuf_merges( /*=========================*/ ulint id) /*!< in: space id */ { fil_space_t* space; mutex_enter(&fil_system->mutex); space = fil_space_get_by_id(id); if (space == NULL) { ib_logger(ib_stream, "InnoDB: Error: decrementing ibuf merge of a" " dropped tablespace %lu\n", (ulong) id); } if (space != NULL) { space->n_pending_ibuf_merges--; } mutex_exit(&fil_system->mutex); } #endif /* !UNIV_HOTBACKUP */ /********************************************************//** Creates the database directory for a table if it does not exist yet. */ static void fil_create_directory_for_tablename( /*===============================*/ const char* name) /*!< in: name in the standard 'databasename/tablename' format */ { ulint len; char* path; const char* namend; len = ut_strlen(srv_data_home); ut_a(len > 0); namend = strchr(name, '/'); ut_a(namend); path = mem_alloc(len + (namend - name) + 2); strncpy(path, srv_data_home, len); srv_normalize_path_for_win(path); ut_a(path[len - 1] == SRV_PATH_SEPARATOR); strncpy(path + len, name, namend - name); ut_a(os_file_create_directory(path, FALSE)); mem_free(path); } #ifndef UNIV_HOTBACKUP /********************************************************//** Writes a log record about an .ibd file create/rename/delete. */ static void fil_op_write_log( /*=============*/ ulint type, /*!< in: MLOG_FILE_CREATE, MLOG_FILE_CREATE2, MLOG_FILE_DELETE, or MLOG_FILE_RENAME */ ulint space_id, /*!< in: space id */ ulint log_flags, /*!< in: redo log flags (stored in the page number field) */ ulint flags, /*!< in: compressed page size and file format if type==MLOG_FILE_CREATE2, or 0 */ const char* name, /*!< in: table name in the familiar 'databasename/tablename' format, or the file path in the case of MLOG_FILE_DELETE */ const char* new_name, /*!< in: if type is MLOG_FILE_RENAME, the new table name in the 'databasename/tablename' format */ mtr_t* mtr) /*!< in: mini-transaction handle */ { byte* log_ptr; ulint len; log_ptr = mlog_open(mtr, 11 + 2 + 1); if (!log_ptr) { /* Logging in mtr is switched off during crash recovery: in that case mlog_open returns NULL */ return; } log_ptr = mlog_write_initial_log_record_for_file_op( type, space_id, log_flags, log_ptr, mtr); if (type == MLOG_FILE_CREATE2) { mach_write_to_4(log_ptr, flags); log_ptr += 4; } /* Let us store the strings as null-terminated for easier readability and handling */ len = strlen(name) + 1; mach_write_to_2(log_ptr, len); log_ptr += 2; mlog_close(mtr, log_ptr); mlog_catenate_string(mtr, (byte*) name, len); if (type == MLOG_FILE_RENAME) { len = strlen(new_name) + 1; log_ptr = mlog_open(mtr, 2 + len); ut_a(log_ptr); mach_write_to_2(log_ptr, len); log_ptr += 2; mlog_close(mtr, log_ptr); mlog_catenate_string(mtr, (byte*) new_name, len); } } #endif /*******************************************************************//** Parses the body of a log record written about an .ibd file operation. That is, the log record part after the standard (type, space id, page no) header of the log record. If desired, also replays the delete or rename operation if the .ibd file exists and the space id in it matches. Replays the create operation if a file at that path does not exist yet. If the database directory for the file to be created does not exist, then we create the directory, too. Note that ibbackup --apply-log sets fil_path_to_client_datadir to point to the datadir that we should use in replaying the file operations. @return end of log record, or NULL if the record was not completely contained between ptr and end_ptr */ UNIV_INTERN byte* fil_op_log_parse_or_replay( /*=======================*/ byte* ptr, /*!< in: buffer containing the log record body, or an initial segment of it, if the record does not fir completely between ptr and end_ptr */ byte* end_ptr, /*!< in: buffer end */ ulint type, /*!< in: the type of this log record */ ulint space_id, /*!< in: the space id of the tablespace in question, or 0 if the log record should only be parsed but not replayed */ ulint log_flags) /*!< in: redo log flags (stored in the page number parameter) */ { ulint name_len; ulint new_name_len; const char* name; const char* new_name = NULL; ulint flags = 0; if (type == MLOG_FILE_CREATE2) { if (end_ptr < ptr + 4) { return(NULL); } flags = mach_read_from_4(ptr); ptr += 4; } if (end_ptr < ptr + 2) { return(NULL); } name_len = mach_read_from_2(ptr); ptr += 2; if (end_ptr < ptr + name_len) { return(NULL); } name = (const char*) ptr; ptr += name_len; if (type == MLOG_FILE_RENAME) { if (end_ptr < ptr + 2) { return(NULL); } new_name_len = mach_read_from_2(ptr); ptr += 2; if (end_ptr < ptr + new_name_len) { return(NULL); } new_name = (const char*) ptr; ptr += new_name_len; } /* We managed to parse a full log record body */ /* printf("Parsed log rec of type %lu space %lu\n" "name %s\n", type, space_id, name); if (type == MLOG_FILE_RENAME) { printf("new name %s\n", new_name); } */ if (!space_id) { return(ptr); } /* Let us try to perform the file operation, if sensible. Note that ibbackup has at this stage already read in all space id info to the fil0fil.c data structures. NOTE that our algorithm is not guaranteed to work correctly if there were renames of tables during the backup. See ibbackup code for more on the problem. */ switch (type) { case MLOG_FILE_DELETE: if (fil_tablespace_exists_in_mem(space_id)) { ut_a(fil_delete_tablespace(space_id)); } break; case MLOG_FILE_RENAME: /* We do the rename based on space id, not old file name; this should guarantee that after the log replay each .ibd file has the correct name for the latest log sequence number; the proof is left as an exercise :) */ if (fil_tablespace_exists_in_mem(space_id)) { /* Create the database directory for the new name, if it does not exist yet */ fil_create_directory_for_tablename(new_name); /* Rename the table if there is not yet a tablespace with the same name */ if (fil_get_space_id_for_table(new_name) == ULINT_UNDEFINED) { /* We do not care of the old name, that is why we pass NULL as the first argument */ if (!fil_rename_tablespace(NULL, space_id, new_name)) { ut_error; } } } break; case MLOG_FILE_CREATE: case MLOG_FILE_CREATE2: if (fil_tablespace_exists_in_mem(space_id)) { /* Do nothing */ } else if (fil_get_space_id_for_table(name) != ULINT_UNDEFINED) { /* Do nothing */ } else if (log_flags & MLOG_FILE_FLAG_TEMP) { /* Temporary table, do nothing */ } else { /* Create the database directory for name, if it does not exist yet */ fil_create_directory_for_tablename(name); if (fil_create_new_single_table_tablespace( &space_id, name, FALSE, flags, FIL_IBD_FILE_INITIAL_SIZE) != DB_SUCCESS) { ut_error; } } break; default: ut_error; } return(ptr); } /*******************************************************************//** Deletes a single-table tablespace. The tablespace must be cached in the memory cache. @return TRUE if success */ UNIV_INTERN ibool fil_delete_tablespace( /*==================*/ ulint id) /*!< in: space id */ { ibool success; fil_space_t* space; fil_node_t* node; ulint count = 0; char* path; ut_a(id != 0); stop_ibuf_merges: mutex_enter(&fil_system->mutex); space = fil_space_get_by_id(id); if (space != NULL) { space->stop_ibuf_merges = TRUE; if (space->n_pending_ibuf_merges == 0) { mutex_exit(&fil_system->mutex); count = 0; goto try_again; } else { if (count > 5000) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Warning: trying to" " delete tablespace "); ut_print_filename(ib_stream, space->name); ib_logger(ib_stream, ",\n" "InnoDB: but there are %lu pending" " ibuf merges on it.\n" "InnoDB: Loop %lu.\n", (ulong) space->n_pending_ibuf_merges, (ulong) count); } mutex_exit(&fil_system->mutex); os_thread_sleep(20000); count++; goto stop_ibuf_merges; } } mutex_exit(&fil_system->mutex); count = 0; try_again: mutex_enter(&fil_system->mutex); space = fil_space_get_by_id(id); if (space == NULL) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Error: cannot delete tablespace %lu\n" "InnoDB: because it is not found in the" " tablespace memory cache.\n", (ulong) id); mutex_exit(&fil_system->mutex); return(FALSE); } ut_a(space); ut_a(space->n_pending_ibuf_merges == 0); space->is_being_deleted = TRUE; ut_a(UT_LIST_GET_LEN(space->chain) == 1); node = UT_LIST_GET_FIRST(space->chain); if (space->n_pending_flushes > 0 || node->n_pending > 0) { if (count > 1000) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Warning: trying to" " delete tablespace "); ut_print_filename(ib_stream, space->name); ib_logger(ib_stream, ",\n" "InnoDB: but there are %lu flushes" " and %lu pending i/o's on it\n" "InnoDB: Loop %lu.\n", (ulong) space->n_pending_flushes, (ulong) node->n_pending, (ulong) count); } mutex_exit(&fil_system->mutex); os_thread_sleep(20000); count++; goto try_again; } path = mem_strdup(space->name); mutex_exit(&fil_system->mutex); #ifndef UNIV_HOTBACKUP /* Invalidate in the buffer pool all pages belonging to the tablespace. Since we have set space->is_being_deleted = TRUE, readahead or ibuf merge can no longer read more pages of this tablespace to the buffer pool. Thus we can clean the tablespace out of the buffer pool completely and permanently. The flag is_being_deleted also prevents fil_flush() from being applied to this tablespace. */ buf_LRU_invalidate_tablespace(id); #endif /* printf("Deleting tablespace %s id %lu\n", space->name, id); */ success = fil_space_free(id, FALSE); if (success) { success = os_file_delete(path); if (!success) { success = os_file_delete_if_exists(path); } } if (success) { #ifndef UNIV_HOTBACKUP /* Write a log record about the deletion of the .ibd file, so that ibbackup can replay it in the --apply-log phase. We use a dummy mtr and the familiar log write mechanism. */ mtr_t mtr; /* When replaying the operation in ibbackup, do not try to write any log record */ mtr_start(&mtr); fil_op_write_log(MLOG_FILE_DELETE, id, 0, 0, path, NULL, &mtr); mtr_commit(&mtr); #endif mem_free(path); return(TRUE); } mem_free(path); return(FALSE); } #ifndef UNIV_HOTBACKUP /*******************************************************************//** Discards a single-table tablespace. The tablespace must be cached in the memory cache. Discarding is like deleting a tablespace, but 1) we do not drop the table from the data dictionary; 2) we remove all insert buffer entries for the tablespace immediately; in DROP TABLE they are only removed gradually in the background; 3) when the user does IMPORT TABLESPACE, the tablespace will have the same id as it originally had. @return TRUE if success */ UNIV_INTERN ibool fil_discard_tablespace( /*===================*/ ulint id) /*!< in: space id */ { ibool success; success = fil_delete_tablespace(id); if (!success) { ib_logger(ib_stream, "InnoDB: Warning: cannot delete tablespace %lu" " in DISCARD TABLESPACE.\n" "InnoDB: But let us remove the" " insert buffer entries for this tablespace.\n", (ulong) id); } /* Remove all insert buffer entries for the tablespace */ ibuf_delete_for_discarded_space(id); return(success); } #endif /* !UNIV_HOTBACKUP */ /*******************************************************************//** Renames the memory cache structures of a single-table tablespace. @return TRUE if success */ static ibool fil_rename_tablespace_in_mem( /*=========================*/ fil_space_t* space, /*!< in: tablespace memory object */ fil_node_t* node, /*!< in: file node of that tablespace */ const char* path) /*!< in: new name */ { fil_space_t* space2; const char* old_name = space->name; ut_ad(mutex_own(&fil_system->mutex)); space2 = fil_space_get_by_name(old_name); if (space != space2) { ib_logger(ib_stream, "InnoDB: Error: cannot find "); ut_print_filename(ib_stream, old_name); ib_logger(ib_stream, " in tablespace memory cache\n"); return(FALSE); } space2 = fil_space_get_by_name(path); if (space2 != NULL) { ib_logger(ib_stream, "InnoDB: Error: "); ut_print_filename(ib_stream, path); ib_logger(ib_stream, " is already in tablespace memory cache\n"); return(FALSE); } HASH_DELETE(fil_space_t, name_hash, fil_system->name_hash, ut_fold_string(space->name), space); mem_free(space->name); mem_free(node->name); space->name = mem_strdup(path); node->name = mem_strdup(path); HASH_INSERT(fil_space_t, name_hash, fil_system->name_hash, ut_fold_string(path), space); return(TRUE); } /*******************************************************************//** Allocates a file name for a single-table tablespace. The string must be freed by caller with mem_free(). @return own: file name */ static char* fil_make_ibd_name( /*==============*/ const char* name, /*!< in: table name or a dir path of a TEMPORARY table */ ibool is_temp) /*!< in: TRUE if it is a dir path */ { ulint sz; ulint dirlen; char* filename; ulint namelen = ut_strlen(name); dirlen = ut_strlen(srv_data_home); sz = dirlen + namelen + sizeof("/.ibd"); filename = mem_alloc(sz); ut_snprintf(filename, sz, "%s%s.ibd", fil_normalize_path(srv_data_home), name); srv_normalize_path_for_win(filename); return(filename); } /*******************************************************************//** Renames a single-table tablespace. The tablespace must be cached in the tablespace memory cache. @return TRUE if success */ UNIV_INTERN ibool fil_rename_tablespace( /*==================*/ const char* old_name, /*!< in: old table name in the standard databasename/tablename format of InnoDB, or NULL if we do the rename based on the space id only */ ulint id, /*!< in: space id */ const char* new_name) /*!< in: new table name in the standard databasename/tablename format of InnoDB */ { ibool success; fil_space_t* space; fil_node_t* node; ulint count = 0; char* path; ibool old_name_was_specified = TRUE; char* old_path; ut_a(id != 0); if (old_name == NULL) { old_name = "(name not specified)"; old_name_was_specified = FALSE; } retry: count++; if (count > 1000) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Warning: problems renaming "); ut_print_filename(ib_stream, old_name); ib_logger(ib_stream, " to "); ut_print_filename(ib_stream, new_name); ib_logger(ib_stream, ", %lu iterations\n", (ulong) count); } mutex_enter(&fil_system->mutex); space = fil_space_get_by_id(id); if (space == NULL) { ib_logger(ib_stream, "InnoDB: Error: cannot find space id %lu" " in the tablespace memory cache\n" "InnoDB: though the table ", (ulong) id); ut_print_filename(ib_stream, old_name); ib_logger(ib_stream, " in a rename operation should have that id\n"); mutex_exit(&fil_system->mutex); return(FALSE); } if (count > 25000) { space->stop_ios = FALSE; mutex_exit(&fil_system->mutex); return(FALSE); } /* We temporarily close the .ibd file because we do not trust that operating systems can rename an open file. For the closing we have to wait until there are no pending i/o's or flushes on the file. */ space->stop_ios = TRUE; ut_a(UT_LIST_GET_LEN(space->chain) == 1); node = UT_LIST_GET_FIRST(space->chain); if (node->n_pending > 0 || node->n_pending_flushes > 0) { /* There are pending i/o's or flushes, sleep for a while and retry */ mutex_exit(&fil_system->mutex); os_thread_sleep(20000); goto retry; } else if (node->modification_counter > node->flush_counter) { /* Flush the space */ mutex_exit(&fil_system->mutex); os_thread_sleep(20000); fil_flush(id); goto retry; } else if (node->open) { /* Close the file */ fil_node_close_file(node, fil_system); } /* Check that the old name in the space is right */ if (old_name_was_specified) { old_path = fil_make_ibd_name(old_name, FALSE); ut_a(fil_tablename_compare(space->name, old_path) == 0); ut_a(fil_tablename_compare(node->name, old_path) == 0); } else { old_path = mem_strdup(space->name); } /* Rename the tablespace and the node in the memory cache */ path = fil_make_ibd_name(new_name, FALSE); success = fil_rename_tablespace_in_mem(space, node, path); if (success) { success = os_file_rename(old_path, path); if (!success) { /* We have to revert the changes we made to the tablespace memory cache */ ut_a(fil_rename_tablespace_in_mem(space, node, old_path)); } } mem_free(path); mem_free(old_path); space->stop_ios = FALSE; mutex_exit(&fil_system->mutex); #ifndef UNIV_HOTBACKUP if (success) { mtr_t mtr; mtr_start(&mtr); fil_op_write_log(MLOG_FILE_RENAME, id, 0, 0, old_name, new_name, &mtr); mtr_commit(&mtr); } #endif return(success); } /*******************************************************************//** Creates a new single-table tablespace to a database directory of the server. Database directories are under the 'datadir' of the server. The datadir is the directory of a running process. We can refer to it by simply the path '.'. Tables created with CREATE TEMPORARY TABLE we place in the temp dir of the server. @return DB_SUCCESS or error code */ UNIV_INTERN ulint fil_create_new_single_table_tablespace( /*===================================*/ ulint* space_id, /*!< in/out: space id; if this is != 0, then this is an input parameter, otherwise output */ const char* tablename, /*!< in: the table name in the usual databasename/tablename format of InnoDB, or a dir path to a temp table */ ibool is_temp, /*!< in: TRUE if a table created with CREATE TEMPORARY TABLE */ ulint flags, /*!< in: tablespace flags */ ulint size) /*!< in: the initial size of the tablespace file in pages, must be >= FIL_IBD_FILE_INITIAL_SIZE */ { os_file_t file; ibool ret; ulint err; byte* buf2; byte* page; ibool success; char* path; ut_a(size >= FIL_IBD_FILE_INITIAL_SIZE); /* The tablespace flags (FSP_SPACE_FLAGS) should be 0 for ROW_FORMAT=COMPACT ((table->flags & ~(~0 << DICT_TF_BITS)) == DICT_TF_COMPACT) and ROW_FORMAT=REDUNDANT (table->flags == 0). For any other format, the tablespace flags should equal (table->flags & ~(~0 << DICT_TF_BITS)). */ ut_a(flags != DICT_TF_COMPACT); ut_a(!(flags & (~0UL << DICT_TF_BITS))); path = fil_make_ibd_name(tablename, is_temp); file = os_file_create(path, OS_FILE_CREATE, OS_FILE_NORMAL, OS_DATA_FILE, &ret); if (ret == FALSE) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Error creating file "); ut_print_filename(ib_stream, path); ib_logger(ib_stream, ".\n"); /* The following call will print an error message */ err = os_file_get_last_error(TRUE); if (err == OS_FILE_ALREADY_EXISTS) { ib_logger(ib_stream, "InnoDB: The file already exists though" " the corresponding table did not\n" "InnoDB: exist in the InnoDB data dictionary." " Have you moved InnoDB\n" "InnoDB: .ibd files around without using the" " SQL commands\n" "InnoDB: DISCARD TABLESPACE and" " IMPORT TABLESPACE, or did\n" "InnoDB: the server crash in the middle of" " CREATE TABLE? You can\n" "InnoDB: resolve the problem by" " removing the file "); ut_print_filename(ib_stream, path); ib_logger(ib_stream, "\n" "InnoDB: under the 'datadir' of the server.\n"); mem_free(path); return(DB_TABLESPACE_ALREADY_EXISTS); } if (err == OS_FILE_DISK_FULL) { mem_free(path); return(DB_OUT_OF_FILE_SPACE); } mem_free(path); return(DB_ERROR); } buf2 = ut_malloc(3 * UNIV_PAGE_SIZE); /* Align the memory for file i/o if we might have O_DIRECT set */ page = ut_align(buf2, UNIV_PAGE_SIZE); ret = os_file_set_size(path, file, size * UNIV_PAGE_SIZE, 0); if (!ret) { ut_free(buf2); os_file_close(file); os_file_delete(path); mem_free(path); return(DB_OUT_OF_FILE_SPACE); } if (*space_id == 0) { *space_id = fil_assign_new_space_id(); } /* printf("Creating tablespace %s id %lu\n", path, *space_id); */ if (*space_id == ULINT_UNDEFINED) { ut_free(buf2); error_exit: os_file_close(file); error_exit2: os_file_delete(path); mem_free(path); return(DB_ERROR); } /* We have to write the space id to the file immediately and flush the file to disk. This is because in crash recovery we must be aware what tablespaces exist and what are their space id's, so that we can apply the log records to the right file. It may take quite a while until buffer pool flush algorithms write anything to the file and flush it to disk. If we would not write here anything, the file would be filled with zeros from the call of os_file_set_size(), until a buffer pool flush would write to it. */ memset(page, '\0', UNIV_PAGE_SIZE); fsp_header_init_fields(page, *space_id, flags); mach_write_to_4(page + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID, *space_id); if (!(flags & DICT_TF_ZSSIZE_MASK)) { buf_flush_init_for_writing(page, NULL, 0); ret = os_file_write(path, file, page, 0, 0, UNIV_PAGE_SIZE); } else { page_zip_des_t page_zip; ulint zip_size; zip_size = ((PAGE_ZIP_MIN_SIZE >> 1) << ((flags & DICT_TF_ZSSIZE_MASK) >> DICT_TF_ZSSIZE_SHIFT)); page_zip_set_size(&page_zip, zip_size); page_zip.data = page + UNIV_PAGE_SIZE; #ifdef UNIV_DEBUG page_zip.m_start = #endif /* UNIV_DEBUG */ page_zip.m_end = page_zip.m_nonempty = page_zip.n_blobs = 0; buf_flush_init_for_writing(page, &page_zip, 0); ret = os_file_write(path, file, page_zip.data, 0, 0, zip_size); } ut_free(buf2); if (!ret) { ib_logger(ib_stream, "InnoDB: Error: could not write the first page" " to tablespace "); ut_print_filename(ib_stream, path); ib_logger(ib_stream, "\n"); goto error_exit; } ret = os_file_flush(file); if (!ret) { ib_logger(ib_stream, "InnoDB: Error: file flush of tablespace "); ut_print_filename(ib_stream, path); ib_logger(ib_stream, " failed\n"); goto error_exit; } os_file_close(file); if (*space_id == ULINT_UNDEFINED) { goto error_exit2; } success = fil_space_create(path, *space_id, flags, FIL_TABLESPACE); if (!success) { goto error_exit2; } fil_node_create(path, size, *space_id, FALSE); #ifndef UNIV_HOTBACKUP { mtr_t mtr; mtr_start(&mtr); fil_op_write_log(flags ? MLOG_FILE_CREATE2 : MLOG_FILE_CREATE, *space_id, is_temp ? MLOG_FILE_FLAG_TEMP : 0, flags, tablename, NULL, &mtr); mtr_commit(&mtr); } #endif mem_free(path); return(DB_SUCCESS); } #ifndef UNIV_HOTBACKUP /********************************************************************//** It is possible, though very improbable, that the lsn's in the tablespace to be imported have risen above the current system lsn, if a lengthy purge, ibuf merge, or rollback was performed on a backup taken with ibbackup. If that is the case, reset page lsn's in the file. We assume that the server was shut down after it performed these cleanup operations on the .ibd file, so that it at the shutdown stamped the latest lsn to the FIL_PAGE_FILE_FLUSH_LSN in the first page of the .ibd file, and we can determine whether we need to reset the lsn's just by looking at that flush lsn. @return TRUE if success */ UNIV_INTERN ibool fil_reset_too_high_lsns( /*====================*/ const char* name, /*!< in: table name in the databasename/tablename format */ ib_uint64_t current_lsn) /*!< in: reset lsn's if the lsn stamped to FIL_PAGE_FILE_FLUSH_LSN in the first page is too high */ { os_file_t file; char* filepath; byte* page; byte* buf2; ib_uint64_t flush_lsn; ulint space_id; ib_int64_t file_size; ib_int64_t offset; ulint zip_size; ibool success; page_zip_des_t page_zip; filepath = fil_make_ibd_name(name, FALSE); file = os_file_create_simple_no_error_handling( filepath, OS_FILE_OPEN, OS_FILE_READ_WRITE, &success); if (!success) { /* The following call prints an error message */ os_file_get_last_error(TRUE); ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Error: trying to open a table," " but could not\n" "InnoDB: open the tablespace file "); ut_print_filename(ib_stream, filepath); ib_logger(ib_stream, "!\n"); mem_free(filepath); return(FALSE); } /* Read the first page of the tablespace */ buf2 = ut_malloc(3 * UNIV_PAGE_SIZE); /* Align the memory for file i/o if we might have O_DIRECT set */ page = ut_align(buf2, UNIV_PAGE_SIZE); success = os_file_read(file, page, 0, 0, UNIV_PAGE_SIZE); if (!success) { goto func_exit; } /* We have to read the file flush lsn from the header of the file */ flush_lsn = mach_read_ull(page + FIL_PAGE_FILE_FLUSH_LSN); if (current_lsn >= flush_lsn) { /* Ok */ success = TRUE; goto func_exit; } space_id = fsp_header_get_space_id(page); zip_size = fsp_header_get_zip_size(page); page_zip_des_init(&page_zip); page_zip_set_size(&page_zip, zip_size); if (zip_size) { page_zip.data = page + UNIV_PAGE_SIZE; } ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Flush lsn in the tablespace file %lu" " to be imported\n" "InnoDB: is %llu, which exceeds current" " system lsn %llu.\n" "InnoDB: We reset the lsn's in the file ", (ulong) space_id, flush_lsn, current_lsn); ut_print_filename(ib_stream, filepath); ib_logger(ib_stream, ".\n"); ut_a(ut_is_2pow(zip_size)); ut_a(zip_size <= UNIV_PAGE_SIZE); /* Loop through all the pages in the tablespace and reset the lsn and the page checksum if necessary */ file_size = os_file_get_size_as_iblonglong(file); for (offset = 0; offset < file_size; offset += zip_size ? zip_size : UNIV_PAGE_SIZE) { success = os_file_read(file, page, (ulint)(offset & 0xFFFFFFFFUL), (ulint)(offset >> 32), zip_size ? zip_size : UNIV_PAGE_SIZE); if (!success) { goto func_exit; } if (mach_read_ull(page + FIL_PAGE_LSN) > current_lsn) { /* We have to reset the lsn */ if (zip_size) { memcpy(page_zip.data, page, zip_size); buf_flush_init_for_writing( page, &page_zip, current_lsn); success = os_file_write( filepath, file, page_zip.data, (ulint) offset & 0xFFFFFFFFUL, (ulint) (offset >> 32), zip_size); } else { buf_flush_init_for_writing( page, NULL, current_lsn); success = os_file_write( filepath, file, page, (ulint)(offset & 0xFFFFFFFFUL), (ulint)(offset >> 32), UNIV_PAGE_SIZE); } if (!success) { goto func_exit; } } } success = os_file_flush(file); if (!success) { goto func_exit; } /* We now update the flush_lsn stamp at the start of the file */ success = os_file_read(file, page, 0, 0, zip_size ? zip_size : UNIV_PAGE_SIZE); if (!success) { goto func_exit; } mach_write_ull(page + FIL_PAGE_FILE_FLUSH_LSN, current_lsn); success = os_file_write(filepath, file, page, 0, 0, zip_size ? zip_size : UNIV_PAGE_SIZE); if (!success) { goto func_exit; } success = os_file_flush(file); func_exit: os_file_close(file); ut_free(buf2); mem_free(filepath); return(success); } /********************************************************************//** Tries to open a single-table tablespace and optionally checks the space id is right in it. If does not succeed, prints an error message to the .err log. This function is used to open a tablespace when we start up the server, and also in IMPORT TABLESPACE. NOTE that we assume this operation is used either at the database startup or under the protection of the dictionary mutex, so that two users cannot race here. This operation does not leave the file associated with the tablespace open, but closes it after we have looked at the space id in it. @return TRUE if success */ UNIV_INTERN ibool fil_open_single_table_tablespace( /*=============================*/ ibool check_space_id, /*!< in: should we check that the space id in the file is right; we assume that this function runs much faster if no check is made, since accessing the file inode probably is much faster (the OS caches them) than accessing the first page of the file */ ulint id, /*!< in: space id */ ulint flags, /*!< in: tablespace flags */ const char* name) /*!< in: table name in the databasename/tablename format */ { os_file_t file; char* filepath; ibool success; byte* buf2; byte* page; ulint space_id; ulint space_flags; filepath = fil_make_ibd_name(name, FALSE); /* The tablespace flags (FSP_SPACE_FLAGS) should be 0 for ROW_FORMAT=COMPACT ((table->flags & ~(~0 << DICT_TF_BITS)) == DICT_TF_COMPACT) and ROW_FORMAT=REDUNDANT (table->flags == 0). For any other format, the tablespace flags should equal (table->flags & ~(~0 << DICT_TF_BITS)). */ ut_a(flags != DICT_TF_COMPACT); ut_a(!(flags & (~0UL << DICT_TF_BITS))); file = os_file_create_simple_no_error_handling( filepath, OS_FILE_OPEN, OS_FILE_READ_ONLY, &success); if (!success) { /* The following call prints an error message */ os_file_get_last_error(TRUE); ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Error: trying to open a table," " but could not\n" "InnoDB: open the tablespace file "); ut_print_filename(ib_stream, filepath); ib_logger(ib_stream, "!\n" "InnoDB: Have you moved InnoDB .ibd files around" " without using the\n" "InnoDB: commands DISCARD TABLESPACE and" " IMPORT TABLESPACE?\n" "InnoDB: It is also possible that this is" " a temporary table ...,\n" "InnoDB: and the server removed the .ibd file for this.\n" "InnoDB: Please refer to\n" "InnoDB: the InnoDB website for details\n" "InnoDB: for how to resolve the issue.\n"); mem_free(filepath); return(FALSE); } if (!check_space_id) { space_id = id; goto skip_check; } /* Read the first page of the tablespace */ buf2 = ut_malloc(2 * UNIV_PAGE_SIZE); /* Align the memory for file i/o if we might have O_DIRECT set */ page = ut_align(buf2, UNIV_PAGE_SIZE); success = os_file_read(file, page, 0, 0, UNIV_PAGE_SIZE); /* We have to read the tablespace id and flags from the file. */ space_id = fsp_header_get_space_id(page); space_flags = fsp_header_get_flags(page); ut_free(buf2); if (UNIV_UNLIKELY(space_id != id || space_flags != (flags & ~(~0 << DICT_TF_BITS)))) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Error: tablespace id and flags in file "); ut_print_filename(ib_stream, filepath); ib_logger(ib_stream, " are %lu and %lu, but in the InnoDB\n" "InnoDB: data dictionary they are %lu and %lu.\n" "InnoDB: Have you moved InnoDB .ibd files" " around without using the\n" "InnoDB: commands DISCARD TABLESPACE and" " IMPORT TABLESPACE?\n" "InnoDB: Please refer to\n" "InnoDB: the InnoDB website for details\n" "InnoDB: for how to resolve the issue.\n", (ulong) space_id, (ulong) space_flags, (ulong) id, (ulong) flags); success = FALSE; goto func_exit; } skip_check: success = fil_space_create(filepath, space_id, flags, FIL_TABLESPACE); if (!success) { goto func_exit; } /* We do not measure the size of the file, that is why we pass the 0 below */ fil_node_create(filepath, 0, space_id, FALSE); func_exit: os_file_close(file); mem_free(filepath); return(success); } #endif /* !UNIV_HOTBACKUP */ #ifdef UNIV_HOTBACKUP /*******************************************************************//** Allocates a file name for an old version of a single-table tablespace. The string must be freed by caller with mem_free()! @return own: file name */ static char* fil_make_ibbackup_old_name( /*=======================*/ const char* name) /*!< in: original file name */ { static const char suffix[] = "_ibbackup_old_vers_"; ulint len = strlen(name); char* path = mem_alloc(len + (15 + sizeof suffix)); memcpy(path, name, len); memcpy(path + len, suffix, (sizeof suffix) - 1); ut_sprintf_timestamp_without_extra_chars(path + len + sizeof suffix); return(path); } #endif /* UNIV_HOTBACKUP */ /********************************************************************//** Opens an .ibd file and adds the associated single-table tablespace to the InnoDB fil0fil.c data structures. */ static void fil_load_single_table_tablespace( /*=============================*/ ib_recovery_t recovery, /*!< in: recovery flag */ const char* dbname, /*!< in: database name */ const char* filename) /*!< in: file name (not a path), including the .ibd extension */ { os_file_t file; char* filepath; ibool success; byte* buf2; byte* page; ulint space_id; ulint flags; ulint size_low; ulint size_high; ib_int64_t size; ulint len; const char* ptr; ulint dbname_len; char dir[OS_FILE_MAX_PATH]; ut_strcpy(dir, srv_data_home); srv_normalize_path_for_win(dir); ptr = fil_normalize_path(dir); #ifdef UNIV_HOTBACKUP fil_space_t* space; #endif len = ut_strlen(dbname) + ut_strlen(filename) + ut_strlen(dir) + 3; filepath = mem_alloc(len); dbname_len = ut_strlen(dbname); if (ut_strlen(ptr) > 0) { ut_snprintf(filepath, len, "%s%s/%s", ptr, dbname, filename); } else if (dbname_len == 0 || dbname[dbname_len - 1] == SRV_PATH_SEPARATOR) { ut_snprintf(filepath, len, "%s%s", dbname, filename); } else { ut_snprintf(filepath, len, "%s/%s", dbname, filename); } srv_normalize_path_for_win(filepath); #ifdef __WIN__ # ifndef UNIV_HOTBACKUP /* If lower_case_table_names is 0 or 2, then allow database directory names with upper case letters. On Windows, all table and database names in InnoDB are internally always in lower case. Put the file path to lower case, so that we are consistent with InnoDB's internal data dictionary. */ dict_casedn_str(filepath); # endif /* !UNIV_HOTBACKUP */ #endif file = os_file_create_simple_no_error_handling( filepath, OS_FILE_OPEN, OS_FILE_READ_ONLY, &success); if (!success) { /* The following call prints an error message */ os_file_get_last_error(TRUE); ib_logger(ib_stream, "InnoDB: Error: could not open single-table tablespace" " file\n" "InnoDB: %s!\n" "InnoDB: We do not continue the crash recovery," " because the table may become\n" "InnoDB: corrupt if we cannot apply the log records" " in the InnoDB log to it.\n" "InnoDB: To fix the problem and start InnoDB:\n" "InnoDB: 1) If there is a permission problem" " in the file and InnoDB cannot\n" "InnoDB: open the file, you should" " modify the permissions.\n" "InnoDB: 2) If the table is not needed, or you can" " restore it from a backup,\n" "InnoDB: then you can remove the .ibd file," " and InnoDB will do a normal\n" "InnoDB: crash recovery and ignore that table.\n" "InnoDB: 3) If the file system or the" " disk is broken, and you cannot remove\n" "InnoDB: the .ibd file, you can set" " force_recovery != IB_RECOVERY_DEFAULT \n" "InnoDB: and force InnoDB to continue crash" " recovery here.\n", filepath); mem_free(filepath); if (recovery != IB_RECOVERY_DEFAULT) { ib_logger(ib_stream, "InnoDB: force_recovery" " was set to %lu. Continuing crash recovery\n" "InnoDB: even though we cannot access" " the .ibd file of this table.\n", recovery); return; } srv_panic(DB_CORRUPTION, "Cannot access .ibd file: %s", filepath); } success = os_file_get_size(file, &size_low, &size_high); if (!success) { /* The following call prints an error message */ os_file_get_last_error(TRUE); ib_logger(ib_stream, "InnoDB: Error: could not measure the size" " of single-table tablespace file\n" "InnoDB: %s!\n" "InnoDB: We do not continue crash recovery," " because the table will become\n" "InnoDB: corrupt if we cannot apply the log records" " in the InnoDB log to it.\n" "InnoDB: To fix the problem and start the server:\n" "InnoDB: 1) If there is a permission problem" " in the file and the server cannot\n" "InnoDB: access the file, you should" " modify the permissions.\n" "InnoDB: 2) If the table is not needed," " or you can restore it from a backup,\n" "InnoDB: then you can remove the .ibd file," " and InnoDB will do a normal\n" "InnoDB: crash recovery and ignore that table.\n" "InnoDB: 3) If the file system or the disk is broken," " and you cannot remove\n" "InnoDB: the .ibd file, you can set" " force_recovery != IB_RECOVERY_DEFAULT\n" "InnoDB: and force InnoDB to continue" " crash recovery here.\n", filepath); os_file_close(file); mem_free(filepath); if (recovery != IB_RECOVERY_DEFAULT) { ib_logger(ib_stream, "InnoDB: force_recovery" " was set to %lu. Continuing crash recovery\n" "InnoDB: even though we cannot access" " the .ibd file of this table.\n", recovery); return; } srv_panic(DB_CORRUPTION, "Could not measure size of %s", filepath); } /* TODO: What to do in other cases where we cannot access an .ibd file during a crash recovery? */ /* Every .ibd file is created >= 4 pages in size. Smaller files cannot be ok. */ size = (((ib_int64_t)size_high) << 32) + (ib_int64_t)size_low; #ifndef UNIV_HOTBACKUP if (size < FIL_IBD_FILE_INITIAL_SIZE * UNIV_PAGE_SIZE) { ib_logger(ib_stream, "InnoDB: Error: the size of single-table tablespace" " file %s\n" "InnoDB: is only %lu %lu, should be at least %lu!", filepath, (ulong) size_high, (ulong) size_low, (ulong) (4 * UNIV_PAGE_SIZE)); os_file_close(file); mem_free(filepath); return; } #endif /* Read the first page of the tablespace if the size big enough */ buf2 = ut_malloc(2 * UNIV_PAGE_SIZE); /* Align the memory for file i/o if we might have O_DIRECT set */ page = ut_align(buf2, UNIV_PAGE_SIZE); if (size >= FIL_IBD_FILE_INITIAL_SIZE * UNIV_PAGE_SIZE) { success = os_file_read(file, page, 0, 0, UNIV_PAGE_SIZE); /* We have to read the tablespace id from the file */ space_id = fsp_header_get_space_id(page); flags = fsp_header_get_flags(page); } else { space_id = ULINT_UNDEFINED; flags = 0; } #ifndef UNIV_HOTBACKUP if (space_id == ULINT_UNDEFINED || space_id == 0) { ib_logger(ib_stream, "InnoDB: Error: tablespace id %lu in file %s" " is not sensible\n", (ulong) space_id, filepath); goto func_exit; } #else if (space_id == ULINT_UNDEFINED || space_id == 0) { char* new_path; ib_logger(ib_stream, "InnoDB: Renaming tablespace %s of id %lu,\n" "InnoDB: to %s_ibbackup_old_vers_\n" "InnoDB: because its size %" PRId64 " is too small" " (< 4 pages 16 kB each),\n" "InnoDB: or the space id in the file header" " is not sensible.\n" "InnoDB: This can happen in an ibbackup run," " and is not dangerous.\n", filepath, space_id, filepath, size); os_file_close(file); new_path = fil_make_ibbackup_old_name(filepath); ut_a(os_file_rename(filepath, new_path)); ut_free(buf2); mem_free(filepath); mem_free(new_path); return; } /* A backup may contain the same space several times, if the space got renamed at a sensitive time. Since it is enough to have one version of the space, we rename the file if a space with the same space id already exists in the tablespace memory cache. We rather rename the file than delete it, because if there is a bug, we do not want to destroy valuable data. */ mutex_enter(&fil_system->mutex); space = fil_space_get_by_id(space_id); if (space) { char* new_path; ib_logger(ib_stream, "InnoDB: Renaming tablespace %s of id %lu,\n" "InnoDB: to %s_ibbackup_old_vers_\n" "InnoDB: because space %s with the same id\n" "InnoDB: was scanned earlier. This can happen" " if you have renamed tables\n" "InnoDB: during an ibbackup run.\n", filepath, space_id, filepath, space->name); os_file_close(file); new_path = fil_make_ibbackup_old_name(filepath); mutex_exit(&fil_system->mutex); ut_a(os_file_rename(filepath, new_path)); ut_free(buf2); mem_free(filepath); mem_free(new_path); return; } mutex_exit(&fil_system->mutex); #endif success = fil_space_create(filepath, space_id, flags, FIL_TABLESPACE); if (!success) { if (srv_force_recovery > 0) { ib_logger(ib_stream, "InnoDB: innodb_force_recovery" " was set to %lu. Continuing crash recovery\n" "InnoDB: even though the tablespace creation" " of this table failed.\n", srv_force_recovery); goto func_exit; } srv_panic(DB_CORRUPTION, "During recovery"); } /* We do not use the size information we have about the file, because the rounding formula for extents and pages is somewhat complex; we let fil_node_open() do that task. */ fil_node_create(filepath, 0, space_id, FALSE); func_exit: os_file_close(file); ut_free(buf2); mem_free(filepath); } /***********************************************************************//** A fault-tolerant function that tries to read the next file name in the directory. We retry 100 times if os_file_readdir_next_file() returns -1. The idea is to read as much good data as we can and jump over bad data. @return 0 if ok, -1 if error even after the retries, 1 if at the end of the directory */ static int fil_file_readdir_next_file( /*=======================*/ ulint* err, /*!< out: this is set to DB_ERROR if an error was encountered, otherwise not changed */ const char* dirname,/*!< in: directory name or path */ os_file_dir_t dir, /*!< in: directory stream */ os_file_stat_t* info) /*!< in/out: buffer where the info is returned */ { ulint i; int ret; for (i = 0; i < 100; i++) { ret = os_file_readdir_next_file(dirname, dir, info); if (ret != -1) { return(ret); } ib_logger(ib_stream, "InnoDB: Error: os_file_readdir_next_file()" " returned -1 in\n" "InnoDB: directory %s\n" "InnoDB: Crash recovery may have failed" " for some .ibd files!\n", dirname); *err = DB_ERROR; } return(-1); } /********************************************************************//** At the server startup, if we need crash recovery, scans the database directories under the server datadir, looking for .ibd files. Those files are single-table tablespaces. We need to know the space id in each of them so that we know into which file we should look to check the contents of a page stored in the doublewrite buffer, also to know where to apply log records where the space id is != 0. @return DB_SUCCESS or error number */ UNIV_INTERN ulint fil_load_single_table_tablespaces( /*==============================*/ ib_recovery_t recovery) /*!< in: recovery flag */ { int ret; char* dbpath = NULL; ulint dbpath_len = 100; os_file_dir_t dir; os_file_dir_t dbdir; os_file_stat_t dbinfo; os_file_stat_t fileinfo; ulint err = DB_SUCCESS; char home[OS_FILE_MAX_PATH]; /* The datadir of the server is always the default directory. */ ut_strcpy(home, srv_data_home); srv_normalize_path_for_win(home); dir = os_file_opendir(home, TRUE); if (dir == NULL) { return(DB_ERROR); } dbpath = mem_alloc(dbpath_len); /* Scan all directories under the datadir. They are the database directories of the server. */ ret = fil_file_readdir_next_file(&err, home, dir, &dbinfo); while (ret == 0) { ulint len; /* printf("Looking at %s in datadir\n", dbinfo.name); */ if (dbinfo.type == OS_FILE_TYPE_FILE || dbinfo.type == OS_FILE_TYPE_UNKNOWN) { goto next_datadir_item; } /* We found a symlink or a directory; try opening it to see if a symlink is a directory */ len = ut_strlen(home) + ut_strlen(dbinfo.name) + 2; if (len > dbpath_len) { dbpath_len = len; if (dbpath) { mem_free(dbpath); } dbpath = mem_alloc(dbpath_len); } len = ut_strlen(home); ut_a(home[len - 1] == SRV_PATH_SEPARATOR); ut_snprintf(dbpath, dbpath_len, "%s%s", home, dbinfo.name); srv_normalize_path_for_win(dbpath); dbdir = os_file_opendir(dbpath, FALSE); if (dbdir != NULL) { /* printf("Opened dir %s\n", dbinfo.name); */ /* We found a database directory; loop through it, looking for possible .ibd files in it */ ret = fil_file_readdir_next_file( &err, dbpath, dbdir, &fileinfo); while (ret == 0) { /* printf( " Looking at file %s\n", fileinfo.name); */ if (fileinfo.type == OS_FILE_TYPE_DIR) { goto next_file_item; } len = ut_strlen(fileinfo.name); /* We found a symlink or a file */ if (len > 4 && 0 == ut_strcmp(fileinfo.name + len - 4, ".ibd")) { /* The name ends in .ibd; try opening the file */ fil_load_single_table_tablespace( recovery, dbinfo.name, fileinfo.name); } next_file_item: ret = fil_file_readdir_next_file( &err, dbpath, dbdir, &fileinfo); } if (0 != os_file_closedir(dbdir)) { ib_logger(ib_stream, "InnoDB: Warning: could not" " close database directory "); ut_print_filename(ib_stream, dbpath); ib_logger(ib_stream, "\n"); err = DB_ERROR; } } next_datadir_item: ret = fil_file_readdir_next_file(&err, home, dir, &dbinfo); } mem_free(dbpath); if (0 != os_file_closedir(dir)) { ib_logger(ib_stream, "InnoDB: Error: could not close datadir\n"); return(DB_ERROR); } return(err); } /********************************************************************//** If we need crash recovery, and we have called fil_load_single_table_tablespaces() and dict_load_single_table_tablespaces(), we can call this function to print an error message of orphaned .ibd files for which there is not a data dictionary entry with a matching table name and space id. */ UNIV_INTERN void fil_print_orphaned_tablespaces(void) /*================================*/ { fil_space_t* space; mutex_enter(&fil_system->mutex); space = UT_LIST_GET_FIRST(fil_system->space_list); while (space) { if (space->purpose == FIL_TABLESPACE && space->id != 0 && !space->mark) { ib_logger(ib_stream, "InnoDB: Warning: tablespace "); ut_print_filename(ib_stream, space->name); ib_logger(ib_stream, " of id %lu has no matching table in\n" "InnoDB: the InnoDB data dictionary.\n", (ulong) space->id); } space = UT_LIST_GET_NEXT(space_list, space); } mutex_exit(&fil_system->mutex); } /*******************************************************************//** Returns TRUE if a single-table tablespace does not exist in the memory cache, or is being deleted there. @return TRUE if does not exist or is being\ deleted */ UNIV_INTERN ibool fil_tablespace_deleted_or_being_deleted_in_mem( /*===========================================*/ ulint id, /*!< in: space id */ ib_int64_t version)/*!< in: tablespace_version should be this; if you pass -1 as the value of this, then this parameter is ignored */ { fil_space_t* space; ut_ad(fil_system); mutex_enter(&fil_system->mutex); space = fil_space_get_by_id(id); if (space == NULL || space->is_being_deleted) { mutex_exit(&fil_system->mutex); return(TRUE); } if (version != ((ib_int64_t)-1) && space->tablespace_version != version) { mutex_exit(&fil_system->mutex); return(TRUE); } mutex_exit(&fil_system->mutex); return(FALSE); } /*******************************************************************//** Returns TRUE if a single-table tablespace exists in the memory cache. @return TRUE if exists */ UNIV_INTERN ibool fil_tablespace_exists_in_mem( /*=========================*/ ulint id) /*!< in: space id */ { fil_space_t* space; ut_ad(fil_system); mutex_enter(&fil_system->mutex); space = fil_space_get_by_id(id); mutex_exit(&fil_system->mutex); return(space != NULL); } /*******************************************************************//** Returns TRUE if a matching tablespace exists in the InnoDB tablespace memory cache. Note that if we have not done a crash recovery at the database startup, there may be many tablespaces which are not yet in the memory cache. @return TRUE if a matching tablespace exists in the memory cache */ UNIV_INTERN ibool fil_space_for_table_exists_in_mem( /*==============================*/ ulint id, /*!< in: space id */ const char* name, /*!< in: table name in the standard 'databasename/tablename' format or the dir path to a temp table */ ibool is_temp, /*!< in: TRUE if created with CREATE TEMPORARY TABLE */ ibool mark_space, /*!< in: in crash recovery, at database startup we mark all spaces which have an associated table in the InnoDB data dictionary, so that we can print a warning about orphaned tablespaces */ ibool print_error_if_does_not_exist) /*!< in: print detailed error information to the .err log if a matching tablespace is not found from memory */ { fil_space_t* namespace; fil_space_t* space; char* path; ut_ad(fil_system); mutex_enter(&fil_system->mutex); path = fil_make_ibd_name(name, is_temp); /* Look if there is a space with the same id */ space = fil_space_get_by_id(id); /* Look if there is a space with the same name; the name is the directory path from the datadir to the file */ namespace = fil_space_get_by_name(path); if (space && space == namespace) { /* Found */ if (mark_space) { space->mark = TRUE; } mem_free(path); mutex_exit(&fil_system->mutex); return(TRUE); } if (!print_error_if_does_not_exist) { mem_free(path); mutex_exit(&fil_system->mutex); return(FALSE); } if (space == NULL) { if (namespace == NULL) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Error: table "); ut_print_filename(ib_stream, name); ib_logger(ib_stream, "\n" "InnoDB: in InnoDB data dictionary" " has tablespace id %lu,\n" "InnoDB: but tablespace with that id" " or name does not exist. Have\n" "InnoDB: you deleted or moved .ibd files?\n", (ulong) id); } else { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Error: table "); ut_print_filename(ib_stream, name); ib_logger(ib_stream, "\n" "InnoDB: in InnoDB data dictionary has" " tablespace id %lu,\n" "InnoDB: but a tablespace with that id" " does not exist. There is\n" "InnoDB: a tablespace of name %s and id %lu," " though. Have\n" "InnoDB: you deleted or moved .ibd files?\n", (ulong) id, namespace->name, (ulong) namespace->id); } error_exit: ib_logger(ib_stream, "InnoDB: Please refer to\n" "InnoDB: the InnoDB website for details\n" "InnoDB: for how to resolve the issue.\n"); mem_free(path); mutex_exit(&fil_system->mutex); return(FALSE); } if (0 != fil_tablename_compare(space->name, path)) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Error: table "); ut_print_filename(ib_stream, name); ib_logger(ib_stream, "\n" "InnoDB: in InnoDB data dictionary has" " tablespace id %lu,\n" "InnoDB: but the tablespace with that id" " has name %s.\n" "InnoDB: Have you deleted or moved .ibd files?\n", (ulong) id, space->name); if (namespace != NULL) { ib_logger(ib_stream, "InnoDB: There is a tablespace" " with the right name\n" "InnoDB: "); ut_print_filename(ib_stream, namespace->name); ib_logger(ib_stream, ", but its id is %lu.\n", (ulong) namespace->id); } goto error_exit; } mem_free(path); mutex_exit(&fil_system->mutex); return(FALSE); } /*******************************************************************//** Checks if a single-table tablespace for a given table name exists in the tablespace memory cache. @return space id, ULINT_UNDEFINED if not found */ static ulint fil_get_space_id_for_table( /*=======================*/ const char* name) /*!< in: table name in the standard 'databasename/tablename' format */ { fil_space_t* namespace; ulint id = ULINT_UNDEFINED; char* path; ut_ad(fil_system); mutex_enter(&fil_system->mutex); path = fil_make_ibd_name(name, FALSE); /* Look if there is a space with the same name; the name is the directory path to the file */ namespace = fil_space_get_by_name(path); if (namespace) { id = namespace->id; } mem_free(path); mutex_exit(&fil_system->mutex); return(id); } /**********************************************************************//** Tries to extend a data file so that it would accommodate the number of pages given. The tablespace must be cached in the memory cache. If the space is big enough already, does nothing. @return TRUE if success */ UNIV_INTERN ibool fil_extend_space_to_desired_size( /*=============================*/ ulint* actual_size, /*!< out: size of the space after extension; if we ran out of disk space this may be lower than the desired size */ ulint space_id, /*!< in: space id */ ulint size_after_extend)/*!< in: desired size in pages after the extension; if the current space size is bigger than this already, the function does nothing */ { fil_node_t* node; fil_space_t* space; byte* buf2; byte* buf; ulint buf_size; ulint start_page_no; ulint file_start_page_no; ulint offset_high; ulint offset_low; ulint page_size; ibool success = TRUE; fil_mutex_enter_and_prepare_for_io(space_id); space = fil_space_get_by_id(space_id); ut_a(space); if (space->size >= size_after_extend) { /* Space already big enough */ *actual_size = space->size; mutex_exit(&fil_system->mutex); return(TRUE); } page_size = dict_table_flags_to_zip_size(space->flags); if (!page_size) { page_size = UNIV_PAGE_SIZE; } node = UT_LIST_GET_LAST(space->chain); fil_node_prepare_for_io(node, fil_system, space); start_page_no = space->size; file_start_page_no = space->size - node->size; /* Extend at most 64 pages at a time */ buf_size = ut_min(64, size_after_extend - start_page_no) * page_size; buf2 = mem_alloc(buf_size + page_size); buf = ut_align(buf2, page_size); memset(buf, 0, buf_size); while (start_page_no < size_after_extend) { ulint n_pages = ut_min(buf_size / page_size, size_after_extend - start_page_no); offset_high = (start_page_no - file_start_page_no) / (4096 * ((1024 * 1024) / page_size)); offset_low = ((start_page_no - file_start_page_no) % (4096 * ((1024 * 1024) / page_size))) * page_size; #ifdef UNIV_HOTBACKUP success = os_file_write(node->name, node->handle, buf, offset_low, offset_high, page_size * n_pages); #else success = os_aio(OS_FILE_WRITE, OS_AIO_SYNC, node->name, node->handle, buf, offset_low, offset_high, page_size * n_pages, NULL, NULL); #endif if (success) { node->size += n_pages; space->size += n_pages; os_has_said_disk_full = FALSE; } else { /* Let us measure the size of the file to determine how much we were able to extend it */ n_pages = ((ulint) (os_file_get_size_as_iblonglong( node->handle) / page_size)) - node->size; node->size += n_pages; space->size += n_pages; break; } start_page_no += n_pages; } mem_free(buf2); fil_node_complete_io(node, fil_system, OS_FILE_WRITE); *actual_size = space->size; #ifndef UNIV_HOTBACKUP if (space_id == 0) { ulint pages_per_mb = (1024 * 1024) / page_size; /* Keep the last data file size info up to date, rounded to full megabytes */ srv_data_file_sizes[srv_n_data_files - 1] = (node->size / pages_per_mb) * pages_per_mb; } #endif /* !UNIV_HOTBACKUP */ /* printf("Extended %s to %lu, actual size %lu pages\n", space->name, size_after_extend, *actual_size); */ mutex_exit(&fil_system->mutex); fil_flush(space_id); return(success); } #ifdef UNIV_HOTBACKUP /********************************************************************//** Extends all tablespaces to the size stored in the space header. During the ibbackup --apply-log phase we extended the spaces on-demand so that log records could be applied, but that may have left spaces still too small compared to the size stored in the space header. */ UNIV_INTERN void fil_extend_tablespaces_to_stored_len(void) /*======================================*/ { fil_space_t* space; byte* buf; ulint actual_size; ulint size_in_header; ulint error; ibool success; buf = mem_alloc(UNIV_PAGE_SIZE); mutex_enter(&fil_system->mutex); space = UT_LIST_GET_FIRST(fil_system->space_list); while (space) { ut_a(space->purpose == FIL_TABLESPACE); mutex_exit(&fil_system->mutex); /* no need to protect with a mutex, because this is a single-threaded operation */ error = fil_read(TRUE, space->id, dict_table_flags_to_zip_size(space->flags), 0, 0, UNIV_PAGE_SIZE, buf, NULL); ut_a(error == DB_SUCCESS); size_in_header = fsp_get_size_low(buf); success = fil_extend_space_to_desired_size( &actual_size, space->id, size_in_header); if (!success) { srv_panic(DB_ERROR, "InnoDB: Error: could not extend the" " tablespace of %s\n" "InnoDB: to the size stored in header," " %lu pages;\n" "InnoDB: size after extension %lu pages\n" "InnoDB: Check that you have free disk space" " and retry!\n", space->name, size_in_header, actual_size); } mutex_enter(&fil_system->mutex); space = UT_LIST_GET_NEXT(space_list, space); } mutex_exit(&fil_system->mutex); mem_free(buf); } #endif /*========== RESERVE FREE EXTENTS (for a B-tree split, for example) ===*/ /*******************************************************************//** Tries to reserve free extents in a file space. @return TRUE if succeed */ UNIV_INTERN ibool fil_space_reserve_free_extents( /*===========================*/ ulint id, /*!< in: space id */ ulint n_free_now, /*!< in: number of free extents now */ ulint n_to_reserve) /*!< in: how many one wants to reserve */ { fil_space_t* space; ibool success; ut_ad(fil_system); mutex_enter(&fil_system->mutex); space = fil_space_get_by_id(id); ut_a(space); if (space->n_reserved_extents + n_to_reserve > n_free_now) { success = FALSE; } else { space->n_reserved_extents += n_to_reserve; success = TRUE; } mutex_exit(&fil_system->mutex); return(success); } /*******************************************************************//** Releases free extents in a file space. */ UNIV_INTERN void fil_space_release_free_extents( /*===========================*/ ulint id, /*!< in: space id */ ulint n_reserved) /*!< in: how many one reserved */ { fil_space_t* space; ut_ad(fil_system); mutex_enter(&fil_system->mutex); space = fil_space_get_by_id(id); ut_a(space); ut_a(space->n_reserved_extents >= n_reserved); space->n_reserved_extents -= n_reserved; mutex_exit(&fil_system->mutex); } /*******************************************************************//** Gets the number of reserved extents. If the database is silent, this number should be zero. */ UNIV_INTERN ulint fil_space_get_n_reserved_extents( /*=============================*/ ulint id) /*!< in: space id */ { fil_space_t* space; ulint n; ut_ad(fil_system); mutex_enter(&fil_system->mutex); space = fil_space_get_by_id(id); ut_a(space); n = space->n_reserved_extents; mutex_exit(&fil_system->mutex); return(n); } /*============================ FILE I/O ================================*/ /********************************************************************//** NOTE: you must call fil_mutex_enter_and_prepare_for_io() first! Prepares a file node for i/o. Opens the file if it is closed. Updates the pending i/o's field in the node and the system appropriately. Takes the node off the LRU list if it is in the LRU list. The caller must hold the fil_sys mutex. */ static void fil_node_prepare_for_io( /*====================*/ fil_node_t* node, /*!< in: file node */ fil_system_t* system, /*!< in: tablespace memory cache */ fil_space_t* space) /*!< in: space */ { ut_ad(node && system && space); ut_ad(mutex_own(&(system->mutex))); if (system->n_open > system->max_n_open + 5) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Warning: open files %lu" " exceeds the limit %lu\n", (ulong) system->n_open, (ulong) system->max_n_open); } if (node->open == FALSE) { /* File is closed: open it */ ut_a(node->n_pending == 0); fil_node_open_file(node, system, space); } if (node->n_pending == 0 && space->purpose == FIL_TABLESPACE && space->id != 0) { /* The node is in the LRU list, remove it */ ut_a(UT_LIST_GET_LEN(system->LRU) > 0); UT_LIST_REMOVE(LRU, system->LRU, node); } node->n_pending++; } /********************************************************************//** Updates the data structures when an i/o operation finishes. Updates the pending i/o's field in the node appropriately. */ static void fil_node_complete_io( /*=================*/ fil_node_t* node, /*!< in: file node */ fil_system_t* system, /*!< in: tablespace memory cache */ ulint type) /*!< in: OS_FILE_WRITE or OS_FILE_READ; marks the node as modified if type == OS_FILE_WRITE */ { ut_ad(node); ut_ad(system); ut_ad(mutex_own(&(system->mutex))); ut_a(node->n_pending > 0); node->n_pending--; if (type == OS_FILE_WRITE) { system->modification_counter++; node->modification_counter = system->modification_counter; if (!node->space->is_in_unflushed_spaces) { node->space->is_in_unflushed_spaces = TRUE; UT_LIST_ADD_FIRST(unflushed_spaces, system->unflushed_spaces, node->space); } } if (node->n_pending == 0 && node->space->purpose == FIL_TABLESPACE && node->space->id != 0) { /* The node must be put back to the LRU list */ UT_LIST_ADD_FIRST(LRU, system->LRU, node); } } /********************************************************************//** Report information about an invalid page access. */ static void fil_report_invalid_page_access( /*===========================*/ ulint block_offset, /*!< in: block offset */ ulint space_id, /*!< in: space id */ const char* space_name, /*!< in: space name */ ulint byte_offset, /*!< in: byte offset */ ulint len, /*!< in: I/O length */ ulint type) /*!< in: I/O type */ { ib_logger(ib_stream, "InnoDB: Error: trying to access page number %lu" " in space %lu,\n" "InnoDB: space name %s,\n" "InnoDB: which is outside the tablespace bounds.\n" "InnoDB: Byte offset %lu, len %lu, i/o type %lu.\n" "InnoDB: If you get this error at server startup," " please check that\n" "InnoDB: your config file matches the ibdata files" " that you have in the\n" "InnoDB: server.\n", (ulong) block_offset, (ulong) space_id, space_name, (ulong) byte_offset, (ulong) len, (ulong) type); } /********************************************************************//** Reads or writes data. This operation is asynchronous (aio). @return DB_SUCCESS, or DB_TABLESPACE_DELETED if we are trying to do i/o on a tablespace which does not exist */ UNIV_INTERN ulint fil_io( /*===*/ ulint type, /*!< in: OS_FILE_READ or OS_FILE_WRITE, ORed to OS_FILE_LOG, if a log i/o and ORed to OS_AIO_SIMULATED_WAKE_LATER if simulated aio and we want to post a batch of i/os; NOTE that a simulated batch may introduce hidden chances of deadlocks, because i/os are not actually handled until all have been posted: use with great caution! */ ibool sync, /*!< in: TRUE if synchronous aio is desired */ ulint space_id, /*!< in: space id */ ulint zip_size, /*!< in: compressed page size in bytes; 0 for uncompressed pages */ ulint block_offset, /*!< in: offset in number of blocks */ ulint byte_offset, /*!< in: remainder of offset in bytes; in aio this must be divisible by the OS block size */ ulint len, /*!< in: how many bytes to read or write; this must not cross a file boundary; in aio this must be a block size multiple */ void* buf, /*!< in/out: buffer where to store read data or from where to write; in aio this must be appropriately aligned */ void* message) /*!< in: message for aio handler if non-sync aio used, else ignored */ { ulint mode; fil_space_t* space; fil_node_t* node; ulint offset_high; ulint offset_low; ibool ret; ulint is_log; ulint wake_later; is_log = type & OS_FILE_LOG; type = type & ~OS_FILE_LOG; wake_later = type & OS_AIO_SIMULATED_WAKE_LATER; type = type & ~OS_AIO_SIMULATED_WAKE_LATER; ut_ad(byte_offset < UNIV_PAGE_SIZE); ut_ad(!zip_size || !byte_offset); ut_ad(ut_is_2pow(zip_size)); ut_ad(buf); ut_ad(len > 0); #if (1 << UNIV_PAGE_SIZE_SHIFT) != UNIV_PAGE_SIZE # error "(1 << UNIV_PAGE_SIZE_SHIFT) != UNIV_PAGE_SIZE" #endif ut_ad(fil_validate()); #ifndef UNIV_HOTBACKUP # ifndef UNIV_LOG_DEBUG /* ibuf bitmap pages must be read in the sync aio mode: */ ut_ad(recv_no_ibuf_operations || (type == OS_FILE_WRITE) || !ibuf_bitmap_page(zip_size, block_offset) || sync || is_log); ut_ad(!ibuf_inside() || is_log || (type == OS_FILE_WRITE) || ibuf_page(space_id, zip_size, block_offset, NULL)); # endif /* UNIV_LOG_DEBUG */ if (sync) { mode = OS_AIO_SYNC; } else if (is_log) { mode = OS_AIO_LOG; } else if (type == OS_FILE_READ && !recv_no_ibuf_operations && ibuf_page(space_id, zip_size, block_offset, NULL)) { mode = OS_AIO_IBUF; } else { mode = OS_AIO_NORMAL; } #else /* !UNIV_HOTBACKUP */ ut_a(sync); mode = OS_AIO_SYNC; #endif /* !UNIV_HOTBACKUP */ if (type == OS_FILE_READ) { srv_data_read+= len; } else if (type == OS_FILE_WRITE) { srv_data_written+= len; } /* Reserve the fil_system mutex and make sure that we can open at least one file while holding it, if the file is not already open */ fil_mutex_enter_and_prepare_for_io(space_id); space = fil_space_get_by_id(space_id); if (!space) { mutex_exit(&fil_system->mutex); ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Error: trying to do i/o" " to a tablespace which does not exist.\n" "InnoDB: i/o type %lu, space id %lu," " page no. %lu, i/o length %lu bytes\n", (ulong) type, (ulong) space_id, (ulong) block_offset, (ulong) len); return(DB_TABLESPACE_DELETED); } ut_ad((mode != OS_AIO_IBUF) || (space->purpose == FIL_TABLESPACE)); node = UT_LIST_GET_FIRST(space->chain); for (;;) { if (UNIV_UNLIKELY(node == NULL)) { fil_report_invalid_page_access( block_offset, space_id, space->name, byte_offset, len, type); ut_error; } if (space->id != 0 && node->size == 0) { /* We do not know the size of a single-table tablespace before we open the file */ break; } if (node->size > block_offset) { /* Found! */ break; } else { block_offset -= node->size; node = UT_LIST_GET_NEXT(chain, node); } } /* Open file if closed */ fil_node_prepare_for_io(node, fil_system, space); /* Check that at least the start offset is within the bounds of a single-table tablespace */ if (UNIV_UNLIKELY(node->size <= block_offset) && space->id != 0 && space->purpose == FIL_TABLESPACE) { fil_report_invalid_page_access( block_offset, space_id, space->name, byte_offset, len, type); ut_error; } /* Now we have made the changes in the data structures of fil_system */ mutex_exit(&fil_system->mutex); /* Calculate the low 32 bits and the high 32 bits of the file offset */ if (!zip_size) { offset_high = (block_offset >> (32 - UNIV_PAGE_SIZE_SHIFT)); offset_low = ((block_offset << UNIV_PAGE_SIZE_SHIFT) & 0xFFFFFFFFUL) + byte_offset; ut_a(node->size - block_offset >= ((byte_offset + len + (UNIV_PAGE_SIZE - 1)) / UNIV_PAGE_SIZE)); } else { ulint zip_size_shift; switch (zip_size) { case 1024: zip_size_shift = 10; break; case 2048: zip_size_shift = 11; break; case 4096: zip_size_shift = 12; break; case 8192: zip_size_shift = 13; break; case 16384: zip_size_shift = 14; break; default: ut_error; } offset_high = block_offset >> (32 - zip_size_shift); offset_low = (block_offset << zip_size_shift & 0xFFFFFFFFUL) + byte_offset; ut_a(node->size - block_offset >= (len + (zip_size - 1)) / zip_size); } /* Do aio */ ut_a(byte_offset % OS_FILE_LOG_BLOCK_SIZE == 0); ut_a((len % OS_FILE_LOG_BLOCK_SIZE) == 0); #ifdef UNIV_HOTBACKUP /* In ibbackup do normal i/o, not aio */ if (type == OS_FILE_READ) { ret = os_file_read(node->handle, buf, offset_low, offset_high, len); } else { ret = os_file_write(node->name, node->handle, buf, offset_low, offset_high, len); } #else /* Queue the aio request */ ret = os_aio(type, mode | wake_later, node->name, node->handle, buf, offset_low, offset_high, len, node, message); #endif ut_a(ret); if (mode == OS_AIO_SYNC) { /* The i/o operation is already completed when we return from os_aio: */ mutex_enter(&fil_system->mutex); fil_node_complete_io(node, fil_system, type); mutex_exit(&fil_system->mutex); ut_ad(fil_validate()); } return(DB_SUCCESS); } #ifndef UNIV_HOTBACKUP /**********************************************************************//** Waits for an aio operation to complete. This function is used to write the handler for completed requests. The aio array of pending requests is divided into segments (see os0file.c for more info). The thread specifies which segment it wants to wait for. */ UNIV_INTERN void fil_aio_wait( /*=========*/ ulint segment) /*!< in: the number of the segment in the aio array to wait for */ { ibool ret; fil_node_t* fil_node; void* message; ulint type; ut_ad(fil_validate()); if (os_aio_use_native_aio) { os_set_io_thread_op_info(segment, "native aio handle"); #ifdef WIN_ASYNC_IO ret = os_aio_windows_handle(segment, 0, &fil_node, &message, &type); #else ret = 0; /* Eliminate compiler warning */ ut_error; #endif } else { os_set_io_thread_op_info(segment, "simulated aio handle"); ret = os_aio_simulated_handle(segment, &fil_node, &message, &type); } ut_a(ret); os_set_io_thread_op_info(segment, "complete io for fil node"); mutex_enter(&fil_system->mutex); fil_node_complete_io(fil_node, fil_system, type); mutex_exit(&fil_system->mutex); ut_ad(fil_validate()); /* Do the i/o handling */ /* IMPORTANT: since i/o handling for reads will read also the insert buffer in tablespace 0, you have to be very careful not to introduce deadlocks in the i/o system. We keep tablespace 0 data files always open, and use a special i/o thread to serve insert buffer requests. */ if (fil_node->space->purpose == FIL_TABLESPACE) { os_set_io_thread_op_info(segment, "complete io for buf page"); buf_page_io_complete(message); } else { os_set_io_thread_op_info(segment, "complete io for log"); log_io_complete(message); } } #endif /* UNIV_HOTBACKUP */ /**********************************************************************//** Flushes to disk possible writes cached by the OS. If the space does not exist or is being dropped, does not do anything. */ UNIV_INTERN void fil_flush( /*======*/ ulint space_id) /*!< in: file space id (this can be a group of log files or a tablespace of the database) */ { fil_space_t* space; fil_node_t* node; os_file_t file; ib_int64_t old_mod_counter; mutex_enter(&fil_system->mutex); space = fil_space_get_by_id(space_id); if (!space || space->is_being_deleted) { mutex_exit(&fil_system->mutex); return; } space->n_pending_flushes++; /*!< prevent dropping of the space while we are flushing */ node = UT_LIST_GET_FIRST(space->chain); while (node) { if (node->modification_counter > node->flush_counter) { ut_a(node->open); /* We want to flush the changes at least up to old_mod_counter */ old_mod_counter = node->modification_counter; if (space->purpose == FIL_TABLESPACE) { fil_n_pending_tablespace_flushes++; } else { fil_n_pending_log_flushes++; fil_n_log_flushes++; } #ifdef __WIN__ if (node->is_raw_disk) { goto skip_flush; } #endif retry: if (node->n_pending_flushes > 0) { /* We want to avoid calling os_file_flush() on the file twice at the same time, because we do not know what bugs OS's may contain in file i/o; sleep for a while */ mutex_exit(&fil_system->mutex); os_thread_sleep(20000); mutex_enter(&fil_system->mutex); if (node->flush_counter >= old_mod_counter) { goto skip_flush; } goto retry; } ut_a(node->open); file = node->handle; node->n_pending_flushes++; mutex_exit(&fil_system->mutex); /* ib_logger(ib_stream, "Flushing to file %s\n", node->name); */ os_file_flush(file); mutex_enter(&fil_system->mutex); node->n_pending_flushes--; skip_flush: if (node->flush_counter < old_mod_counter) { node->flush_counter = old_mod_counter; if (space->is_in_unflushed_spaces && fil_space_is_flushed(space)) { space->is_in_unflushed_spaces = FALSE; UT_LIST_REMOVE( unflushed_spaces, fil_system->unflushed_spaces, space); } } if (space->purpose == FIL_TABLESPACE) { fil_n_pending_tablespace_flushes--; } else { fil_n_pending_log_flushes--; } } node = UT_LIST_GET_NEXT(chain, node); } space->n_pending_flushes--; mutex_exit(&fil_system->mutex); } /**********************************************************************//** Flushes to disk the writes in file spaces of the given type possibly cached by the OS. */ UNIV_INTERN void fil_flush_file_spaces( /*==================*/ ulint purpose) /*!< in: FIL_TABLESPACE, FIL_LOG */ { fil_space_t* space; ulint* space_ids; ulint n_space_ids; ulint i; mutex_enter(&fil_system->mutex); n_space_ids = UT_LIST_GET_LEN(fil_system->unflushed_spaces); if (n_space_ids == 0) { mutex_exit(&fil_system->mutex); return; } /* Assemble a list of space ids to flush. Previously, we traversed fil_system->unflushed_spaces and called UT_LIST_GET_NEXT() on a space that was just removed from the list by fil_flush(). Thus, the space could be dropped and the memory overwritten. */ space_ids = mem_alloc(n_space_ids * sizeof *space_ids); i = 0; for (space = UT_LIST_GET_FIRST(fil_system->unflushed_spaces); space; space = UT_LIST_GET_NEXT(unflushed_spaces, space)) { if (space->purpose == purpose && !space->is_being_deleted) { ut_ad(i < n_space_ids); space_ids[i++] = space->id; } } n_space_ids = i; mutex_exit(&fil_system->mutex); /* Flush the spaces. It will not hurt to call fil_flush() on a non-existing space id. */ for (i = 0; i < n_space_ids; i++) { fil_flush(space_ids[i]); } mem_free(space_ids); } /******************************************************************//** Checks the consistency of the tablespace cache. @return TRUE if ok */ UNIV_INTERN ibool fil_validate(void) /*==============*/ { fil_space_t* space; fil_node_t* fil_node; ulint n_open = 0; ulint i; mutex_enter(&fil_system->mutex); /* Look for spaces in the hash table */ for (i = 0; i < hash_get_n_cells(fil_system->spaces); i++) { space = HASH_GET_FIRST(fil_system->spaces, i); while (space != NULL) { UT_LIST_VALIDATE(chain, fil_node_t, space->chain, ut_a(ut_list_node_313->open || !ut_list_node_313->n_pending)); fil_node = UT_LIST_GET_FIRST(space->chain); while (fil_node != NULL) { if (fil_node->n_pending > 0) { ut_a(fil_node->open); } if (fil_node->open) { n_open++; } fil_node = UT_LIST_GET_NEXT(chain, fil_node); } space = HASH_GET_NEXT(hash, space); } } ut_a(fil_system->n_open == n_open); UT_LIST_VALIDATE(LRU, fil_node_t, fil_system->LRU, (void) 0); fil_node = UT_LIST_GET_FIRST(fil_system->LRU); while (fil_node != NULL) { ut_a(fil_node->n_pending == 0); ut_a(fil_node->open); ut_a(fil_node->space->purpose == FIL_TABLESPACE); ut_a(fil_node->space->id != 0); fil_node = UT_LIST_GET_NEXT(LRU, fil_node); } mutex_exit(&fil_system->mutex); return(TRUE); } /********************************************************************//** Returns TRUE if file address is undefined. @return TRUE if undefined */ UNIV_INTERN ibool fil_addr_is_null( /*=============*/ fil_addr_t addr) /*!< in: address */ { return(addr.page == FIL_NULL); } /********************************************************************//** Get the predecessor of a file page. @return FIL_PAGE_PREV */ UNIV_INTERN ulint fil_page_get_prev( /*==============*/ const byte* page) /*!< in: file page */ { return(mach_read_from_4(page + FIL_PAGE_PREV)); } /********************************************************************//** Get the successor of a file page. @return FIL_PAGE_NEXT */ UNIV_INTERN ulint fil_page_get_next( /*==============*/ const byte* page) /*!< in: file page */ { return(mach_read_from_4(page + FIL_PAGE_NEXT)); } /*********************************************************************//** Sets the file page type. */ UNIV_INTERN void fil_page_set_type( /*==============*/ byte* page, /*!< in/out: file page */ ulint type) /*!< in: type */ { ut_ad(page); mach_write_to_2(page + FIL_PAGE_TYPE, type); } /*********************************************************************//** Gets the file page type. @return type; NOTE that if the type has not been written to page, the return value not defined */ UNIV_INTERN ulint fil_page_get_type( /*==============*/ const byte* page) /*!< in: file page */ { ut_ad(page); return(mach_read_from_2(page + FIL_PAGE_TYPE)); } /******************************************************************** Deinitializes the tablespace memory cache. */ UNIV_INTERN void fil_close(void) /*===========*/ { ulint i; fil_system_t* system = fil_system; /* This can happen if we abort during the startup phase. */ if (system == NULL) { return; } mutex_free(&system->mutex); memset(&system->mutex, 0x0, sizeof(system->mutex)); /* Free the hash elements. We don't remove them from the table because we are going to destroy the table anyway. */ for (i = 0; i < hash_get_n_cells(system->spaces); i++) { fil_space_t* space; space = HASH_GET_FIRST(system->spaces, i); while (space) { fil_space_t* prev_space = space; space = HASH_GET_NEXT(hash, prev_space); ut_a(prev_space->magic_n == FIL_SPACE_MAGIC_N); mem_free(prev_space); } } hash_table_free(system->spaces); /* The elements in this hash table are the same in system->spaces, therefore no need to free the individual elements. */ hash_table_free(system->name_hash); ut_a(UT_LIST_GET_LEN(system->LRU) == 0); ut_a(UT_LIST_GET_LEN(system->unflushed_spaces) == 0); ut_a(UT_LIST_GET_LEN(system->space_list) == 0); mem_free(system); fil_system = NULL; } /******************************************************************** Remove the underlying directory where the database .ibd files are stored. @return TRUE on success */ UNIV_INTERN ibool fil_rmdir( /*======*/ const char* dbname) /*!< in: database name */ { ibool success = FALSE; char dir[OS_FILE_MAX_PATH]; ut_snprintf(dir, sizeof(dir), "%s%s", srv_data_home, dbname); srv_normalize_path_for_win(dir); #ifdef HAVE_UNISTD_H if (rmdir(dbname) != 0) { ib_logger(ib_stream, "InnoDB: Error removing directory: %s\n", dbname); } else { success = TRUE; } #elif defined(__WIN__) if (RemoveDirectory(dbname) == 0) { ib_logger(ib_stream, "InnoDB: Error removing directory: %s\n", dbname); } else { success = TRUE; } #else #error "Need rmdir() equivalent" #endif return(success); } /******************************************************************** Create the underlying directory where the database .ibd files are stored. @return TRUE on success */ UNIV_INTERN ibool fil_mkdir( /*======*/ const char* dbname) /*!< in: database name */ { char dir[OS_FILE_MAX_PATH]; ut_snprintf(dir, sizeof(dir), "%s%s", srv_data_home, dbname); srv_normalize_path_for_win(dir); /* If exists (FALSE) then don't return error. */ return(os_file_create_directory(dir, FALSE)); } haildb-2.3.2/mem/0000755000175000017500000000000011513177437014420 5ustar00pcrewspcrews00000000000000haildb-2.3.2/mem/mem0mem.c0000644000175000017500000004623411513177357016133 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1994, 2010, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /********************************************************************//** @file mem/mem0mem.c The memory management Created 6/9/1994 Heikki Tuuri *************************************************************************/ #include "mem0mem.h" #ifdef UNIV_NONINL #include "mem0mem.ic" #endif #include "buf0buf.h" #include "srv0srv.h" #include /* THE MEMORY MANAGEMENT ===================== The basic element of the memory management is called a memory heap. A memory heap is conceptually a stack from which memory can be allocated. The stack may grow infinitely. The top element of the stack may be freed, or the whole stack can be freed at one time. The advantage of the memory heap concept is that we can avoid using the malloc and free functions of C which are quite expensive, for example, on the Solaris + GCC system (50 MHz Sparc, 1993) the pair takes 3 microseconds, on Win NT + 100MHz Pentium, 2.5 microseconds. When we use a memory heap, we can allocate larger blocks of memory at a time and thus reduce overhead. Slightly more efficient the method is when we allocate the memory from the index page buffer pool, as we can claim a new page fast. This is called buffer allocation. When we allocate the memory from the dynamic memory of the C environment, that is called dynamic allocation. The default way of operation of the memory heap is the following. First, when the heap is created, an initial block of memory is allocated. In dynamic allocation this may be about 50 bytes. If more space is needed, additional blocks are allocated and they are put into a linked list. After the initial block, each allocated block is twice the size of the previous, until a threshold is attained, after which the sizes of the blocks stay the same. An exception is, of course, the case where the caller requests a memory buffer whose size is bigger than the threshold. In that case a block big enough must be allocated. The heap is physically arranged so that if the current block becomes full, a new block is allocated and always inserted in the chain of blocks as the last block. In the debug version of the memory management, all the allocated heaps are kept in a list (which is implemented as a hash table). Thus we can notice if the caller tries to free an already freed heap. In addition, each buffer given to the caller contains start field at the start and a trailer field at the end of the buffer. The start field has the following content: A. sizeof(ulint) bytes of field length (in the standard byte order) B. sizeof(ulint) bytes of check field (a random number) The trailer field contains: A. sizeof(ulint) bytes of check field (the same random number as at the start) Thus we can notice if something has been copied over the borders of the buffer, which is illegal. The memory in the buffers is initialized to a random byte sequence. After freeing, all the blocks in the heap are set to random bytes to help us discover errors which result from the use of buffers in an already freed heap. */ /**********************************************************************//** Duplicates a NUL-terminated string, allocated from a memory heap. @return own: a copy of the string */ UNIV_INTERN char* mem_heap_strdup( /*============*/ mem_heap_t* heap, /*!< in: memory heap where string is allocated */ const char* str) /*!< in: string to be copied */ { return(mem_heap_dup(heap, str, strlen(str) + 1)); } /**********************************************************************//** Duplicate a block of data, allocated from a memory heap. @return own: a copy of the data */ UNIV_INTERN void* mem_heap_dup( /*=========*/ mem_heap_t* heap, /*!< in: memory heap where copy is allocated */ const void* data, /*!< in: data to be copied */ ulint len) /*!< in: length of data, in bytes */ { return(memcpy(mem_heap_alloc(heap, len), data, len)); } /**********************************************************************//** Concatenate two strings and return the result, using a memory heap. @return own: the result */ UNIV_INTERN char* mem_heap_strcat( /*============*/ mem_heap_t* heap, /*!< in: memory heap where string is allocated */ const char* s1, /*!< in: string 1 */ const char* s2) /*!< in: string 2 */ { char* s; ulint s1_len = strlen(s1); ulint s2_len = strlen(s2); s = mem_heap_alloc(heap, s1_len + s2_len + 1); memcpy(s, s1, s1_len); memcpy(s + s1_len, s2, s2_len); s[s1_len + s2_len] = '\0'; return(s); } /****************************************************************//** Helper function for mem_heap_printf. @return length of formatted string, including terminating NUL */ static ulint mem_heap_printf_low( /*================*/ char* buf, /*!< in/out: buffer to store formatted string in, or NULL to just calculate length */ const char* format, /*!< in: format string */ va_list ap) /*!< in: arguments */ { ulint len = 0; while (*format) { /* Does this format specifier have the 'l' length modifier. */ ibool is_long = FALSE; /* Length of one parameter. */ size_t plen; if (*format++ != '%') { /* Non-format character. */ len++; if (buf) { *buf++ = *(format - 1); } continue; } if (*format == 'l') { is_long = TRUE; format++; } switch (*format++) { case 's': /* string */ { char* s = va_arg(ap, char*); /* "%ls" is a non-sensical format specifier. */ ut_a(!is_long); plen = strlen(s); len += plen; if (buf) { memcpy(buf, s, plen); buf += plen; } } break; case 'u': /* unsigned int */ { char tmp[32]; unsigned long val; /* We only support 'long' values for now. */ ut_a(is_long); val = va_arg(ap, unsigned long); plen = sprintf(tmp, "%lu", val); len += plen; if (buf) { memcpy(buf, tmp, plen); buf += plen; } } break; case '%': /* "%l%" is a non-sensical format specifier. */ ut_a(!is_long); len++; if (buf) { *buf++ = '%'; } break; default: ut_error; } } /* For the NUL character. */ len++; if (buf) { *buf = '\0'; } return(len); } /****************************************************************//** A simple (s)printf replacement that dynamically allocates the space for the formatted string from the given heap. This supports a very limited set of the printf syntax: types 's' and 'u' and length modifier 'l' (which is required for the 'u' type). @return heap-allocated formatted string */ UNIV_INTERN char* mem_heap_printf( /*============*/ mem_heap_t* heap, /*!< in: memory heap */ const char* format, /*!< in: format string */ ...) { va_list ap; char* str; ulint len; /* Calculate length of string */ len = 0; va_start(ap, format); len = mem_heap_printf_low(NULL, format, ap); va_end(ap); /* Now create it for real. */ str = mem_heap_alloc(heap, len); va_start(ap, format); mem_heap_printf_low(str, format, ap); va_end(ap); return(str); } /***************************************************************//** Creates a memory heap block where data can be allocated. @return own: memory heap block, NULL if did not succeed (only possible for MEM_HEAP_BTR_SEARCH type heaps) */ UNIV_INTERN mem_block_t* mem_heap_create_block( /*==================*/ mem_heap_t* heap, /*!< in: memory heap or NULL if first block should be created */ ulint n, /*!< in: number of bytes needed for user data */ ulint type, /*!< in: type of heap: MEM_HEAP_DYNAMIC or MEM_HEAP_BUFFER */ const char* file_name,/*!< in: file name where created */ ulint line) /*!< in: line where created */ { #ifndef UNIV_HOTBACKUP buf_block_t* buf_block = NULL; #endif /* !UNIV_HOTBACKUP */ mem_block_t* block; ulint len; ut_ad((type == MEM_HEAP_DYNAMIC) || (type == MEM_HEAP_BUFFER) || (type == MEM_HEAP_BUFFER + MEM_HEAP_BTR_SEARCH)); if (heap && heap->magic_n != MEM_BLOCK_MAGIC_N) { ut_error; } /* In dynamic allocation, calculate the size: block header + data. */ len = MEM_BLOCK_HEADER_SIZE + MEM_SPACE_NEEDED(n); #ifndef UNIV_HOTBACKUP if (type == MEM_HEAP_DYNAMIC || len < UNIV_PAGE_SIZE / 2) { ut_a(type == MEM_HEAP_DYNAMIC || n <= MEM_MAX_ALLOC_IN_BUF); block = (mem_block_t*) malloc(len); } else { len = UNIV_PAGE_SIZE; if ((type & MEM_HEAP_BTR_SEARCH) && heap) { /* We cannot allocate the block from the buffer pool, but must get the free block from the heap header free block field */ buf_block = heap->free_block; heap->free_block = NULL; if (UNIV_UNLIKELY(buf_block == NULL)) { return(NULL); } } else { buf_block = buf_block_alloc(0); } block = (mem_block_t*) buf_block->frame; } ut_ad(block); block->buf_block = buf_block; block->free_block = NULL; #else /* !UNIV_HOTBACKUP */ len = MEM_BLOCK_HEADER_SIZE + MEM_SPACE_NEEDED(n); block = ut_malloc(len); ut_ad(block); #endif /* !UNIV_HOTBACKUP */ block->magic_n = MEM_BLOCK_MAGIC_N; ut_strlcpy_rev(block->file_name, file_name, sizeof(block->file_name)); block->line = line; mem_block_set_len(block, len); mem_block_set_type(block, type); mem_block_set_free(block, MEM_BLOCK_HEADER_SIZE); mem_block_set_start(block, MEM_BLOCK_HEADER_SIZE); if (UNIV_UNLIKELY(heap == NULL)) { /* This is the first block of the heap. The field total_size should be initialized here */ block->total_size = len; } else { /* Not the first allocation for the heap. This block's total_length field should be set to undefined. */ ut_d(block->total_size = ULINT_UNDEFINED); UNIV_MEM_INVALID(&block->total_size, sizeof block->total_size); heap->total_size += len; } ut_ad((ulint)MEM_BLOCK_HEADER_SIZE < len); return(block); } /***************************************************************//** Adds a new block to a memory heap. @return created block, NULL if did not succeed (only possible for MEM_HEAP_BTR_SEARCH type heaps) */ UNIV_INTERN mem_block_t* mem_heap_add_block( /*===============*/ mem_heap_t* heap, /*!< in: memory heap */ ulint n) /*!< in: number of bytes user needs */ { mem_block_t* block; mem_block_t* new_block; ulint new_size; ut_ad(mem_heap_check(heap)); block = UT_LIST_GET_LAST(heap->base); /* We have to allocate a new block. The size is always at least doubled until the standard size is reached. After that the size stays the same, except in cases where the caller needs more space. */ new_size = 2 * mem_block_get_len(block); if (heap->type != MEM_HEAP_DYNAMIC) { /* From the buffer pool we allocate buffer frames */ ut_a(n <= MEM_MAX_ALLOC_IN_BUF); if (new_size > MEM_MAX_ALLOC_IN_BUF) { new_size = MEM_MAX_ALLOC_IN_BUF; } } else if (new_size > MEM_BLOCK_STANDARD_SIZE) { new_size = MEM_BLOCK_STANDARD_SIZE; } if (new_size < n) { new_size = n; } new_block = mem_heap_create_block(heap, new_size, heap->type, heap->file_name, heap->line); if (new_block == NULL) { return(NULL); } /* Add the new block as the last block */ UT_LIST_INSERT_AFTER(list, heap->base, block, new_block); return(new_block); } /******************************************************************//** Frees a block from a memory heap. */ UNIV_INTERN void mem_heap_block_free( /*================*/ mem_heap_t* heap, /*!< in: heap */ mem_block_t* block) /*!< in: block to free */ { ulint type; ulint len; #ifndef UNIV_HOTBACKUP buf_block_t* buf_block = block->buf_block; #endif /* !UNIV_HOTBACKUP */ if (block->magic_n != MEM_BLOCK_MAGIC_N) { ut_error; } UT_LIST_REMOVE(list, heap->base, block); ut_ad(heap->total_size >= block->len); heap->total_size -= block->len; type = heap->type; len = block->len; block->magic_n = MEM_FREED_BLOCK_MAGIC_N; #ifndef UNIV_HOTBACKUP if (!srv_use_sys_malloc) { #ifdef UNIV_MEM_DEBUG /* In the debug version we set the memory to a random combination of hex 0xDE and 0xAD. */ mem_erase_buf((byte*)block, len); #else /* UNIV_MEM_DEBUG */ UNIV_MEM_ASSERT_AND_FREE(block, len); #endif /* UNIV_MEM_DEBUG */ } if (type == MEM_HEAP_DYNAMIC || len < UNIV_PAGE_SIZE / 2) { free(block); } else { buf_block_free(buf_block); } #else /* !UNIV_HOTBACKUP */ #ifdef UNIV_MEM_DEBUG /* In the debug version we set the memory to a random combination of hex 0xDE and 0xAD. */ mem_erase_buf((byte*)block, len); #else /* UNIV_MEM_DEBUG */ UNIV_MEM_ASSERT_AND_FREE(block, len); #endif /* UNIV_MEM_DEBUG */ ut_free(block); #endif /* !UNIV_HOTBACKUP */ } #ifndef UNIV_HOTBACKUP /******************************************************************//** Frees the free_block field from a memory heap. */ UNIV_INTERN void mem_heap_free_block_free( /*=====================*/ mem_heap_t* heap) /*!< in: heap */ { if (UNIV_LIKELY_NULL(heap->free_block)) { buf_block_free(heap->free_block); heap->free_block = NULL; } } #endif /* !UNIV_HOTBACKUP */ #ifdef UNIV_DEBUG /******************************************************************//** Goes through the list of all allocated mem blocks, checks their magic numbers, and reports possible corruption. */ UNIV_INTERN ibool mem_heap_check( /*===========*/ mem_heap_t* heap) /*!< in: memory heap */ { ut_a(heap->magic_n == MEM_BLOCK_MAGIC_N); return(TRUE); } #endif /* UNIV_DEBUG */ #if defined UNIV_MEM_DEBUG || defined UNIV_DEBUG /******************************************************************* Checks a memory heap for consistency and prints the contents if requested. Outputs the sum of sizes of buffers given to the user (only in the debug version), the physical size of the heap and the number of blocks in the heap. In case of error returns 0 as sizes and number of blocks. */ void mem_heap_validate_or_print( /*=======================*/ mem_heap_t* heap, /*!< in: memory heap */ byte* top __attribute__((unused)), /*!< in: calculate and validate only until this top pointer in the heap is reached, if this pointer is NULL, ignored */ ibool print, /*!< in: if TRUE, prints the contents of the heap; works only in the debug version */ ibool* error, /*!< out: TRUE if error */ ulint* us_size,/*!< out: allocated memory (for the user) in the heap, if a NULL pointer is passed as this argument, it is ignored; in the non-debug version this is always -1 */ ulint* ph_size,/*!< out: physical size of the heap, if a NULL pointer is passed as this argument, it is ignored */ ulint* n_blocks) /*!< out: number of blocks in the heap, if a NULL pointer is passed as this argument, it is ignored */ { mem_block_t* block; ulint total_len = 0; ulint block_count = 0; ulint phys_len = 0; #ifdef UNIV_MEM_DEBUG ulint len; byte* field; byte* user_field; ulint check_field; #endif /* Pessimistically, we set the parameters to error values */ if (us_size != NULL) { *us_size = 0; } if (ph_size != NULL) { *ph_size = 0; } if (n_blocks != NULL) { *n_blocks = 0; } *error = TRUE; block = heap; if (block->magic_n != MEM_BLOCK_MAGIC_N) { return; } if (print) { ib_logger(ib_stream, "Memory heap:"); } while (block != NULL) { phys_len += mem_block_get_len(block); if ((block->type == MEM_HEAP_BUFFER) && (mem_block_get_len(block) > UNIV_PAGE_SIZE)) { ib_logger(ib_stream, "InnoDB: Error: mem block %p" " length %lu > UNIV_PAGE_SIZE\n", (void*) block, (ulong) mem_block_get_len(block)); /* error */ return; } #ifdef UNIV_MEM_DEBUG /* We can trace the fields of the block only in the debug version */ if (print) { ib_logger(ib_stream, " Block %ld:", block_count); } field = (byte*)block + mem_block_get_start(block); if (top && (field == top)) { goto completed; } while (field < (byte*)block + mem_block_get_free(block)) { /* Calculate the pointer to the storage which was given to the user */ user_field = field + MEM_FIELD_HEADER_SIZE; len = mem_field_header_get_len(user_field); if (print) { ut_print_buf(ib_stream, user_field, len); } total_len += len; check_field = mem_field_header_get_check(user_field); if (check_field != mem_field_trailer_get_check(user_field)) { /* error */ ib_logger(ib_stream, "InnoDB: Error: block %lx mem" " field %lx len %lu\n" "InnoDB: header check field is" " %lx but trailer %lx\n", (ulint)block, (ulint)field, len, check_field, mem_field_trailer_get_check( user_field)); return; } /* Move to next field */ field = field + MEM_SPACE_NEEDED(len); if (top && (field == top)) { goto completed; } } /* At the end check that we have arrived to the first free position */ if (field != (byte*)block + mem_block_get_free(block)) { /* error */ ib_logger(ib_stream, "InnoDB: Error: block %lx end of" " mem fields %lx\n" "InnoDB: but block free at %lx\n", (ulint)block, (ulint)field, (ulint)((byte*)block + mem_block_get_free(block))); return; } #endif block = UT_LIST_GET_NEXT(list, block); block_count++; } #ifdef UNIV_MEM_DEBUG completed: #endif if (us_size != NULL) { *us_size = total_len; } if (ph_size != NULL) { *ph_size = phys_len; } if (n_blocks != NULL) { *n_blocks = block_count; } *error = FALSE; } /****************************************************************** Prints the contents of a memory heap. */ static void mem_heap_print( /*===========*/ mem_heap_t* heap) /*!< in: memory heap */ { ibool error; ulint us_size; ulint phys_size; ulint n_blocks; ut_ad(mem_heap_check(heap)); mem_heap_validate_or_print(heap, NULL, TRUE, &error, &us_size, &phys_size, &n_blocks); ib_logger(ib_stream, "\nheap type: %lu; size: user size %lu;" " physical size %lu; blocks %lu.\n", (ulong) heap->type, (ulong) us_size, (ulong) phys_size, (ulong) n_blocks); ut_a(!error); } /****************************************************************** Validates the contents of a memory heap. @return TRUE if ok */ ibool mem_heap_validate( /*==============*/ mem_heap_t* heap) /*!< in: memory heap */ { ibool error; ulint us_size; ulint phys_size; ulint n_blocks; ut_ad(mem_heap_check(heap)); mem_heap_validate_or_print(heap, NULL, FALSE, &error, &us_size, &phys_size, &n_blocks); if (error) { mem_heap_print(heap); } ut_a(!error); return(TRUE); } #endif /* UNIV_MEM_DEBUG || UNIV_DEBUG */ #ifdef UNIV_DEBUG /****************************************************************** Verify that the heap is not corrupt. */ UNIV_INTERN void mem_heap_verify( /*============*/ const mem_heap_t* heap) /*!< in: heap to verify */ { mem_block_t* block; block = UT_LIST_GET_FIRST(heap->base); while (block != NULL) { ut_a(block->magic_n == MEM_BLOCK_MAGIC_N); block = UT_LIST_GET_NEXT(list, block); } } #endif haildb-2.3.2/configure0000755000175000017500000251323411513177420015553 0ustar00pcrewspcrews00000000000000#! /bin/sh # Grabbing changelog and version information from bzr # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.67 for haildb 2.3.2. # # Report bugs to . # # # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, # 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software # Foundation, Inc. # # # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH if test "x$CONFIG_SHELL" = x; then as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST else case \`(set -o) 2>/dev/null\` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi " as_required="as_fn_return () { (exit \$1); } as_fn_success () { as_fn_return 0; } as_fn_failure () { as_fn_return 1; } as_fn_ret_success () { return 0; } as_fn_ret_failure () { return 1; } exitcode=0 as_fn_success || { exitcode=1; echo as_fn_success failed.; } as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : else exitcode=1; echo positional parameters were not saved. fi test x\$exitcode = x0 || exit 1" as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 test \$(( 1 + 1 )) = 2 || exit 1" if (eval "$as_required") 2>/dev/null; then : as_have_required=yes else as_have_required=no fi if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. as_found=: case $as_dir in #( /*) for as_base in sh bash ksh sh5; do # Try only shells that exist, to save several forks. as_shell=$as_dir/$as_base if { test -f "$as_shell" || test -f "$as_shell.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : CONFIG_SHELL=$as_shell as_have_required=yes if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : break 2 fi fi done;; esac as_found=false done $as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : CONFIG_SHELL=$SHELL as_have_required=yes fi; } IFS=$as_save_IFS if test "x$CONFIG_SHELL" != x; then : # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV export CONFIG_SHELL exec "$CONFIG_SHELL" "$as_myself" ${1+"$@"} fi if test x$as_have_required = xno; then : $as_echo "$0: This script requires a shell more modern than all" $as_echo "$0: the shells that I found on your system." if test x${ZSH_VERSION+set} = xset ; then $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" $as_echo "$0: be upgraded to zsh 4.3.4 or later." else $as_echo "$0: Please tell bug-autoconf@gnu.org and $0: http://bugs.launchpad.net/haildb about your system, $0: including any error possibly output before this $0: message. Then install a modern shell, or manually run $0: the script under such a shell if you do have one." fi exit 1 fi fi fi SHELL=${CONFIG_SHELL-/bin/sh} export SHELL # Unset more variables known to interfere with behavior of common tools. CLICOLOR_FORCE= GREP_OPTIONS= unset CLICOLOR_FORCE GREP_OPTIONS ## --------------------- ## ## M4sh Shell Functions. ## ## --------------------- ## # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits as_lineno_1=$LINENO as_lineno_1a=$LINENO as_lineno_2=$LINENO as_lineno_2a=$LINENO eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) sed -n ' p /[$]LINENO/= ' <$as_myself | sed ' s/[$]LINENO.*/&-/ t lineno b :lineno N :loop s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ t loop s/-\n.*// ' >$as_me.lineno && chmod +x "$as_me.lineno" || { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensitive to this). . "./$as_me.lineno" # Exit status is that of the last command. exit } ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -p'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -p' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -p' fi else as_ln_s='cp -p' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi if test -x / >/dev/null 2>&1; then as_test_x='test -x' else if ls -dL / >/dev/null 2>&1; then as_ls_L_option=L else as_ls_L_option= fi as_test_x=' eval sh -c '\'' if test -d "$1"; then test -d "$1/."; else case $1 in #( -*)set "./$1";; esac; case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #(( ???[sx]*):;;*)false;;esac;fi '\'' sh ' fi as_executable_p=$as_test_x # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" # Check that we are running under the correct shell. SHELL=${CONFIG_SHELL-/bin/sh} case X$lt_ECHO in X*--fallback-echo) # Remove one level of quotation (which was required for Make). ECHO=`echo "$lt_ECHO" | sed 's,\\\\\$\\$0,'$0','` ;; esac ECHO=${lt_ECHO-echo} if test "X$1" = X--no-reexec; then # Discard the --no-reexec flag, and continue. shift elif test "X$1" = X--fallback-echo; then # Avoid inline document here, it may be left over : elif test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t' ; then # Yippee, $ECHO works! : else # Restart under the correct shell. exec $SHELL "$0" --no-reexec ${1+"$@"} fi if test "X$1" = X--fallback-echo; then # used as fallback echo shift cat <<_LT_EOF $* _LT_EOF exit 0 fi # The HP-UX ksh and POSIX shell print the target directory to stdout # if CDPATH is set. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH if test -z "$lt_ECHO"; then if test "X${echo_test_string+set}" != Xset; then # find a string as large as possible, as long as the shell can cope with it for cmd in 'sed 50q "$0"' 'sed 20q "$0"' 'sed 10q "$0"' 'sed 2q "$0"' 'echo test'; do # expected sizes: less than 2Kb, 1Kb, 512 bytes, 16 bytes, ... if { echo_test_string=`eval $cmd`; } 2>/dev/null && { test "X$echo_test_string" = "X$echo_test_string"; } 2>/dev/null then break fi done fi if test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t' && echo_testing_string=`{ $ECHO "$echo_test_string"; } 2>/dev/null` && test "X$echo_testing_string" = "X$echo_test_string"; then : else # The Solaris, AIX, and Digital Unix default echo programs unquote # backslashes. This makes it impossible to quote backslashes using # echo "$something" | sed 's/\\/\\\\/g' # # So, first we look for a working echo in the user's PATH. lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR for dir in $PATH /usr/ucb; do IFS="$lt_save_ifs" if (test -f $dir/echo || test -f $dir/echo$ac_exeext) && test "X`($dir/echo '\t') 2>/dev/null`" = 'X\t' && echo_testing_string=`($dir/echo "$echo_test_string") 2>/dev/null` && test "X$echo_testing_string" = "X$echo_test_string"; then ECHO="$dir/echo" break fi done IFS="$lt_save_ifs" if test "X$ECHO" = Xecho; then # We didn't find a better echo, so look for alternatives. if test "X`{ print -r '\t'; } 2>/dev/null`" = 'X\t' && echo_testing_string=`{ print -r "$echo_test_string"; } 2>/dev/null` && test "X$echo_testing_string" = "X$echo_test_string"; then # This shell has a builtin print -r that does the trick. ECHO='print -r' elif { test -f /bin/ksh || test -f /bin/ksh$ac_exeext; } && test "X$CONFIG_SHELL" != X/bin/ksh; then # If we have ksh, try running configure again with it. ORIGINAL_CONFIG_SHELL=${CONFIG_SHELL-/bin/sh} export ORIGINAL_CONFIG_SHELL CONFIG_SHELL=/bin/ksh export CONFIG_SHELL exec $CONFIG_SHELL "$0" --no-reexec ${1+"$@"} else # Try using printf. ECHO='printf %s\n' if test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t' && echo_testing_string=`{ $ECHO "$echo_test_string"; } 2>/dev/null` && test "X$echo_testing_string" = "X$echo_test_string"; then # Cool, printf works : elif echo_testing_string=`($ORIGINAL_CONFIG_SHELL "$0" --fallback-echo '\t') 2>/dev/null` && test "X$echo_testing_string" = 'X\t' && echo_testing_string=`($ORIGINAL_CONFIG_SHELL "$0" --fallback-echo "$echo_test_string") 2>/dev/null` && test "X$echo_testing_string" = "X$echo_test_string"; then CONFIG_SHELL=$ORIGINAL_CONFIG_SHELL export CONFIG_SHELL SHELL="$CONFIG_SHELL" export SHELL ECHO="$CONFIG_SHELL $0 --fallback-echo" elif echo_testing_string=`($CONFIG_SHELL "$0" --fallback-echo '\t') 2>/dev/null` && test "X$echo_testing_string" = 'X\t' && echo_testing_string=`($CONFIG_SHELL "$0" --fallback-echo "$echo_test_string") 2>/dev/null` && test "X$echo_testing_string" = "X$echo_test_string"; then ECHO="$CONFIG_SHELL $0 --fallback-echo" else # maybe with a smaller string... prev=: for cmd in 'echo test' 'sed 2q "$0"' 'sed 10q "$0"' 'sed 20q "$0"' 'sed 50q "$0"'; do if { test "X$echo_test_string" = "X`eval $cmd`"; } 2>/dev/null then break fi prev="$cmd" done if test "$prev" != 'sed 50q "$0"'; then echo_test_string=`eval $prev` export echo_test_string exec ${ORIGINAL_CONFIG_SHELL-${CONFIG_SHELL-/bin/sh}} "$0" ${1+"$@"} else # Oops. We lost completely, so just stick with echo. ECHO=echo fi fi fi fi fi fi # Copy echo and quote the copy suitably for passing to libtool from # the Makefile, instead of quoting the original, which is used later. lt_ECHO=$ECHO if test "X$lt_ECHO" = "X$CONFIG_SHELL $0 --fallback-echo"; then lt_ECHO="$CONFIG_SHELL \\\$\$0 --fallback-echo" fi test -n "$DJDIR" || exec 7<&0 &1 # Name of the host. # hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, # so uname gets run too. ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` # # Initializations. # ac_default_prefix=/usr/local ac_clean_files= ac_config_libobj_dir=. LIBOBJS= cross_compiling=no subdirs= MFLAGS= MAKEFLAGS= # Identity of this package. PACKAGE_NAME='haildb' PACKAGE_TARNAME='haildb' PACKAGE_VERSION='2.3.2' PACKAGE_STRING='haildb 2.3.2' PACKAGE_BUGREPORT='http://bugs.launchpad.net/haildb' PACKAGE_URL='' ac_unique_file="include/univ.i" # Factoring default headers for most tests. ac_includes_default="\ #include #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef STDC_HEADERS # include # include #else # ifdef HAVE_STDLIB_H # include # endif #endif #ifdef HAVE_STRING_H # if !defined STDC_HEADERS && defined HAVE_MEMORY_H # include # endif # include #endif #ifdef HAVE_STRINGS_H # include #endif #ifdef HAVE_INTTYPES_H # include #endif #ifdef HAVE_STDINT_H # include #endif #ifdef HAVE_UNISTD_H # include #endif" ac_subst_vars='am__EXEEXT_FALSE am__EXEEXT_TRUE LTLIBOBJS LIBOBJS HAVE_PTHREAD_BARRIER_FALSE HAVE_PTHREAD_BARRIER_TRUE WITH_XOPEN_FALSE WITH_XOPEN_TRUE WITH_ZIP_FALSE WITH_ZIP_TRUE LD_VERSION_SCRIPT HAVE_BISON_FALSE HAVE_BISON_TRUE YACC IB_API_VERSION IB_API_VERSION_AGE IB_API_VERSION_REVISION uname_prog AM_LDFLAGS AM_CPPFLAGS AM_CXXFLAGS AM_CFLAGS BUILD_PO_FALSE BUILD_PO_TRUE HAVE_RECENT_SPHINX_FALSE HAVE_RECENT_SPHINX_TRUE HAVE_SPHINX_FALSE HAVE_SPHINX_TRUE HAVE_DPKG_GENSYMBOLS_FALSE HAVE_DPKG_GENSYMBOLS_TRUE SPHINXBUILD LCOV_GENHTML LCOV DPKG_GENSYMBOLS PERL DOXYGEN BETTER_MALLOC_LIBS DTRACE_NEEDS_OBJECTS_FALSE DTRACE_NEEDS_OBJECTS_TRUE HAVE_DTRACE_FALSE HAVE_DTRACE_TRUE DTRACEFLAGS DTRACE GCOV_LIBS NO_WERROR PERMISSIVE_WARNINGS BOOSTSKIP_WARNINGS INNOBASE_SKIP_WARNINGS PROTOSKIP_WARNINGS NO_OLD_STYLE_CAST NO_EFF_CXX NO_STRICT_ALIASING NO_SHADOW NO_UNREACHED NO_REDUNDANT_DECLS NO_CONVERSION LIBC_P HAVE_VISIBILITY NO_VISIBILITY CFLAG_VISIBILITY LIBM CXX_VERSION CC_VERSION CXX_STANDARD CXXCPP OTOOL64 OTOOL LIPO NMEDIT DSYMUTIL lt_ECHO RANLIB AR OBJDUMP LN_S NM ac_ct_DUMPBIN DUMPBIN LD FGREP SED LIBTOOL PANDORA_OPTIMIZE_BITFIELD BUILD_WIN32_FALSE BUILD_WIN32_TRUE TARGET_WINDOWS TARGET_FREEBSD TARGET_SOLARIS TARGET_OSX TARGET_LINUX am__fastdepCXX_FALSE am__fastdepCXX_TRUE CXXDEPMODE ac_ct_CXX CXXFLAGS CXX PANDORA_HEX_VERSION PANDORA_RELEASE_ID PANDORA_RELEASE_VERSION PANDORA_RELEASE_COMMENT ISAINFO CXX45 CC45 CXX44 CC44 CXX_4_2 CC_4_2 EGREP GREP CPP am__fastdepCC_FALSE am__fastdepCC_TRUE CCDEPMODE AMDEPBACKSLASH AMDEP_FALSE AMDEP_TRUE am__quote am__include DEPDIR OBJEXT EXEEXT ac_ct_CC CPPFLAGS LDFLAGS CFLAGS CC AM_BACKSLASH AM_DEFAULT_VERBOSITY am__untar am__tar AMTAR am__leading_dot SET_MAKE AWK mkdir_p MKDIR_P INSTALL_STRIP_PROGRAM STRIP install_sh MAKEINFO AUTOHEADER AUTOMAKE AUTOCONF ACLOCAL VERSION PACKAGE CYGPATH_W am__isrc INSTALL_DATA INSTALL_SCRIPT INSTALL_PROGRAM target_os target_vendor target_cpu target host_os host_vendor host_cpu host build_os build_vendor build_cpu build HAILDB_FULL_VERSION target_alias host_alias build_alias LIBS ECHO_T ECHO_N ECHO_C DEFS mandir localedir libdir psdir pdfdir dvidir htmldir infodir docdir oldincludedir includedir localstatedir sharedstatedir sysconfdir datadir datarootdir libexecdir sbindir bindir program_transform_name prefix exec_prefix PACKAGE_URL PACKAGE_BUGREPORT PACKAGE_STRING PACKAGE_VERSION PACKAGE_TARNAME PACKAGE_NAME PATH_SEPARATOR SHELL' ac_subst_files='' ac_user_opts=' enable_option_checking enable_fat_binaries enable_silent_rules enable_dependency_tracking enable_64bit enable_static enable_shared with_pic enable_fast_install with_gnu_ld enable_libtool_lock enable_largefile with_debug enable_assert enable_gcc_profile_mode enable_profiling enable_coverage enable_dtrace with_lib_prefix enable_umem enable_tcmalloc enable_mtmalloc with_atomic_ops enable_compression enable_xa ' ac_precious_vars='build_alias host_alias target_alias CC CFLAGS LDFLAGS LIBS CPPFLAGS CPP CXX CXXFLAGS CCC CXXCPP' # Initialize some variables set by options. ac_init_help= ac_init_version=false ac_unrecognized_opts= ac_unrecognized_sep= # The variables have the same names as the options, with # dashes changed to underlines. cache_file=/dev/null exec_prefix=NONE no_create= no_recursion= prefix=NONE program_prefix=NONE program_suffix=NONE program_transform_name=s,x,x, silent= site= srcdir= verbose= x_includes=NONE x_libraries=NONE # Installation directory options. # These are left unexpanded so users can "make install exec_prefix=/foo" # and all the variables that are supposed to be based on exec_prefix # by default will actually change. # Use braces instead of parens because sh, perl, etc. also accept them. # (The list follows the same order as the GNU Coding Standards.) bindir='${exec_prefix}/bin' sbindir='${exec_prefix}/sbin' libexecdir='${exec_prefix}/libexec' datarootdir='${prefix}/share' datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' infodir='${datarootdir}/info' htmldir='${docdir}' dvidir='${docdir}' pdfdir='${docdir}' psdir='${docdir}' libdir='${exec_prefix}/lib' localedir='${datarootdir}/locale' mandir='${datarootdir}/man' ac_prev= ac_dashdash= for ac_option do # If the previous option needs an argument, assign it. if test -n "$ac_prev"; then eval $ac_prev=\$ac_option ac_prev= continue fi case $ac_option in *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; *=) ac_optarg= ;; *) ac_optarg=yes ;; esac # Accept the important Cygnus configure options, so we can diagnose typos. case $ac_dashdash$ac_option in --) ac_dashdash=yes ;; -bindir | --bindir | --bindi | --bind | --bin | --bi) ac_prev=bindir ;; -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) bindir=$ac_optarg ;; -build | --build | --buil | --bui | --bu) ac_prev=build_alias ;; -build=* | --build=* | --buil=* | --bui=* | --bu=*) build_alias=$ac_optarg ;; -cache-file | --cache-file | --cache-fil | --cache-fi \ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) ac_prev=cache_file ;; -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) cache_file=$ac_optarg ;; --config-cache | -C) cache_file=config.cache ;; -datadir | --datadir | --datadi | --datad) ac_prev=datadir ;; -datadir=* | --datadir=* | --datadi=* | --datad=*) datadir=$ac_optarg ;; -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ | --dataroo | --dataro | --datar) ac_prev=datarootdir ;; -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) datarootdir=$ac_optarg ;; -disable-* | --disable-*) ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=no ;; -docdir | --docdir | --docdi | --doc | --do) ac_prev=docdir ;; -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) docdir=$ac_optarg ;; -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) ac_prev=dvidir ;; -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) dvidir=$ac_optarg ;; -enable-* | --enable-*) ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=\$ac_optarg ;; -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ | --exec | --exe | --ex) ac_prev=exec_prefix ;; -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ | --exec=* | --exe=* | --ex=*) exec_prefix=$ac_optarg ;; -gas | --gas | --ga | --g) # Obsolete; use --with-gas. with_gas=yes ;; -help | --help | --hel | --he | -h) ac_init_help=long ;; -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) ac_init_help=recursive ;; -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) ac_init_help=short ;; -host | --host | --hos | --ho) ac_prev=host_alias ;; -host=* | --host=* | --hos=* | --ho=*) host_alias=$ac_optarg ;; -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) ac_prev=htmldir ;; -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ | --ht=*) htmldir=$ac_optarg ;; -includedir | --includedir | --includedi | --included | --include \ | --includ | --inclu | --incl | --inc) ac_prev=includedir ;; -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ | --includ=* | --inclu=* | --incl=* | --inc=*) includedir=$ac_optarg ;; -infodir | --infodir | --infodi | --infod | --info | --inf) ac_prev=infodir ;; -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) infodir=$ac_optarg ;; -libdir | --libdir | --libdi | --libd) ac_prev=libdir ;; -libdir=* | --libdir=* | --libdi=* | --libd=*) libdir=$ac_optarg ;; -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ | --libexe | --libex | --libe) ac_prev=libexecdir ;; -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ | --libexe=* | --libex=* | --libe=*) libexecdir=$ac_optarg ;; -localedir | --localedir | --localedi | --localed | --locale) ac_prev=localedir ;; -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) localedir=$ac_optarg ;; -localstatedir | --localstatedir | --localstatedi | --localstated \ | --localstate | --localstat | --localsta | --localst | --locals) ac_prev=localstatedir ;; -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) localstatedir=$ac_optarg ;; -mandir | --mandir | --mandi | --mand | --man | --ma | --m) ac_prev=mandir ;; -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) mandir=$ac_optarg ;; -nfp | --nfp | --nf) # Obsolete; use --without-fp. with_fp=no ;; -no-create | --no-create | --no-creat | --no-crea | --no-cre \ | --no-cr | --no-c | -n) no_create=yes ;; -no-recursion | --no-recursion | --no-recursio | --no-recursi \ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) no_recursion=yes ;; -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ | --oldin | --oldi | --old | --ol | --o) ac_prev=oldincludedir ;; -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) oldincludedir=$ac_optarg ;; -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ac_prev=prefix ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) prefix=$ac_optarg ;; -program-prefix | --program-prefix | --program-prefi | --program-pref \ | --program-pre | --program-pr | --program-p) ac_prev=program_prefix ;; -program-prefix=* | --program-prefix=* | --program-prefi=* \ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) program_prefix=$ac_optarg ;; -program-suffix | --program-suffix | --program-suffi | --program-suff \ | --program-suf | --program-su | --program-s) ac_prev=program_suffix ;; -program-suffix=* | --program-suffix=* | --program-suffi=* \ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) program_suffix=$ac_optarg ;; -program-transform-name | --program-transform-name \ | --program-transform-nam | --program-transform-na \ | --program-transform-n | --program-transform- \ | --program-transform | --program-transfor \ | --program-transfo | --program-transf \ | --program-trans | --program-tran \ | --progr-tra | --program-tr | --program-t) ac_prev=program_transform_name ;; -program-transform-name=* | --program-transform-name=* \ | --program-transform-nam=* | --program-transform-na=* \ | --program-transform-n=* | --program-transform-=* \ | --program-transform=* | --program-transfor=* \ | --program-transfo=* | --program-transf=* \ | --program-trans=* | --program-tran=* \ | --progr-tra=* | --program-tr=* | --program-t=*) program_transform_name=$ac_optarg ;; -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) ac_prev=pdfdir ;; -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) pdfdir=$ac_optarg ;; -psdir | --psdir | --psdi | --psd | --ps) ac_prev=psdir ;; -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) psdir=$ac_optarg ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) silent=yes ;; -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ | --sbi=* | --sb=*) sbindir=$ac_optarg ;; -sharedstatedir | --sharedstatedir | --sharedstatedi \ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ | --sharedst | --shareds | --shared | --share | --shar \ | --sha | --sh) ac_prev=sharedstatedir ;; -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ | --sha=* | --sh=*) sharedstatedir=$ac_optarg ;; -site | --site | --sit) ac_prev=site ;; -site=* | --site=* | --sit=*) site=$ac_optarg ;; -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ac_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) srcdir=$ac_optarg ;; -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ | --syscon | --sysco | --sysc | --sys | --sy) ac_prev=sysconfdir ;; -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) sysconfdir=$ac_optarg ;; -target | --target | --targe | --targ | --tar | --ta | --t) ac_prev=target_alias ;; -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) target_alias=$ac_optarg ;; -v | -verbose | --verbose | --verbos | --verbo | --verb) verbose=yes ;; -version | --version | --versio | --versi | --vers | -V) ac_init_version=: ;; -with-* | --with-*) ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=\$ac_optarg ;; -without-* | --without-*) ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=no ;; --x) # Obsolete; use --with-x. with_x=yes ;; -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ | --x-incl | --x-inc | --x-in | --x-i) ac_prev=x_includes ;; -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) x_includes=$ac_optarg ;; -x-libraries | --x-libraries | --x-librarie | --x-librari \ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) ac_prev=x_libraries ;; -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; -*) as_fn_error $? "unrecognized option: \`$ac_option' Try \`$0 --help' for more information" ;; *=*) ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` # Reject names that are not valid shell variable names. case $ac_envvar in #( '' | [0-9]* | *[!_$as_cr_alnum]* ) as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; esac eval $ac_envvar=\$ac_optarg export $ac_envvar ;; *) # FIXME: should be removed in autoconf 3.0. $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option} ;; esac done if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` as_fn_error $? "missing argument to $ac_option" fi if test -n "$ac_unrecognized_opts"; then case $enable_option_checking in no) ;; fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; esac fi # Check all directory arguments for consistency. for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ libdir localedir mandir do eval ac_val=\$$ac_var # Remove trailing slashes. case $ac_val in */ ) ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` eval $ac_var=\$ac_val;; esac # Be sure to have absolute directory names. case $ac_val in [\\/$]* | ?:[\\/]* ) continue;; NONE | '' ) case $ac_var in *prefix ) continue;; esac;; esac as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" done # There might be people who depend on the old broken behavior: `$host' # used to hold the argument of --host etc. # FIXME: To remove some day. build=$build_alias host=$host_alias target=$target_alias # FIXME: To remove some day. if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe $as_echo "$as_me: WARNING: if you wanted to set the --build type, don't use --host. If a cross compiler is detected then cross compile mode will be used" >&2 elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi fi ac_tool_prefix= test -n "$host_alias" && ac_tool_prefix=$host_alias- test "$silent" = yes && exec 6>/dev/null ac_pwd=`pwd` && test -n "$ac_pwd" && ac_ls_di=`ls -di .` && ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || as_fn_error $? "working directory cannot be determined" test "X$ac_ls_di" = "X$ac_pwd_ls_di" || as_fn_error $? "pwd does not report name of working directory" # Find the source files, if location was not specified. if test -z "$srcdir"; then ac_srcdir_defaulted=yes # Try the directory containing this script, then the parent directory. ac_confdir=`$as_dirname -- "$as_myself" || $as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_myself" : 'X\(//\)[^/]' \| \ X"$as_myself" : 'X\(//\)$' \| \ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_myself" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` srcdir=$ac_confdir if test ! -r "$srcdir/$ac_unique_file"; then srcdir=.. fi else ac_srcdir_defaulted=no fi if test ! -r "$srcdir/$ac_unique_file"; then test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" fi ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" ac_abs_confdir=`( cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" pwd)` # When building in place, set srcdir=. if test "$ac_abs_confdir" = "$ac_pwd"; then srcdir=. fi # Remove unnecessary trailing slashes from srcdir. # Double slashes in file names in object file debugging info # mess up M-x gdb in Emacs. case $srcdir in */) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; esac for ac_var in $ac_precious_vars; do eval ac_env_${ac_var}_set=\${${ac_var}+set} eval ac_env_${ac_var}_value=\$${ac_var} eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} eval ac_cv_env_${ac_var}_value=\$${ac_var} done # # Report the --help message. # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF \`configure' configures haildb 2.3.2 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. Defaults for the options are specified in brackets. Configuration: -h, --help display this help and exit --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit -q, --quiet, --silent do not print \`checking ...' messages --cache-file=FILE cache test results in FILE [disabled] -C, --config-cache alias for \`--cache-file=config.cache' -n, --no-create do not create output files --srcdir=DIR find the sources in DIR [configure dir or \`..'] Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX [$ac_default_prefix] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [PREFIX] By default, \`make install' will install all the files in \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify an installation prefix other than \`$ac_default_prefix' using \`--prefix', for instance \`--prefix=\$HOME'. For better control, use the options below. Fine tuning of the installation directories: --bindir=DIR user executables [EPREFIX/bin] --sbindir=DIR system admin executables [EPREFIX/sbin] --libexecdir=DIR program executables [EPREFIX/libexec] --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] --datadir=DIR read-only architecture-independent data [DATAROOTDIR] --infodir=DIR info documentation [DATAROOTDIR/info] --localedir=DIR locale-dependent data [DATAROOTDIR/locale] --mandir=DIR man documentation [DATAROOTDIR/man] --docdir=DIR documentation root [DATAROOTDIR/doc/haildb] --htmldir=DIR html documentation [DOCDIR] --dvidir=DIR dvi documentation [DOCDIR] --pdfdir=DIR pdf documentation [DOCDIR] --psdir=DIR ps documentation [DOCDIR] _ACEOF cat <<\_ACEOF Program names: --program-prefix=PREFIX prepend PREFIX to installed program names --program-suffix=SUFFIX append SUFFIX to installed program names --program-transform-name=PROGRAM run sed PROGRAM on installed program names System types: --build=BUILD configure for building on BUILD [guessed] --host=HOST cross-compile to build programs to run on HOST [BUILD] --target=TARGET configure for building compilers for TARGET [HOST] _ACEOF fi if test -n "$ac_init_help"; then case $ac_init_help in short | recursive ) echo "Configuration of haildb 2.3.2:";; esac cat <<\_ACEOF Optional Features: --disable-option-checking ignore unrecognized --enable/--with options --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] --enable-fat-binaries Enable fat binary support on OSX [default=off] --enable-silent-rules less verbose build output (undo: `make V=1') --disable-silent-rules verbose build output (undo: `make V=0') --disable-dependency-tracking speeds up one-time build --enable-dependency-tracking do not reject slow dependency extractors --disable-64bit Build 64 bit binary [default=on] --enable-static[=PKGS] build static libraries [default=no] --enable-shared[=PKGS] build shared libraries [default=yes] --enable-fast-install[=PKGS] optimize for fast installation [default=yes] --disable-libtool-lock avoid locking (might break parallel builds) --disable-largefile omit support for large files --disable-assert Turn off assertions --enable-gcc-profile-mode Toggle gcc profile mode [default=off] --enable-profiling Toggle profiling [default=off] --enable-coverage Toggle coverage [default=off] --disable-dtrace Build with support for the DTRACE. [default=on] --enable-umem Enable linking with libumem [default=off] --enable-tcmalloc Enable linking with tcmalloc [default=off] --disable-mtmalloc Enable linking with mtmalloc [default=on] --disable-compression disable compressed tables support --disable-xa disable XA support Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) --with-pic try to use only PIC/non-PIC objects [default=use both] --with-gnu-ld assume the C compiler uses GNU ld [default=no] --with-debug Add debug code/turns off optimizations (yes|no) [default=no] --with-lib-prefix[=DIR] search for libraries in DIR/include and DIR/lib --without-lib-prefix don't search for libraries in includedir and libdir --with-atomic-ops= gcc_builtins|solaris|innodb Implement the atomic operations using GCC builtin atomic functions or using the Solaris 10 atomic_ops(3C) from libc or InnoDB's own implementation Some influential environment variables: CC C compiler command CFLAGS C compiler flags LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory LIBS libraries to pass to the linker, e.g. -l CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if you have headers in a nonstandard directory CPP C preprocessor CXX C++ compiler command CXXFLAGS C++ compiler flags CXXCPP C++ preprocessor Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. Report bugs to . _ACEOF ac_status=$? fi if test "$ac_init_help" = "recursive"; then # If there are subdirs, report their specific --help. for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue test -d "$ac_dir" || { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || continue ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix cd "$ac_dir" || { ac_status=$?; continue; } # Check for guested configure. if test -f "$ac_srcdir/configure.gnu"; then echo && $SHELL "$ac_srcdir/configure.gnu" --help=recursive elif test -f "$ac_srcdir/configure"; then echo && $SHELL "$ac_srcdir/configure" --help=recursive else $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 fi || ac_status=$? cd "$ac_pwd" || { ac_status=$?; break; } done fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF haildb configure 2.3.2 generated by GNU Autoconf 2.67 Copyright (C) 2010 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF exit fi ## ------------------------ ## ## Autoconf initialization. ## ## ------------------------ ## # ac_fn_c_try_compile LINENO # -------------------------- # Try to compile conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} as_fn_set_status $ac_retval } # ac_fn_c_try_compile # ac_fn_c_try_cpp LINENO # ---------------------- # Try to preprocess conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_cpp () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } > conftest.i && { test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || test ! -s conftest.err }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} as_fn_set_status $ac_retval } # ac_fn_c_try_cpp # ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists, giving a warning if it cannot be compiled using # the include files in INCLUDES and setting the cache variable VAR # accordingly. ac_fn_c_check_header_mongrel () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if eval "test \"\${$3+set}\"" = set; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval "test \"\${$3+set}\"" = set; then : $as_echo_n "(cached) " >&6 fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } else # Is the header compilable? { $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 $as_echo_n "checking $2 usability... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_header_compiler=yes else ac_header_compiler=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 $as_echo "$ac_header_compiler" >&6; } # Is the header present? { $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 $as_echo_n "checking $2 presence... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include <$2> _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : ac_header_preproc=yes else ac_header_preproc=no fi rm -f conftest.err conftest.i conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 $as_echo "$ac_header_preproc" >&6; } # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #(( yes:no: ) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 $as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} ;; no:yes:* ) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 $as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 $as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 $as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 $as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} ( $as_echo "## ----------------------------------------------- ## ## Report this to http://bugs.launchpad.net/haildb ## ## ----------------------------------------------- ##" ) | sed "s/^/$as_me: WARNING: /" >&2 ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval "test \"\${$3+set}\"" = set; then : $as_echo_n "(cached) " >&6 else eval "$3=\$ac_header_compiler" fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } fi eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} } # ac_fn_c_check_header_mongrel # ac_fn_c_try_run LINENO # ---------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. Assumes # that executables *can* be run. ac_fn_c_try_run () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then : ac_retval=0 else $as_echo "$as_me: program exited with status $ac_status" >&5 $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=$ac_status fi rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} as_fn_set_status $ac_retval } # ac_fn_c_try_run # ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists and can be compiled using the include files in # INCLUDES, setting the cache variable VAR accordingly. ac_fn_c_check_header_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval "test \"\${$3+set}\"" = set; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} } # ac_fn_c_check_header_compile # ac_fn_cxx_try_compile LINENO # ---------------------------- # Try to compile conftest.$ac_ext, and return whether this succeeded. ac_fn_cxx_try_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_cxx_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} as_fn_set_status $ac_retval } # ac_fn_cxx_try_compile # ac_fn_c_check_decl LINENO SYMBOL VAR INCLUDES # --------------------------------------------- # Tests whether SYMBOL is declared in INCLUDES, setting cache variable VAR # accordingly. ac_fn_c_check_decl () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack as_decl_name=`echo $2|sed 's/ *(.*//'` as_decl_use=`echo $2|sed -e 's/(/((/' -e 's/)/) 0&/' -e 's/,/) 0& (/g'` { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $as_decl_name is declared" >&5 $as_echo_n "checking whether $as_decl_name is declared... " >&6; } if eval "test \"\${$3+set}\"" = set; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main () { #ifndef $as_decl_name #ifdef __cplusplus (void) $as_decl_use; #else (void) $as_decl_name; #endif #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} } # ac_fn_c_check_decl # ac_fn_c_try_link LINENO # ----------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_link () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext conftest$ac_exeext if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || $as_test_x conftest$ac_exeext }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would # interfere with the next link command; also delete a directory that is # left behind by Apple's compiler. We do this before executing the actions. rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} as_fn_set_status $ac_retval } # ac_fn_c_try_link # ac_fn_c_check_func LINENO FUNC VAR # ---------------------------------- # Tests whether FUNC exists, setting the cache variable VAR accordingly ac_fn_c_check_func () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval "test \"\${$3+set}\"" = set; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Define $2 to an innocuous variant, in case declares $2. For example, HP-UX 11i declares gettimeofday. */ #define $2 innocuous_$2 /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $2 (); below. Prefer to if __STDC__ is defined, since exists even on freestanding compilers. */ #ifdef __STDC__ # include #else # include #endif #undef $2 /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char $2 (); /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined __stub_$2 || defined __stub___$2 choke me #endif int main () { return $2 (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} } # ac_fn_c_check_func # ac_fn_cxx_try_cpp LINENO # ------------------------ # Try to preprocess conftest.$ac_ext, and return whether this succeeded. ac_fn_cxx_try_cpp () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } > conftest.i && { test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" || test ! -s conftest.err }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} as_fn_set_status $ac_retval } # ac_fn_cxx_try_cpp # ac_fn_cxx_try_link LINENO # ------------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. ac_fn_cxx_try_link () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext conftest$ac_exeext if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_cxx_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || $as_test_x conftest$ac_exeext }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would # interfere with the next link command; also delete a directory that is # left behind by Apple's compiler. We do this before executing the actions. rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} as_fn_set_status $ac_retval } # ac_fn_cxx_try_link # ac_fn_c_check_type LINENO TYPE VAR INCLUDES # ------------------------------------------- # Tests whether TYPE exists after having included INCLUDES, setting cache # variable VAR accordingly. ac_fn_c_check_type () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval "test \"\${$3+set}\"" = set; then : $as_echo_n "(cached) " >&6 else eval "$3=no" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main () { if (sizeof ($2)) return 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main () { if (sizeof (($2))) return 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : else eval "$3=yes" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} } # ac_fn_c_check_type # ac_fn_c_compute_int LINENO EXPR VAR INCLUDES # -------------------------------------------- # Tries to find the compile-time value of EXPR in a program that includes # INCLUDES, setting VAR accordingly. Returns whether the value could be # computed ac_fn_c_compute_int () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if test "$cross_compiling" = yes; then # Depending upon the size, compute the lo and hi bounds. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main () { static int test_array [1 - 2 * !(($2) >= 0)]; test_array [0] = 0 ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_lo=0 ac_mid=0 while :; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main () { static int test_array [1 - 2 * !(($2) <= $ac_mid)]; test_array [0] = 0 ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_hi=$ac_mid; break else as_fn_arith $ac_mid + 1 && ac_lo=$as_val if test $ac_lo -le $ac_mid; then ac_lo= ac_hi= break fi as_fn_arith 2 '*' $ac_mid + 1 && ac_mid=$as_val fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext done else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main () { static int test_array [1 - 2 * !(($2) < 0)]; test_array [0] = 0 ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_hi=-1 ac_mid=-1 while :; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main () { static int test_array [1 - 2 * !(($2) >= $ac_mid)]; test_array [0] = 0 ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_lo=$ac_mid; break else as_fn_arith '(' $ac_mid ')' - 1 && ac_hi=$as_val if test $ac_mid -le $ac_hi; then ac_lo= ac_hi= break fi as_fn_arith 2 '*' $ac_mid && ac_mid=$as_val fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext done else ac_lo= ac_hi= fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext # Binary search between lo and hi bounds. while test "x$ac_lo" != "x$ac_hi"; do as_fn_arith '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo && ac_mid=$as_val cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main () { static int test_array [1 - 2 * !(($2) <= $ac_mid)]; test_array [0] = 0 ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_hi=$ac_mid else as_fn_arith '(' $ac_mid ')' + 1 && ac_lo=$as_val fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext done case $ac_lo in #(( ?*) eval "$3=\$ac_lo"; ac_retval=0 ;; '') ac_retval=1 ;; esac else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 static long int longval () { return $2; } static unsigned long int ulongval () { return $2; } #include #include int main () { FILE *f = fopen ("conftest.val", "w"); if (! f) return 1; if (($2) < 0) { long int i = longval (); if (i != ($2)) return 1; fprintf (f, "%ld", i); } else { unsigned long int i = ulongval (); if (i != ($2)) return 1; fprintf (f, "%lu", i); } /* Do not output a trailing newline, as this causes \r\n confusion on some platforms. */ return ferror (f) || fclose (f) != 0; ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : echo >>conftest.val; read $3 &5 $as_echo_n "checking whether $as_decl_name is declared... " >&6; } if eval "test \"\${$3+set}\"" = set; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main () { #ifndef $as_decl_name #ifdef __cplusplus (void) $as_decl_use; #else (void) $as_decl_name; #endif #endif ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} } # ac_fn_cxx_check_decl # ac_fn_c_check_member LINENO AGGR MEMBER VAR INCLUDES # ---------------------------------------------------- # Tries to find if the field MEMBER exists in type AGGR, after including # INCLUDES, setting cache variable VAR accordingly. ac_fn_c_check_member () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2.$3" >&5 $as_echo_n "checking for $2.$3... " >&6; } if eval "test \"\${$4+set}\"" = set; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $5 int main () { static $2 ac_aggr; if (ac_aggr.$3) return 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$4=yes" else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $5 int main () { static $2 ac_aggr; if (sizeof ac_aggr.$3) return 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$4=yes" else eval "$4=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi eval ac_res=\$$4 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} } # ac_fn_c_check_member cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. It was created by haildb $as_me 2.3.2, which was generated by GNU Autoconf 2.67. Invocation command line was $ $0 $@ _ACEOF exec 5>>config.log { cat <<_ASUNAME ## --------- ## ## Platform. ## ## --------- ## hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` /bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` /bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` /usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` /bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` /bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` _ASUNAME as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. $as_echo "PATH: $as_dir" done IFS=$as_save_IFS } >&5 cat >&5 <<_ACEOF ## ----------- ## ## Core tests. ## ## ----------- ## _ACEOF # Keep a trace of the command line. # Strip out --no-create and --no-recursion so they do not pile up. # Strip out --silent because we don't want to record it for future runs. # Also quote any args containing shell meta-characters. # Make two passes to allow for proper duplicate-argument suppression. ac_configure_args= ac_configure_args0= ac_configure_args1= ac_must_keep_next=false for ac_pass in 1 2 do for ac_arg do case $ac_arg in -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) continue ;; *\'*) ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac case $ac_pass in 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; 2) as_fn_append ac_configure_args1 " '$ac_arg'" if test $ac_must_keep_next = true; then ac_must_keep_next=false # Got value, back to normal. else case $ac_arg in *=* | --config-cache | -C | -disable-* | --disable-* \ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ | -with-* | --with-* | -without-* | --without-* | --x) case "$ac_configure_args0 " in "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; esac ;; -* ) ac_must_keep_next=true ;; esac fi as_fn_append ac_configure_args " '$ac_arg'" ;; esac done done { ac_configure_args0=; unset ac_configure_args0;} { ac_configure_args1=; unset ac_configure_args1;} # When interrupted or exit'd, cleanup temporary files, and complete # config.log. We remove comments because anyway the quotes in there # would cause problems or look ugly. # WARNING: Use '\'' to represent an apostrophe within the trap. # WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. trap 'exit_status=$? # Save into config.log some information that might help in debugging. { echo $as_echo "## ---------------- ## ## Cache variables. ## ## ---------------- ##" echo # The following way of writing the cache mishandles newlines in values, ( for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( *${as_nl}ac_space=\ *) sed -n \ "s/'\''/'\''\\\\'\'''\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" ;; #( *) sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) echo $as_echo "## ----------------- ## ## Output variables. ## ## ----------------- ##" echo for ac_var in $ac_subst_vars do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo if test -n "$ac_subst_files"; then $as_echo "## ------------------- ## ## File substitutions. ## ## ------------------- ##" echo for ac_var in $ac_subst_files do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo fi if test -s confdefs.h; then $as_echo "## ----------- ## ## confdefs.h. ## ## ----------- ##" echo cat confdefs.h echo fi test "$ac_signal" != 0 && $as_echo "$as_me: caught signal $ac_signal" $as_echo "$as_me: exit $exit_status" } >&5 rm -f core *.core core.conftest.* && rm -f -r conftest* confdefs* conf$$* $ac_clean_files && exit $exit_status ' 0 for ac_signal in 1 2 13 15; do trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal done ac_signal=0 # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -f -r conftest* confdefs.h $as_echo "/* confdefs.h */" > confdefs.h # Predefined preprocessor variables. cat >>confdefs.h <<_ACEOF #define PACKAGE_NAME "$PACKAGE_NAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_TARNAME "$PACKAGE_TARNAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_VERSION "$PACKAGE_VERSION" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_STRING "$PACKAGE_STRING" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_URL "$PACKAGE_URL" _ACEOF # Let the site file select an alternate cache file if it wants to. # Prefer an explicitly selected file to automatically selected ones. ac_site_file1=NONE ac_site_file2=NONE if test -n "$CONFIG_SITE"; then # We do not want a PATH search for config.site. case $CONFIG_SITE in #(( -*) ac_site_file1=./$CONFIG_SITE;; */*) ac_site_file1=$CONFIG_SITE;; *) ac_site_file1=./$CONFIG_SITE;; esac elif test "x$prefix" != xNONE; then ac_site_file1=$prefix/share/config.site ac_site_file2=$prefix/etc/config.site else ac_site_file1=$ac_default_prefix/share/config.site ac_site_file2=$ac_default_prefix/etc/config.site fi for ac_site_file in "$ac_site_file1" "$ac_site_file2" do test "x$ac_site_file" = xNONE && continue if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 $as_echo "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" \ || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "failed to load site script $ac_site_file See \`config.log' for more details" "$LINENO" 5 ; } fi done if test -r "$cache_file"; then # Some versions of bash will fail to source /dev/null (special files # actually), so we avoid doing that. DJGPP emulates it as a regular file. if test /dev/null != "$cache_file" && test -f "$cache_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 $as_echo "$as_me: loading cache $cache_file" >&6;} case $cache_file in [\\/]* | ?:[\\/]* ) . "$cache_file";; *) . "./$cache_file";; esac fi else { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 $as_echo "$as_me: creating cache $cache_file" >&6;} >$cache_file fi # Check that the precious variables saved in the cache have kept the same # value. ac_cache_corrupted=false for ac_var in $ac_precious_vars; do eval ac_old_set=\$ac_cv_env_${ac_var}_set eval ac_new_set=\$ac_env_${ac_var}_set eval ac_old_val=\$ac_cv_env_${ac_var}_value eval ac_new_val=\$ac_env_${ac_var}_value case $ac_old_set,$ac_new_set in set,) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} ac_cache_corrupted=: ;; ,set) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} ac_cache_corrupted=: ;; ,);; *) if test "x$ac_old_val" != "x$ac_new_val"; then # differences in whitespace do not lead to failure. ac_old_val_w=`echo x $ac_old_val` ac_new_val_w=`echo x $ac_new_val` if test "$ac_old_val_w" != "$ac_new_val_w"; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 $as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} ac_cache_corrupted=: else { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 $as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} eval $ac_var=\$ac_old_val fi { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 $as_echo "$as_me: former value: \`$ac_old_val'" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 $as_echo "$as_me: current value: \`$ac_new_val'" >&2;} fi;; esac # Pass precious variables to config.status. if test "$ac_new_set" = set; then case $ac_new_val in *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; *) ac_arg=$ac_var=$ac_new_val ;; esac case " $ac_configure_args " in *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. *) as_fn_append ac_configure_args " '$ac_arg'" ;; esac fi done if $ac_cache_corrupted; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 $as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 fi ## -------------------- ## ## Main body of script. ## ## -------------------- ## ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu MAJOR_VERSION=2 MINOR_VERSION=3 SUB_VERSION=2 HAILDB_FULL_VERSION=$MAJOR_VERSION.$MINOR_VERSION.$SUB_VERSION #################################### ac_aux_dir= for ac_dir in config "$srcdir"/config; do if test -f "$ac_dir/install-sh"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install-sh -c" break elif test -f "$ac_dir/install.sh"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install.sh -c" break elif test -f "$ac_dir/shtool"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/shtool install -c" break fi done if test -z "$ac_aux_dir"; then as_fn_error $? "cannot find install-sh, install.sh, or shtool in config \"$srcdir\"/config" "$LINENO" 5 fi # These three variables are undocumented and unsupported, # and are intended to be withdrawn in a future Autoconf release. # They can cause serious problems if a builder's source tree is in a directory # whose full name contains unusual characters. ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. # Check whether --enable-fat-binaries was given. if test "${enable_fat_binaries+set}" = set; then : enableval=$enable_fat_binaries; ac_enable_fat_binaries="$enableval" else ac_enable_fat_binaries="no" fi if test "x${enable_dependency_tracking}" = "x"; then : enable_dependency_tracking=yes fi if test "x${ac_enable_fat_binaries}" = "xyes"; then : enable_dependency_tracking=no fi # Make sure we can run config.sub. $SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 || as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5 { $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 $as_echo_n "checking build system type... " >&6; } if test "${ac_cv_build+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_build_alias=$build_alias test "x$ac_build_alias" = x && ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"` test "x$ac_build_alias" = x && as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` || as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5 fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 $as_echo "$ac_cv_build" >&6; } case $ac_cv_build in *-*-*) ;; *) as_fn_error $? "invalid value of canonical build" "$LINENO" 5 ;; esac build=$ac_cv_build ac_save_IFS=$IFS; IFS='-' set x $ac_cv_build shift build_cpu=$1 build_vendor=$2 shift; shift # Remember, the first character of IFS is used to create $*, # except with old shells: build_os=$* IFS=$ac_save_IFS case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 $as_echo_n "checking host system type... " >&6; } if test "${ac_cv_host+set}" = set; then : $as_echo_n "(cached) " >&6 else if test "x$host_alias" = x; then ac_cv_host=$ac_cv_build else ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` || as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5 fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 $as_echo "$ac_cv_host" >&6; } case $ac_cv_host in *-*-*) ;; *) as_fn_error $? "invalid value of canonical host" "$LINENO" 5 ;; esac host=$ac_cv_host ac_save_IFS=$IFS; IFS='-' set x $ac_cv_host shift host_cpu=$1 host_vendor=$2 shift; shift # Remember, the first character of IFS is used to create $*, # except with old shells: host_os=$* IFS=$ac_save_IFS case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking target system type" >&5 $as_echo_n "checking target system type... " >&6; } if test "${ac_cv_target+set}" = set; then : $as_echo_n "(cached) " >&6 else if test "x$target_alias" = x; then ac_cv_target=$ac_cv_host else ac_cv_target=`$SHELL "$ac_aux_dir/config.sub" $target_alias` || as_fn_error $? "$SHELL $ac_aux_dir/config.sub $target_alias failed" "$LINENO" 5 fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_target" >&5 $as_echo "$ac_cv_target" >&6; } case $ac_cv_target in *-*-*) ;; *) as_fn_error $? "invalid value of canonical target" "$LINENO" 5 ;; esac target=$ac_cv_target ac_save_IFS=$IFS; IFS='-' set x $ac_cv_target shift target_cpu=$1 target_vendor=$2 shift; shift # Remember, the first character of IFS is used to create $*, # except with old shells: target_os=$* IFS=$ac_save_IFS case $target_os in *\ *) target_os=`echo "$target_os" | sed 's/ /-/g'`;; esac # The aliases save the names the user supplied, while $host etc. # will get canonicalized. test -n "$target_alias" && test "$program_prefix$program_suffix$program_transform_name" = \ NONENONEs,x,x, && program_prefix=${target_alias}- am__api_version='1.11' # Find a good install program. We prefer a C program (faster), # so one script is as good as another. But avoid the broken or # incompatible versions: # SysV /etc/install, /usr/sbin/install # SunOS /usr/etc/install # IRIX /sbin/install # AIX /bin/install # AmigaOS /C/install, which installs bootblocks on floppy discs # AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag # AFS /usr/afsws/bin/install, which mishandles nonexistent args # SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" # OS/2's system install, which has a completely different semantic # ./install, which can be erroneously created by make from ./install.sh. # Reject install programs that cannot install multiple files. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 $as_echo_n "checking for a BSD-compatible install... " >&6; } if test -z "$INSTALL"; then if test "${ac_cv_path_install+set}" = set; then : $as_echo_n "(cached) " >&6 else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. # Account for people who put trailing slashes in PATH elements. case $as_dir/ in #(( ./ | .// | /[cC]/* | \ /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ /usr/ucb/* ) ;; *) # OSF1 and SCO ODT 3.0 have their own names for install. # Don't use installbsd from OSF since it installs stuff as root # by default. for ac_prog in ginstall scoinst install; do for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; }; then if test $ac_prog = install && grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # AIX install. It has an incompatible calling convention. : elif test $ac_prog = install && grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # program-specific install script used by HP pwplus--don't use. : else rm -rf conftest.one conftest.two conftest.dir echo one > conftest.one echo two > conftest.two mkdir conftest.dir if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" && test -s conftest.one && test -s conftest.two && test -s conftest.dir/conftest.one && test -s conftest.dir/conftest.two then ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" break 3 fi fi fi done done ;; esac done IFS=$as_save_IFS rm -rf conftest.one conftest.two conftest.dir fi if test "${ac_cv_path_install+set}" = set; then INSTALL=$ac_cv_path_install else # As a last resort, use the slow shell script. Don't cache a # value for INSTALL within a source directory, because that will # break other packages using the cache if that directory is # removed, or if the value is a relative name. INSTALL=$ac_install_sh fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 $as_echo "$INSTALL" >&6; } # Use test -z because SunOS4 sh mishandles braces in ${var-val}. # It thinks the first close brace ends the variable substitution. test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether build environment is sane" >&5 $as_echo_n "checking whether build environment is sane... " >&6; } # Just in case sleep 1 echo timestamp > conftest.file # Reject unsafe characters in $srcdir or the absolute working directory # name. Accept space and tab only in the latter. am_lf=' ' case `pwd` in *[\\\"\#\$\&\'\`$am_lf]*) as_fn_error $? "unsafe absolute working directory name" "$LINENO" 5 ;; esac case $srcdir in *[\\\"\#\$\&\'\`$am_lf\ \ ]*) as_fn_error $? "unsafe srcdir value: \`$srcdir'" "$LINENO" 5 ;; esac # Do `set' in a subshell so we don't clobber the current shell's # arguments. Must try -L first in case configure is actually a # symlink; some systems play weird games with the mod time of symlinks # (eg FreeBSD returns the mod time of the symlink's containing # directory). if ( set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` if test "$*" = "X"; then # -L didn't work. set X `ls -t "$srcdir/configure" conftest.file` fi rm -f conftest.file if test "$*" != "X $srcdir/configure conftest.file" \ && test "$*" != "X conftest.file $srcdir/configure"; then # If neither matched, then we have a broken ls. This can happen # if, for instance, CONFIG_SHELL is bash and it inherits a # broken ls alias from the environment. This has actually # happened. Such a system could not be considered "sane". as_fn_error $? "ls -t appears to fail. Make sure there is not a broken alias in your environment" "$LINENO" 5 fi test "$2" = conftest.file ) then # Ok. : else as_fn_error $? "newly created file is older than distributed files! Check your system clock" "$LINENO" 5 fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } test "$program_prefix" != NONE && program_transform_name="s&^&$program_prefix&;$program_transform_name" # Use a double $ so make ignores it. test "$program_suffix" != NONE && program_transform_name="s&\$&$program_suffix&;$program_transform_name" # Double any \ or $. # By default was `s,x,x', remove it if useless. ac_script='s/[\\$]/&&/g;s/;s,x,x,$//' program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"` # expand $ac_aux_dir to an absolute path am_aux_dir=`cd $ac_aux_dir && pwd` if test x"${MISSING+set}" != xset; then case $am_aux_dir in *\ * | *\ *) MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; *) MISSING="\${SHELL} $am_aux_dir/missing" ;; esac fi # Use eval to expand $SHELL if eval "$MISSING --run true"; then am_missing_run="$MISSING --run " else am_missing_run= { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: \`missing' script is too old or missing" >&5 $as_echo "$as_me: WARNING: \`missing' script is too old or missing" >&2;} fi if test x"${install_sh}" != xset; then case $am_aux_dir in *\ * | *\ *) install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; *) install_sh="\${SHELL} $am_aux_dir/install-sh" esac fi # Installed binaries are usually stripped using `strip' when the user # run `make install-strip'. However `strip' might not be the right # tool to use in cross-compilation environments, therefore Automake # will honor the `STRIP' environment variable to overrule this program. if test "$cross_compiling" != no; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. set dummy ${ac_tool_prefix}strip; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_STRIP+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$STRIP"; then ac_cv_prog_STRIP="$STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_STRIP="${ac_tool_prefix}strip" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi STRIP=$ac_cv_prog_STRIP if test -n "$STRIP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 $as_echo "$STRIP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_STRIP"; then ac_ct_STRIP=$STRIP # Extract the first word of "strip", so it can be a program name with args. set dummy strip; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_ac_ct_STRIP+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_STRIP"; then ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_STRIP="strip" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP if test -n "$ac_ct_STRIP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 $as_echo "$ac_ct_STRIP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_STRIP" = x; then STRIP=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac STRIP=$ac_ct_STRIP fi else STRIP="$ac_cv_prog_STRIP" fi fi INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a thread-safe mkdir -p" >&5 $as_echo_n "checking for a thread-safe mkdir -p... " >&6; } if test -z "$MKDIR_P"; then if test "${ac_cv_path_mkdir+set}" = set; then : $as_echo_n "(cached) " >&6 else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in mkdir gmkdir; do for ac_exec_ext in '' $ac_executable_extensions; do { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; } || continue case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #( 'mkdir (GNU coreutils) '* | \ 'mkdir (coreutils) '* | \ 'mkdir (fileutils) '4.1*) ac_cv_path_mkdir=$as_dir/$ac_prog$ac_exec_ext break 3;; esac done done done IFS=$as_save_IFS fi test -d ./--version && rmdir ./--version if test "${ac_cv_path_mkdir+set}" = set; then MKDIR_P="$ac_cv_path_mkdir -p" else # As a last resort, use the slow shell script. Don't cache a # value for MKDIR_P within a source directory, because that will # break other packages using the cache if that directory is # removed, or if the value is a relative name. MKDIR_P="$ac_install_sh -d" fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5 $as_echo "$MKDIR_P" >&6; } mkdir_p="$MKDIR_P" case $mkdir_p in [\\/$]* | ?:[\\/]*) ;; */*) mkdir_p="\$(top_builddir)/$mkdir_p" ;; esac for ac_prog in gawk mawk nawk awk do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_AWK+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$AWK"; then ac_cv_prog_AWK="$AWK" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_AWK="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi AWK=$ac_cv_prog_AWK if test -n "$AWK"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5 $as_echo "$AWK" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$AWK" && break done { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 $as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } set x ${MAKE-make} ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` if eval "test \"\${ac_cv_prog_make_${ac_make}_set+set}\"" = set; then : $as_echo_n "(cached) " >&6 else cat >conftest.make <<\_ACEOF SHELL = /bin/sh all: @echo '@@@%%%=$(MAKE)=@@@%%%' _ACEOF # GNU make sometimes prints "make[1]: Entering ...", which would confuse us. case `${MAKE-make} -f conftest.make 2>/dev/null` in *@@@%%%=?*=@@@%%%*) eval ac_cv_prog_make_${ac_make}_set=yes;; *) eval ac_cv_prog_make_${ac_make}_set=no;; esac rm -f conftest.make fi if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } SET_MAKE= else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } SET_MAKE="MAKE=${MAKE-make}" fi rm -rf .tst 2>/dev/null mkdir .tst 2>/dev/null if test -d .tst; then am__leading_dot=. else am__leading_dot=_ fi rmdir .tst 2>/dev/null DEPDIR="${am__leading_dot}deps" ac_config_commands="$ac_config_commands depfiles" am_make=${MAKE-make} cat > confinc << 'END' am__doit: @echo this is the am__doit target .PHONY: am__doit END # If we don't find an include directive, just comment out the code. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for style of include used by $am_make" >&5 $as_echo_n "checking for style of include used by $am_make... " >&6; } am__include="#" am__quote= _am_result=none # First try GNU make style include. echo "include confinc" > confmf # Ignore all kinds of additional output from `make'. case `$am_make -s -f confmf 2> /dev/null` in #( *the\ am__doit\ target*) am__include=include am__quote= _am_result=GNU ;; esac # Now try BSD make style include. if test "$am__include" = "#"; then echo '.include "confinc"' > confmf case `$am_make -s -f confmf 2> /dev/null` in #( *the\ am__doit\ target*) am__include=.include am__quote="\"" _am_result=BSD ;; esac fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $_am_result" >&5 $as_echo "$_am_result" >&6; } rm -f confinc confmf # Check whether --enable-dependency-tracking was given. if test "${enable_dependency_tracking+set}" = set; then : enableval=$enable_dependency_tracking; fi if test "x$enable_dependency_tracking" != xno; then am_depcomp="$ac_aux_dir/depcomp" AMDEPBACKSLASH='\' fi if test "x$enable_dependency_tracking" != xno; then AMDEP_TRUE= AMDEP_FALSE='#' else AMDEP_TRUE='#' AMDEP_FALSE= fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. set dummy ${ac_tool_prefix}gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_CC+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_CC="${ac_tool_prefix}gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_ac_ct_CC+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_CC="gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. set dummy ${ac_tool_prefix}cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_CC+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_CC="${ac_tool_prefix}cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi fi if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_CC+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else ac_prog_rejected=no as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS if test $ac_prog_rejected = yes; then # We found a bogon in the path, so make sure we never use it. set dummy $ac_cv_prog_CC shift if test $# != 0; then # We chose a different compiler from the bogus one. # However, it has the same basename, so the bogon will be chosen # first if we set CC to just the basename; use the full file name. shift ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" fi fi fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then for ac_prog in cl.exe do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_CC+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in cl.exe do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_ac_ct_CC+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_CC="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_CC" && break done if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi fi fi test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "no acceptable C compiler found in \$PATH See \`config.log' for more details" "$LINENO" 5 ; } # Provide some information about the compiler. $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" # Try to create an executable without -o first, disregard a.out. # It will help us diagnose broken compilers, and finding out an intuition # of exeext. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 $as_echo_n "checking whether the C compiler works... " >&6; } ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` # The possible output files: ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" ac_rmfiles= for ac_file in $ac_files do case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; * ) ac_rmfiles="$ac_rmfiles $ac_file";; esac done rm -f $ac_rmfiles if { { ac_try="$ac_link_default" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link_default") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. # So ignore a value of `no', otherwise this would lead to `EXEEXT = no' # in a Makefile. We should not override ac_cv_exeext if it was cached, # so that the user can short-circuit this test for compilers unknown to # Autoconf. for ac_file in $ac_files '' do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; [ab].out ) # We found the default executable, but exeext='' is most # certainly right. break;; *.* ) if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; then :; else ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` fi # We set ac_cv_exeext here because the later test for it is not # safe: cross compilers may not add the suffix if given an `-o' # argument, so we may need to know it at that point already. # Even if this section looks crufty: it has the advantage of # actually working. break;; * ) break;; esac done test "$ac_cv_exeext" = no && ac_cv_exeext= else ac_file='' fi if test -z "$ac_file"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "C compiler cannot create executables See \`config.log' for more details" "$LINENO" 5 ; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 $as_echo_n "checking for C compiler default output file name... " >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 $as_echo "$ac_file" >&6; } ac_exeext=$ac_cv_exeext rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 $as_echo_n "checking for suffix of executables... " >&6; } if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : # If both `conftest.exe' and `conftest' are `present' (well, observable) # catch `conftest.exe'. For instance with Cygwin, `ls conftest' will # work properly (i.e., refer to `conftest.exe'), while it won't with # `rm'. for ac_file in conftest.exe conftest conftest.*; do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` break;; * ) break;; esac done else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of executables: cannot compile and link See \`config.log' for more details" "$LINENO" 5 ; } fi rm -f conftest conftest$ac_cv_exeext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 $as_echo "$ac_cv_exeext" >&6; } rm -f conftest.$ac_ext EXEEXT=$ac_cv_exeext ac_exeext=$EXEEXT cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { FILE *f = fopen ("conftest.out", "w"); return ferror (f) || fclose (f) != 0; ; return 0; } _ACEOF ac_clean_files="$ac_clean_files conftest.out" # Check that the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 $as_echo_n "checking whether we are cross compiling... " >&6; } if test "$cross_compiling" != yes; then { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if { ac_try='./conftest$ac_cv_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then cross_compiling=no else if test "$cross_compiling" = maybe; then cross_compiling=yes else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot run C compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details" "$LINENO" 5 ; } fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 $as_echo "$cross_compiling" >&6; } rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 $as_echo_n "checking for suffix of object files... " >&6; } if test "${ac_cv_objext+set}" = set; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.o conftest.obj if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : for ac_file in conftest.o conftest.obj conftest.*; do test -f "$ac_file" || continue; case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` break;; esac done else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of object files: cannot compile See \`config.log' for more details" "$LINENO" 5 ; } fi rm -f conftest.$ac_cv_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 $as_echo "$ac_cv_objext" >&6; } OBJEXT=$ac_cv_objext ac_objext=$OBJEXT { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 $as_echo_n "checking whether we are using the GNU C compiler... " >&6; } if test "${ac_cv_c_compiler_gnu+set}" = set; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_compiler_gnu=yes else ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 $as_echo "$ac_cv_c_compiler_gnu" >&6; } if test $ac_compiler_gnu = yes; then GCC=yes else GCC= fi ac_test_CFLAGS=${CFLAGS+set} ac_save_CFLAGS=$CFLAGS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 $as_echo_n "checking whether $CC accepts -g... " >&6; } if test "${ac_cv_prog_cc_g+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes ac_cv_prog_cc_g=no CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes else CFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : else ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 $as_echo "$ac_cv_prog_cc_g" >&6; } if test "$ac_test_CFLAGS" = set; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 $as_echo_n "checking for $CC option to accept ISO C89... " >&6; } if test "${ac_cv_prog_cc_c89+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_cv_prog_cc_c89=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ struct buf { int x; }; FILE * (*rcsopen) (struct buf *, struct stat *, int); static char *e (p, i) char **p; int i; { return p[i]; } static char *f (char * (*g) (char **, int), char **p, ...) { char *s; va_list v; va_start (v,p); s = g (p, va_arg (v,int)); va_end (v); return s; } /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has function prototypes and stuff, but not '\xHH' hex character constants. These don't provoke an error unfortunately, instead are silently treated as 'x'. The following induces an error, until -std is added to get proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an array size at least. It's necessary to write '\x00'==0 to get something that's true only with -std. */ int osf4_cc_array ['\x00' == 0 ? 1 : -1]; /* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters inside strings and character constants. */ #define FOO(x) 'x' int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; int test (int i, double x); struct s1 {int (*f) (int a);}; struct s2 {int (*f) (double a);}; int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); int argc; char **argv; int main () { return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; ; return 0; } _ACEOF for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_c89=$ac_arg fi rm -f core conftest.err conftest.$ac_objext test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi # AC_CACHE_VAL case "x$ac_cv_prog_cc_c89" in x) { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 $as_echo "none needed" >&6; } ;; xno) { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 $as_echo "unsupported" >&6; } ;; *) CC="$CC $ac_cv_prog_cc_c89" { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 $as_echo "$ac_cv_prog_cc_c89" >&6; } ;; esac if test "x$ac_cv_prog_cc_c89" != xno; then : fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu depcc="$CC" am_compiler_list= { $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 $as_echo_n "checking dependency style of $depcc... " >&6; } if test "${am_cv_CC_dependencies_compiler_type+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then # We make a subdir and do the tests there. Otherwise we can end up # making bogus files that we don't know about and never remove. For # instance it was reported that on HP-UX the gcc test will end up # making a dummy file named `D' -- because `-MD' means `put the output # in D'. mkdir conftest.dir # Copy depcomp to subdir because otherwise we won't find it if we're # using a relative directory. cp "$am_depcomp" conftest.dir cd conftest.dir # We will build objects and dependencies in a subdirectory because # it helps to detect inapplicable dependency modes. For instance # both Tru64's cc and ICC support -MD to output dependencies as a # side effect of compilation, but ICC will put the dependencies in # the current directory while Tru64 will put them in the object # directory. mkdir sub am_cv_CC_dependencies_compiler_type=none if test "$am_compiler_list" = ""; then am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` fi am__universal=false case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac for depmode in $am_compiler_list; do # Setup a source with many dependencies, because some compilers # like to wrap large dependency lists on column 80 (with \), and # we should not choose a depcomp mode which is confused by this. # # We need to recreate these files for each test, as the compiler may # overwrite some of them when testing with obscure command lines. # This happens at least with the AIX C compiler. : > sub/conftest.c for i in 1 2 3 4 5 6; do echo '#include "conftst'$i'.h"' >> sub/conftest.c # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with # Solaris 8's {/usr,}/bin/sh. touch sub/conftst$i.h done echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf # We check with `-c' and `-o' for the sake of the "dashmstdout" # mode. It turns out that the SunPro C++ compiler does not properly # handle `-M -o', and we need to detect this. Also, some Intel # versions had trouble with output in subdirs am__obj=sub/conftest.${OBJEXT-o} am__minus_obj="-o $am__obj" case $depmode in gcc) # This depmode causes a compiler race in universal mode. test "$am__universal" = false || continue ;; nosideeffect) # after this tag, mechanisms are not by side-effect, so they'll # only be used when explicitly requested if test "x$enable_dependency_tracking" = xyes; then continue else break fi ;; msvisualcpp | msvcmsys) # This compiler won't grok `-c -o', but also, the minuso test has # not run yet. These depmodes are late enough in the game, and # so weak that their functioning should not be impacted. am__obj=conftest.${OBJEXT-o} am__minus_obj= ;; none) break ;; esac if depmode=$depmode \ source=sub/conftest.c object=$am__obj \ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ >/dev/null 2>conftest.err && grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && grep $am__obj sub/conftest.Po > /dev/null 2>&1 && ${MAKE-make} -s -f confmf > /dev/null 2>&1; then # icc doesn't choke on unknown options, it will just issue warnings # or remarks (even with -Werror). So we grep stderr for any message # that says an option was ignored or not supported. # When given -MP, icc 7.0 and 7.1 complain thusly: # icc: Command line warning: ignoring option '-M'; no argument required # The diagnosis changed in icc 8.0: # icc: Command line remark: option '-MP' not supported if (grep 'ignoring option' conftest.err || grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else am_cv_CC_dependencies_compiler_type=$depmode break fi fi done cd .. rm -rf conftest.dir else am_cv_CC_dependencies_compiler_type=none fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5 $as_echo "$am_cv_CC_dependencies_compiler_type" >&6; } CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type if test "x$enable_dependency_tracking" != xno \ && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then am__fastdepCC_TRUE= am__fastdepCC_FALSE='#' else am__fastdepCC_TRUE='#' am__fastdepCC_FALSE= fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 $as_echo_n "checking how to run the C preprocessor... " >&6; } # On Suns, sometimes $CPP names a directory. if test -n "$CPP" && test -d "$CPP"; then CPP= fi if test -z "$CPP"; then if test "${ac_cv_prog_CPP+set}" = set; then : $as_echo_n "(cached) " >&6 else # Double quotes because CPP needs to be expanded for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" do ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : break fi done ac_cv_prog_CPP=$CPP fi CPP=$ac_cv_prog_CPP else ac_cv_prog_CPP=$CPP fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 $as_echo "$CPP" >&6; } ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "C preprocessor \"$CPP\" fails sanity check See \`config.log' for more details" "$LINENO" 5 ; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 $as_echo_n "checking for grep that handles long lines and -e... " >&6; } if test "${ac_cv_path_GREP+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -z "$GREP"; then ac_path_GREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in grep ggrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" { test -f "$ac_path_GREP" && $as_test_x "$ac_path_GREP"; } || continue # Check for GNU ac_path_GREP and select it if it is found. # Check for GNU $ac_path_GREP case `"$ac_path_GREP" --version 2>&1` in *GNU*) ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'GREP' >> "conftest.nl" "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_GREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_GREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_GREP"; then as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_GREP=$GREP fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 $as_echo "$ac_cv_path_GREP" >&6; } GREP="$ac_cv_path_GREP" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 $as_echo_n "checking for egrep... " >&6; } if test "${ac_cv_path_EGREP+set}" = set; then : $as_echo_n "(cached) " >&6 else if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 then ac_cv_path_EGREP="$GREP -E" else if test -z "$EGREP"; then ac_path_EGREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in egrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" { test -f "$ac_path_EGREP" && $as_test_x "$ac_path_EGREP"; } || continue # Check for GNU ac_path_EGREP and select it if it is found. # Check for GNU $ac_path_EGREP case `"$ac_path_EGREP" --version 2>&1` in *GNU*) ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'EGREP' >> "conftest.nl" "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_EGREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_EGREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_EGREP"; then as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_EGREP=$EGREP fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 $as_echo "$ac_cv_path_EGREP" >&6; } EGREP="$ac_cv_path_EGREP" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 $as_echo_n "checking for ANSI C header files... " >&6; } if test "${ac_cv_header_stdc+set}" = set; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_header_stdc=yes else ac_cv_header_stdc=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext if test $ac_cv_header_stdc = yes; then # SunOS 4.x string.h does not declare mem*, contrary to ANSI. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "memchr" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "free" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. if test "$cross_compiling" = yes; then : : else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #if ((' ' & 0x0FF) == 0x020) # define ISLOWER(c) ('a' <= (c) && (c) <= 'z') # define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) #else # define ISLOWER(c) \ (('a' <= (c) && (c) <= 'i') \ || ('j' <= (c) && (c) <= 'r') \ || ('s' <= (c) && (c) <= 'z')) # define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) #endif #define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) int main () { int i; for (i = 0; i < 256; i++) if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) return 2; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : else ac_cv_header_stdc=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 $as_echo "$ac_cv_header_stdc" >&6; } if test $ac_cv_header_stdc = yes; then $as_echo "#define STDC_HEADERS 1" >>confdefs.h fi # On IRIX 5.3, sys/types and inttypes.h are conflicting. for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ inttypes.h stdint.h unistd.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default " if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done ac_fn_c_check_header_mongrel "$LINENO" "minix/config.h" "ac_cv_header_minix_config_h" "$ac_includes_default" if test "x$ac_cv_header_minix_config_h" = x""yes; then : MINIX=yes else MINIX= fi if test "$MINIX" = yes; then $as_echo "#define _POSIX_SOURCE 1" >>confdefs.h $as_echo "#define _POSIX_1_SOURCE 2" >>confdefs.h $as_echo "#define _MINIX 1" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether it is safe to define __EXTENSIONS__" >&5 $as_echo_n "checking whether it is safe to define __EXTENSIONS__... " >&6; } if test "${ac_cv_safe_to_define___extensions__+set}" = set; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ # define __EXTENSIONS__ 1 $ac_includes_default int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_safe_to_define___extensions__=yes else ac_cv_safe_to_define___extensions__=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_safe_to_define___extensions__" >&5 $as_echo "$ac_cv_safe_to_define___extensions__" >&6; } test $ac_cv_safe_to_define___extensions__ = yes && $as_echo "#define __EXTENSIONS__ 1" >>confdefs.h $as_echo "#define _ALL_SOURCE 1" >>confdefs.h $as_echo "#define _GNU_SOURCE 1" >>confdefs.h $as_echo "#define _POSIX_PTHREAD_SEMANTICS 1" >>confdefs.h $as_echo "#define _TANDEM_SOURCE 1" >>confdefs.h if test "$GCC" = "yes"; then : if test "$host_vendor" = "apple" -a "x${ac_cv_env_CC_set}" = "x"; then : host_os_version=`echo ${host_os} | perl -ple 's/^\D+//g;s,\..*,,'` if test "$host_os_version" -lt 10; then : for ac_prog in gcc-4.2 do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_CC_4_2+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$CC_4_2"; then ac_cv_prog_CC_4_2="$CC_4_2" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_CC_4_2="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC_4_2=$ac_cv_prog_CC_4_2 if test -n "$CC_4_2"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC_4_2" >&5 $as_echo "$CC_4_2" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$CC_4_2" && break done for ac_prog in g++-4.2 do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_CXX_4_2+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$CXX_4_2"; then ac_cv_prog_CXX_4_2="$CXX_4_2" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_CXX_4_2="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CXX_4_2=$ac_cv_prog_CXX_4_2 if test -n "$CXX_4_2"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXX_4_2" >&5 $as_echo "$CXX_4_2" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$CXX_4_2" && break done if test "x${CC_4_2}" != "x" -a "x${ac_cv_env_CC_set}" = "x"; then : CC="${CC_4_2}" fi if test "x${CXX_4_2}" != "x" -a "x${ac_cv_env_CCX_set}" = "x"; then : CXX="${CXX_4_2}" fi if test "x${CC_4_2}" != "x" -a "x${ac_cv_env_CPP_set}" = "x"; then : CPP="${CC_4_2} -E" fi fi fi fi if test "$GCC" = "yes"; then : if test "x${ac_cv_env_CC_set}" = "x"; then : for ac_prog in gcc44 do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_CC44+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$CC44"; then ac_cv_prog_CC44="$CC44" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_CC44="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC44=$ac_cv_prog_CC44 if test -n "$CC44"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC44" >&5 $as_echo "$CC44" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$CC44" && break done for ac_prog in g++44 do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_CXX44+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$CXX44"; then ac_cv_prog_CXX44="$CXX44" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_CXX44="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CXX44=$ac_cv_prog_CXX44 if test -n "$CXX44"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXX44" >&5 $as_echo "$CXX44" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$CXX44" && break done if test "x${CC44}" != "x" -a "x${ac_cv_env_CC_set}" = "x"; then : CC="${CC44}" fi if test "x${CXX44}" != "x" -a "x${ac_cv_env_CCX_set}" = "x"; then : CXX="${CXX44}" fi if test "x${CC44}" != "x" -a "x${ac_cv_env_CPP_set}" = "x"; then : CPP="${CC44} -E" fi for ac_prog in gcc45 do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_CC45+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$CC45"; then ac_cv_prog_CC45="$CC45" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_CC45="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC45=$ac_cv_prog_CC45 if test -n "$CC45"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC45" >&5 $as_echo "$CC45" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$CC45" && break done for ac_prog in g++45 do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_CXX45+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$CXX45"; then ac_cv_prog_CXX45="$CXX45" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_CXX45="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CXX45=$ac_cv_prog_CXX45 if test -n "$CXX45"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXX45" >&5 $as_echo "$CXX45" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$CXX45" && break done if test "x${CC45}" != "x" -a "x${ac_cv_env_CC_set}" = "x"; then : CC="${CC45}" fi if test "x${CXX45}" != "x" -a "x${ac_cv_env_CCX_set}" = "x"; then : CXX="${CXX45}" fi if test "x${CC45}" != "x" -a "x${ac_cv_env_CPP_set}" = "x"; then : CPP="${CC45} -E" fi fi fi if test "$GCC" = "yes"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking if GCC is recent enough" >&5 $as_echo_n "checking if GCC is recent enough... " >&6; } if test "${ac_cv_gcc_recent+set}" = set; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #if !defined(__GNUC__) || (__GNUC__ < 4) || ((__GNUC__ >= 4) && (__GNUC_MINOR__ < 2)) # error GCC is Too Old! #endif int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_gcc_recent=yes else ac_cv_gcc_recent=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_gcc_recent" >&5 $as_echo "$ac_cv_gcc_recent" >&6; } if test "$ac_cv_gcc_recent" = "no" -a "$host_vendor" = "apple"; then : as_fn_error $? "Your version of GCC is too old. At least version 4.2 is required on OSX. You may need to install a version of XCode >= 3.1.2" "$LINENO" 5 fi if test "$ac_cv_gcc_recent" = "no"; then : as_fn_error $? "Your version of GCC is too old. At least version 4.2 is required. On RHEL/CentOS systems this is found in the gcc44 and gcc44-c++ packages." "$LINENO" 5 fi fi # Check whether --enable-64bit was given. if test "${enable_64bit+set}" = set; then : enableval=$enable_64bit; ac_enable_64bit="$enableval" else ac_enable_64bit="yes" fi for ac_prog in isainfo do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_ISAINFO+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$ISAINFO"; then ac_cv_prog_ISAINFO="$ISAINFO" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ISAINFO="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ISAINFO=$ac_cv_prog_ISAINFO if test -n "$ISAINFO"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ISAINFO" >&5 $as_echo "$ISAINFO" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ISAINFO" && break done test -n "$ISAINFO" || ISAINFO="no" if test "x$ISAINFO" != "xno"; then : isainfo_b=`${ISAINFO} -b` else isainfo_b="x" fi if test "$isainfo_b" != "x"; then : isainfo_k=`${ISAINFO} -k` DTRACEFLAGS="${DTRACEFLAGS} -${isainfo_b}" if test "x$ac_enable_64bit" = "xyes"; then : if test "x${ac_cv_env_LDFLAGS_set}" = "x"; then : LDFLAGS="-L/usr/local/lib/${isainfo_k} ${LDFLAGS}" fi if test "x$libdir" = "x\${exec_prefix}/lib"; then : libdir="${libdir}/${isainfo_k}" fi if test "x${ac_cv_env_CFLAGS_set}" = "x"; then : CFLAGS="${CFLAGS} -m64" ac_cv_env_CFLAGS_set=set ac_cv_env_CFLAGS_value='-m64' fi if test "x${ac_cv_env_CXXFLAGS_set}" = "x"; then : CXXFLAGS="${CXXFLAGS} -m64" ac_cv_env_CXXFLAGS_set=set ac_cv_env_CXXFLAGS_value='-m64' fi if test "$target_cpu" = "sparc" -a "x$SUNCC" = "xyes"; then : AM_CFLAGS="-xmemalign=8s ${AM_CFLAGS}" AM_CXXFLAGS="-xmemalign=8s ${AM_CXXFLAGS}" fi fi fi ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu if test -z "$CXX"; then if test -n "$CCC"; then CXX=$CCC else if test -n "$ac_tool_prefix"; then for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_CXX+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$CXX"; then ac_cv_prog_CXX="$CXX" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_CXX="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CXX=$ac_cv_prog_CXX if test -n "$CXX"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXX" >&5 $as_echo "$CXX" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$CXX" && break done fi if test -z "$CXX"; then ac_ct_CXX=$CXX for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_ac_ct_CXX+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CXX"; then ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_CXX="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CXX=$ac_cv_prog_ac_ct_CXX if test -n "$ac_ct_CXX"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CXX" >&5 $as_echo "$ac_ct_CXX" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_CXX" && break done if test "x$ac_ct_CXX" = x; then CXX="g++" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CXX=$ac_ct_CXX fi fi fi fi # Provide some information about the compiler. $as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C++ compiler" >&5 $as_echo_n "checking whether we are using the GNU C++ compiler... " >&6; } if test "${ac_cv_cxx_compiler_gnu+set}" = set; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_compiler_gnu=yes else ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_cxx_compiler_gnu=$ac_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_compiler_gnu" >&5 $as_echo "$ac_cv_cxx_compiler_gnu" >&6; } if test $ac_compiler_gnu = yes; then GXX=yes else GXX= fi ac_test_CXXFLAGS=${CXXFLAGS+set} ac_save_CXXFLAGS=$CXXFLAGS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX accepts -g" >&5 $as_echo_n "checking whether $CXX accepts -g... " >&6; } if test "${ac_cv_prog_cxx_g+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_save_cxx_werror_flag=$ac_cxx_werror_flag ac_cxx_werror_flag=yes ac_cv_prog_cxx_g=no CXXFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_cv_prog_cxx_g=yes else CXXFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : else ac_cxx_werror_flag=$ac_save_cxx_werror_flag CXXFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_cv_prog_cxx_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cxx_werror_flag=$ac_save_cxx_werror_flag fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_g" >&5 $as_echo "$ac_cv_prog_cxx_g" >&6; } if test "$ac_test_CXXFLAGS" = set; then CXXFLAGS=$ac_save_CXXFLAGS elif test $ac_cv_prog_cxx_g = yes; then if test "$GXX" = yes; then CXXFLAGS="-g -O2" else CXXFLAGS="-g" fi else if test "$GXX" = yes; then CXXFLAGS="-O2" else CXXFLAGS= fi fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu depcc="$CXX" am_compiler_list= { $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 $as_echo_n "checking dependency style of $depcc... " >&6; } if test "${am_cv_CXX_dependencies_compiler_type+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then # We make a subdir and do the tests there. Otherwise we can end up # making bogus files that we don't know about and never remove. For # instance it was reported that on HP-UX the gcc test will end up # making a dummy file named `D' -- because `-MD' means `put the output # in D'. mkdir conftest.dir # Copy depcomp to subdir because otherwise we won't find it if we're # using a relative directory. cp "$am_depcomp" conftest.dir cd conftest.dir # We will build objects and dependencies in a subdirectory because # it helps to detect inapplicable dependency modes. For instance # both Tru64's cc and ICC support -MD to output dependencies as a # side effect of compilation, but ICC will put the dependencies in # the current directory while Tru64 will put them in the object # directory. mkdir sub am_cv_CXX_dependencies_compiler_type=none if test "$am_compiler_list" = ""; then am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` fi am__universal=false case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac for depmode in $am_compiler_list; do # Setup a source with many dependencies, because some compilers # like to wrap large dependency lists on column 80 (with \), and # we should not choose a depcomp mode which is confused by this. # # We need to recreate these files for each test, as the compiler may # overwrite some of them when testing with obscure command lines. # This happens at least with the AIX C compiler. : > sub/conftest.c for i in 1 2 3 4 5 6; do echo '#include "conftst'$i'.h"' >> sub/conftest.c # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with # Solaris 8's {/usr,}/bin/sh. touch sub/conftst$i.h done echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf # We check with `-c' and `-o' for the sake of the "dashmstdout" # mode. It turns out that the SunPro C++ compiler does not properly # handle `-M -o', and we need to detect this. Also, some Intel # versions had trouble with output in subdirs am__obj=sub/conftest.${OBJEXT-o} am__minus_obj="-o $am__obj" case $depmode in gcc) # This depmode causes a compiler race in universal mode. test "$am__universal" = false || continue ;; nosideeffect) # after this tag, mechanisms are not by side-effect, so they'll # only be used when explicitly requested if test "x$enable_dependency_tracking" = xyes; then continue else break fi ;; msvisualcpp | msvcmsys) # This compiler won't grok `-c -o', but also, the minuso test has # not run yet. These depmodes are late enough in the game, and # so weak that their functioning should not be impacted. am__obj=conftest.${OBJEXT-o} am__minus_obj= ;; none) break ;; esac if depmode=$depmode \ source=sub/conftest.c object=$am__obj \ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ >/dev/null 2>conftest.err && grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && grep $am__obj sub/conftest.Po > /dev/null 2>&1 && ${MAKE-make} -s -f confmf > /dev/null 2>&1; then # icc doesn't choke on unknown options, it will just issue warnings # or remarks (even with -Werror). So we grep stderr for any message # that says an option was ignored or not supported. # When given -MP, icc 7.0 and 7.1 complain thusly: # icc: Command line warning: ignoring option '-M'; no argument required # The diagnosis changed in icc 8.0: # icc: Command line remark: option '-MP' not supported if (grep 'ignoring option' conftest.err || grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else am_cv_CXX_dependencies_compiler_type=$depmode break fi fi done cd .. rm -rf conftest.dir else am_cv_CXX_dependencies_compiler_type=none fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CXX_dependencies_compiler_type" >&5 $as_echo "$am_cv_CXX_dependencies_compiler_type" >&6; } CXXDEPMODE=depmode=$am_cv_CXX_dependencies_compiler_type if test "x$enable_dependency_tracking" != xno \ && test "$am_cv_CXX_dependencies_compiler_type" = gcc3; then am__fastdepCXX_TRUE= am__fastdepCXX_FALSE='#' else am__fastdepCXX_TRUE='#' am__fastdepCXX_FALSE= fi if test "x$CC" != xcc; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC and cc understand -c and -o together" >&5 $as_echo_n "checking whether $CC and cc understand -c and -o together... " >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether cc understands -c and -o together" >&5 $as_echo_n "checking whether cc understands -c and -o together... " >&6; } fi set dummy $CC; ac_cc=`$as_echo "$2" | sed 's/[^a-zA-Z0-9_]/_/g;s/^[0-9]/_/'` if eval "test \"\${ac_cv_prog_cc_${ac_cc}_c_o+set}\"" = set; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF # Make sure it works both with $CC and with simple cc. # We do the test twice because some compilers refuse to overwrite an # existing .o file with -o, though they will create one. ac_try='$CC -c conftest.$ac_ext -o conftest2.$ac_objext >&5' rm -f conftest2.* if { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && test -f conftest2.$ac_objext && { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then eval ac_cv_prog_cc_${ac_cc}_c_o=yes if test "x$CC" != xcc; then # Test first that cc exists at all. if { ac_try='cc -c conftest.$ac_ext >&5' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then ac_try='cc -c conftest.$ac_ext -o conftest2.$ac_objext >&5' rm -f conftest2.* if { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && test -f conftest2.$ac_objext && { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then # cc works too. : else # cc exists but doesn't like -o. eval ac_cv_prog_cc_${ac_cc}_c_o=no fi fi fi else eval ac_cv_prog_cc_${ac_cc}_c_o=no fi rm -f core conftest* fi if eval test \$ac_cv_prog_cc_${ac_cc}_c_o = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } $as_echo "#define NO_MINUS_C_MINUS_O 1" >>confdefs.h fi # Check whether --enable-static was given. if test "${enable_static+set}" = set; then : enableval=$enable_static; p=${PACKAGE-default} case $enableval in yes) enable_static=yes ;; no) enable_static=no ;; *) enable_static=no # Look at the argument we got. We use all the common list separators. lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," for pkg in $enableval; do IFS="$lt_save_ifs" if test "X$pkg" = "X$p"; then enable_static=yes fi done IFS="$lt_save_ifs" ;; esac else enable_static=no fi case `pwd` in *\ * | *\ *) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&5 $as_echo "$as_me: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&2;} ;; esac macro_version='2.2.6b' macro_revision='1.3017' ltmain="$ac_aux_dir/ltmain.sh" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a sed that does not truncate output" >&5 $as_echo_n "checking for a sed that does not truncate output... " >&6; } if test "${ac_cv_path_SED+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ for ac_i in 1 2 3 4 5 6 7; do ac_script="$ac_script$as_nl$ac_script" done echo "$ac_script" 2>/dev/null | sed 99q >conftest.sed { ac_script=; unset ac_script;} if test -z "$SED"; then ac_path_SED_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in sed gsed; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_SED="$as_dir/$ac_prog$ac_exec_ext" { test -f "$ac_path_SED" && $as_test_x "$ac_path_SED"; } || continue # Check for GNU ac_path_SED and select it if it is found. # Check for GNU $ac_path_SED case `"$ac_path_SED" --version 2>&1` in *GNU*) ac_cv_path_SED="$ac_path_SED" ac_path_SED_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo '' >> "conftest.nl" "$ac_path_SED" -f conftest.sed < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_SED_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_SED="$ac_path_SED" ac_path_SED_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_SED_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_SED"; then as_fn_error $? "no acceptable sed could be found in \$PATH" "$LINENO" 5 fi else ac_cv_path_SED=$SED fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_SED" >&5 $as_echo "$ac_cv_path_SED" >&6; } SED="$ac_cv_path_SED" rm -f conftest.sed test -z "$SED" && SED=sed Xsed="$SED -e 1s/^X//" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for fgrep" >&5 $as_echo_n "checking for fgrep... " >&6; } if test "${ac_cv_path_FGREP+set}" = set; then : $as_echo_n "(cached) " >&6 else if echo 'ab*c' | $GREP -F 'ab*c' >/dev/null 2>&1 then ac_cv_path_FGREP="$GREP -F" else if test -z "$FGREP"; then ac_path_FGREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in fgrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_FGREP="$as_dir/$ac_prog$ac_exec_ext" { test -f "$ac_path_FGREP" && $as_test_x "$ac_path_FGREP"; } || continue # Check for GNU ac_path_FGREP and select it if it is found. # Check for GNU $ac_path_FGREP case `"$ac_path_FGREP" --version 2>&1` in *GNU*) ac_cv_path_FGREP="$ac_path_FGREP" ac_path_FGREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'FGREP' >> "conftest.nl" "$ac_path_FGREP" FGREP < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_FGREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_FGREP="$ac_path_FGREP" ac_path_FGREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_FGREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_FGREP"; then as_fn_error $? "no acceptable fgrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_FGREP=$FGREP fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_FGREP" >&5 $as_echo "$ac_cv_path_FGREP" >&6; } FGREP="$ac_cv_path_FGREP" test -z "$GREP" && GREP=grep # Check whether --with-gnu-ld was given. if test "${with_gnu_ld+set}" = set; then : withval=$with_gnu_ld; test "$withval" = no || with_gnu_ld=yes else with_gnu_ld=no fi ac_prog=ld if test "$GCC" = yes; then # Check if gcc -print-prog-name=ld gives a path. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld used by $CC" >&5 $as_echo_n "checking for ld used by $CC... " >&6; } case $host in *-*-mingw*) # gcc leaves a trailing carriage return which upsets mingw ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; *) ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; esac case $ac_prog in # Accept absolute paths. [\\/]* | ?:[\\/]*) re_direlt='/[^/][^/]*/\.\./' # Canonicalize the pathname of ld ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` done test -z "$LD" && LD="$ac_prog" ;; "") # If it fails, then pretend we aren't using GCC. ac_prog=ld ;; *) # If it is relative, then search for the first ld in PATH. with_gnu_ld=unknown ;; esac elif test "$with_gnu_ld" = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5 $as_echo_n "checking for GNU ld... " >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5 $as_echo_n "checking for non-GNU ld... " >&6; } fi if test "${lt_cv_path_LD+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -z "$LD"; then lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR for ac_dir in $PATH; do IFS="$lt_save_ifs" test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then lt_cv_path_LD="$ac_dir/$ac_prog" # Check to see if the program is GNU ld. I'd rather use --version, # but apparently some variants of GNU ld only accept -v. # Break only if it was the GNU/non-GNU ld that we prefer. case `"$lt_cv_path_LD" -v 2>&1 &5 $as_echo "$LD" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5 { $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5 $as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; } if test "${lt_cv_prog_gnu_ld+set}" = set; then : $as_echo_n "(cached) " >&6 else # I'd rather use --version here, but apparently some GNU lds only accept -v. case `$LD -v 2>&1 &5 $as_echo "$lt_cv_prog_gnu_ld" >&6; } with_gnu_ld=$lt_cv_prog_gnu_ld { $as_echo "$as_me:${as_lineno-$LINENO}: checking for BSD- or MS-compatible name lister (nm)" >&5 $as_echo_n "checking for BSD- or MS-compatible name lister (nm)... " >&6; } if test "${lt_cv_path_NM+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$NM"; then # Let the user override the test. lt_cv_path_NM="$NM" else lt_nm_to_check="${ac_tool_prefix}nm" if test -n "$ac_tool_prefix" && test "$build" = "$host"; then lt_nm_to_check="$lt_nm_to_check nm" fi for lt_tmp_nm in $lt_nm_to_check; do lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do IFS="$lt_save_ifs" test -z "$ac_dir" && ac_dir=. tmp_nm="$ac_dir/$lt_tmp_nm" if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext" ; then # Check to see if the nm accepts a BSD-compat flag. # Adding the `sed 1q' prevents false positives on HP-UX, which says: # nm: unknown option "B" ignored # Tru64's nm complains that /dev/null is an invalid object file case `"$tmp_nm" -B /dev/null 2>&1 | sed '1q'` in */dev/null* | *'Invalid file or object type'*) lt_cv_path_NM="$tmp_nm -B" break ;; *) case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in */dev/null*) lt_cv_path_NM="$tmp_nm -p" break ;; *) lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but continue # so that we can try to find one that supports BSD flags ;; esac ;; esac fi done IFS="$lt_save_ifs" done : ${lt_cv_path_NM=no} fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_NM" >&5 $as_echo "$lt_cv_path_NM" >&6; } if test "$lt_cv_path_NM" != "no"; then NM="$lt_cv_path_NM" else # Didn't find any BSD compatible name lister, look for dumpbin. if test -n "$ac_tool_prefix"; then for ac_prog in "dumpbin -symbols" "link -dump -symbols" do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_DUMPBIN+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$DUMPBIN"; then ac_cv_prog_DUMPBIN="$DUMPBIN" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_DUMPBIN="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi DUMPBIN=$ac_cv_prog_DUMPBIN if test -n "$DUMPBIN"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DUMPBIN" >&5 $as_echo "$DUMPBIN" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$DUMPBIN" && break done fi if test -z "$DUMPBIN"; then ac_ct_DUMPBIN=$DUMPBIN for ac_prog in "dumpbin -symbols" "link -dump -symbols" do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_ac_ct_DUMPBIN+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_DUMPBIN"; then ac_cv_prog_ac_ct_DUMPBIN="$ac_ct_DUMPBIN" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_DUMPBIN="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_DUMPBIN=$ac_cv_prog_ac_ct_DUMPBIN if test -n "$ac_ct_DUMPBIN"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DUMPBIN" >&5 $as_echo "$ac_ct_DUMPBIN" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_DUMPBIN" && break done if test "x$ac_ct_DUMPBIN" = x; then DUMPBIN=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac DUMPBIN=$ac_ct_DUMPBIN fi fi if test "$DUMPBIN" != ":"; then NM="$DUMPBIN" fi fi test -z "$NM" && NM=nm { $as_echo "$as_me:${as_lineno-$LINENO}: checking the name lister ($NM) interface" >&5 $as_echo_n "checking the name lister ($NM) interface... " >&6; } if test "${lt_cv_nm_interface+set}" = set; then : $as_echo_n "(cached) " >&6 else lt_cv_nm_interface="BSD nm" echo "int some_variable = 0;" > conftest.$ac_ext (eval echo "\"\$as_me:6441: $ac_compile\"" >&5) (eval "$ac_compile" 2>conftest.err) cat conftest.err >&5 (eval echo "\"\$as_me:6444: $NM \\\"conftest.$ac_objext\\\"\"" >&5) (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) cat conftest.err >&5 (eval echo "\"\$as_me:6447: output\"" >&5) cat conftest.out >&5 if $GREP 'External.*some_variable' conftest.out > /dev/null; then lt_cv_nm_interface="MS dumpbin" fi rm -f conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_nm_interface" >&5 $as_echo "$lt_cv_nm_interface" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5 $as_echo_n "checking whether ln -s works... " >&6; } LN_S=$as_ln_s if test "$LN_S" = "ln -s"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5 $as_echo "no, using $LN_S" >&6; } fi # find the maximum length of command line arguments { $as_echo "$as_me:${as_lineno-$LINENO}: checking the maximum length of command line arguments" >&5 $as_echo_n "checking the maximum length of command line arguments... " >&6; } if test "${lt_cv_sys_max_cmd_len+set}" = set; then : $as_echo_n "(cached) " >&6 else i=0 teststring="ABCD" case $build_os in msdosdjgpp*) # On DJGPP, this test can blow up pretty badly due to problems in libc # (any single argument exceeding 2000 bytes causes a buffer overrun # during glob expansion). Even if it were fixed, the result of this # check would be larger than it should be. lt_cv_sys_max_cmd_len=12288; # 12K is about right ;; gnu*) # Under GNU Hurd, this test is not required because there is # no limit to the length of command line arguments. # Libtool will interpret -1 as no limit whatsoever lt_cv_sys_max_cmd_len=-1; ;; cygwin* | mingw* | cegcc*) # On Win9x/ME, this test blows up -- it succeeds, but takes # about 5 minutes as the teststring grows exponentially. # Worse, since 9x/ME are not pre-emptively multitasking, # you end up with a "frozen" computer, even though with patience # the test eventually succeeds (with a max line length of 256k). # Instead, let's just punt: use the minimum linelength reported by # all of the supported platforms: 8192 (on NT/2K/XP). lt_cv_sys_max_cmd_len=8192; ;; amigaos*) # On AmigaOS with pdksh, this test takes hours, literally. # So we just punt and use a minimum line length of 8192. lt_cv_sys_max_cmd_len=8192; ;; netbsd* | freebsd* | openbsd* | darwin* | dragonfly*) # This has been around since 386BSD, at least. Likely further. if test -x /sbin/sysctl; then lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax` elif test -x /usr/sbin/sysctl; then lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax` else lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs fi # And add a safety zone lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` ;; interix*) # We know the value 262144 and hardcode it with a safety zone (like BSD) lt_cv_sys_max_cmd_len=196608 ;; osf*) # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not # nice to cause kernel panics so lets avoid the loop below. # First set a reasonable default. lt_cv_sys_max_cmd_len=16384 # if test -x /sbin/sysconfig; then case `/sbin/sysconfig -q proc exec_disable_arg_limit` in *1*) lt_cv_sys_max_cmd_len=-1 ;; esac fi ;; sco3.2v5*) lt_cv_sys_max_cmd_len=102400 ;; sysv5* | sco5v6* | sysv4.2uw2*) kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null` if test -n "$kargmax"; then lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[ ]//'` else lt_cv_sys_max_cmd_len=32768 fi ;; *) lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null` if test -n "$lt_cv_sys_max_cmd_len"; then lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` else # Make teststring a little bigger before we do anything with it. # a 1K string should be a reasonable start. for i in 1 2 3 4 5 6 7 8 ; do teststring=$teststring$teststring done SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}} # If test is not a shell built-in, we'll probably end up computing a # maximum length that is only half of the actual maximum length, but # we can't tell. while { test "X"`$SHELL $0 --fallback-echo "X$teststring$teststring" 2>/dev/null` \ = "XX$teststring$teststring"; } >/dev/null 2>&1 && test $i != 17 # 1/2 MB should be enough do i=`expr $i + 1` teststring=$teststring$teststring done # Only check the string length outside the loop. lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1` teststring= # Add a significant safety factor because C++ compilers can tack on # massive amounts of additional arguments before passing them to the # linker. It appears as though 1/2 is a usable value. lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2` fi ;; esac fi if test -n $lt_cv_sys_max_cmd_len ; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sys_max_cmd_len" >&5 $as_echo "$lt_cv_sys_max_cmd_len" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: none" >&5 $as_echo "none" >&6; } fi max_cmd_len=$lt_cv_sys_max_cmd_len : ${CP="cp -f"} : ${MV="mv -f"} : ${RM="rm -f"} { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the shell understands some XSI constructs" >&5 $as_echo_n "checking whether the shell understands some XSI constructs... " >&6; } # Try some XSI features xsi_shell=no ( _lt_dummy="a/b/c" test "${_lt_dummy##*/},${_lt_dummy%/*},"${_lt_dummy%"$_lt_dummy"}, \ = c,a/b,, \ && eval 'test $(( 1 + 1 )) -eq 2 \ && test "${#_lt_dummy}" -eq 5' ) >/dev/null 2>&1 \ && xsi_shell=yes { $as_echo "$as_me:${as_lineno-$LINENO}: result: $xsi_shell" >&5 $as_echo "$xsi_shell" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the shell understands \"+=\"" >&5 $as_echo_n "checking whether the shell understands \"+=\"... " >&6; } lt_shell_append=no ( foo=bar; set foo baz; eval "$1+=\$2" && test "$foo" = barbaz ) \ >/dev/null 2>&1 \ && lt_shell_append=yes { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_shell_append" >&5 $as_echo "$lt_shell_append" >&6; } if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then lt_unset=unset else lt_unset=false fi # test EBCDIC or ASCII case `echo X|tr X '\101'` in A) # ASCII based system # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr lt_SP2NL='tr \040 \012' lt_NL2SP='tr \015\012 \040\040' ;; *) # EBCDIC based system lt_SP2NL='tr \100 \n' lt_NL2SP='tr \r\n \100\100' ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $LD option to reload object files" >&5 $as_echo_n "checking for $LD option to reload object files... " >&6; } if test "${lt_cv_ld_reload_flag+set}" = set; then : $as_echo_n "(cached) " >&6 else lt_cv_ld_reload_flag='-r' fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_reload_flag" >&5 $as_echo "$lt_cv_ld_reload_flag" >&6; } reload_flag=$lt_cv_ld_reload_flag case $reload_flag in "" | " "*) ;; *) reload_flag=" $reload_flag" ;; esac reload_cmds='$LD$reload_flag -o $output$reload_objs' case $host_os in darwin*) if test "$GCC" = yes; then reload_cmds='$LTCC $LTCFLAGS -nostdlib ${wl}-r -o $output$reload_objs' else reload_cmds='$LD$reload_flag -o $output$reload_objs' fi ;; esac if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}objdump", so it can be a program name with args. set dummy ${ac_tool_prefix}objdump; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_OBJDUMP+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$OBJDUMP"; then ac_cv_prog_OBJDUMP="$OBJDUMP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_OBJDUMP="${ac_tool_prefix}objdump" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi OBJDUMP=$ac_cv_prog_OBJDUMP if test -n "$OBJDUMP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OBJDUMP" >&5 $as_echo "$OBJDUMP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_OBJDUMP"; then ac_ct_OBJDUMP=$OBJDUMP # Extract the first word of "objdump", so it can be a program name with args. set dummy objdump; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_ac_ct_OBJDUMP+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_OBJDUMP"; then ac_cv_prog_ac_ct_OBJDUMP="$ac_ct_OBJDUMP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_OBJDUMP="objdump" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_OBJDUMP=$ac_cv_prog_ac_ct_OBJDUMP if test -n "$ac_ct_OBJDUMP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OBJDUMP" >&5 $as_echo "$ac_ct_OBJDUMP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_OBJDUMP" = x; then OBJDUMP="false" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac OBJDUMP=$ac_ct_OBJDUMP fi else OBJDUMP="$ac_cv_prog_OBJDUMP" fi test -z "$OBJDUMP" && OBJDUMP=objdump { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to recognize dependent libraries" >&5 $as_echo_n "checking how to recognize dependent libraries... " >&6; } if test "${lt_cv_deplibs_check_method+set}" = set; then : $as_echo_n "(cached) " >&6 else lt_cv_file_magic_cmd='$MAGIC_CMD' lt_cv_file_magic_test_file= lt_cv_deplibs_check_method='unknown' # Need to set the preceding variable on all platforms that support # interlibrary dependencies. # 'none' -- dependencies not supported. # `unknown' -- same as none, but documents that we really don't know. # 'pass_all' -- all dependencies passed with no checks. # 'test_compile' -- check by making test program. # 'file_magic [[regex]]' -- check by looking for files in library path # which responds to the $file_magic_cmd with a given extended regex. # If you have `file' or equivalent on your system and you're not sure # whether `pass_all' will *always* work, you probably want this one. case $host_os in aix[4-9]*) lt_cv_deplibs_check_method=pass_all ;; beos*) lt_cv_deplibs_check_method=pass_all ;; bsdi[45]*) lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib)' lt_cv_file_magic_cmd='/usr/bin/file -L' lt_cv_file_magic_test_file=/shlib/libc.so ;; cygwin*) # func_win32_libid is a shell function defined in ltmain.sh lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' lt_cv_file_magic_cmd='func_win32_libid' ;; mingw* | pw32*) # Base MSYS/MinGW do not provide the 'file' command needed by # func_win32_libid shell function, so use a weaker test based on 'objdump', # unless we find 'file', for example because we are cross-compiling. if ( file / ) >/dev/null 2>&1; then lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' lt_cv_file_magic_cmd='func_win32_libid' else lt_cv_deplibs_check_method='file_magic file format pei*-i386(.*architecture: i386)?' lt_cv_file_magic_cmd='$OBJDUMP -f' fi ;; cegcc) # use the weaker test based on 'objdump'. See mingw*. lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?' lt_cv_file_magic_cmd='$OBJDUMP -f' ;; darwin* | rhapsody*) lt_cv_deplibs_check_method=pass_all ;; freebsd* | dragonfly*) if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then case $host_cpu in i*86 ) # Not sure whether the presence of OpenBSD here was a mistake. # Let's accept both of them until this is cleared up. lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[3-9]86 (compact )?demand paged shared library' lt_cv_file_magic_cmd=/usr/bin/file lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*` ;; esac else lt_cv_deplibs_check_method=pass_all fi ;; gnu*) lt_cv_deplibs_check_method=pass_all ;; hpux10.20* | hpux11*) lt_cv_file_magic_cmd=/usr/bin/file case $host_cpu in ia64*) lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - IA64' lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so ;; hppa*64*) lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - PA-RISC [0-9].[0-9]' lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl ;; *) lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|PA-RISC[0-9].[0-9]) shared library' lt_cv_file_magic_test_file=/usr/lib/libc.sl ;; esac ;; interix[3-9]*) # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|\.a)$' ;; irix5* | irix6* | nonstopux*) case $LD in *-32|*"-32 ") libmagic=32-bit;; *-n32|*"-n32 ") libmagic=N32;; *-64|*"-64 ") libmagic=64-bit;; *) libmagic=never-match;; esac lt_cv_deplibs_check_method=pass_all ;; # This must be Linux ELF. linux* | k*bsd*-gnu | kopensolaris*-gnu) lt_cv_deplibs_check_method=pass_all ;; netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$' else lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|_pic\.a)$' fi ;; newos6*) lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (executable|dynamic lib)' lt_cv_file_magic_cmd=/usr/bin/file lt_cv_file_magic_test_file=/usr/lib/libnls.so ;; *nto* | *qnx*) lt_cv_deplibs_check_method=pass_all ;; openbsd*) if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|\.so|_pic\.a)$' else lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$' fi ;; osf3* | osf4* | osf5*) lt_cv_deplibs_check_method=pass_all ;; rdos*) lt_cv_deplibs_check_method=pass_all ;; solaris*) lt_cv_deplibs_check_method=pass_all ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) lt_cv_deplibs_check_method=pass_all ;; sysv4 | sysv4.3*) case $host_vendor in motorola) lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib) M[0-9][0-9]* Version [0-9]' lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*` ;; ncr) lt_cv_deplibs_check_method=pass_all ;; sequent) lt_cv_file_magic_cmd='/bin/file' lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [LM]SB (shared object|dynamic lib )' ;; sni) lt_cv_file_magic_cmd='/bin/file' lt_cv_deplibs_check_method="file_magic ELF [0-9][0-9]*-bit [LM]SB dynamic lib" lt_cv_file_magic_test_file=/lib/libc.so ;; siemens) lt_cv_deplibs_check_method=pass_all ;; pc) lt_cv_deplibs_check_method=pass_all ;; esac ;; tpf*) lt_cv_deplibs_check_method=pass_all ;; esac fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_deplibs_check_method" >&5 $as_echo "$lt_cv_deplibs_check_method" >&6; } file_magic_cmd=$lt_cv_file_magic_cmd deplibs_check_method=$lt_cv_deplibs_check_method test -z "$deplibs_check_method" && deplibs_check_method=unknown if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}ar", so it can be a program name with args. set dummy ${ac_tool_prefix}ar; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_AR+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$AR"; then ac_cv_prog_AR="$AR" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_AR="${ac_tool_prefix}ar" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi AR=$ac_cv_prog_AR if test -n "$AR"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5 $as_echo "$AR" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_AR"; then ac_ct_AR=$AR # Extract the first word of "ar", so it can be a program name with args. set dummy ar; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_ac_ct_AR+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_AR"; then ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_AR="ar" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_AR=$ac_cv_prog_ac_ct_AR if test -n "$ac_ct_AR"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5 $as_echo "$ac_ct_AR" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_AR" = x; then AR="false" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac AR=$ac_ct_AR fi else AR="$ac_cv_prog_AR" fi test -z "$AR" && AR=ar test -z "$AR_FLAGS" && AR_FLAGS=cru if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. set dummy ${ac_tool_prefix}strip; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_STRIP+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$STRIP"; then ac_cv_prog_STRIP="$STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_STRIP="${ac_tool_prefix}strip" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi STRIP=$ac_cv_prog_STRIP if test -n "$STRIP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 $as_echo "$STRIP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_STRIP"; then ac_ct_STRIP=$STRIP # Extract the first word of "strip", so it can be a program name with args. set dummy strip; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_ac_ct_STRIP+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_STRIP"; then ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_STRIP="strip" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP if test -n "$ac_ct_STRIP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 $as_echo "$ac_ct_STRIP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_STRIP" = x; then STRIP=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac STRIP=$ac_ct_STRIP fi else STRIP="$ac_cv_prog_STRIP" fi test -z "$STRIP" && STRIP=: if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. set dummy ${ac_tool_prefix}ranlib; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_RANLIB+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$RANLIB"; then ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi RANLIB=$ac_cv_prog_RANLIB if test -n "$RANLIB"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5 $as_echo "$RANLIB" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_RANLIB"; then ac_ct_RANLIB=$RANLIB # Extract the first word of "ranlib", so it can be a program name with args. set dummy ranlib; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_ac_ct_RANLIB+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_RANLIB"; then ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_RANLIB="ranlib" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB if test -n "$ac_ct_RANLIB"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5 $as_echo "$ac_ct_RANLIB" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_RANLIB" = x; then RANLIB=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac RANLIB=$ac_ct_RANLIB fi else RANLIB="$ac_cv_prog_RANLIB" fi test -z "$RANLIB" && RANLIB=: # Determine commands to create old-style static archives. old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs' old_postinstall_cmds='chmod 644 $oldlib' old_postuninstall_cmds= if test -n "$RANLIB"; then case $host_os in openbsd*) old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$oldlib" ;; *) old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$oldlib" ;; esac old_archive_cmds="$old_archive_cmds~\$RANLIB \$oldlib" fi # If no C compiler was specified, use CC. LTCC=${LTCC-"$CC"} # If no C compiler flags were specified, use CFLAGS. LTCFLAGS=${LTCFLAGS-"$CFLAGS"} # Allow CC to be a program name with arguments. compiler=$CC # Check for command to grab the raw symbol name followed by C symbol from nm. { $as_echo "$as_me:${as_lineno-$LINENO}: checking command to parse $NM output from $compiler object" >&5 $as_echo_n "checking command to parse $NM output from $compiler object... " >&6; } if test "${lt_cv_sys_global_symbol_pipe+set}" = set; then : $as_echo_n "(cached) " >&6 else # These are sane defaults that work on at least a few old systems. # [They come from Ultrix. What could be older than Ultrix?!! ;)] # Character class describing NM global symbol codes. symcode='[BCDEGRST]' # Regexp to match symbols that can be accessed directly from C. sympat='\([_A-Za-z][_A-Za-z0-9]*\)' # Define system-specific variables. case $host_os in aix*) symcode='[BCDT]' ;; cygwin* | mingw* | pw32* | cegcc*) symcode='[ABCDGISTW]' ;; hpux*) if test "$host_cpu" = ia64; then symcode='[ABCDEGRST]' fi ;; irix* | nonstopux*) symcode='[BCDEGRST]' ;; osf*) symcode='[BCDEGQRST]' ;; solaris*) symcode='[BDRT]' ;; sco3.2v5*) symcode='[DT]' ;; sysv4.2uw2*) symcode='[DT]' ;; sysv5* | sco5v6* | unixware* | OpenUNIX*) symcode='[ABDT]' ;; sysv4) symcode='[DFNSTU]' ;; esac # If we're using GNU nm, then use its standard symbol codes. case `$NM -V 2>&1` in *GNU* | *'with BFD'*) symcode='[ABCDGIRSTW]' ;; esac # Transform an extracted symbol line into a proper C declaration. # Some systems (esp. on ia64) link data and code symbols differently, # so use this general approach. lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'" # Transform an extracted symbol line into symbol name and symbol address lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([^ ]*\) $/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([^ ]*\) \([^ ]*\)$/ {\"\2\", (void *) \&\2},/p'" lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n -e 's/^: \([^ ]*\) $/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([^ ]*\) \(lib[^ ]*\)$/ {\"\2\", (void *) \&\2},/p' -e 's/^$symcode* \([^ ]*\) \([^ ]*\)$/ {\"lib\2\", (void *) \&\2},/p'" # Handle CRLF in mingw tool chain opt_cr= case $build_os in mingw*) opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp ;; esac # Try without a prefix underscore, then with it. for ac_symprfx in "" "_"; do # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol. symxfrm="\\1 $ac_symprfx\\2 \\2" # Write the raw and C identifiers. if test "$lt_cv_nm_interface" = "MS dumpbin"; then # Fake it for dumpbin and say T for any non-static function # and D for any global variable. # Also find C++ and __fastcall symbols from MSVC++, # which start with @ or ?. lt_cv_sys_global_symbol_pipe="$AWK '"\ " {last_section=section; section=\$ 3};"\ " /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\ " \$ 0!~/External *\|/{next};"\ " / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\ " {if(hide[section]) next};"\ " {f=0}; \$ 0~/\(\).*\|/{f=1}; {printf f ? \"T \" : \"D \"};"\ " {split(\$ 0, a, /\||\r/); split(a[2], s)};"\ " s[1]~/^[@?]/{print s[1], s[1]; next};"\ " s[1]~prfx {split(s[1],t,\"@\"); print t[1], substr(t[1],length(prfx))}"\ " ' prfx=^$ac_symprfx" else lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[ ]\($symcode$symcode*\)[ ][ ]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'" fi # Check to see that the pipe works correctly. pipe_works=no rm -f conftest* cat > conftest.$ac_ext <<_LT_EOF #ifdef __cplusplus extern "C" { #endif char nm_test_var; void nm_test_func(void); void nm_test_func(void){} #ifdef __cplusplus } #endif int main(){nm_test_var='a';nm_test_func();return(0);} _LT_EOF if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then # Now try to grab the symbols. nlist=conftest.nm if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$NM conftest.$ac_objext \| $lt_cv_sys_global_symbol_pipe \> $nlist\""; } >&5 (eval $NM conftest.$ac_objext \| $lt_cv_sys_global_symbol_pipe \> $nlist) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && test -s "$nlist"; then # Try sorting and uniquifying the output. if sort "$nlist" | uniq > "$nlist"T; then mv -f "$nlist"T "$nlist" else rm -f "$nlist"T fi # Make sure that we snagged all the symbols we need. if $GREP ' nm_test_var$' "$nlist" >/dev/null; then if $GREP ' nm_test_func$' "$nlist" >/dev/null; then cat <<_LT_EOF > conftest.$ac_ext #ifdef __cplusplus extern "C" { #endif _LT_EOF # Now generate the symbol file. eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext' cat <<_LT_EOF >> conftest.$ac_ext /* The mapping between symbol names and symbols. */ const struct { const char *name; void *address; } lt__PROGRAM__LTX_preloaded_symbols[] = { { "@PROGRAM@", (void *) 0 }, _LT_EOF $SED "s/^$symcode$symcode* \(.*\) \(.*\)$/ {\"\2\", (void *) \&\2},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext cat <<\_LT_EOF >> conftest.$ac_ext {0, (void *) 0} }; /* This works around a problem in FreeBSD linker */ #ifdef FREEBSD_WORKAROUND static const void *lt_preloaded_setup() { return lt__PROGRAM__LTX_preloaded_symbols; } #endif #ifdef __cplusplus } #endif _LT_EOF # Now try linking the two files. mv conftest.$ac_objext conftstm.$ac_objext lt_save_LIBS="$LIBS" lt_save_CFLAGS="$CFLAGS" LIBS="conftstm.$ac_objext" CFLAGS="$CFLAGS$lt_prog_compiler_no_builtin_flag" if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 (eval $ac_link) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && test -s conftest${ac_exeext}; then pipe_works=yes fi LIBS="$lt_save_LIBS" CFLAGS="$lt_save_CFLAGS" else echo "cannot find nm_test_func in $nlist" >&5 fi else echo "cannot find nm_test_var in $nlist" >&5 fi else echo "cannot run $lt_cv_sys_global_symbol_pipe" >&5 fi else echo "$progname: failed program was:" >&5 cat conftest.$ac_ext >&5 fi rm -rf conftest* conftst* # Do not use the global_symbol_pipe unless it works. if test "$pipe_works" = yes; then break else lt_cv_sys_global_symbol_pipe= fi done fi if test -z "$lt_cv_sys_global_symbol_pipe"; then lt_cv_sys_global_symbol_to_cdecl= fi if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: failed" >&5 $as_echo "failed" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: ok" >&5 $as_echo "ok" >&6; } fi # Check whether --enable-libtool-lock was given. if test "${enable_libtool_lock+set}" = set; then : enableval=$enable_libtool_lock; fi test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes # Some flags need to be propagated to the compiler or linker for good # libtool support. case $host in ia64-*-hpux*) # Find out which ABI we are using. echo 'int i;' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then case `/usr/bin/file conftest.$ac_objext` in *ELF-32*) HPUX_IA64_MODE="32" ;; *ELF-64*) HPUX_IA64_MODE="64" ;; esac fi rm -rf conftest* ;; *-*-irix6*) # Find out which ABI we are using. echo '#line 7652 "configure"' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then if test "$lt_cv_prog_gnu_ld" = yes; then case `/usr/bin/file conftest.$ac_objext` in *32-bit*) LD="${LD-ld} -melf32bsmip" ;; *N32*) LD="${LD-ld} -melf32bmipn32" ;; *64-bit*) LD="${LD-ld} -melf64bmip" ;; esac else case `/usr/bin/file conftest.$ac_objext` in *32-bit*) LD="${LD-ld} -32" ;; *N32*) LD="${LD-ld} -n32" ;; *64-bit*) LD="${LD-ld} -64" ;; esac fi fi rm -rf conftest* ;; x86_64-*kfreebsd*-gnu|x86_64-*linux*|ppc*-*linux*|powerpc*-*linux*| \ s390*-*linux*|s390*-*tpf*|sparc*-*linux*) # Find out which ABI we are using. echo 'int i;' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then case `/usr/bin/file conftest.o` in *32-bit*) case $host in x86_64-*kfreebsd*-gnu) LD="${LD-ld} -m elf_i386_fbsd" ;; x86_64-*linux*) LD="${LD-ld} -m elf_i386" ;; ppc64-*linux*|powerpc64-*linux*) LD="${LD-ld} -m elf32ppclinux" ;; s390x-*linux*) LD="${LD-ld} -m elf_s390" ;; sparc64-*linux*) LD="${LD-ld} -m elf32_sparc" ;; esac ;; *64-bit*) case $host in x86_64-*kfreebsd*-gnu) LD="${LD-ld} -m elf_x86_64_fbsd" ;; x86_64-*linux*) LD="${LD-ld} -m elf_x86_64" ;; ppc*-*linux*|powerpc*-*linux*) LD="${LD-ld} -m elf64ppc" ;; s390*-*linux*|s390*-*tpf*) LD="${LD-ld} -m elf64_s390" ;; sparc*-*linux*) LD="${LD-ld} -m elf64_sparc" ;; esac ;; esac fi rm -rf conftest* ;; *-*-sco3.2v5*) # On SCO OpenServer 5, we need -belf to get full-featured binaries. SAVE_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -belf" { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler needs -belf" >&5 $as_echo_n "checking whether the C compiler needs -belf... " >&6; } if test "${lt_cv_cc_needs_belf+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : lt_cv_cc_needs_belf=yes else lt_cv_cc_needs_belf=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_cc_needs_belf" >&5 $as_echo "$lt_cv_cc_needs_belf" >&6; } if test x"$lt_cv_cc_needs_belf" != x"yes"; then # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf CFLAGS="$SAVE_CFLAGS" fi ;; sparc*-*solaris*) # Find out which ABI we are using. echo 'int i;' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then case `/usr/bin/file conftest.o` in *64-bit*) case $lt_cv_prog_gnu_ld in yes*) LD="${LD-ld} -m elf64_sparc" ;; *) if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then LD="${LD-ld} -64" fi ;; esac ;; esac fi rm -rf conftest* ;; esac need_locks="$enable_libtool_lock" case $host_os in rhapsody* | darwin*) if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}dsymutil", so it can be a program name with args. set dummy ${ac_tool_prefix}dsymutil; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_DSYMUTIL+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$DSYMUTIL"; then ac_cv_prog_DSYMUTIL="$DSYMUTIL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_DSYMUTIL="${ac_tool_prefix}dsymutil" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi DSYMUTIL=$ac_cv_prog_DSYMUTIL if test -n "$DSYMUTIL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DSYMUTIL" >&5 $as_echo "$DSYMUTIL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_DSYMUTIL"; then ac_ct_DSYMUTIL=$DSYMUTIL # Extract the first word of "dsymutil", so it can be a program name with args. set dummy dsymutil; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_ac_ct_DSYMUTIL+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_DSYMUTIL"; then ac_cv_prog_ac_ct_DSYMUTIL="$ac_ct_DSYMUTIL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_DSYMUTIL="dsymutil" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_DSYMUTIL=$ac_cv_prog_ac_ct_DSYMUTIL if test -n "$ac_ct_DSYMUTIL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DSYMUTIL" >&5 $as_echo "$ac_ct_DSYMUTIL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_DSYMUTIL" = x; then DSYMUTIL=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac DSYMUTIL=$ac_ct_DSYMUTIL fi else DSYMUTIL="$ac_cv_prog_DSYMUTIL" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}nmedit", so it can be a program name with args. set dummy ${ac_tool_prefix}nmedit; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_NMEDIT+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$NMEDIT"; then ac_cv_prog_NMEDIT="$NMEDIT" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_NMEDIT="${ac_tool_prefix}nmedit" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi NMEDIT=$ac_cv_prog_NMEDIT if test -n "$NMEDIT"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $NMEDIT" >&5 $as_echo "$NMEDIT" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_NMEDIT"; then ac_ct_NMEDIT=$NMEDIT # Extract the first word of "nmedit", so it can be a program name with args. set dummy nmedit; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_ac_ct_NMEDIT+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_NMEDIT"; then ac_cv_prog_ac_ct_NMEDIT="$ac_ct_NMEDIT" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_NMEDIT="nmedit" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_NMEDIT=$ac_cv_prog_ac_ct_NMEDIT if test -n "$ac_ct_NMEDIT"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_NMEDIT" >&5 $as_echo "$ac_ct_NMEDIT" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_NMEDIT" = x; then NMEDIT=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac NMEDIT=$ac_ct_NMEDIT fi else NMEDIT="$ac_cv_prog_NMEDIT" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}lipo", so it can be a program name with args. set dummy ${ac_tool_prefix}lipo; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_LIPO+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$LIPO"; then ac_cv_prog_LIPO="$LIPO" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_LIPO="${ac_tool_prefix}lipo" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi LIPO=$ac_cv_prog_LIPO if test -n "$LIPO"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIPO" >&5 $as_echo "$LIPO" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_LIPO"; then ac_ct_LIPO=$LIPO # Extract the first word of "lipo", so it can be a program name with args. set dummy lipo; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_ac_ct_LIPO+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_LIPO"; then ac_cv_prog_ac_ct_LIPO="$ac_ct_LIPO" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_LIPO="lipo" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_LIPO=$ac_cv_prog_ac_ct_LIPO if test -n "$ac_ct_LIPO"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_LIPO" >&5 $as_echo "$ac_ct_LIPO" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_LIPO" = x; then LIPO=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac LIPO=$ac_ct_LIPO fi else LIPO="$ac_cv_prog_LIPO" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}otool", so it can be a program name with args. set dummy ${ac_tool_prefix}otool; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_OTOOL+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$OTOOL"; then ac_cv_prog_OTOOL="$OTOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_OTOOL="${ac_tool_prefix}otool" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi OTOOL=$ac_cv_prog_OTOOL if test -n "$OTOOL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL" >&5 $as_echo "$OTOOL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_OTOOL"; then ac_ct_OTOOL=$OTOOL # Extract the first word of "otool", so it can be a program name with args. set dummy otool; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_ac_ct_OTOOL+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_OTOOL"; then ac_cv_prog_ac_ct_OTOOL="$ac_ct_OTOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_OTOOL="otool" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_OTOOL=$ac_cv_prog_ac_ct_OTOOL if test -n "$ac_ct_OTOOL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL" >&5 $as_echo "$ac_ct_OTOOL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_OTOOL" = x; then OTOOL=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac OTOOL=$ac_ct_OTOOL fi else OTOOL="$ac_cv_prog_OTOOL" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}otool64", so it can be a program name with args. set dummy ${ac_tool_prefix}otool64; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_OTOOL64+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$OTOOL64"; then ac_cv_prog_OTOOL64="$OTOOL64" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_OTOOL64="${ac_tool_prefix}otool64" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi OTOOL64=$ac_cv_prog_OTOOL64 if test -n "$OTOOL64"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL64" >&5 $as_echo "$OTOOL64" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_OTOOL64"; then ac_ct_OTOOL64=$OTOOL64 # Extract the first word of "otool64", so it can be a program name with args. set dummy otool64; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_ac_ct_OTOOL64+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_OTOOL64"; then ac_cv_prog_ac_ct_OTOOL64="$ac_ct_OTOOL64" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_OTOOL64="otool64" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_OTOOL64=$ac_cv_prog_ac_ct_OTOOL64 if test -n "$ac_ct_OTOOL64"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL64" >&5 $as_echo "$ac_ct_OTOOL64" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_OTOOL64" = x; then OTOOL64=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac OTOOL64=$ac_ct_OTOOL64 fi else OTOOL64="$ac_cv_prog_OTOOL64" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -single_module linker flag" >&5 $as_echo_n "checking for -single_module linker flag... " >&6; } if test "${lt_cv_apple_cc_single_mod+set}" = set; then : $as_echo_n "(cached) " >&6 else lt_cv_apple_cc_single_mod=no if test -z "${LT_MULTI_MODULE}"; then # By default we will add the -single_module flag. You can override # by either setting the environment variable LT_MULTI_MODULE # non-empty at configure time, or by adding -multi_module to the # link flags. rm -rf libconftest.dylib* echo "int foo(void){return 1;}" > conftest.c echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ -dynamiclib -Wl,-single_module conftest.c" >&5 $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ -dynamiclib -Wl,-single_module conftest.c 2>conftest.err _lt_result=$? if test -f libconftest.dylib && test ! -s conftest.err && test $_lt_result = 0; then lt_cv_apple_cc_single_mod=yes else cat conftest.err >&5 fi rm -rf libconftest.dylib* rm -f conftest.* fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_apple_cc_single_mod" >&5 $as_echo "$lt_cv_apple_cc_single_mod" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -exported_symbols_list linker flag" >&5 $as_echo_n "checking for -exported_symbols_list linker flag... " >&6; } if test "${lt_cv_ld_exported_symbols_list+set}" = set; then : $as_echo_n "(cached) " >&6 else lt_cv_ld_exported_symbols_list=no save_LDFLAGS=$LDFLAGS echo "_main" > conftest.sym LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : lt_cv_ld_exported_symbols_list=yes else lt_cv_ld_exported_symbols_list=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LDFLAGS="$save_LDFLAGS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_exported_symbols_list" >&5 $as_echo "$lt_cv_ld_exported_symbols_list" >&6; } case $host_os in rhapsody* | darwin1.[012]) _lt_dar_allow_undefined='${wl}-undefined ${wl}suppress' ;; darwin1.*) _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;; darwin*) # darwin 5.x on # if running on 10.5 or later, the deployment target defaults # to the OS version, if on x86, and 10.4, the deployment # target defaults to 10.4. Don't you love it? case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in 10.0,*86*-darwin8*|10.0,*-darwin[91]*) _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;; 10.[012]*) _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;; 10.*) _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;; esac ;; esac if test "$lt_cv_apple_cc_single_mod" = "yes"; then _lt_dar_single_mod='$single_module' fi if test "$lt_cv_ld_exported_symbols_list" = "yes"; then _lt_dar_export_syms=' ${wl}-exported_symbols_list,$output_objdir/${libname}-symbols.expsym' else _lt_dar_export_syms='~$NMEDIT -s $output_objdir/${libname}-symbols.expsym ${lib}' fi if test "$DSYMUTIL" != ":"; then _lt_dsymutil='~$DSYMUTIL $lib || :' else _lt_dsymutil= fi ;; esac for ac_header in dlfcn.h do : ac_fn_c_check_header_compile "$LINENO" "dlfcn.h" "ac_cv_header_dlfcn_h" "$ac_includes_default " if test "x$ac_cv_header_dlfcn_h" = x""yes; then : cat >>confdefs.h <<_ACEOF #define HAVE_DLFCN_H 1 _ACEOF fi done ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu if test -z "$CXX"; then if test -n "$CCC"; then CXX=$CCC else if test -n "$ac_tool_prefix"; then for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_CXX+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$CXX"; then ac_cv_prog_CXX="$CXX" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_CXX="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CXX=$ac_cv_prog_CXX if test -n "$CXX"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXX" >&5 $as_echo "$CXX" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$CXX" && break done fi if test -z "$CXX"; then ac_ct_CXX=$CXX for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_ac_ct_CXX+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CXX"; then ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_CXX="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CXX=$ac_cv_prog_ac_ct_CXX if test -n "$ac_ct_CXX"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CXX" >&5 $as_echo "$ac_ct_CXX" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_CXX" && break done if test "x$ac_ct_CXX" = x; then CXX="g++" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CXX=$ac_ct_CXX fi fi fi fi # Provide some information about the compiler. $as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C++ compiler" >&5 $as_echo_n "checking whether we are using the GNU C++ compiler... " >&6; } if test "${ac_cv_cxx_compiler_gnu+set}" = set; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_compiler_gnu=yes else ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_cxx_compiler_gnu=$ac_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_compiler_gnu" >&5 $as_echo "$ac_cv_cxx_compiler_gnu" >&6; } if test $ac_compiler_gnu = yes; then GXX=yes else GXX= fi ac_test_CXXFLAGS=${CXXFLAGS+set} ac_save_CXXFLAGS=$CXXFLAGS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX accepts -g" >&5 $as_echo_n "checking whether $CXX accepts -g... " >&6; } if test "${ac_cv_prog_cxx_g+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_save_cxx_werror_flag=$ac_cxx_werror_flag ac_cxx_werror_flag=yes ac_cv_prog_cxx_g=no CXXFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_cv_prog_cxx_g=yes else CXXFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : else ac_cxx_werror_flag=$ac_save_cxx_werror_flag CXXFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_cv_prog_cxx_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cxx_werror_flag=$ac_save_cxx_werror_flag fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_g" >&5 $as_echo "$ac_cv_prog_cxx_g" >&6; } if test "$ac_test_CXXFLAGS" = set; then CXXFLAGS=$ac_save_CXXFLAGS elif test $ac_cv_prog_cxx_g = yes; then if test "$GXX" = yes; then CXXFLAGS="-g -O2" else CXXFLAGS="-g" fi else if test "$GXX" = yes; then CXXFLAGS="-O2" else CXXFLAGS= fi fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu depcc="$CXX" am_compiler_list= { $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 $as_echo_n "checking dependency style of $depcc... " >&6; } if test "${am_cv_CXX_dependencies_compiler_type+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then # We make a subdir and do the tests there. Otherwise we can end up # making bogus files that we don't know about and never remove. For # instance it was reported that on HP-UX the gcc test will end up # making a dummy file named `D' -- because `-MD' means `put the output # in D'. mkdir conftest.dir # Copy depcomp to subdir because otherwise we won't find it if we're # using a relative directory. cp "$am_depcomp" conftest.dir cd conftest.dir # We will build objects and dependencies in a subdirectory because # it helps to detect inapplicable dependency modes. For instance # both Tru64's cc and ICC support -MD to output dependencies as a # side effect of compilation, but ICC will put the dependencies in # the current directory while Tru64 will put them in the object # directory. mkdir sub am_cv_CXX_dependencies_compiler_type=none if test "$am_compiler_list" = ""; then am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` fi am__universal=false case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac for depmode in $am_compiler_list; do # Setup a source with many dependencies, because some compilers # like to wrap large dependency lists on column 80 (with \), and # we should not choose a depcomp mode which is confused by this. # # We need to recreate these files for each test, as the compiler may # overwrite some of them when testing with obscure command lines. # This happens at least with the AIX C compiler. : > sub/conftest.c for i in 1 2 3 4 5 6; do echo '#include "conftst'$i'.h"' >> sub/conftest.c # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with # Solaris 8's {/usr,}/bin/sh. touch sub/conftst$i.h done echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf # We check with `-c' and `-o' for the sake of the "dashmstdout" # mode. It turns out that the SunPro C++ compiler does not properly # handle `-M -o', and we need to detect this. Also, some Intel # versions had trouble with output in subdirs am__obj=sub/conftest.${OBJEXT-o} am__minus_obj="-o $am__obj" case $depmode in gcc) # This depmode causes a compiler race in universal mode. test "$am__universal" = false || continue ;; nosideeffect) # after this tag, mechanisms are not by side-effect, so they'll # only be used when explicitly requested if test "x$enable_dependency_tracking" = xyes; then continue else break fi ;; msvisualcpp | msvcmsys) # This compiler won't grok `-c -o', but also, the minuso test has # not run yet. These depmodes are late enough in the game, and # so weak that their functioning should not be impacted. am__obj=conftest.${OBJEXT-o} am__minus_obj= ;; none) break ;; esac if depmode=$depmode \ source=sub/conftest.c object=$am__obj \ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ >/dev/null 2>conftest.err && grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && grep $am__obj sub/conftest.Po > /dev/null 2>&1 && ${MAKE-make} -s -f confmf > /dev/null 2>&1; then # icc doesn't choke on unknown options, it will just issue warnings # or remarks (even with -Werror). So we grep stderr for any message # that says an option was ignored or not supported. # When given -MP, icc 7.0 and 7.1 complain thusly: # icc: Command line warning: ignoring option '-M'; no argument required # The diagnosis changed in icc 8.0: # icc: Command line remark: option '-MP' not supported if (grep 'ignoring option' conftest.err || grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else am_cv_CXX_dependencies_compiler_type=$depmode break fi fi done cd .. rm -rf conftest.dir else am_cv_CXX_dependencies_compiler_type=none fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CXX_dependencies_compiler_type" >&5 $as_echo "$am_cv_CXX_dependencies_compiler_type" >&6; } CXXDEPMODE=depmode=$am_cv_CXX_dependencies_compiler_type if test "x$enable_dependency_tracking" != xno \ && test "$am_cv_CXX_dependencies_compiler_type" = gcc3; then am__fastdepCXX_TRUE= am__fastdepCXX_FALSE='#' else am__fastdepCXX_TRUE='#' am__fastdepCXX_FALSE= fi if test -n "$CXX" && ( test "X$CXX" != "Xno" && ( (test "X$CXX" = "Xg++" && `g++ -v >/dev/null 2>&1` ) || (test "X$CXX" != "Xg++"))) ; then ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C++ preprocessor" >&5 $as_echo_n "checking how to run the C++ preprocessor... " >&6; } if test -z "$CXXCPP"; then if test "${ac_cv_prog_CXXCPP+set}" = set; then : $as_echo_n "(cached) " >&6 else # Double quotes because CXXCPP needs to be expanded for CXXCPP in "$CXX -E" "/lib/cpp" do ac_preproc_ok=false for ac_cxx_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_cxx_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_cxx_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : break fi done ac_cv_prog_CXXCPP=$CXXCPP fi CXXCPP=$ac_cv_prog_CXXCPP else ac_cv_prog_CXXCPP=$CXXCPP fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXXCPP" >&5 $as_echo "$CXXCPP" >&6; } ac_preproc_ok=false for ac_cxx_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_cxx_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_cxx_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} _lt_caught_CXX_error=yes; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu else _lt_caught_CXX_error=yes fi # Set options enable_dlopen=no enable_win32_dll=no # Check whether --enable-shared was given. if test "${enable_shared+set}" = set; then : enableval=$enable_shared; p=${PACKAGE-default} case $enableval in yes) enable_shared=yes ;; no) enable_shared=no ;; *) enable_shared=no # Look at the argument we got. We use all the common list separators. lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," for pkg in $enableval; do IFS="$lt_save_ifs" if test "X$pkg" = "X$p"; then enable_shared=yes fi done IFS="$lt_save_ifs" ;; esac else enable_shared=yes fi # Check whether --with-pic was given. if test "${with_pic+set}" = set; then : withval=$with_pic; pic_mode="$withval" else pic_mode=default fi test -z "$pic_mode" && pic_mode=default # Check whether --enable-fast-install was given. if test "${enable_fast_install+set}" = set; then : enableval=$enable_fast_install; p=${PACKAGE-default} case $enableval in yes) enable_fast_install=yes ;; no) enable_fast_install=no ;; *) enable_fast_install=no # Look at the argument we got. We use all the common list separators. lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," for pkg in $enableval; do IFS="$lt_save_ifs" if test "X$pkg" = "X$p"; then enable_fast_install=yes fi done IFS="$lt_save_ifs" ;; esac else enable_fast_install=yes fi # This can be used to rebuild libtool when needed LIBTOOL_DEPS="$ltmain" # Always use our own libtool. LIBTOOL='$(SHELL) $(top_builddir)/libtool' test -z "$LN_S" && LN_S="ln -s" if test -n "${ZSH_VERSION+set}" ; then setopt NO_GLOB_SUBST fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for objdir" >&5 $as_echo_n "checking for objdir... " >&6; } if test "${lt_cv_objdir+set}" = set; then : $as_echo_n "(cached) " >&6 else rm -f .libs 2>/dev/null mkdir .libs 2>/dev/null if test -d .libs; then lt_cv_objdir=.libs else # MS-DOS does not allow filenames that begin with a dot. lt_cv_objdir=_libs fi rmdir .libs 2>/dev/null fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_objdir" >&5 $as_echo "$lt_cv_objdir" >&6; } objdir=$lt_cv_objdir cat >>confdefs.h <<_ACEOF #define LT_OBJDIR "$lt_cv_objdir/" _ACEOF case $host_os in aix3*) # AIX sometimes has problems with the GCC collect2 program. For some # reason, if we set the COLLECT_NAMES environment variable, the problems # vanish in a puff of smoke. if test "X${COLLECT_NAMES+set}" != Xset; then COLLECT_NAMES= export COLLECT_NAMES fi ;; esac # Sed substitution that helps us do robust quoting. It backslashifies # metacharacters that are still active within double-quoted strings. sed_quote_subst='s/\(["`$\\]\)/\\\1/g' # Same as above, but do not quote variable references. double_quote_subst='s/\(["`\\]\)/\\\1/g' # Sed substitution to delay expansion of an escaped shell variable in a # double_quote_subst'ed string. delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g' # Sed substitution to delay expansion of an escaped single quote. delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g' # Sed substitution to avoid accidental globbing in evaled expressions no_glob_subst='s/\*/\\\*/g' # Global variables: ofile=libtool can_build_shared=yes # All known linkers require a `.a' archive for static linking (except MSVC, # which needs '.lib'). libext=a with_gnu_ld="$lt_cv_prog_gnu_ld" old_CC="$CC" old_CFLAGS="$CFLAGS" # Set sane defaults for various variables test -z "$CC" && CC=cc test -z "$LTCC" && LTCC=$CC test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS test -z "$LD" && LD=ld test -z "$ac_objext" && ac_objext=o for cc_temp in $compiler""; do case $cc_temp in compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; \-*) ;; *) break;; esac done cc_basename=`$ECHO "X$cc_temp" | $Xsed -e 's%.*/%%' -e "s%^$host_alias-%%"` # Only perform the check for file, if the check method requires it test -z "$MAGIC_CMD" && MAGIC_CMD=file case $deplibs_check_method in file_magic*) if test "$file_magic_cmd" = '$MAGIC_CMD'; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${ac_tool_prefix}file" >&5 $as_echo_n "checking for ${ac_tool_prefix}file... " >&6; } if test "${lt_cv_path_MAGIC_CMD+set}" = set; then : $as_echo_n "(cached) " >&6 else case $MAGIC_CMD in [\\/*] | ?:[\\/]*) lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path. ;; *) lt_save_MAGIC_CMD="$MAGIC_CMD" lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR ac_dummy="/usr/bin$PATH_SEPARATOR$PATH" for ac_dir in $ac_dummy; do IFS="$lt_save_ifs" test -z "$ac_dir" && ac_dir=. if test -f $ac_dir/${ac_tool_prefix}file; then lt_cv_path_MAGIC_CMD="$ac_dir/${ac_tool_prefix}file" if test -n "$file_magic_test_file"; then case $deplibs_check_method in "file_magic "*) file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` MAGIC_CMD="$lt_cv_path_MAGIC_CMD" if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | $EGREP "$file_magic_regex" > /dev/null; then : else cat <<_LT_EOF 1>&2 *** Warning: the command libtool uses to detect shared libraries, *** $file_magic_cmd, produces output that libtool cannot recognize. *** The result is that libtool may fail to recognize shared libraries *** as such. This will affect the creation of libtool libraries that *** depend on shared libraries, but programs linked with such libtool *** libraries will work regardless of this problem. Nevertheless, you *** may want to report the problem to your system manager and/or to *** bug-libtool@gnu.org _LT_EOF fi ;; esac fi break fi done IFS="$lt_save_ifs" MAGIC_CMD="$lt_save_MAGIC_CMD" ;; esac fi MAGIC_CMD="$lt_cv_path_MAGIC_CMD" if test -n "$MAGIC_CMD"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5 $as_echo "$MAGIC_CMD" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test -z "$lt_cv_path_MAGIC_CMD"; then if test -n "$ac_tool_prefix"; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for file" >&5 $as_echo_n "checking for file... " >&6; } if test "${lt_cv_path_MAGIC_CMD+set}" = set; then : $as_echo_n "(cached) " >&6 else case $MAGIC_CMD in [\\/*] | ?:[\\/]*) lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path. ;; *) lt_save_MAGIC_CMD="$MAGIC_CMD" lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR ac_dummy="/usr/bin$PATH_SEPARATOR$PATH" for ac_dir in $ac_dummy; do IFS="$lt_save_ifs" test -z "$ac_dir" && ac_dir=. if test -f $ac_dir/file; then lt_cv_path_MAGIC_CMD="$ac_dir/file" if test -n "$file_magic_test_file"; then case $deplibs_check_method in "file_magic "*) file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` MAGIC_CMD="$lt_cv_path_MAGIC_CMD" if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | $EGREP "$file_magic_regex" > /dev/null; then : else cat <<_LT_EOF 1>&2 *** Warning: the command libtool uses to detect shared libraries, *** $file_magic_cmd, produces output that libtool cannot recognize. *** The result is that libtool may fail to recognize shared libraries *** as such. This will affect the creation of libtool libraries that *** depend on shared libraries, but programs linked with such libtool *** libraries will work regardless of this problem. Nevertheless, you *** may want to report the problem to your system manager and/or to *** bug-libtool@gnu.org _LT_EOF fi ;; esac fi break fi done IFS="$lt_save_ifs" MAGIC_CMD="$lt_save_MAGIC_CMD" ;; esac fi MAGIC_CMD="$lt_cv_path_MAGIC_CMD" if test -n "$MAGIC_CMD"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5 $as_echo "$MAGIC_CMD" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi else MAGIC_CMD=: fi fi fi ;; esac # Use C for the default configuration in the libtool script lt_save_CC="$CC" ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu # Source file extension for C test sources. ac_ext=c # Object file extension for compiled C test sources. objext=o objext=$objext # Code to be used in simple compile tests lt_simple_compile_test_code="int some_variable = 0;" # Code to be used in simple link tests lt_simple_link_test_code='int main(){return(0);}' # If no C compiler was specified, use CC. LTCC=${LTCC-"$CC"} # If no C compiler flags were specified, use CFLAGS. LTCFLAGS=${LTCFLAGS-"$CFLAGS"} # Allow CC to be a program name with arguments. compiler=$CC # Save the default compiler, since it gets overwritten when the other # tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP. compiler_DEFAULT=$CC # save warnings/boilerplate of simple test code ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" >conftest.$ac_ext eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_compiler_boilerplate=`cat conftest.err` $RM conftest* ac_outfile=conftest.$ac_objext echo "$lt_simple_link_test_code" >conftest.$ac_ext eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_linker_boilerplate=`cat conftest.err` $RM -r conftest* ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... if test -n "$compiler"; then lt_prog_compiler_no_builtin_flag= if test "$GCC" = yes; then lt_prog_compiler_no_builtin_flag=' -fno-builtin' { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -fno-rtti -fno-exceptions" >&5 $as_echo_n "checking if $compiler supports -fno-rtti -fno-exceptions... " >&6; } if test "${lt_cv_prog_compiler_rtti_exceptions+set}" = set; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_rtti_exceptions=no ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-fno-rtti -fno-exceptions" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. # The option is referenced via a variable to avoid confusing sed. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:9411: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 echo "$as_me:9415: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' >conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then lt_cv_prog_compiler_rtti_exceptions=yes fi fi $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_rtti_exceptions" >&5 $as_echo "$lt_cv_prog_compiler_rtti_exceptions" >&6; } if test x"$lt_cv_prog_compiler_rtti_exceptions" = xyes; then lt_prog_compiler_no_builtin_flag="$lt_prog_compiler_no_builtin_flag -fno-rtti -fno-exceptions" else : fi fi lt_prog_compiler_wl= lt_prog_compiler_pic= lt_prog_compiler_static= { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $compiler option to produce PIC" >&5 $as_echo_n "checking for $compiler option to produce PIC... " >&6; } if test "$GCC" = yes; then lt_prog_compiler_wl='-Wl,' lt_prog_compiler_static='-static' case $host_os in aix*) # All AIX code is PIC. if test "$host_cpu" = ia64; then # AIX 5 now supports IA64 processor lt_prog_compiler_static='-Bstatic' fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support lt_prog_compiler_pic='-fPIC' ;; m68k) # FIXME: we need at least 68020 code to build shared libraries, but # adding the `-m68020' flag to GCC prevents building anything better, # like `-m68040'. lt_prog_compiler_pic='-m68020 -resident32 -malways-restore-a4' ;; esac ;; beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) # PIC is the default for these OSes. ;; mingw* | cygwin* | pw32* | os2* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). # Although the cygwin gcc ignores -fPIC, still need this for old-style # (--disable-auto-import) libraries lt_prog_compiler_pic='-DDLL_EXPORT' ;; darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files lt_prog_compiler_pic='-fno-common' ;; hpux*) # PIC is the default for 64-bit PA HP-UX, but not for 32-bit # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag # sets the default TLS model and affects inlining. case $host_cpu in hppa*64*) # +Z the default ;; *) lt_prog_compiler_pic='-fPIC' ;; esac ;; interix[3-9]*) # Interix 3.x gcc -fpic/-fPIC options generate broken code. # Instead, we relocate shared libraries at runtime. ;; msdosdjgpp*) # Just because we use GCC doesn't mean we suddenly get shared libraries # on systems that don't support them. lt_prog_compiler_can_build_shared=no enable_shared=no ;; *nto* | *qnx*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. lt_prog_compiler_pic='-fPIC -shared' ;; sysv4*MP*) if test -d /usr/nec; then lt_prog_compiler_pic=-Kconform_pic fi ;; *) lt_prog_compiler_pic='-fPIC' ;; esac else # PORTME Check for flag to pass linker flags through the system compiler. case $host_os in aix*) lt_prog_compiler_wl='-Wl,' if test "$host_cpu" = ia64; then # AIX 5 now supports IA64 processor lt_prog_compiler_static='-Bstatic' else lt_prog_compiler_static='-bnso -bI:/lib/syscalls.exp' fi ;; mingw* | cygwin* | pw32* | os2* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). lt_prog_compiler_pic='-DDLL_EXPORT' ;; hpux9* | hpux10* | hpux11*) lt_prog_compiler_wl='-Wl,' # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but # not for PA HP-UX. case $host_cpu in hppa*64*|ia64*) # +Z the default ;; *) lt_prog_compiler_pic='+Z' ;; esac # Is there a better lt_prog_compiler_static that works with the bundled CC? lt_prog_compiler_static='${wl}-a ${wl}archive' ;; irix5* | irix6* | nonstopux*) lt_prog_compiler_wl='-Wl,' # PIC (with -KPIC) is the default. lt_prog_compiler_static='-non_shared' ;; linux* | k*bsd*-gnu | kopensolaris*-gnu) case $cc_basename in # old Intel for x86_64 which still supported -KPIC. ecc*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-static' ;; # icc used to be incompatible with GCC. # ICC 10 doesn't accept -KPIC any more. icc* | ifort*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-fPIC' lt_prog_compiler_static='-static' ;; # Lahey Fortran 8.1. lf95*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='--shared' lt_prog_compiler_static='--static' ;; pgcc* | pgf77* | pgf90* | pgf95*) # Portland Group compilers (*not* the Pentium gcc compiler, # which looks to be a dead project) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-fpic' lt_prog_compiler_static='-Bstatic' ;; ccc*) lt_prog_compiler_wl='-Wl,' # All Alpha code is PIC. lt_prog_compiler_static='-non_shared' ;; xl*) # IBM XL C 8.0/Fortran 10.1 on PPC lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-qpic' lt_prog_compiler_static='-qstaticlink' ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C 5.9 lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' lt_prog_compiler_wl='-Wl,' ;; *Sun\ F*) # Sun Fortran 8.3 passes all unrecognized flags to the linker lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' lt_prog_compiler_wl='' ;; esac ;; esac ;; newsos6) lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' ;; *nto* | *qnx*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. lt_prog_compiler_pic='-fPIC -shared' ;; osf3* | osf4* | osf5*) lt_prog_compiler_wl='-Wl,' # All OSF/1 code is PIC. lt_prog_compiler_static='-non_shared' ;; rdos*) lt_prog_compiler_static='-non_shared' ;; solaris*) lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' case $cc_basename in f77* | f90* | f95*) lt_prog_compiler_wl='-Qoption ld ';; *) lt_prog_compiler_wl='-Wl,';; esac ;; sunos4*) lt_prog_compiler_wl='-Qoption ld ' lt_prog_compiler_pic='-PIC' lt_prog_compiler_static='-Bstatic' ;; sysv4 | sysv4.2uw2* | sysv4.3*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' ;; sysv4*MP*) if test -d /usr/nec ;then lt_prog_compiler_pic='-Kconform_pic' lt_prog_compiler_static='-Bstatic' fi ;; sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' ;; unicos*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_can_build_shared=no ;; uts4*) lt_prog_compiler_pic='-pic' lt_prog_compiler_static='-Bstatic' ;; *) lt_prog_compiler_can_build_shared=no ;; esac fi case $host_os in # For platforms which do not support PIC, -DPIC is meaningless: *djgpp*) lt_prog_compiler_pic= ;; *) lt_prog_compiler_pic="$lt_prog_compiler_pic -DPIC" ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_prog_compiler_pic" >&5 $as_echo "$lt_prog_compiler_pic" >&6; } # # Check to make sure the PIC flag actually works. # if test -n "$lt_prog_compiler_pic"; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler PIC flag $lt_prog_compiler_pic works" >&5 $as_echo_n "checking if $compiler PIC flag $lt_prog_compiler_pic works... " >&6; } if test "${lt_cv_prog_compiler_pic_works+set}" = set; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_pic_works=no ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="$lt_prog_compiler_pic -DPIC" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. # The option is referenced via a variable to avoid confusing sed. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:9750: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 echo "$as_me:9754: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' >conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then lt_cv_prog_compiler_pic_works=yes fi fi $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works" >&5 $as_echo "$lt_cv_prog_compiler_pic_works" >&6; } if test x"$lt_cv_prog_compiler_pic_works" = xyes; then case $lt_prog_compiler_pic in "" | " "*) ;; *) lt_prog_compiler_pic=" $lt_prog_compiler_pic" ;; esac else lt_prog_compiler_pic= lt_prog_compiler_can_build_shared=no fi fi # # Check to make sure the static flag actually works. # wl=$lt_prog_compiler_wl eval lt_tmp_static_flag=\"$lt_prog_compiler_static\" { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler static flag $lt_tmp_static_flag works" >&5 $as_echo_n "checking if $compiler static flag $lt_tmp_static_flag works... " >&6; } if test "${lt_cv_prog_compiler_static_works+set}" = set; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_static_works=no save_LDFLAGS="$LDFLAGS" LDFLAGS="$LDFLAGS $lt_tmp_static_flag" echo "$lt_simple_link_test_code" > conftest.$ac_ext if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then # The linker can only warn and ignore the option if not recognized # So say no if there are warnings if test -s conftest.err; then # Append any errors to the config.log. cat conftest.err 1>&5 $ECHO "X$_lt_linker_boilerplate" | $Xsed -e '/^$/d' > conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if diff conftest.exp conftest.er2 >/dev/null; then lt_cv_prog_compiler_static_works=yes fi else lt_cv_prog_compiler_static_works=yes fi fi $RM -r conftest* LDFLAGS="$save_LDFLAGS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works" >&5 $as_echo "$lt_cv_prog_compiler_static_works" >&6; } if test x"$lt_cv_prog_compiler_static_works" = xyes; then : else lt_prog_compiler_static= fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 $as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } if test "${lt_cv_prog_compiler_c_o+set}" = set; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_c_o=no $RM -r conftest 2>/dev/null mkdir conftest cd conftest mkdir out echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-o out/conftest2.$ac_objext" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:9855: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 echo "$as_me:9859: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' > out/conftest.exp $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then lt_cv_prog_compiler_c_o=yes fi fi chmod u+w . 2>&5 $RM conftest* # SGI C++ compiler will create directory out/ii_files/ for # template instantiation test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files $RM out/* && rmdir out cd .. $RM -r conftest $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5 $as_echo "$lt_cv_prog_compiler_c_o" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 $as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } if test "${lt_cv_prog_compiler_c_o+set}" = set; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_c_o=no $RM -r conftest 2>/dev/null mkdir conftest cd conftest mkdir out echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-o out/conftest2.$ac_objext" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:9910: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 echo "$as_me:9914: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' > out/conftest.exp $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then lt_cv_prog_compiler_c_o=yes fi fi chmod u+w . 2>&5 $RM conftest* # SGI C++ compiler will create directory out/ii_files/ for # template instantiation test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files $RM out/* && rmdir out cd .. $RM -r conftest $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5 $as_echo "$lt_cv_prog_compiler_c_o" >&6; } hard_links="nottested" if test "$lt_cv_prog_compiler_c_o" = no && test "$need_locks" != no; then # do not overwrite the value of need_locks provided by the user { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we can lock with hard links" >&5 $as_echo_n "checking if we can lock with hard links... " >&6; } hard_links=yes $RM conftest* ln conftest.a conftest.b 2>/dev/null && hard_links=no touch conftest.a ln conftest.a conftest.b 2>&5 || hard_links=no ln conftest.a conftest.b 2>/dev/null && hard_links=no { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hard_links" >&5 $as_echo "$hard_links" >&6; } if test "$hard_links" = no; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&5 $as_echo "$as_me: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&2;} need_locks=warn fi else need_locks=no fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5 $as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; } runpath_var= allow_undefined_flag= always_export_symbols=no archive_cmds= archive_expsym_cmds= compiler_needs_object=no enable_shared_with_static_runtimes=no export_dynamic_flag_spec= export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' hardcode_automatic=no hardcode_direct=no hardcode_direct_absolute=no hardcode_libdir_flag_spec= hardcode_libdir_flag_spec_ld= hardcode_libdir_separator= hardcode_minus_L=no hardcode_shlibpath_var=unsupported inherit_rpath=no link_all_deplibs=unknown module_cmds= module_expsym_cmds= old_archive_from_new_cmds= old_archive_from_expsyms_cmds= thread_safe_flag_spec= whole_archive_flag_spec= # include_expsyms should be a list of space-separated symbols to be *always* # included in the symbol list include_expsyms= # exclude_expsyms can be an extended regexp of symbols to exclude # it will be wrapped by ` (' and `)$', so one must not match beginning or # end of line. Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc', # as well as any symbol that contains `d'. exclude_expsyms='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*' # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out # platforms (ab)use it in PIC code, but their linkers get confused if # the symbol is explicitly referenced. Since portable code cannot # rely on this symbol name, it's probably fine to never include it in # preloaded symbol tables. # Exclude shared library initialization/finalization symbols. extract_expsyms_cmds= case $host_os in cygwin* | mingw* | pw32* | cegcc*) # FIXME: the MSVC++ port hasn't been tested in a loooong time # When not using gcc, we currently assume that we are using # Microsoft Visual C++. if test "$GCC" != yes; then with_gnu_ld=no fi ;; interix*) # we just hope/assume this is gcc and not c89 (= MSVC++) with_gnu_ld=yes ;; openbsd*) with_gnu_ld=no ;; linux* | k*bsd*-gnu) link_all_deplibs=no ;; esac ld_shlibs=yes if test "$with_gnu_ld" = yes; then # If archive_cmds runs LD, not CC, wlarc should be empty wlarc='${wl}' # Set some defaults for GNU ld with shared library support. These # are reset later if shared libraries are not supported. Putting them # here allows them to be overridden if necessary. runpath_var=LD_RUN_PATH hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' export_dynamic_flag_spec='${wl}--export-dynamic' # ancient GNU ld didn't support --whole-archive et. al. if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then whole_archive_flag_spec="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' else whole_archive_flag_spec= fi supports_anon_versioning=no case `$LD -v 2>&1` in *GNU\ gold*) supports_anon_versioning=yes ;; *\ [01].* | *\ 2.[0-9].* | *\ 2.10.*) ;; # catch versions < 2.11 *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ... *\ 2.11.*) ;; # other 2.11 versions *) supports_anon_versioning=yes ;; esac # See if GNU ld supports shared libraries. case $host_os in aix[3-9]*) # On AIX/PPC, the GNU linker is very broken if test "$host_cpu" != ia64; then ld_shlibs=no cat <<_LT_EOF 1>&2 *** Warning: the GNU linker, at least up to release 2.9.1, is reported *** to be unable to reliably create shared libraries on AIX. *** Therefore, libtool is disabling shared libraries support. If you *** really care for shared libraries, you may want to modify your PATH *** so that a non-GNU linker is found, and then restart. _LT_EOF fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' archive_expsym_cmds='' ;; m68k) archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes ;; esac ;; beos*) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then allow_undefined_flag=unsupported # Joseph Beckenbach says some releases of gcc # support --undefined. This deserves some investigation. FIXME archive_cmds='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' else ld_shlibs=no fi ;; cygwin* | mingw* | pw32* | cegcc*) # _LT_TAGVAR(hardcode_libdir_flag_spec, ) is actually meaningless, # as there is no search path for DLLs. hardcode_libdir_flag_spec='-L$libdir' allow_undefined_flag=unsupported always_export_symbols=no enable_shared_with_static_runtimes=yes export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/'\'' | $SED -e '\''/^[AITW][ ]/s/.*[ ]//'\'' | sort | uniq > $export_symbols' if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' # If the export-symbols file already is a .def file (1st line # is EXPORTS), use it as is; otherwise, prepend... archive_expsym_cmds='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then cp $export_symbols $output_objdir/$soname.def; else echo EXPORTS > $output_objdir/$soname.def; cat $export_symbols >> $output_objdir/$soname.def; fi~ $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' else ld_shlibs=no fi ;; interix[3-9]*) hardcode_direct=no hardcode_shlibpath_var=no hardcode_libdir_flag_spec='${wl}-rpath,$libdir' export_dynamic_flag_spec='${wl}-E' # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. # Instead, shared libraries are loaded at an image base (0x10000000 by # default) and relocated if they conflict, which is a slow very memory # consuming and fragmenting process. To avoid this, we pick a random, # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link # time. Moving up from 0x10000000 also allows more sbrk(2) space. archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' archive_expsym_cmds='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' ;; gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu) tmp_diet=no if test "$host_os" = linux-dietlibc; then case $cc_basename in diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn) esac fi if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \ && test "$tmp_diet" = no then tmp_addflag= tmp_sharedflag='-shared' case $cc_basename,$host_cpu in pgcc*) # Portland Group C compiler whole_archive_flag_spec='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive' tmp_addflag=' $pic_flag' ;; pgf77* | pgf90* | pgf95*) # Portland Group f77 and f90 compilers whole_archive_flag_spec='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive' tmp_addflag=' $pic_flag -Mnomain' ;; ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64 tmp_addflag=' -i_dynamic' ;; efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64 tmp_addflag=' -i_dynamic -nofor_main' ;; ifc* | ifort*) # Intel Fortran compiler tmp_addflag=' -nofor_main' ;; lf95*) # Lahey Fortran 8.1 whole_archive_flag_spec= tmp_sharedflag='--shared' ;; xl[cC]*) # IBM XL C 8.0 on PPC (deal with xlf below) tmp_sharedflag='-qmkshrobj' tmp_addflag= ;; esac case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C 5.9 whole_archive_flag_spec='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive' compiler_needs_object=yes tmp_sharedflag='-G' ;; *Sun\ F*) # Sun Fortran 8.3 tmp_sharedflag='-G' ;; esac archive_cmds='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' if test "x$supports_anon_versioning" = xyes; then archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' fi case $cc_basename in xlf*) # IBM XL Fortran 10.1 on PPC cannot create shared libs itself whole_archive_flag_spec='--whole-archive$convenience --no-whole-archive' hardcode_libdir_flag_spec= hardcode_libdir_flag_spec_ld='-rpath $libdir' archive_cmds='$LD -shared $libobjs $deplibs $compiler_flags -soname $soname -o $lib' if test "x$supports_anon_versioning" = xyes; then archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $LD -shared $libobjs $deplibs $compiler_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib' fi ;; esac else ld_shlibs=no fi ;; netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then archive_cmds='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' wlarc= else archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' fi ;; solaris*) if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then ld_shlibs=no cat <<_LT_EOF 1>&2 *** Warning: The releases 2.8.* of the GNU linker cannot reliably *** create shared libraries on Solaris systems. Therefore, libtool *** is disabling shared libraries support. We urge you to upgrade GNU *** binutils to release 2.9.1 or newer. Another option is to modify *** your PATH or compiler configuration so that the native linker is *** used, and then restart. _LT_EOF elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' else ld_shlibs=no fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) case `$LD -v 2>&1` in *\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*) ld_shlibs=no cat <<_LT_EOF 1>&2 *** Warning: Releases of the GNU linker prior to 2.16.91.0.3 can not *** reliably create shared libraries on SCO systems. Therefore, libtool *** is disabling shared libraries support. We urge you to upgrade GNU *** binutils to release 2.16.91.0.3 or newer. Another option is to modify *** your PATH or compiler configuration so that the native linker is *** used, and then restart. _LT_EOF ;; *) # For security reasons, it is highly recommended that you always # use absolute paths for naming shared libraries, and exclude the # DT_RUNPATH tag from executables and libraries. But doing so # requires that you compile everything twice, which is a pain. if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' else ld_shlibs=no fi ;; esac ;; sunos4*) archive_cmds='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' wlarc= hardcode_direct=yes hardcode_shlibpath_var=no ;; *) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' else ld_shlibs=no fi ;; esac if test "$ld_shlibs" = no; then runpath_var= hardcode_libdir_flag_spec= export_dynamic_flag_spec= whole_archive_flag_spec= fi else # PORTME fill in a description of your system's linker (not GNU ld) case $host_os in aix3*) allow_undefined_flag=unsupported always_export_symbols=yes archive_expsym_cmds='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' # Note: this linker hardcodes the directories in LIBPATH if there # are no directories specified by -L. hardcode_minus_L=yes if test "$GCC" = yes && test -z "$lt_prog_compiler_static"; then # Neither direct hardcoding nor static linking is supported with a # broken collect2. hardcode_direct=unsupported fi ;; aix[4-9]*) if test "$host_cpu" = ia64; then # On IA64, the linker does run time linking by default, so we don't # have to do anything special. aix_use_runtimelinking=no exp_sym_flag='-Bexport' no_entry_flag="" else # If we're using GNU nm, then we don't want the "-C" option. # -C means demangle to AIX nm, but means don't demangle with GNU nm if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then export_symbols_cmds='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' else export_symbols_cmds='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' fi aix_use_runtimelinking=no # Test if we are trying to use run time linking or normal # AIX style linking. If -brtl is somewhere in LDFLAGS, we # need to do runtime linking. case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*) for ld_flag in $LDFLAGS; do if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then aix_use_runtimelinking=yes break fi done ;; esac exp_sym_flag='-bexport' no_entry_flag='-bnoentry' fi # When large executables or shared objects are built, AIX ld can # have problems creating the table of contents. If linking a library # or program results in "error TOC overflow" add -mminimal-toc to # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. archive_cmds='' hardcode_direct=yes hardcode_direct_absolute=yes hardcode_libdir_separator=':' link_all_deplibs=yes file_list_spec='${wl}-f,' if test "$GCC" = yes; then case $host_os in aix4.[012]|aix4.[012].*) # We only want to do this on AIX 4.2 and lower, the check # below for broken collect2 doesn't work under 4.3+ collect2name=`${CC} -print-prog-name=collect2` if test -f "$collect2name" && strings "$collect2name" | $GREP resolve_lib_name >/dev/null then # We have reworked collect2 : else # We have old collect2 hardcode_direct=unsupported # It fails to find uninstalled libraries when the uninstalled # path is not listed in the libpath. Setting hardcode_minus_L # to unsupported forces relinking hardcode_minus_L=yes hardcode_libdir_flag_spec='-L$libdir' hardcode_libdir_separator= fi ;; esac shared_flag='-shared' if test "$aix_use_runtimelinking" = yes; then shared_flag="$shared_flag "'${wl}-G' fi link_all_deplibs=no else # not using gcc if test "$host_cpu" = ia64; then # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release # chokes on -Wl,-G. The following line is correct: shared_flag='-G' else if test "$aix_use_runtimelinking" = yes; then shared_flag='${wl}-G' else shared_flag='${wl}-bM:SRE' fi fi fi export_dynamic_flag_spec='${wl}-bexpall' # It seems that -bexpall does not export symbols beginning with # underscore (_), so it is better to generate a list of symbols to export. always_export_symbols=yes if test "$aix_use_runtimelinking" = yes; then # Warning - without using the other runtime loading flags (-brtl), # -berok will link without error, but may produce a broken library. allow_undefined_flag='-berok' # Determine the default libpath from the value encoded in an # empty executable. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : lt_aix_libpath_sed=' /Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/ p } }' aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` # Check for a 64-bit object if we didn't find anything. if test -z "$aix_libpath"; then aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` fi fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath" archive_expsym_cmds='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then $ECHO "X${wl}${allow_undefined_flag}" | $Xsed; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" else if test "$host_cpu" = ia64; then hardcode_libdir_flag_spec='${wl}-R $libdir:/usr/lib:/lib' allow_undefined_flag="-z nodefs" archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" else # Determine the default libpath from the value encoded in an # empty executable. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : lt_aix_libpath_sed=' /Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/ p } }' aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` # Check for a 64-bit object if we didn't find anything. if test -z "$aix_libpath"; then aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` fi fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath" # Warning - without using the other run time loading flags, # -berok will link without error, but may produce a broken library. no_undefined_flag=' ${wl}-bernotok' allow_undefined_flag=' ${wl}-berok' # Exported symbols can be pulled into shared objects from archives whole_archive_flag_spec='$convenience' archive_cmds_need_lc=yes # This is similar to how AIX traditionally builds its shared libraries. archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' fi fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' archive_expsym_cmds='' ;; m68k) archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes ;; esac ;; bsdi[45]*) export_dynamic_flag_spec=-rdynamic ;; cygwin* | mingw* | pw32* | cegcc*) # When not using gcc, we currently assume that we are using # Microsoft Visual C++. # hardcode_libdir_flag_spec is actually meaningless, as there is # no search path for DLLs. hardcode_libdir_flag_spec=' ' allow_undefined_flag=unsupported # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=".dll" # FIXME: Setting linknames here is a bad hack. archive_cmds='$CC -o $lib $libobjs $compiler_flags `$ECHO "X$deplibs" | $Xsed -e '\''s/ -lc$//'\''` -link -dll~linknames=' # The linker will automatically build a .lib file if we build a DLL. old_archive_from_new_cmds='true' # FIXME: Should let the user specify the lib program. old_archive_cmds='lib -OUT:$oldlib$oldobjs$old_deplibs' fix_srcfile_path='`cygpath -w "$srcfile"`' enable_shared_with_static_runtimes=yes ;; darwin* | rhapsody*) archive_cmds_need_lc=no hardcode_direct=no hardcode_automatic=yes hardcode_shlibpath_var=unsupported whole_archive_flag_spec='' link_all_deplibs=yes allow_undefined_flag="$_lt_dar_allow_undefined" case $cc_basename in ifort*) _lt_dar_can_shared=yes ;; *) _lt_dar_can_shared=$GCC ;; esac if test "$_lt_dar_can_shared" = "yes"; then output_verbose_link_cmd=echo archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" else ld_shlibs=no fi ;; dgux*) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_libdir_flag_spec='-L$libdir' hardcode_shlibpath_var=no ;; freebsd1*) ld_shlibs=no ;; # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor # support. Future versions do this automatically, but an explicit c++rt0.o # does not break anything, and helps significantly (at the cost of a little # extra space). freebsd2.2*) archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' hardcode_libdir_flag_spec='-R$libdir' hardcode_direct=yes hardcode_shlibpath_var=no ;; # Unfortunately, older versions of FreeBSD 2 do not have this feature. freebsd2*) archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' hardcode_direct=yes hardcode_minus_L=yes hardcode_shlibpath_var=no ;; # FreeBSD 3 and greater uses gcc -shared to do shared libraries. freebsd* | dragonfly*) archive_cmds='$CC -shared -o $lib $libobjs $deplibs $compiler_flags' hardcode_libdir_flag_spec='-R$libdir' hardcode_direct=yes hardcode_shlibpath_var=no ;; hpux9*) if test "$GCC" = yes; then archive_cmds='$RM $output_objdir/$soname~$CC -shared -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' else archive_cmds='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' fi hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' hardcode_libdir_separator=: hardcode_direct=yes # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. hardcode_minus_L=yes export_dynamic_flag_spec='${wl}-E' ;; hpux10*) if test "$GCC" = yes -a "$with_gnu_ld" = no; then archive_cmds='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' else archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' fi if test "$with_gnu_ld" = no; then hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' hardcode_libdir_flag_spec_ld='+b $libdir' hardcode_libdir_separator=: hardcode_direct=yes hardcode_direct_absolute=yes export_dynamic_flag_spec='${wl}-E' # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. hardcode_minus_L=yes fi ;; hpux11*) if test "$GCC" = yes -a "$with_gnu_ld" = no; then case $host_cpu in hppa*64*) archive_cmds='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' ;; ia64*) archive_cmds='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' ;; *) archive_cmds='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' ;; esac else case $host_cpu in hppa*64*) archive_cmds='$CC -b ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' ;; ia64*) archive_cmds='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' ;; *) archive_cmds='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' ;; esac fi if test "$with_gnu_ld" = no; then hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' hardcode_libdir_separator=: case $host_cpu in hppa*64*|ia64*) hardcode_direct=no hardcode_shlibpath_var=no ;; *) hardcode_direct=yes hardcode_direct_absolute=yes export_dynamic_flag_spec='${wl}-E' # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. hardcode_minus_L=yes ;; esac fi ;; irix5* | irix6* | nonstopux*) if test "$GCC" = yes; then archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' # Try to use the -exported_symbol ld option, if it does not # work, assume that -exports_file does not work either and # implicitly export all symbols. save_LDFLAGS="$LDFLAGS" LDFLAGS="$LDFLAGS -shared ${wl}-exported_symbol ${wl}foo ${wl}-update_registry ${wl}/dev/null" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int foo(void) {} _ACEOF if ac_fn_c_try_link "$LINENO"; then : archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations ${wl}-exports_file ${wl}$export_symbols -o $lib' fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LDFLAGS="$save_LDFLAGS" else archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib' archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -exports_file $export_symbols -o $lib' fi archive_cmds_need_lc='no' hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' hardcode_libdir_separator=: inherit_rpath=yes link_all_deplibs=yes ;; netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out else archive_cmds='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF fi hardcode_libdir_flag_spec='-R$libdir' hardcode_direct=yes hardcode_shlibpath_var=no ;; newsos6) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_direct=yes hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' hardcode_libdir_separator=: hardcode_shlibpath_var=no ;; *nto* | *qnx*) ;; openbsd*) if test -f /usr/libexec/ld.so; then hardcode_direct=yes hardcode_shlibpath_var=no hardcode_direct_absolute=yes if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-retain-symbols-file,$export_symbols' hardcode_libdir_flag_spec='${wl}-rpath,$libdir' export_dynamic_flag_spec='${wl}-E' else case $host_os in openbsd[01].* | openbsd2.[0-7] | openbsd2.[0-7].*) archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' hardcode_libdir_flag_spec='-R$libdir' ;; *) archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' hardcode_libdir_flag_spec='${wl}-rpath,$libdir' ;; esac fi else ld_shlibs=no fi ;; os2*) hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes allow_undefined_flag=unsupported archive_cmds='$ECHO "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~$ECHO DATA >> $output_objdir/$libname.def~$ECHO " SINGLE NONSHARED" >> $output_objdir/$libname.def~$ECHO EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def' old_archive_from_new_cmds='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def' ;; osf3*) if test "$GCC" = yes; then allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*' archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' else allow_undefined_flag=' -expect_unresolved \*' archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib' fi archive_cmds_need_lc='no' hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' hardcode_libdir_separator=: ;; osf4* | osf5*) # as osf3* with the addition of -msym flag if test "$GCC" = yes; then allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*' archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' else allow_undefined_flag=' -expect_unresolved \*' archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib' archive_expsym_cmds='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~ $CC -shared${allow_undefined_flag} ${wl}-input ${wl}$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib~$RM $lib.exp' # Both c and cxx compiler support -rpath directly hardcode_libdir_flag_spec='-rpath $libdir' fi archive_cmds_need_lc='no' hardcode_libdir_separator=: ;; solaris*) no_undefined_flag=' -z defs' if test "$GCC" = yes; then wlarc='${wl}' archive_cmds='$CC -shared ${wl}-z ${wl}text ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -shared ${wl}-z ${wl}text ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' else case `$CC -V 2>&1` in *"Compilers 5.0"*) wlarc='' archive_cmds='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags' archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp' ;; *) wlarc='${wl}' archive_cmds='$CC -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' ;; esac fi hardcode_libdir_flag_spec='-R$libdir' hardcode_shlibpath_var=no case $host_os in solaris2.[0-5] | solaris2.[0-5].*) ;; *) # The compiler driver will combine and reorder linker options, # but understands `-z linker_flag'. GCC discards it without `$wl', # but is careful enough not to reorder. # Supported since Solaris 2.6 (maybe 2.5.1?) if test "$GCC" = yes; then whole_archive_flag_spec='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract' else whole_archive_flag_spec='-z allextract$convenience -z defaultextract' fi ;; esac link_all_deplibs=yes ;; sunos4*) if test "x$host_vendor" = xsequent; then # Use $CC to link under sequent, because it throws in some extra .o # files that make .init and .fini sections work. archive_cmds='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags' else archive_cmds='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' fi hardcode_libdir_flag_spec='-L$libdir' hardcode_direct=yes hardcode_minus_L=yes hardcode_shlibpath_var=no ;; sysv4) case $host_vendor in sni) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_direct=yes # is this really true??? ;; siemens) ## LD is ld it makes a PLAMLIB ## CC just makes a GrossModule. archive_cmds='$LD -G -o $lib $libobjs $deplibs $linker_flags' reload_cmds='$CC -r -o $output$reload_objs' hardcode_direct=no ;; motorola) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_direct=no #Motorola manual says yes, but my tests say they lie ;; esac runpath_var='LD_RUN_PATH' hardcode_shlibpath_var=no ;; sysv4.3*) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_shlibpath_var=no export_dynamic_flag_spec='-Bexport' ;; sysv4*MP*) if test -d /usr/nec; then archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_shlibpath_var=no runpath_var=LD_RUN_PATH hardcode_runpath_var=yes ld_shlibs=yes fi ;; sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*) no_undefined_flag='${wl}-z,text' archive_cmds_need_lc=no hardcode_shlibpath_var=no runpath_var='LD_RUN_PATH' if test "$GCC" = yes; then archive_cmds='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' else archive_cmds='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' fi ;; sysv5* | sco3.2v5* | sco5v6*) # Note: We can NOT use -z defs as we might desire, because we do not # link with -lc, and that would cause any symbols used from libc to # always be unresolved, which means just about no library would # ever link correctly. If we're not using GNU ld we use -z text # though, which does catch some bad symbols but isn't as heavy-handed # as -z defs. no_undefined_flag='${wl}-z,text' allow_undefined_flag='${wl}-z,nodefs' archive_cmds_need_lc=no hardcode_shlibpath_var=no hardcode_libdir_flag_spec='${wl}-R,$libdir' hardcode_libdir_separator=':' link_all_deplibs=yes export_dynamic_flag_spec='${wl}-Bexport' runpath_var='LD_RUN_PATH' if test "$GCC" = yes; then archive_cmds='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' else archive_cmds='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' fi ;; uts4*) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_libdir_flag_spec='-L$libdir' hardcode_shlibpath_var=no ;; *) ld_shlibs=no ;; esac if test x$host_vendor = xsni; then case $host in sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) export_dynamic_flag_spec='${wl}-Blargedynsym' ;; esac fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs" >&5 $as_echo "$ld_shlibs" >&6; } test "$ld_shlibs" = no && can_build_shared=no with_gnu_ld=$with_gnu_ld # # Do we need to explicitly link libc? # case "x$archive_cmds_need_lc" in x|xyes) # Assume -lc should be added archive_cmds_need_lc=yes if test "$enable_shared" = yes && test "$GCC" = yes; then case $archive_cmds in *'~'*) # FIXME: we may have to deal with multi-command sequences. ;; '$CC '*) # Test whether the compiler implicitly links with -lc since on some # systems, -lgcc has to come before -lc. If gcc already passes -lc # to ld, don't add -lc before -lgcc. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -lc should be explicitly linked in" >&5 $as_echo_n "checking whether -lc should be explicitly linked in... " >&6; } $RM conftest* echo "$lt_simple_compile_test_code" > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } 2>conftest.err; then soname=conftest lib=conftest libobjs=conftest.$ac_objext deplibs= wl=$lt_prog_compiler_wl pic_flag=$lt_prog_compiler_pic compiler_flags=-v linker_flags=-v verstring= output_objdir=. libname=conftest lt_save_allow_undefined_flag=$allow_undefined_flag allow_undefined_flag= if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1\""; } >&5 (eval $archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } then archive_cmds_need_lc=no else archive_cmds_need_lc=yes fi allow_undefined_flag=$lt_save_allow_undefined_flag else cat conftest.err 1>&5 fi $RM conftest* { $as_echo "$as_me:${as_lineno-$LINENO}: result: $archive_cmds_need_lc" >&5 $as_echo "$archive_cmds_need_lc" >&6; } ;; esac fi ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking dynamic linker characteristics" >&5 $as_echo_n "checking dynamic linker characteristics... " >&6; } if test "$GCC" = yes; then case $host_os in darwin*) lt_awk_arg="/^libraries:/,/LR/" ;; *) lt_awk_arg="/^libraries:/" ;; esac lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e "s,=/,/,g"` if $ECHO "$lt_search_path_spec" | $GREP ';' >/dev/null ; then # if the path contains ";" then we assume it to be the separator # otherwise default to the standard path separator (i.e. ":") - it is # assumed that no part of a normal pathname contains ";" but that should # okay in the real world where ";" in dirpaths is itself problematic. lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED -e 's/;/ /g'` else lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` fi # Ok, now we have the path, separated by spaces, we can step through it # and add multilib dir if necessary. lt_tmp_lt_search_path_spec= lt_multi_os_dir=`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null` for lt_sys_path in $lt_search_path_spec; do if test -d "$lt_sys_path/$lt_multi_os_dir"; then lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path/$lt_multi_os_dir" else test -d "$lt_sys_path" && \ lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path" fi done lt_search_path_spec=`$ECHO $lt_tmp_lt_search_path_spec | awk ' BEGIN {RS=" "; FS="/|\n";} { lt_foo=""; lt_count=0; for (lt_i = NF; lt_i > 0; lt_i--) { if ($lt_i != "" && $lt_i != ".") { if ($lt_i == "..") { lt_count++; } else { if (lt_count == 0) { lt_foo="/" $lt_i lt_foo; } else { lt_count--; } } } } if (lt_foo != "") { lt_freq[lt_foo]++; } if (lt_freq[lt_foo] == 1) { print lt_foo; } }'` sys_lib_search_path_spec=`$ECHO $lt_search_path_spec` else sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" fi library_names_spec= libname_spec='lib$name' soname_spec= shrext_cmds=".so" postinstall_cmds= postuninstall_cmds= finish_cmds= finish_eval= shlibpath_var= shlibpath_overrides_runpath=unknown version_type=none dynamic_linker="$host_os ld.so" sys_lib_dlsearch_path_spec="/lib /usr/lib" need_lib_prefix=unknown hardcode_into_libs=no # when you set need_version to no, make sure it does not cause -set_version # flags to be left without arguments need_version=unknown case $host_os in aix3*) version_type=linux library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a' shlibpath_var=LIBPATH # AIX 3 has no versioning support, so we append a major version to the name. soname_spec='${libname}${release}${shared_ext}$major' ;; aix[4-9]*) version_type=linux need_lib_prefix=no need_version=no hardcode_into_libs=yes if test "$host_cpu" = ia64; then # AIX 5 supports IA64 library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}' shlibpath_var=LD_LIBRARY_PATH else # With GCC up to 2.95.x, collect2 would create an import file # for dependence libraries. The import file would start with # the line `#! .'. This would cause the generated library to # depend on `.', always an invalid library. This was fixed in # development snapshots of GCC prior to 3.0. case $host_os in aix4 | aix4.[01] | aix4.[01].*) if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' echo ' yes ' echo '#endif'; } | ${CC} -E - | $GREP yes > /dev/null; then : else can_build_shared=no fi ;; esac # AIX (on Power*) has no versioning support, so currently we can not hardcode correct # soname into executable. Probably we can add versioning support to # collect2, so additional links can be useful in future. if test "$aix_use_runtimelinking" = yes; then # If using run time linking (on AIX 4.2 or later) use lib.so # instead of lib.a to let people know that these are not # typical AIX shared libraries. library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' else # We preserve .a as extension for shared libraries through AIX4.2 # and later when we are not doing run time linking. library_names_spec='${libname}${release}.a $libname.a' soname_spec='${libname}${release}${shared_ext}$major' fi shlibpath_var=LIBPATH fi ;; amigaos*) case $host_cpu in powerpc) # Since July 2007 AmigaOS4 officially supports .so libraries. # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' ;; m68k) library_names_spec='$libname.ixlibrary $libname.a' # Create ${libname}_ixlibrary.a entries in /sys/libs. finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`$ECHO "X$lib" | $Xsed -e '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; test $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' ;; esac ;; beos*) library_names_spec='${libname}${shared_ext}' dynamic_linker="$host_os ld.so" shlibpath_var=LIBRARY_PATH ;; bsdi[45]*) version_type=linux need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" # the default ld.so.conf also contains /usr/contrib/lib and # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow # libtool to hard-code these into programs ;; cygwin* | mingw* | pw32* | cegcc*) version_type=windows shrext_cmds=".dll" need_version=no need_lib_prefix=no case $GCC,$host_os in yes,cygwin* | yes,mingw* | yes,pw32* | yes,cegcc*) library_names_spec='$libname.dll.a' # DLL is installed to $(libdir)/../bin by postinstall_cmds postinstall_cmds='base_file=`basename \${file}`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname~ chmod a+x \$dldir/$dlname~ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; fi' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' shlibpath_overrides_runpath=yes case $host_os in cygwin*) # Cygwin DLLs use 'cyg' prefix rather than 'lib' soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' sys_lib_search_path_spec="/usr/lib /lib/w32api /lib /usr/local/lib" ;; mingw* | cegcc*) # MinGW DLLs use traditional 'lib' prefix soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' sys_lib_search_path_spec=`$CC -print-search-dirs | $GREP "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"` if $ECHO "$sys_lib_search_path_spec" | $GREP ';[c-zC-Z]:/' >/dev/null; then # It is most probably a Windows format PATH printed by # mingw gcc, but we are running on Cygwin. Gcc prints its search # path with ; separators, and with drive letters. We can handle the # drive letters (cygwin fileutils understands them), so leave them, # especially as we might pass files found there to a mingw objdump, # which wouldn't understand a cygwinified path. Ahh. sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` else sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` fi ;; pw32*) # pw32 DLLs use 'pw' prefix rather than 'lib' library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' ;; esac ;; *) library_names_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext} $libname.lib' ;; esac dynamic_linker='Win32 ld.exe' # FIXME: first we should search . and the directory the executable is in shlibpath_var=PATH ;; darwin* | rhapsody*) dynamic_linker="$host_os dyld" version_type=darwin need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${major}$shared_ext ${libname}$shared_ext' soname_spec='${libname}${release}${major}$shared_ext' shlibpath_overrides_runpath=yes shlibpath_var=DYLD_LIBRARY_PATH shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib" sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' ;; dgux*) version_type=linux need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH ;; freebsd1*) dynamic_linker=no ;; freebsd* | dragonfly*) # DragonFly does not have aout. When/if they implement a new # versioning mechanism, adjust this. if test -x /usr/bin/objformat; then objformat=`/usr/bin/objformat` else case $host_os in freebsd[123]*) objformat=aout ;; *) objformat=elf ;; esac fi version_type=freebsd-$objformat case $version_type in freebsd-elf*) library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' need_version=no need_lib_prefix=no ;; freebsd-*) library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix' need_version=yes ;; esac shlibpath_var=LD_LIBRARY_PATH case $host_os in freebsd2*) shlibpath_overrides_runpath=yes ;; freebsd3.[01]* | freebsdelf3.[01]*) shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; freebsd3.[2-9]* | freebsdelf3.[2-9]* | \ freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1) shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; *) # from 4.6 on, and DragonFly shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; esac ;; gnu*) version_type=linux need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH hardcode_into_libs=yes ;; hpux9* | hpux10* | hpux11*) # Give a soname corresponding to the major version so that dld.sl refuses to # link against other versions. version_type=sunos need_lib_prefix=no need_version=no case $host_cpu in ia64*) shrext_cmds='.so' hardcode_into_libs=yes dynamic_linker="$host_os dld.so" shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' if test "X$HPUX_IA64_MODE" = X32; then sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" else sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" fi sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; hppa*64*) shrext_cmds='.sl' hardcode_into_libs=yes dynamic_linker="$host_os dld.sl" shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; *) shrext_cmds='.sl' dynamic_linker="$host_os dld.sl" shlibpath_var=SHLIB_PATH shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' ;; esac # HP-UX runs *really* slowly unless shared libraries are mode 555. postinstall_cmds='chmod 555 $lib' ;; interix[3-9]*) version_type=linux need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; irix5* | irix6* | nonstopux*) case $host_os in nonstopux*) version_type=nonstopux ;; *) if test "$lt_cv_prog_gnu_ld" = yes; then version_type=linux else version_type=irix fi ;; esac need_lib_prefix=no need_version=no soname_spec='${libname}${release}${shared_ext}$major' library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}' case $host_os in irix5* | nonstopux*) libsuff= shlibsuff= ;; *) case $LD in # libtool.m4 will add one of these switches to LD *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") libsuff= shlibsuff= libmagic=32-bit;; *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") libsuff=32 shlibsuff=N32 libmagic=N32;; *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") libsuff=64 shlibsuff=64 libmagic=64-bit;; *) libsuff= shlibsuff= libmagic=never-match;; esac ;; esac shlibpath_var=LD_LIBRARY${shlibsuff}_PATH shlibpath_overrides_runpath=no sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}" sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}" hardcode_into_libs=yes ;; # No shared lib support for Linux oldld, aout, or coff. linux*oldld* | linux*aout* | linux*coff*) dynamic_linker=no ;; # This must be Linux ELF. linux* | k*bsd*-gnu | kopensolaris*-gnu) version_type=linux need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no # Some binutils ld are patched to set DT_RUNPATH save_LDFLAGS=$LDFLAGS save_libdir=$libdir eval "libdir=/foo; wl=\"$lt_prog_compiler_wl\"; \ LDFLAGS=\"\$LDFLAGS $hardcode_libdir_flag_spec\"" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : if ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null; then : shlibpath_overrides_runpath=yes fi fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LDFLAGS=$save_LDFLAGS libdir=$save_libdir # This implies no fast_install, which is unacceptable. # Some rework will be needed to allow for fast_install # before this can be enabled. hardcode_into_libs=yes # Append ld.so.conf contents to the search path if test -f /etc/ld.so.conf; then lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;/^$/d' | tr '\n' ' '` sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" fi # We used to test for /lib/ld.so.1 and disable shared libraries on # powerpc, because MkLinux only supported shared libraries with the # GNU dynamic linker. Since this was broken with cross compilers, # most powerpc-linux boxes support dynamic linking these days and # people can always --disable-shared, the test was removed, and we # assume the GNU/Linux dynamic linker is in use. dynamic_linker='GNU/Linux ld.so' ;; netbsdelf*-gnu) version_type=linux need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes dynamic_linker='NetBSD ld.elf_so' ;; netbsd*) version_type=sunos need_lib_prefix=no need_version=no if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' dynamic_linker='NetBSD (a.out) ld.so' else library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' dynamic_linker='NetBSD ld.elf_so' fi shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; newsos6) version_type=linux library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes ;; *nto* | *qnx*) version_type=qnx need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes dynamic_linker='ldqnx.so' ;; openbsd*) version_type=sunos sys_lib_dlsearch_path_spec="/usr/lib" need_lib_prefix=no # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs. case $host_os in openbsd3.3 | openbsd3.3.*) need_version=yes ;; *) need_version=no ;; esac library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' shlibpath_var=LD_LIBRARY_PATH if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then case $host_os in openbsd2.[89] | openbsd2.[89].*) shlibpath_overrides_runpath=no ;; *) shlibpath_overrides_runpath=yes ;; esac else shlibpath_overrides_runpath=yes fi ;; os2*) libname_spec='$name' shrext_cmds=".dll" need_lib_prefix=no library_names_spec='$libname${shared_ext} $libname.a' dynamic_linker='OS/2 ld.exe' shlibpath_var=LIBPATH ;; osf3* | osf4* | osf5*) version_type=osf need_lib_prefix=no need_version=no soname_spec='${libname}${release}${shared_ext}$major' library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec" ;; rdos*) dynamic_linker=no ;; solaris*) version_type=linux need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes # ldd complains unless libraries are executable postinstall_cmds='chmod +x $lib' ;; sunos4*) version_type=sunos library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes if test "$with_gnu_ld" = yes; then need_lib_prefix=no fi need_version=yes ;; sysv4 | sysv4.3*) version_type=linux library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH case $host_vendor in sni) shlibpath_overrides_runpath=no need_lib_prefix=no runpath_var=LD_RUN_PATH ;; siemens) need_lib_prefix=no ;; motorola) need_lib_prefix=no need_version=no shlibpath_overrides_runpath=no sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' ;; esac ;; sysv4*MP*) if test -d /usr/nec ;then version_type=linux library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}' soname_spec='$libname${shared_ext}.$major' shlibpath_var=LD_LIBRARY_PATH fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) version_type=freebsd-elf need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes if test "$with_gnu_ld" = yes; then sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' else sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' case $host_os in sco3.2v5*) sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" ;; esac fi sys_lib_dlsearch_path_spec='/usr/lib' ;; tpf*) # TPF is a cross-target only. Preferred cross-host = GNU/Linux. version_type=linux need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; uts4*) version_type=linux library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH ;; *) dynamic_linker=no ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: $dynamic_linker" >&5 $as_echo "$dynamic_linker" >&6; } test "$dynamic_linker" = no && can_build_shared=no variables_saved_for_relink="PATH $shlibpath_var $runpath_var" if test "$GCC" = yes; then variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" fi if test "${lt_cv_sys_lib_search_path_spec+set}" = set; then sys_lib_search_path_spec="$lt_cv_sys_lib_search_path_spec" fi if test "${lt_cv_sys_lib_dlsearch_path_spec+set}" = set; then sys_lib_dlsearch_path_spec="$lt_cv_sys_lib_dlsearch_path_spec" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to hardcode library paths into programs" >&5 $as_echo_n "checking how to hardcode library paths into programs... " >&6; } hardcode_action= if test -n "$hardcode_libdir_flag_spec" || test -n "$runpath_var" || test "X$hardcode_automatic" = "Xyes" ; then # We can hardcode non-existent directories. if test "$hardcode_direct" != no && # If the only mechanism to avoid hardcoding is shlibpath_var, we # have to relink, otherwise we might link with an installed library # when we should be linking with a yet-to-be-installed one ## test "$_LT_TAGVAR(hardcode_shlibpath_var, )" != no && test "$hardcode_minus_L" != no; then # Linking always hardcodes the temporary library directory. hardcode_action=relink else # We can link without hardcoding, and we can hardcode nonexisting dirs. hardcode_action=immediate fi else # We cannot hardcode anything, or else we can only hardcode existing # directories. hardcode_action=unsupported fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hardcode_action" >&5 $as_echo "$hardcode_action" >&6; } if test "$hardcode_action" = relink || test "$inherit_rpath" = yes; then # Fast installation is not supported enable_fast_install=no elif test "$shlibpath_overrides_runpath" = yes || test "$enable_shared" = no; then # Fast installation is not necessary enable_fast_install=needless fi if test "x$enable_dlopen" != xyes; then enable_dlopen=unknown enable_dlopen_self=unknown enable_dlopen_self_static=unknown else lt_cv_dlopen=no lt_cv_dlopen_libs= case $host_os in beos*) lt_cv_dlopen="load_add_on" lt_cv_dlopen_libs= lt_cv_dlopen_self=yes ;; mingw* | pw32* | cegcc*) lt_cv_dlopen="LoadLibrary" lt_cv_dlopen_libs= ;; cygwin*) lt_cv_dlopen="dlopen" lt_cv_dlopen_libs= ;; darwin*) # if libdl is installed we need to link against it { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5 $as_echo_n "checking for dlopen in -ldl... " >&6; } if test "${ac_cv_lib_dl_dlopen+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-ldl $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char dlopen (); int main () { return dlopen (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_dl_dlopen=yes else ac_cv_lib_dl_dlopen=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5 $as_echo "$ac_cv_lib_dl_dlopen" >&6; } if test "x$ac_cv_lib_dl_dlopen" = x""yes; then : lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl" else lt_cv_dlopen="dyld" lt_cv_dlopen_libs= lt_cv_dlopen_self=yes fi ;; *) ac_fn_c_check_func "$LINENO" "shl_load" "ac_cv_func_shl_load" if test "x$ac_cv_func_shl_load" = x""yes; then : lt_cv_dlopen="shl_load" else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for shl_load in -ldld" >&5 $as_echo_n "checking for shl_load in -ldld... " >&6; } if test "${ac_cv_lib_dld_shl_load+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-ldld $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char shl_load (); int main () { return shl_load (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_dld_shl_load=yes else ac_cv_lib_dld_shl_load=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_shl_load" >&5 $as_echo "$ac_cv_lib_dld_shl_load" >&6; } if test "x$ac_cv_lib_dld_shl_load" = x""yes; then : lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-ldld" else ac_fn_c_check_func "$LINENO" "dlopen" "ac_cv_func_dlopen" if test "x$ac_cv_func_dlopen" = x""yes; then : lt_cv_dlopen="dlopen" else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5 $as_echo_n "checking for dlopen in -ldl... " >&6; } if test "${ac_cv_lib_dl_dlopen+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-ldl $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char dlopen (); int main () { return dlopen (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_dl_dlopen=yes else ac_cv_lib_dl_dlopen=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5 $as_echo "$ac_cv_lib_dl_dlopen" >&6; } if test "x$ac_cv_lib_dl_dlopen" = x""yes; then : lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl" else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -lsvld" >&5 $as_echo_n "checking for dlopen in -lsvld... " >&6; } if test "${ac_cv_lib_svld_dlopen+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lsvld $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char dlopen (); int main () { return dlopen (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_svld_dlopen=yes else ac_cv_lib_svld_dlopen=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_svld_dlopen" >&5 $as_echo "$ac_cv_lib_svld_dlopen" >&6; } if test "x$ac_cv_lib_svld_dlopen" = x""yes; then : lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld" else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dld_link in -ldld" >&5 $as_echo_n "checking for dld_link in -ldld... " >&6; } if test "${ac_cv_lib_dld_dld_link+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-ldld $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char dld_link (); int main () { return dld_link (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_dld_dld_link=yes else ac_cv_lib_dld_dld_link=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_dld_link" >&5 $as_echo "$ac_cv_lib_dld_dld_link" >&6; } if test "x$ac_cv_lib_dld_dld_link" = x""yes; then : lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-ldld" fi fi fi fi fi fi ;; esac if test "x$lt_cv_dlopen" != xno; then enable_dlopen=yes else enable_dlopen=no fi case $lt_cv_dlopen in dlopen) save_CPPFLAGS="$CPPFLAGS" test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" save_LDFLAGS="$LDFLAGS" wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" save_LIBS="$LIBS" LIBS="$lt_cv_dlopen_libs $LIBS" { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a program can dlopen itself" >&5 $as_echo_n "checking whether a program can dlopen itself... " >&6; } if test "${lt_cv_dlopen_self+set}" = set; then : $as_echo_n "(cached) " >&6 else if test "$cross_compiling" = yes; then : lt_cv_dlopen_self=cross else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF #line 12294 "configure" #include "confdefs.h" #if HAVE_DLFCN_H #include #endif #include #ifdef RTLD_GLOBAL # define LT_DLGLOBAL RTLD_GLOBAL #else # ifdef DL_GLOBAL # define LT_DLGLOBAL DL_GLOBAL # else # define LT_DLGLOBAL 0 # endif #endif /* We may have to define LT_DLLAZY_OR_NOW in the command line if we find out it does not work in some platform. */ #ifndef LT_DLLAZY_OR_NOW # ifdef RTLD_LAZY # define LT_DLLAZY_OR_NOW RTLD_LAZY # else # ifdef DL_LAZY # define LT_DLLAZY_OR_NOW DL_LAZY # else # ifdef RTLD_NOW # define LT_DLLAZY_OR_NOW RTLD_NOW # else # ifdef DL_NOW # define LT_DLLAZY_OR_NOW DL_NOW # else # define LT_DLLAZY_OR_NOW 0 # endif # endif # endif # endif #endif void fnord() { int i=42;} int main () { void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); int status = $lt_dlunknown; if (self) { if (dlsym (self,"fnord")) status = $lt_dlno_uscore; else if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; /* dlclose (self); */ } else puts (dlerror ()); return status; } _LT_EOF if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 (eval $ac_link) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && test -s conftest${ac_exeext} 2>/dev/null; then (./conftest; exit; ) >&5 2>/dev/null lt_status=$? case x$lt_status in x$lt_dlno_uscore) lt_cv_dlopen_self=yes ;; x$lt_dlneed_uscore) lt_cv_dlopen_self=yes ;; x$lt_dlunknown|x*) lt_cv_dlopen_self=no ;; esac else : # compilation failed lt_cv_dlopen_self=no fi fi rm -fr conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self" >&5 $as_echo "$lt_cv_dlopen_self" >&6; } if test "x$lt_cv_dlopen_self" = xyes; then wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\" { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a statically linked program can dlopen itself" >&5 $as_echo_n "checking whether a statically linked program can dlopen itself... " >&6; } if test "${lt_cv_dlopen_self_static+set}" = set; then : $as_echo_n "(cached) " >&6 else if test "$cross_compiling" = yes; then : lt_cv_dlopen_self_static=cross else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF #line 12390 "configure" #include "confdefs.h" #if HAVE_DLFCN_H #include #endif #include #ifdef RTLD_GLOBAL # define LT_DLGLOBAL RTLD_GLOBAL #else # ifdef DL_GLOBAL # define LT_DLGLOBAL DL_GLOBAL # else # define LT_DLGLOBAL 0 # endif #endif /* We may have to define LT_DLLAZY_OR_NOW in the command line if we find out it does not work in some platform. */ #ifndef LT_DLLAZY_OR_NOW # ifdef RTLD_LAZY # define LT_DLLAZY_OR_NOW RTLD_LAZY # else # ifdef DL_LAZY # define LT_DLLAZY_OR_NOW DL_LAZY # else # ifdef RTLD_NOW # define LT_DLLAZY_OR_NOW RTLD_NOW # else # ifdef DL_NOW # define LT_DLLAZY_OR_NOW DL_NOW # else # define LT_DLLAZY_OR_NOW 0 # endif # endif # endif # endif #endif void fnord() { int i=42;} int main () { void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); int status = $lt_dlunknown; if (self) { if (dlsym (self,"fnord")) status = $lt_dlno_uscore; else if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; /* dlclose (self); */ } else puts (dlerror ()); return status; } _LT_EOF if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 (eval $ac_link) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && test -s conftest${ac_exeext} 2>/dev/null; then (./conftest; exit; ) >&5 2>/dev/null lt_status=$? case x$lt_status in x$lt_dlno_uscore) lt_cv_dlopen_self_static=yes ;; x$lt_dlneed_uscore) lt_cv_dlopen_self_static=yes ;; x$lt_dlunknown|x*) lt_cv_dlopen_self_static=no ;; esac else : # compilation failed lt_cv_dlopen_self_static=no fi fi rm -fr conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self_static" >&5 $as_echo "$lt_cv_dlopen_self_static" >&6; } fi CPPFLAGS="$save_CPPFLAGS" LDFLAGS="$save_LDFLAGS" LIBS="$save_LIBS" ;; esac case $lt_cv_dlopen_self in yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;; *) enable_dlopen_self=unknown ;; esac case $lt_cv_dlopen_self_static in yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;; *) enable_dlopen_self_static=unknown ;; esac fi striplib= old_striplib= { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether stripping libraries is possible" >&5 $as_echo_n "checking whether stripping libraries is possible... " >&6; } if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then test -z "$old_striplib" && old_striplib="$STRIP --strip-debug" test -z "$striplib" && striplib="$STRIP --strip-unneeded" { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else # FIXME - insert some real tests, host_os isn't really good enough case $host_os in darwin*) if test -n "$STRIP" ; then striplib="$STRIP -x" old_striplib="$STRIP -S" { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi ;; *) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; esac fi # Report which library types will actually be built { $as_echo "$as_me:${as_lineno-$LINENO}: checking if libtool supports shared libraries" >&5 $as_echo_n "checking if libtool supports shared libraries... " >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: $can_build_shared" >&5 $as_echo "$can_build_shared" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build shared libraries" >&5 $as_echo_n "checking whether to build shared libraries... " >&6; } test "$can_build_shared" = "no" && enable_shared=no # On AIX, shared libraries and static libraries use the same namespace, and # are all built from PIC. case $host_os in aix3*) test "$enable_shared" = yes && enable_static=no if test -n "$RANLIB"; then archive_cmds="$archive_cmds~\$RANLIB \$lib" postinstall_cmds='$RANLIB $lib' fi ;; aix[4-9]*) if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then test "$enable_shared" = yes && enable_static=no fi ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_shared" >&5 $as_echo "$enable_shared" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build static libraries" >&5 $as_echo_n "checking whether to build static libraries... " >&6; } # Make sure either enable_shared or enable_static is yes. test "$enable_shared" = yes || enable_static=yes { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_static" >&5 $as_echo "$enable_static" >&6; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu CC="$lt_save_CC" ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu archive_cmds_need_lc_CXX=no allow_undefined_flag_CXX= always_export_symbols_CXX=no archive_expsym_cmds_CXX= compiler_needs_object_CXX=no export_dynamic_flag_spec_CXX= hardcode_direct_CXX=no hardcode_direct_absolute_CXX=no hardcode_libdir_flag_spec_CXX= hardcode_libdir_flag_spec_ld_CXX= hardcode_libdir_separator_CXX= hardcode_minus_L_CXX=no hardcode_shlibpath_var_CXX=unsupported hardcode_automatic_CXX=no inherit_rpath_CXX=no module_cmds_CXX= module_expsym_cmds_CXX= link_all_deplibs_CXX=unknown old_archive_cmds_CXX=$old_archive_cmds no_undefined_flag_CXX= whole_archive_flag_spec_CXX= enable_shared_with_static_runtimes_CXX=no # Source file extension for C++ test sources. ac_ext=cpp # Object file extension for compiled C++ test sources. objext=o objext_CXX=$objext # No sense in running all these tests if we already determined that # the CXX compiler isn't working. Some variables (like enable_shared) # are currently assumed to apply to all compilers on this platform, # and will be corrupted by setting them based on a non-working compiler. if test "$_lt_caught_CXX_error" != yes; then # Code to be used in simple compile tests lt_simple_compile_test_code="int some_variable = 0;" # Code to be used in simple link tests lt_simple_link_test_code='int main(int, char *[]) { return(0); }' # ltmain only uses $CC for tagged configurations so make sure $CC is set. # If no C compiler was specified, use CC. LTCC=${LTCC-"$CC"} # If no C compiler flags were specified, use CFLAGS. LTCFLAGS=${LTCFLAGS-"$CFLAGS"} # Allow CC to be a program name with arguments. compiler=$CC # save warnings/boilerplate of simple test code ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" >conftest.$ac_ext eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_compiler_boilerplate=`cat conftest.err` $RM conftest* ac_outfile=conftest.$ac_objext echo "$lt_simple_link_test_code" >conftest.$ac_ext eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_linker_boilerplate=`cat conftest.err` $RM -r conftest* # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_LD=$LD lt_save_GCC=$GCC GCC=$GXX lt_save_with_gnu_ld=$with_gnu_ld lt_save_path_LD=$lt_cv_path_LD if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx else $as_unset lt_cv_prog_gnu_ld fi if test -n "${lt_cv_path_LDCXX+set}"; then lt_cv_path_LD=$lt_cv_path_LDCXX else $as_unset lt_cv_path_LD fi test -z "${LDCXX+set}" || LD=$LDCXX CC=${CXX-"c++"} compiler=$CC compiler_CXX=$CC for cc_temp in $compiler""; do case $cc_temp in compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; \-*) ;; *) break;; esac done cc_basename=`$ECHO "X$cc_temp" | $Xsed -e 's%.*/%%' -e "s%^$host_alias-%%"` if test -n "$compiler"; then # We don't want -fno-exception when compiling C++ code, so set the # no_builtin_flag separately if test "$GXX" = yes; then lt_prog_compiler_no_builtin_flag_CXX=' -fno-builtin' else lt_prog_compiler_no_builtin_flag_CXX= fi if test "$GXX" = yes; then # Set up default GNU C++ configuration # Check whether --with-gnu-ld was given. if test "${with_gnu_ld+set}" = set; then : withval=$with_gnu_ld; test "$withval" = no || with_gnu_ld=yes else with_gnu_ld=no fi ac_prog=ld if test "$GCC" = yes; then # Check if gcc -print-prog-name=ld gives a path. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld used by $CC" >&5 $as_echo_n "checking for ld used by $CC... " >&6; } case $host in *-*-mingw*) # gcc leaves a trailing carriage return which upsets mingw ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; *) ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; esac case $ac_prog in # Accept absolute paths. [\\/]* | ?:[\\/]*) re_direlt='/[^/][^/]*/\.\./' # Canonicalize the pathname of ld ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` done test -z "$LD" && LD="$ac_prog" ;; "") # If it fails, then pretend we aren't using GCC. ac_prog=ld ;; *) # If it is relative, then search for the first ld in PATH. with_gnu_ld=unknown ;; esac elif test "$with_gnu_ld" = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5 $as_echo_n "checking for GNU ld... " >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5 $as_echo_n "checking for non-GNU ld... " >&6; } fi if test "${lt_cv_path_LD+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -z "$LD"; then lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR for ac_dir in $PATH; do IFS="$lt_save_ifs" test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then lt_cv_path_LD="$ac_dir/$ac_prog" # Check to see if the program is GNU ld. I'd rather use --version, # but apparently some variants of GNU ld only accept -v. # Break only if it was the GNU/non-GNU ld that we prefer. case `"$lt_cv_path_LD" -v 2>&1 &5 $as_echo "$LD" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5 { $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5 $as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; } if test "${lt_cv_prog_gnu_ld+set}" = set; then : $as_echo_n "(cached) " >&6 else # I'd rather use --version here, but apparently some GNU lds only accept -v. case `$LD -v 2>&1 &5 $as_echo "$lt_cv_prog_gnu_ld" >&6; } with_gnu_ld=$lt_cv_prog_gnu_ld # Check if GNU C++ uses GNU ld as the underlying linker, since the # archiving commands below assume that GNU ld is being used. if test "$with_gnu_ld" = yes; then archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' archive_expsym_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir' export_dynamic_flag_spec_CXX='${wl}--export-dynamic' # If archive_cmds runs LD, not CC, wlarc should be empty # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to # investigate it a little bit more. (MM) wlarc='${wl}' # ancient GNU ld didn't support --whole-archive et. al. if eval "`$CC -print-prog-name=ld` --help 2>&1" | $GREP 'no-whole-archive' > /dev/null; then whole_archive_flag_spec_CXX="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' else whole_archive_flag_spec_CXX= fi else with_gnu_ld=no wlarc= # A generic and very simple default shared library creation # command for GNU C++ for the case where it uses the native # linker, instead of GNU ld. If possible, this setting should # overridden to take advantage of the native linker features on # the platform it is being used on. archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' fi # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "\-L"' else GXX=no with_gnu_ld=no wlarc= fi # PORTME: fill in a description of your system's C++ link characteristics { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5 $as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; } ld_shlibs_CXX=yes case $host_os in aix3*) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; aix[4-9]*) if test "$host_cpu" = ia64; then # On IA64, the linker does run time linking by default, so we don't # have to do anything special. aix_use_runtimelinking=no exp_sym_flag='-Bexport' no_entry_flag="" else aix_use_runtimelinking=no # Test if we are trying to use run time linking or normal # AIX style linking. If -brtl is somewhere in LDFLAGS, we # need to do runtime linking. case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*) for ld_flag in $LDFLAGS; do case $ld_flag in *-brtl*) aix_use_runtimelinking=yes break ;; esac done ;; esac exp_sym_flag='-bexport' no_entry_flag='-bnoentry' fi # When large executables or shared objects are built, AIX ld can # have problems creating the table of contents. If linking a library # or program results in "error TOC overflow" add -mminimal-toc to # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. archive_cmds_CXX='' hardcode_direct_CXX=yes hardcode_direct_absolute_CXX=yes hardcode_libdir_separator_CXX=':' link_all_deplibs_CXX=yes file_list_spec_CXX='${wl}-f,' if test "$GXX" = yes; then case $host_os in aix4.[012]|aix4.[012].*) # We only want to do this on AIX 4.2 and lower, the check # below for broken collect2 doesn't work under 4.3+ collect2name=`${CC} -print-prog-name=collect2` if test -f "$collect2name" && strings "$collect2name" | $GREP resolve_lib_name >/dev/null then # We have reworked collect2 : else # We have old collect2 hardcode_direct_CXX=unsupported # It fails to find uninstalled libraries when the uninstalled # path is not listed in the libpath. Setting hardcode_minus_L # to unsupported forces relinking hardcode_minus_L_CXX=yes hardcode_libdir_flag_spec_CXX='-L$libdir' hardcode_libdir_separator_CXX= fi esac shared_flag='-shared' if test "$aix_use_runtimelinking" = yes; then shared_flag="$shared_flag "'${wl}-G' fi else # not using gcc if test "$host_cpu" = ia64; then # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release # chokes on -Wl,-G. The following line is correct: shared_flag='-G' else if test "$aix_use_runtimelinking" = yes; then shared_flag='${wl}-G' else shared_flag='${wl}-bM:SRE' fi fi fi export_dynamic_flag_spec_CXX='${wl}-bexpall' # It seems that -bexpall does not export symbols beginning with # underscore (_), so it is better to generate a list of symbols to # export. always_export_symbols_CXX=yes if test "$aix_use_runtimelinking" = yes; then # Warning - without using the other runtime loading flags (-brtl), # -berok will link without error, but may produce a broken library. allow_undefined_flag_CXX='-berok' # Determine the default libpath from the value encoded in an empty # executable. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : lt_aix_libpath_sed=' /Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/ p } }' aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` # Check for a 64-bit object if we didn't find anything. if test -z "$aix_libpath"; then aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` fi fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi hardcode_libdir_flag_spec_CXX='${wl}-blibpath:$libdir:'"$aix_libpath" archive_expsym_cmds_CXX='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then $ECHO "X${wl}${allow_undefined_flag}" | $Xsed; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" else if test "$host_cpu" = ia64; then hardcode_libdir_flag_spec_CXX='${wl}-R $libdir:/usr/lib:/lib' allow_undefined_flag_CXX="-z nodefs" archive_expsym_cmds_CXX="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" else # Determine the default libpath from the value encoded in an # empty executable. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : lt_aix_libpath_sed=' /Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/ p } }' aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` # Check for a 64-bit object if we didn't find anything. if test -z "$aix_libpath"; then aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` fi fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi hardcode_libdir_flag_spec_CXX='${wl}-blibpath:$libdir:'"$aix_libpath" # Warning - without using the other run time loading flags, # -berok will link without error, but may produce a broken library. no_undefined_flag_CXX=' ${wl}-bernotok' allow_undefined_flag_CXX=' ${wl}-berok' # Exported symbols can be pulled into shared objects from archives whole_archive_flag_spec_CXX='$convenience' archive_cmds_need_lc_CXX=yes # This is similar to how AIX traditionally builds its shared # libraries. archive_expsym_cmds_CXX="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' fi fi ;; beos*) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then allow_undefined_flag_CXX=unsupported # Joseph Beckenbach says some releases of gcc # support --undefined. This deserves some investigation. FIXME archive_cmds_CXX='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' else ld_shlibs_CXX=no fi ;; chorus*) case $cc_basename in *) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; esac ;; cygwin* | mingw* | pw32* | cegcc*) # _LT_TAGVAR(hardcode_libdir_flag_spec, CXX) is actually meaningless, # as there is no search path for DLLs. hardcode_libdir_flag_spec_CXX='-L$libdir' allow_undefined_flag_CXX=unsupported always_export_symbols_CXX=no enable_shared_with_static_runtimes_CXX=yes if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' # If the export-symbols file already is a .def file (1st line # is EXPORTS), use it as is; otherwise, prepend... archive_expsym_cmds_CXX='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then cp $export_symbols $output_objdir/$soname.def; else echo EXPORTS > $output_objdir/$soname.def; cat $export_symbols >> $output_objdir/$soname.def; fi~ $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' else ld_shlibs_CXX=no fi ;; darwin* | rhapsody*) archive_cmds_need_lc_CXX=no hardcode_direct_CXX=no hardcode_automatic_CXX=yes hardcode_shlibpath_var_CXX=unsupported whole_archive_flag_spec_CXX='' link_all_deplibs_CXX=yes allow_undefined_flag_CXX="$_lt_dar_allow_undefined" case $cc_basename in ifort*) _lt_dar_can_shared=yes ;; *) _lt_dar_can_shared=$GCC ;; esac if test "$_lt_dar_can_shared" = "yes"; then output_verbose_link_cmd=echo archive_cmds_CXX="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" module_cmds_CXX="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" archive_expsym_cmds_CXX="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" module_expsym_cmds_CXX="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" if test "$lt_cv_apple_cc_single_mod" != "yes"; then archive_cmds_CXX="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dsymutil}" archive_expsym_cmds_CXX="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dar_export_syms}${_lt_dsymutil}" fi else ld_shlibs_CXX=no fi ;; dgux*) case $cc_basename in ec++*) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; ghcx*) # Green Hills C++ Compiler # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; *) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; esac ;; freebsd[12]*) # C++ shared libraries reported to be fairly broken before # switch to ELF ld_shlibs_CXX=no ;; freebsd-elf*) archive_cmds_need_lc_CXX=no ;; freebsd* | dragonfly*) # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF # conventions ld_shlibs_CXX=yes ;; gnu*) ;; hpux9*) hardcode_libdir_flag_spec_CXX='${wl}+b ${wl}$libdir' hardcode_libdir_separator_CXX=: export_dynamic_flag_spec_CXX='${wl}-E' hardcode_direct_CXX=yes hardcode_minus_L_CXX=yes # Not in the search PATH, # but as the default # location of the library. case $cc_basename in CC*) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; aCC*) archive_cmds_CXX='$RM $output_objdir/$soname~$CC -b ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed' ;; *) if test "$GXX" = yes; then archive_cmds_CXX='$RM $output_objdir/$soname~$CC -shared -nostdlib -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' else # FIXME: insert proper C++ library support ld_shlibs_CXX=no fi ;; esac ;; hpux10*|hpux11*) if test $with_gnu_ld = no; then hardcode_libdir_flag_spec_CXX='${wl}+b ${wl}$libdir' hardcode_libdir_separator_CXX=: case $host_cpu in hppa*64*|ia64*) ;; *) export_dynamic_flag_spec_CXX='${wl}-E' ;; esac fi case $host_cpu in hppa*64*|ia64*) hardcode_direct_CXX=no hardcode_shlibpath_var_CXX=no ;; *) hardcode_direct_CXX=yes hardcode_direct_absolute_CXX=yes hardcode_minus_L_CXX=yes # Not in the search PATH, # but as the default # location of the library. ;; esac case $cc_basename in CC*) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; aCC*) case $host_cpu in hppa*64*) archive_cmds_CXX='$CC -b ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; ia64*) archive_cmds_CXX='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; *) archive_cmds_CXX='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; esac # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed' ;; *) if test "$GXX" = yes; then if test $with_gnu_ld = no; then case $host_cpu in hppa*64*) archive_cmds_CXX='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; ia64*) archive_cmds_CXX='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; *) archive_cmds_CXX='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; esac fi else # FIXME: insert proper C++ library support ld_shlibs_CXX=no fi ;; esac ;; interix[3-9]*) hardcode_direct_CXX=no hardcode_shlibpath_var_CXX=no hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir' export_dynamic_flag_spec_CXX='${wl}-E' # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. # Instead, shared libraries are loaded at an image base (0x10000000 by # default) and relocated if they conflict, which is a slow very memory # consuming and fragmenting process. To avoid this, we pick a random, # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link # time. Moving up from 0x10000000 also allows more sbrk(2) space. archive_cmds_CXX='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' archive_expsym_cmds_CXX='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' ;; irix5* | irix6*) case $cc_basename in CC*) # SGI C++ archive_cmds_CXX='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib' # Archives containing C++ object files must be created using # "CC -ar", where "CC" is the IRIX C++ compiler. This is # necessary to make sure instantiated templates are included # in the archive. old_archive_cmds_CXX='$CC -ar -WR,-u -o $oldlib $oldobjs' ;; *) if test "$GXX" = yes; then if test "$with_gnu_ld" = no; then archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' else archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` -o $lib' fi fi link_all_deplibs_CXX=yes ;; esac hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir' hardcode_libdir_separator_CXX=: inherit_rpath_CXX=yes ;; linux* | k*bsd*-gnu | kopensolaris*-gnu) case $cc_basename in KCC*) # Kuck and Associates, Inc. (KAI) C++ Compiler # KCC will only create a shared library if the output file # ends with ".so" (or ".sl" for HP-UX), so rename the library # to its proper name (with version) after linking. archive_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' archive_expsym_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib ${wl}-retain-symbols-file,$export_symbols; mv \$templib $lib' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed' hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir' export_dynamic_flag_spec_CXX='${wl}--export-dynamic' # Archives containing C++ object files must be created using # "CC -Bstatic", where "CC" is the KAI C++ compiler. old_archive_cmds_CXX='$CC -Bstatic -o $oldlib $oldobjs' ;; icpc* | ecpc* ) # Intel C++ with_gnu_ld=yes # version 8.0 and above of icpc choke on multiply defined symbols # if we add $predep_objects and $postdep_objects, however 7.1 and # earlier do not add the objects themselves. case `$CC -V 2>&1` in *"Version 7."*) archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' archive_expsym_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' ;; *) # Version 8.0 or newer tmp_idyn= case $host_cpu in ia64*) tmp_idyn=' -i_dynamic';; esac archive_cmds_CXX='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' archive_expsym_cmds_CXX='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' ;; esac archive_cmds_need_lc_CXX=no hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir' export_dynamic_flag_spec_CXX='${wl}--export-dynamic' whole_archive_flag_spec_CXX='${wl}--whole-archive$convenience ${wl}--no-whole-archive' ;; pgCC* | pgcpp*) # Portland Group C++ compiler case `$CC -V` in *pgCC\ [1-5]* | *pgcpp\ [1-5]*) prelink_cmds_CXX='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~ compile_command="$compile_command `find $tpldir -name \*.o | $NL2SP`"' old_archive_cmds_CXX='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~ $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | $NL2SP`~ $RANLIB $oldlib' archive_cmds_CXX='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib' archive_expsym_cmds_CXX='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib' ;; *) # Version 6 will use weak symbols archive_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib' archive_expsym_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib' ;; esac hardcode_libdir_flag_spec_CXX='${wl}--rpath ${wl}$libdir' export_dynamic_flag_spec_CXX='${wl}--export-dynamic' whole_archive_flag_spec_CXX='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive' ;; cxx*) # Compaq C++ archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' archive_expsym_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib ${wl}-retain-symbols-file $wl$export_symbols' runpath_var=LD_RUN_PATH hardcode_libdir_flag_spec_CXX='-rpath $libdir' hardcode_libdir_separator_CXX=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`$ECHO "X$templist" | $Xsed -e "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed' ;; xl*) # IBM XL 8.0 on PPC, with GNU ld hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir' export_dynamic_flag_spec_CXX='${wl}--export-dynamic' archive_cmds_CXX='$CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' if test "x$supports_anon_versioning" = xyes; then archive_expsym_cmds_CXX='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' fi ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C++ 5.9 no_undefined_flag_CXX=' -zdefs' archive_cmds_CXX='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' archive_expsym_cmds_CXX='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file ${wl}$export_symbols' hardcode_libdir_flag_spec_CXX='-R$libdir' whole_archive_flag_spec_CXX='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive' compiler_needs_object_CXX=yes # Not sure whether something based on # $CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 # would be better. output_verbose_link_cmd='echo' # Archives containing C++ object files must be created using # "CC -xar", where "CC" is the Sun C++ compiler. This is # necessary to make sure instantiated templates are included # in the archive. old_archive_cmds_CXX='$CC -xar -o $oldlib $oldobjs' ;; esac ;; esac ;; lynxos*) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; m88k*) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; mvs*) case $cc_basename in cxx*) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; *) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; esac ;; netbsd*) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then archive_cmds_CXX='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags' wlarc= hardcode_libdir_flag_spec_CXX='-R$libdir' hardcode_direct_CXX=yes hardcode_shlibpath_var_CXX=no fi # Workaround some broken pre-1.5 toolchains output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"' ;; *nto* | *qnx*) ld_shlibs_CXX=yes ;; openbsd2*) # C++ shared libraries are fairly broken ld_shlibs_CXX=no ;; openbsd*) if test -f /usr/libexec/ld.so; then hardcode_direct_CXX=yes hardcode_shlibpath_var_CXX=no hardcode_direct_absolute_CXX=yes archive_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir' if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then archive_expsym_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file,$export_symbols -o $lib' export_dynamic_flag_spec_CXX='${wl}-E' whole_archive_flag_spec_CXX="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' fi output_verbose_link_cmd=echo else ld_shlibs_CXX=no fi ;; osf3* | osf4* | osf5*) case $cc_basename in KCC*) # Kuck and Associates, Inc. (KAI) C++ Compiler # KCC will only create a shared library if the output file # ends with ".so" (or ".sl" for HP-UX), so rename the library # to its proper name (with version) after linking. archive_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir' hardcode_libdir_separator_CXX=: # Archives containing C++ object files must be created using # the KAI C++ compiler. case $host in osf3*) old_archive_cmds_CXX='$CC -Bstatic -o $oldlib $oldobjs' ;; *) old_archive_cmds_CXX='$CC -o $oldlib $oldobjs' ;; esac ;; RCC*) # Rational C++ 2.4.1 # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; cxx*) case $host in osf3*) allow_undefined_flag_CXX=' ${wl}-expect_unresolved ${wl}\*' archive_cmds_CXX='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $soname `test -n "$verstring" && $ECHO "X${wl}-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib' hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir' ;; *) allow_undefined_flag_CXX=' -expect_unresolved \*' archive_cmds_CXX='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib' archive_expsym_cmds_CXX='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~ echo "-hidden">> $lib.exp~ $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname ${wl}-input ${wl}$lib.exp `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib~ $RM $lib.exp' hardcode_libdir_flag_spec_CXX='-rpath $libdir' ;; esac hardcode_libdir_separator_CXX=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`$ECHO "X$templist" | $Xsed -e "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed' ;; *) if test "$GXX" = yes && test "$with_gnu_ld" = no; then allow_undefined_flag_CXX=' ${wl}-expect_unresolved ${wl}\*' case $host in osf3*) archive_cmds_CXX='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' ;; *) archive_cmds_CXX='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' ;; esac hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir' hardcode_libdir_separator_CXX=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "\-L"' else # FIXME: insert proper C++ library support ld_shlibs_CXX=no fi ;; esac ;; psos*) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; sunos4*) case $cc_basename in CC*) # Sun C++ 4.x # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; lcc*) # Lucid # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; *) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; esac ;; solaris*) case $cc_basename in CC*) # Sun C++ 4.2, 5.x and Centerline C++ archive_cmds_need_lc_CXX=yes no_undefined_flag_CXX=' -zdefs' archive_cmds_CXX='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' archive_expsym_cmds_CXX='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G${allow_undefined_flag} ${wl}-M ${wl}$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' hardcode_libdir_flag_spec_CXX='-R$libdir' hardcode_shlibpath_var_CXX=no case $host_os in solaris2.[0-5] | solaris2.[0-5].*) ;; *) # The compiler driver will combine and reorder linker options, # but understands `-z linker_flag'. # Supported since Solaris 2.6 (maybe 2.5.1?) whole_archive_flag_spec_CXX='-z allextract$convenience -z defaultextract' ;; esac link_all_deplibs_CXX=yes output_verbose_link_cmd='echo' # Archives containing C++ object files must be created using # "CC -xar", where "CC" is the Sun C++ compiler. This is # necessary to make sure instantiated templates are included # in the archive. old_archive_cmds_CXX='$CC -xar -o $oldlib $oldobjs' ;; gcx*) # Green Hills C++ Compiler archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' # The C++ compiler must be used to create the archive. old_archive_cmds_CXX='$CC $LDFLAGS -archive -o $oldlib $oldobjs' ;; *) # GNU C++ compiler with Solaris linker if test "$GXX" = yes && test "$with_gnu_ld" = no; then no_undefined_flag_CXX=' ${wl}-z ${wl}defs' if $CC --version | $GREP -v '^2\.7' > /dev/null; then archive_cmds_CXX='$CC -shared -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' archive_expsym_cmds_CXX='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -shared -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "\-L"' else # g++ 2.7 appears to require `-G' NOT `-shared' on this # platform. archive_cmds_CXX='$CC -G -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' archive_expsym_cmds_CXX='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP "\-L"' fi hardcode_libdir_flag_spec_CXX='${wl}-R $wl$libdir' case $host_os in solaris2.[0-5] | solaris2.[0-5].*) ;; *) whole_archive_flag_spec_CXX='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract' ;; esac fi ;; esac ;; sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*) no_undefined_flag_CXX='${wl}-z,text' archive_cmds_need_lc_CXX=no hardcode_shlibpath_var_CXX=no runpath_var='LD_RUN_PATH' case $cc_basename in CC*) archive_cmds_CXX='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds_CXX='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; *) archive_cmds_CXX='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds_CXX='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; esac ;; sysv5* | sco3.2v5* | sco5v6*) # Note: We can NOT use -z defs as we might desire, because we do not # link with -lc, and that would cause any symbols used from libc to # always be unresolved, which means just about no library would # ever link correctly. If we're not using GNU ld we use -z text # though, which does catch some bad symbols but isn't as heavy-handed # as -z defs. no_undefined_flag_CXX='${wl}-z,text' allow_undefined_flag_CXX='${wl}-z,nodefs' archive_cmds_need_lc_CXX=no hardcode_shlibpath_var_CXX=no hardcode_libdir_flag_spec_CXX='${wl}-R,$libdir' hardcode_libdir_separator_CXX=':' link_all_deplibs_CXX=yes export_dynamic_flag_spec_CXX='${wl}-Bexport' runpath_var='LD_RUN_PATH' case $cc_basename in CC*) archive_cmds_CXX='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds_CXX='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; *) archive_cmds_CXX='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds_CXX='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; esac ;; tandem*) case $cc_basename in NCC*) # NonStop-UX NCC 3.20 # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; *) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; esac ;; vxworks*) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; *) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs_CXX" >&5 $as_echo "$ld_shlibs_CXX" >&6; } test "$ld_shlibs_CXX" = no && can_build_shared=no GCC_CXX="$GXX" LD_CXX="$LD" ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... # Dependencies to place before and after the object being linked: predep_objects_CXX= postdep_objects_CXX= predeps_CXX= postdeps_CXX= compiler_lib_search_path_CXX= cat > conftest.$ac_ext <<_LT_EOF class Foo { public: Foo (void) { a = 0; } private: int a; }; _LT_EOF if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then # Parse the compiler output and extract the necessary # objects, libraries and library flags. # Sentinel used to keep track of whether or not we are before # the conftest object file. pre_test_object_deps_done=no for p in `eval "$output_verbose_link_cmd"`; do case $p in -L* | -R* | -l*) # Some compilers place space between "-{L,R}" and the path. # Remove the space. if test $p = "-L" || test $p = "-R"; then prev=$p continue else prev= fi if test "$pre_test_object_deps_done" = no; then case $p in -L* | -R*) # Internal compiler library paths should come after those # provided the user. The postdeps already come after the # user supplied libs so there is no need to process them. if test -z "$compiler_lib_search_path_CXX"; then compiler_lib_search_path_CXX="${prev}${p}" else compiler_lib_search_path_CXX="${compiler_lib_search_path_CXX} ${prev}${p}" fi ;; # The "-l" case would never come before the object being # linked, so don't bother handling this case. esac else if test -z "$postdeps_CXX"; then postdeps_CXX="${prev}${p}" else postdeps_CXX="${postdeps_CXX} ${prev}${p}" fi fi ;; *.$objext) # This assumes that the test object file only shows up # once in the compiler output. if test "$p" = "conftest.$objext"; then pre_test_object_deps_done=yes continue fi if test "$pre_test_object_deps_done" = no; then if test -z "$predep_objects_CXX"; then predep_objects_CXX="$p" else predep_objects_CXX="$predep_objects_CXX $p" fi else if test -z "$postdep_objects_CXX"; then postdep_objects_CXX="$p" else postdep_objects_CXX="$postdep_objects_CXX $p" fi fi ;; *) ;; # Ignore the rest. esac done # Clean up. rm -f a.out a.exe else echo "libtool.m4: error: problem compiling CXX test program" fi $RM -f confest.$objext # PORTME: override above test on systems where it is broken case $host_os in interix[3-9]*) # Interix 3.5 installs completely hosed .la files for C++, so rather than # hack all around it, let's just trust "g++" to DTRT. predep_objects_CXX= postdep_objects_CXX= postdeps_CXX= ;; linux*) case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C++ 5.9 # The more standards-conforming stlport4 library is # incompatible with the Cstd library. Avoid specifying # it if it's in CXXFLAGS. Ignore libCrun as # -library=stlport4 depends on it. case " $CXX $CXXFLAGS " in *" -library=stlport4 "*) solaris_use_stlport4=yes ;; esac if test "$solaris_use_stlport4" != yes; then postdeps_CXX='-library=Cstd -library=Crun' fi ;; esac ;; solaris*) case $cc_basename in CC*) # The more standards-conforming stlport4 library is # incompatible with the Cstd library. Avoid specifying # it if it's in CXXFLAGS. Ignore libCrun as # -library=stlport4 depends on it. case " $CXX $CXXFLAGS " in *" -library=stlport4 "*) solaris_use_stlport4=yes ;; esac # Adding this requires a known-good setup of shared libraries for # Sun compiler versions before 5.6, else PIC objects from an old # archive will be linked into the output, leading to subtle bugs. if test "$solaris_use_stlport4" != yes; then postdeps_CXX='-library=Cstd -library=Crun' fi ;; esac ;; esac case " $postdeps_CXX " in *" -lc "*) archive_cmds_need_lc_CXX=no ;; esac compiler_lib_search_dirs_CXX= if test -n "${compiler_lib_search_path_CXX}"; then compiler_lib_search_dirs_CXX=`echo " ${compiler_lib_search_path_CXX}" | ${SED} -e 's! -L! !g' -e 's!^ !!'` fi lt_prog_compiler_wl_CXX= lt_prog_compiler_pic_CXX= lt_prog_compiler_static_CXX= { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $compiler option to produce PIC" >&5 $as_echo_n "checking for $compiler option to produce PIC... " >&6; } # C++ specific cases for pic, static, wl, etc. if test "$GXX" = yes; then lt_prog_compiler_wl_CXX='-Wl,' lt_prog_compiler_static_CXX='-static' case $host_os in aix*) # All AIX code is PIC. if test "$host_cpu" = ia64; then # AIX 5 now supports IA64 processor lt_prog_compiler_static_CXX='-Bstatic' fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support lt_prog_compiler_pic_CXX='-fPIC' ;; m68k) # FIXME: we need at least 68020 code to build shared libraries, but # adding the `-m68020' flag to GCC prevents building anything better, # like `-m68040'. lt_prog_compiler_pic_CXX='-m68020 -resident32 -malways-restore-a4' ;; esac ;; beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) # PIC is the default for these OSes. ;; mingw* | cygwin* | os2* | pw32* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). # Although the cygwin gcc ignores -fPIC, still need this for old-style # (--disable-auto-import) libraries lt_prog_compiler_pic_CXX='-DDLL_EXPORT' ;; darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files lt_prog_compiler_pic_CXX='-fno-common' ;; *djgpp*) # DJGPP does not support shared libraries at all lt_prog_compiler_pic_CXX= ;; interix[3-9]*) # Interix 3.x gcc -fpic/-fPIC options generate broken code. # Instead, we relocate shared libraries at runtime. ;; sysv4*MP*) if test -d /usr/nec; then lt_prog_compiler_pic_CXX=-Kconform_pic fi ;; hpux*) # PIC is the default for 64-bit PA HP-UX, but not for 32-bit # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag # sets the default TLS model and affects inlining. case $host_cpu in hppa*64*) ;; *) lt_prog_compiler_pic_CXX='-fPIC' ;; esac ;; *qnx* | *nto*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. lt_prog_compiler_pic_CXX='-fPIC -shared' ;; *) lt_prog_compiler_pic_CXX='-fPIC' ;; esac else case $host_os in aix[4-9]*) # All AIX code is PIC. if test "$host_cpu" = ia64; then # AIX 5 now supports IA64 processor lt_prog_compiler_static_CXX='-Bstatic' else lt_prog_compiler_static_CXX='-bnso -bI:/lib/syscalls.exp' fi ;; chorus*) case $cc_basename in cxch68*) # Green Hills C++ Compiler # _LT_TAGVAR(lt_prog_compiler_static, CXX)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a" ;; esac ;; dgux*) case $cc_basename in ec++*) lt_prog_compiler_pic_CXX='-KPIC' ;; ghcx*) # Green Hills C++ Compiler lt_prog_compiler_pic_CXX='-pic' ;; *) ;; esac ;; freebsd* | dragonfly*) # FreeBSD uses GNU C++ ;; hpux9* | hpux10* | hpux11*) case $cc_basename in CC*) lt_prog_compiler_wl_CXX='-Wl,' lt_prog_compiler_static_CXX='${wl}-a ${wl}archive' if test "$host_cpu" != ia64; then lt_prog_compiler_pic_CXX='+Z' fi ;; aCC*) lt_prog_compiler_wl_CXX='-Wl,' lt_prog_compiler_static_CXX='${wl}-a ${wl}archive' case $host_cpu in hppa*64*|ia64*) # +Z the default ;; *) lt_prog_compiler_pic_CXX='+Z' ;; esac ;; *) ;; esac ;; interix*) # This is c89, which is MS Visual C++ (no shared libs) # Anyone wants to do a port? ;; irix5* | irix6* | nonstopux*) case $cc_basename in CC*) lt_prog_compiler_wl_CXX='-Wl,' lt_prog_compiler_static_CXX='-non_shared' # CC pic flag -KPIC is the default. ;; *) ;; esac ;; linux* | k*bsd*-gnu | kopensolaris*-gnu) case $cc_basename in KCC*) # KAI C++ Compiler lt_prog_compiler_wl_CXX='--backend -Wl,' lt_prog_compiler_pic_CXX='-fPIC' ;; ecpc* ) # old Intel C++ for x86_64 which still supported -KPIC. lt_prog_compiler_wl_CXX='-Wl,' lt_prog_compiler_pic_CXX='-KPIC' lt_prog_compiler_static_CXX='-static' ;; icpc* ) # Intel C++, used to be incompatible with GCC. # ICC 10 doesn't accept -KPIC any more. lt_prog_compiler_wl_CXX='-Wl,' lt_prog_compiler_pic_CXX='-fPIC' lt_prog_compiler_static_CXX='-static' ;; pgCC* | pgcpp*) # Portland Group C++ compiler lt_prog_compiler_wl_CXX='-Wl,' lt_prog_compiler_pic_CXX='-fpic' lt_prog_compiler_static_CXX='-Bstatic' ;; cxx*) # Compaq C++ # Make sure the PIC flag is empty. It appears that all Alpha # Linux and Compaq Tru64 Unix objects are PIC. lt_prog_compiler_pic_CXX= lt_prog_compiler_static_CXX='-non_shared' ;; xlc* | xlC*) # IBM XL 8.0 on PPC lt_prog_compiler_wl_CXX='-Wl,' lt_prog_compiler_pic_CXX='-qpic' lt_prog_compiler_static_CXX='-qstaticlink' ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C++ 5.9 lt_prog_compiler_pic_CXX='-KPIC' lt_prog_compiler_static_CXX='-Bstatic' lt_prog_compiler_wl_CXX='-Qoption ld ' ;; esac ;; esac ;; lynxos*) ;; m88k*) ;; mvs*) case $cc_basename in cxx*) lt_prog_compiler_pic_CXX='-W c,exportall' ;; *) ;; esac ;; netbsd* | netbsdelf*-gnu) ;; *qnx* | *nto*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. lt_prog_compiler_pic_CXX='-fPIC -shared' ;; osf3* | osf4* | osf5*) case $cc_basename in KCC*) lt_prog_compiler_wl_CXX='--backend -Wl,' ;; RCC*) # Rational C++ 2.4.1 lt_prog_compiler_pic_CXX='-pic' ;; cxx*) # Digital/Compaq C++ lt_prog_compiler_wl_CXX='-Wl,' # Make sure the PIC flag is empty. It appears that all Alpha # Linux and Compaq Tru64 Unix objects are PIC. lt_prog_compiler_pic_CXX= lt_prog_compiler_static_CXX='-non_shared' ;; *) ;; esac ;; psos*) ;; solaris*) case $cc_basename in CC*) # Sun C++ 4.2, 5.x and Centerline C++ lt_prog_compiler_pic_CXX='-KPIC' lt_prog_compiler_static_CXX='-Bstatic' lt_prog_compiler_wl_CXX='-Qoption ld ' ;; gcx*) # Green Hills C++ Compiler lt_prog_compiler_pic_CXX='-PIC' ;; *) ;; esac ;; sunos4*) case $cc_basename in CC*) # Sun C++ 4.x lt_prog_compiler_pic_CXX='-pic' lt_prog_compiler_static_CXX='-Bstatic' ;; lcc*) # Lucid lt_prog_compiler_pic_CXX='-pic' ;; *) ;; esac ;; sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) case $cc_basename in CC*) lt_prog_compiler_wl_CXX='-Wl,' lt_prog_compiler_pic_CXX='-KPIC' lt_prog_compiler_static_CXX='-Bstatic' ;; esac ;; tandem*) case $cc_basename in NCC*) # NonStop-UX NCC 3.20 lt_prog_compiler_pic_CXX='-KPIC' ;; *) ;; esac ;; vxworks*) ;; *) lt_prog_compiler_can_build_shared_CXX=no ;; esac fi case $host_os in # For platforms which do not support PIC, -DPIC is meaningless: *djgpp*) lt_prog_compiler_pic_CXX= ;; *) lt_prog_compiler_pic_CXX="$lt_prog_compiler_pic_CXX -DPIC" ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_prog_compiler_pic_CXX" >&5 $as_echo "$lt_prog_compiler_pic_CXX" >&6; } # # Check to make sure the PIC flag actually works. # if test -n "$lt_prog_compiler_pic_CXX"; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler PIC flag $lt_prog_compiler_pic_CXX works" >&5 $as_echo_n "checking if $compiler PIC flag $lt_prog_compiler_pic_CXX works... " >&6; } if test "${lt_cv_prog_compiler_pic_works_CXX+set}" = set; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_pic_works_CXX=no ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="$lt_prog_compiler_pic_CXX -DPIC" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. # The option is referenced via a variable to avoid confusing sed. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:14346: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 echo "$as_me:14350: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' >conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then lt_cv_prog_compiler_pic_works_CXX=yes fi fi $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works_CXX" >&5 $as_echo "$lt_cv_prog_compiler_pic_works_CXX" >&6; } if test x"$lt_cv_prog_compiler_pic_works_CXX" = xyes; then case $lt_prog_compiler_pic_CXX in "" | " "*) ;; *) lt_prog_compiler_pic_CXX=" $lt_prog_compiler_pic_CXX" ;; esac else lt_prog_compiler_pic_CXX= lt_prog_compiler_can_build_shared_CXX=no fi fi # # Check to make sure the static flag actually works. # wl=$lt_prog_compiler_wl_CXX eval lt_tmp_static_flag=\"$lt_prog_compiler_static_CXX\" { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler static flag $lt_tmp_static_flag works" >&5 $as_echo_n "checking if $compiler static flag $lt_tmp_static_flag works... " >&6; } if test "${lt_cv_prog_compiler_static_works_CXX+set}" = set; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_static_works_CXX=no save_LDFLAGS="$LDFLAGS" LDFLAGS="$LDFLAGS $lt_tmp_static_flag" echo "$lt_simple_link_test_code" > conftest.$ac_ext if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then # The linker can only warn and ignore the option if not recognized # So say no if there are warnings if test -s conftest.err; then # Append any errors to the config.log. cat conftest.err 1>&5 $ECHO "X$_lt_linker_boilerplate" | $Xsed -e '/^$/d' > conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if diff conftest.exp conftest.er2 >/dev/null; then lt_cv_prog_compiler_static_works_CXX=yes fi else lt_cv_prog_compiler_static_works_CXX=yes fi fi $RM -r conftest* LDFLAGS="$save_LDFLAGS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works_CXX" >&5 $as_echo "$lt_cv_prog_compiler_static_works_CXX" >&6; } if test x"$lt_cv_prog_compiler_static_works_CXX" = xyes; then : else lt_prog_compiler_static_CXX= fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 $as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } if test "${lt_cv_prog_compiler_c_o_CXX+set}" = set; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_c_o_CXX=no $RM -r conftest 2>/dev/null mkdir conftest cd conftest mkdir out echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-o out/conftest2.$ac_objext" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:14445: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 echo "$as_me:14449: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' > out/conftest.exp $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then lt_cv_prog_compiler_c_o_CXX=yes fi fi chmod u+w . 2>&5 $RM conftest* # SGI C++ compiler will create directory out/ii_files/ for # template instantiation test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files $RM out/* && rmdir out cd .. $RM -r conftest $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o_CXX" >&5 $as_echo "$lt_cv_prog_compiler_c_o_CXX" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 $as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } if test "${lt_cv_prog_compiler_c_o_CXX+set}" = set; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_c_o_CXX=no $RM -r conftest 2>/dev/null mkdir conftest cd conftest mkdir out echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-o out/conftest2.$ac_objext" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:14497: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 echo "$as_me:14501: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' > out/conftest.exp $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then lt_cv_prog_compiler_c_o_CXX=yes fi fi chmod u+w . 2>&5 $RM conftest* # SGI C++ compiler will create directory out/ii_files/ for # template instantiation test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files $RM out/* && rmdir out cd .. $RM -r conftest $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o_CXX" >&5 $as_echo "$lt_cv_prog_compiler_c_o_CXX" >&6; } hard_links="nottested" if test "$lt_cv_prog_compiler_c_o_CXX" = no && test "$need_locks" != no; then # do not overwrite the value of need_locks provided by the user { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we can lock with hard links" >&5 $as_echo_n "checking if we can lock with hard links... " >&6; } hard_links=yes $RM conftest* ln conftest.a conftest.b 2>/dev/null && hard_links=no touch conftest.a ln conftest.a conftest.b 2>&5 || hard_links=no ln conftest.a conftest.b 2>/dev/null && hard_links=no { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hard_links" >&5 $as_echo "$hard_links" >&6; } if test "$hard_links" = no; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&5 $as_echo "$as_me: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&2;} need_locks=warn fi else need_locks=no fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5 $as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; } export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' case $host_os in aix[4-9]*) # If we're using GNU nm, then we don't want the "-C" option. # -C means demangle to AIX nm, but means don't demangle with GNU nm if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then export_symbols_cmds_CXX='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' else export_symbols_cmds_CXX='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' fi ;; pw32*) export_symbols_cmds_CXX="$ltdll_cmds" ;; cygwin* | mingw* | cegcc*) export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/;/^.*[ ]__nm__/s/^.*[ ]__nm__\([^ ]*\)[ ][^ ]*/\1 DATA/;/^I[ ]/d;/^[AITW][ ]/s/.* //'\'' | sort | uniq > $export_symbols' ;; linux* | k*bsd*-gnu) link_all_deplibs_CXX=no ;; *) export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' ;; esac exclude_expsyms_CXX='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*' { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs_CXX" >&5 $as_echo "$ld_shlibs_CXX" >&6; } test "$ld_shlibs_CXX" = no && can_build_shared=no with_gnu_ld_CXX=$with_gnu_ld # # Do we need to explicitly link libc? # case "x$archive_cmds_need_lc_CXX" in x|xyes) # Assume -lc should be added archive_cmds_need_lc_CXX=yes if test "$enable_shared" = yes && test "$GCC" = yes; then case $archive_cmds_CXX in *'~'*) # FIXME: we may have to deal with multi-command sequences. ;; '$CC '*) # Test whether the compiler implicitly links with -lc since on some # systems, -lgcc has to come before -lc. If gcc already passes -lc # to ld, don't add -lc before -lgcc. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -lc should be explicitly linked in" >&5 $as_echo_n "checking whether -lc should be explicitly linked in... " >&6; } $RM conftest* echo "$lt_simple_compile_test_code" > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } 2>conftest.err; then soname=conftest lib=conftest libobjs=conftest.$ac_objext deplibs= wl=$lt_prog_compiler_wl_CXX pic_flag=$lt_prog_compiler_pic_CXX compiler_flags=-v linker_flags=-v verstring= output_objdir=. libname=conftest lt_save_allow_undefined_flag=$allow_undefined_flag_CXX allow_undefined_flag_CXX= if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$archive_cmds_CXX 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1\""; } >&5 (eval $archive_cmds_CXX 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } then archive_cmds_need_lc_CXX=no else archive_cmds_need_lc_CXX=yes fi allow_undefined_flag_CXX=$lt_save_allow_undefined_flag else cat conftest.err 1>&5 fi $RM conftest* { $as_echo "$as_me:${as_lineno-$LINENO}: result: $archive_cmds_need_lc_CXX" >&5 $as_echo "$archive_cmds_need_lc_CXX" >&6; } ;; esac fi ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking dynamic linker characteristics" >&5 $as_echo_n "checking dynamic linker characteristics... " >&6; } library_names_spec= libname_spec='lib$name' soname_spec= shrext_cmds=".so" postinstall_cmds= postuninstall_cmds= finish_cmds= finish_eval= shlibpath_var= shlibpath_overrides_runpath=unknown version_type=none dynamic_linker="$host_os ld.so" sys_lib_dlsearch_path_spec="/lib /usr/lib" need_lib_prefix=unknown hardcode_into_libs=no # when you set need_version to no, make sure it does not cause -set_version # flags to be left without arguments need_version=unknown case $host_os in aix3*) version_type=linux library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a' shlibpath_var=LIBPATH # AIX 3 has no versioning support, so we append a major version to the name. soname_spec='${libname}${release}${shared_ext}$major' ;; aix[4-9]*) version_type=linux need_lib_prefix=no need_version=no hardcode_into_libs=yes if test "$host_cpu" = ia64; then # AIX 5 supports IA64 library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}' shlibpath_var=LD_LIBRARY_PATH else # With GCC up to 2.95.x, collect2 would create an import file # for dependence libraries. The import file would start with # the line `#! .'. This would cause the generated library to # depend on `.', always an invalid library. This was fixed in # development snapshots of GCC prior to 3.0. case $host_os in aix4 | aix4.[01] | aix4.[01].*) if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' echo ' yes ' echo '#endif'; } | ${CC} -E - | $GREP yes > /dev/null; then : else can_build_shared=no fi ;; esac # AIX (on Power*) has no versioning support, so currently we can not hardcode correct # soname into executable. Probably we can add versioning support to # collect2, so additional links can be useful in future. if test "$aix_use_runtimelinking" = yes; then # If using run time linking (on AIX 4.2 or later) use lib.so # instead of lib.a to let people know that these are not # typical AIX shared libraries. library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' else # We preserve .a as extension for shared libraries through AIX4.2 # and later when we are not doing run time linking. library_names_spec='${libname}${release}.a $libname.a' soname_spec='${libname}${release}${shared_ext}$major' fi shlibpath_var=LIBPATH fi ;; amigaos*) case $host_cpu in powerpc) # Since July 2007 AmigaOS4 officially supports .so libraries. # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' ;; m68k) library_names_spec='$libname.ixlibrary $libname.a' # Create ${libname}_ixlibrary.a entries in /sys/libs. finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`$ECHO "X$lib" | $Xsed -e '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; test $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' ;; esac ;; beos*) library_names_spec='${libname}${shared_ext}' dynamic_linker="$host_os ld.so" shlibpath_var=LIBRARY_PATH ;; bsdi[45]*) version_type=linux need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" # the default ld.so.conf also contains /usr/contrib/lib and # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow # libtool to hard-code these into programs ;; cygwin* | mingw* | pw32* | cegcc*) version_type=windows shrext_cmds=".dll" need_version=no need_lib_prefix=no case $GCC,$host_os in yes,cygwin* | yes,mingw* | yes,pw32* | yes,cegcc*) library_names_spec='$libname.dll.a' # DLL is installed to $(libdir)/../bin by postinstall_cmds postinstall_cmds='base_file=`basename \${file}`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname~ chmod a+x \$dldir/$dlname~ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; fi' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' shlibpath_overrides_runpath=yes case $host_os in cygwin*) # Cygwin DLLs use 'cyg' prefix rather than 'lib' soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' sys_lib_search_path_spec="/usr/lib /lib/w32api /lib /usr/local/lib" ;; mingw* | cegcc*) # MinGW DLLs use traditional 'lib' prefix soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' sys_lib_search_path_spec=`$CC -print-search-dirs | $GREP "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"` if $ECHO "$sys_lib_search_path_spec" | $GREP ';[c-zC-Z]:/' >/dev/null; then # It is most probably a Windows format PATH printed by # mingw gcc, but we are running on Cygwin. Gcc prints its search # path with ; separators, and with drive letters. We can handle the # drive letters (cygwin fileutils understands them), so leave them, # especially as we might pass files found there to a mingw objdump, # which wouldn't understand a cygwinified path. Ahh. sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` else sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` fi ;; pw32*) # pw32 DLLs use 'pw' prefix rather than 'lib' library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' ;; esac ;; *) library_names_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext} $libname.lib' ;; esac dynamic_linker='Win32 ld.exe' # FIXME: first we should search . and the directory the executable is in shlibpath_var=PATH ;; darwin* | rhapsody*) dynamic_linker="$host_os dyld" version_type=darwin need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${major}$shared_ext ${libname}$shared_ext' soname_spec='${libname}${release}${major}$shared_ext' shlibpath_overrides_runpath=yes shlibpath_var=DYLD_LIBRARY_PATH shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' ;; dgux*) version_type=linux need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH ;; freebsd1*) dynamic_linker=no ;; freebsd* | dragonfly*) # DragonFly does not have aout. When/if they implement a new # versioning mechanism, adjust this. if test -x /usr/bin/objformat; then objformat=`/usr/bin/objformat` else case $host_os in freebsd[123]*) objformat=aout ;; *) objformat=elf ;; esac fi version_type=freebsd-$objformat case $version_type in freebsd-elf*) library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' need_version=no need_lib_prefix=no ;; freebsd-*) library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix' need_version=yes ;; esac shlibpath_var=LD_LIBRARY_PATH case $host_os in freebsd2*) shlibpath_overrides_runpath=yes ;; freebsd3.[01]* | freebsdelf3.[01]*) shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; freebsd3.[2-9]* | freebsdelf3.[2-9]* | \ freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1) shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; *) # from 4.6 on, and DragonFly shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; esac ;; gnu*) version_type=linux need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH hardcode_into_libs=yes ;; hpux9* | hpux10* | hpux11*) # Give a soname corresponding to the major version so that dld.sl refuses to # link against other versions. version_type=sunos need_lib_prefix=no need_version=no case $host_cpu in ia64*) shrext_cmds='.so' hardcode_into_libs=yes dynamic_linker="$host_os dld.so" shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' if test "X$HPUX_IA64_MODE" = X32; then sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" else sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" fi sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; hppa*64*) shrext_cmds='.sl' hardcode_into_libs=yes dynamic_linker="$host_os dld.sl" shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; *) shrext_cmds='.sl' dynamic_linker="$host_os dld.sl" shlibpath_var=SHLIB_PATH shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' ;; esac # HP-UX runs *really* slowly unless shared libraries are mode 555. postinstall_cmds='chmod 555 $lib' ;; interix[3-9]*) version_type=linux need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; irix5* | irix6* | nonstopux*) case $host_os in nonstopux*) version_type=nonstopux ;; *) if test "$lt_cv_prog_gnu_ld" = yes; then version_type=linux else version_type=irix fi ;; esac need_lib_prefix=no need_version=no soname_spec='${libname}${release}${shared_ext}$major' library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}' case $host_os in irix5* | nonstopux*) libsuff= shlibsuff= ;; *) case $LD in # libtool.m4 will add one of these switches to LD *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") libsuff= shlibsuff= libmagic=32-bit;; *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") libsuff=32 shlibsuff=N32 libmagic=N32;; *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") libsuff=64 shlibsuff=64 libmagic=64-bit;; *) libsuff= shlibsuff= libmagic=never-match;; esac ;; esac shlibpath_var=LD_LIBRARY${shlibsuff}_PATH shlibpath_overrides_runpath=no sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}" sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}" hardcode_into_libs=yes ;; # No shared lib support for Linux oldld, aout, or coff. linux*oldld* | linux*aout* | linux*coff*) dynamic_linker=no ;; # This must be Linux ELF. linux* | k*bsd*-gnu | kopensolaris*-gnu) version_type=linux need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no # Some binutils ld are patched to set DT_RUNPATH save_LDFLAGS=$LDFLAGS save_libdir=$libdir eval "libdir=/foo; wl=\"$lt_prog_compiler_wl_CXX\"; \ LDFLAGS=\"\$LDFLAGS $hardcode_libdir_flag_spec_CXX\"" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : if ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null; then : shlibpath_overrides_runpath=yes fi fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LDFLAGS=$save_LDFLAGS libdir=$save_libdir # This implies no fast_install, which is unacceptable. # Some rework will be needed to allow for fast_install # before this can be enabled. hardcode_into_libs=yes # Append ld.so.conf contents to the search path if test -f /etc/ld.so.conf; then lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;/^$/d' | tr '\n' ' '` sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" fi # We used to test for /lib/ld.so.1 and disable shared libraries on # powerpc, because MkLinux only supported shared libraries with the # GNU dynamic linker. Since this was broken with cross compilers, # most powerpc-linux boxes support dynamic linking these days and # people can always --disable-shared, the test was removed, and we # assume the GNU/Linux dynamic linker is in use. dynamic_linker='GNU/Linux ld.so' ;; netbsdelf*-gnu) version_type=linux need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes dynamic_linker='NetBSD ld.elf_so' ;; netbsd*) version_type=sunos need_lib_prefix=no need_version=no if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' dynamic_linker='NetBSD (a.out) ld.so' else library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' dynamic_linker='NetBSD ld.elf_so' fi shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; newsos6) version_type=linux library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes ;; *nto* | *qnx*) version_type=qnx need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes dynamic_linker='ldqnx.so' ;; openbsd*) version_type=sunos sys_lib_dlsearch_path_spec="/usr/lib" need_lib_prefix=no # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs. case $host_os in openbsd3.3 | openbsd3.3.*) need_version=yes ;; *) need_version=no ;; esac library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' shlibpath_var=LD_LIBRARY_PATH if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then case $host_os in openbsd2.[89] | openbsd2.[89].*) shlibpath_overrides_runpath=no ;; *) shlibpath_overrides_runpath=yes ;; esac else shlibpath_overrides_runpath=yes fi ;; os2*) libname_spec='$name' shrext_cmds=".dll" need_lib_prefix=no library_names_spec='$libname${shared_ext} $libname.a' dynamic_linker='OS/2 ld.exe' shlibpath_var=LIBPATH ;; osf3* | osf4* | osf5*) version_type=osf need_lib_prefix=no need_version=no soname_spec='${libname}${release}${shared_ext}$major' library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec" ;; rdos*) dynamic_linker=no ;; solaris*) version_type=linux need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes # ldd complains unless libraries are executable postinstall_cmds='chmod +x $lib' ;; sunos4*) version_type=sunos library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes if test "$with_gnu_ld" = yes; then need_lib_prefix=no fi need_version=yes ;; sysv4 | sysv4.3*) version_type=linux library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH case $host_vendor in sni) shlibpath_overrides_runpath=no need_lib_prefix=no runpath_var=LD_RUN_PATH ;; siemens) need_lib_prefix=no ;; motorola) need_lib_prefix=no need_version=no shlibpath_overrides_runpath=no sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' ;; esac ;; sysv4*MP*) if test -d /usr/nec ;then version_type=linux library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}' soname_spec='$libname${shared_ext}.$major' shlibpath_var=LD_LIBRARY_PATH fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) version_type=freebsd-elf need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes if test "$with_gnu_ld" = yes; then sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' else sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' case $host_os in sco3.2v5*) sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" ;; esac fi sys_lib_dlsearch_path_spec='/usr/lib' ;; tpf*) # TPF is a cross-target only. Preferred cross-host = GNU/Linux. version_type=linux need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; uts4*) version_type=linux library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH ;; *) dynamic_linker=no ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: $dynamic_linker" >&5 $as_echo "$dynamic_linker" >&6; } test "$dynamic_linker" = no && can_build_shared=no variables_saved_for_relink="PATH $shlibpath_var $runpath_var" if test "$GCC" = yes; then variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" fi if test "${lt_cv_sys_lib_search_path_spec+set}" = set; then sys_lib_search_path_spec="$lt_cv_sys_lib_search_path_spec" fi if test "${lt_cv_sys_lib_dlsearch_path_spec+set}" = set; then sys_lib_dlsearch_path_spec="$lt_cv_sys_lib_dlsearch_path_spec" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to hardcode library paths into programs" >&5 $as_echo_n "checking how to hardcode library paths into programs... " >&6; } hardcode_action_CXX= if test -n "$hardcode_libdir_flag_spec_CXX" || test -n "$runpath_var_CXX" || test "X$hardcode_automatic_CXX" = "Xyes" ; then # We can hardcode non-existent directories. if test "$hardcode_direct_CXX" != no && # If the only mechanism to avoid hardcoding is shlibpath_var, we # have to relink, otherwise we might link with an installed library # when we should be linking with a yet-to-be-installed one ## test "$_LT_TAGVAR(hardcode_shlibpath_var, CXX)" != no && test "$hardcode_minus_L_CXX" != no; then # Linking always hardcodes the temporary library directory. hardcode_action_CXX=relink else # We can link without hardcoding, and we can hardcode nonexisting dirs. hardcode_action_CXX=immediate fi else # We cannot hardcode anything, or else we can only hardcode existing # directories. hardcode_action_CXX=unsupported fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hardcode_action_CXX" >&5 $as_echo "$hardcode_action_CXX" >&6; } if test "$hardcode_action_CXX" = relink || test "$inherit_rpath_CXX" = yes; then # Fast installation is not supported enable_fast_install=no elif test "$shlibpath_overrides_runpath" = yes || test "$enable_shared" = no; then # Fast installation is not necessary enable_fast_install=needless fi fi # test -n "$compiler" CC=$lt_save_CC LDCXX=$LD LD=$lt_save_LD GCC=$lt_save_GCC with_gnu_ld=$lt_save_with_gnu_ld lt_cv_path_LDCXX=$lt_cv_path_LD lt_cv_path_LD=$lt_save_path_LD lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld fi # test "$_lt_caught_CXX_error" != yes ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_config_commands="$ac_config_commands libtool" # Only expand once: CFLAG_VISIBILITY= HAVE_VISIBILITY=0 if test -n "$GCC"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for simple visibility declarations" >&5 $as_echo_n "checking for simple visibility declarations... " >&6; } if test "${gl_cv_cc_visibility+set}" = set; then : $as_echo_n "(cached) " >&6 else gl_save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -fvisibility=hidden -Werror" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ extern __attribute__((__visibility__("hidden"))) int hiddenvar; extern __attribute__((__visibility__("default"))) int exportedvar; extern __attribute__((__visibility__("hidden"))) int hiddenfunc (void); extern __attribute__((__visibility__("default"))) int exportedfunc (void); int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : gl_cv_cc_visibility=yes else gl_cv_cc_visibility=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS="$gl_save_CFLAGS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $gl_cv_cc_visibility" >&5 $as_echo "$gl_cv_cc_visibility" >&6; } if test $gl_cv_cc_visibility = yes; then CFLAG_VISIBILITY="-fvisibility=hidden" NO_VISIBILITY="-fvisibility=default" HAVE_VISIBILITY=1 fi fi if test "x$SUNCC" = "xyes"; then : CFLAG_VISIBILITY="-xldscope=hidden" NO_VISIBILITY="-xldscope=global" HAVE_VISIBILITY=1 fi cat >>confdefs.h <<_ACEOF #define HAVE_VISIBILITY $HAVE_VISIBILITY _ACEOF acl_libdirstem=lib acl_libdirstem2= case "$host_os" in solaris*) { $as_echo "$as_me:${as_lineno-$LINENO}: checking for 64-bit host" >&5 $as_echo_n "checking for 64-bit host... " >&6; } if test "${gl_cv_solaris_64bit+set}" = set; then : $as_echo_n "(cached) " >&6 else if test "$cross_compiling" = yes; then : { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot run test program while cross compiling See \`config.log' for more details" "$LINENO" 5 ; } else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { return sizeof(void*) == 8 ? 0 : 1; ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : gl_cv_solaris_64bit=yes else gl_cv_solaris_64bit=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $gl_cv_solaris_64bit" >&5 $as_echo "$gl_cv_solaris_64bit" >&6; } if test $gl_cv_solaris_64bit = yes; then acl_libdirstem=lib/64 case "$host_cpu" in sparc*) acl_libdirstem2=lib/sparcv9 ;; i*86 | x86_64) acl_libdirstem2=lib/amd64 ;; esac fi ;; *) searchpath=`(LC_ALL=C $CC -print-search-dirs) 2>/dev/null | sed -n -e 's,^libraries: ,,p' | sed -e 's,^=,,'` if test -n "$searchpath"; then acl_save_IFS="${IFS= }"; IFS=":" for searchdir in $searchpath; do if test -d "$searchdir"; then case "$searchdir" in */lib64/ | */lib64 ) acl_libdirstem=lib64 ;; *) searchdir=`cd "$searchdir" && pwd` case "$searchdir" in */lib64 ) acl_libdirstem=lib64 ;; esac ;; esac fi done IFS="$acl_save_IFS" fi ;; esac test -n "$acl_libdirstem2" || acl_libdirstem2="$acl_libdirstem" if test "X$prefix" = "XNONE"; then acl_final_prefix="$ac_default_prefix" else acl_final_prefix="$prefix" fi if test "X$exec_prefix" = "XNONE"; then acl_final_exec_prefix='${prefix}' else acl_final_exec_prefix="$exec_prefix" fi acl_save_prefix="$prefix" prefix="$acl_final_prefix" eval acl_final_exec_prefix=\"$acl_final_exec_prefix\" prefix="$acl_save_prefix" ac_config_headers="$ac_config_headers config.h" if test "x${prefix}" = "x"; then : as_fn_error $? "--prefix requires an argument" "$LINENO" 5 fi # We need to prevent canonical target # from injecting -O2 into CFLAGS - but we won't modify anything if we have # set CFLAGS on the command line, since that should take ultimate precedence if test "x${ac_cv_env_CFLAGS_set}" = "x"; then : CFLAGS="" fi if test "x${ac_cv_env_CXXFLAGS_set}" = "x"; then : CXXFLAGS="" fi if test "`cd $srcdir && pwd`" != "`pwd`"; then # Use -I$(srcdir) only when $(srcdir) != ., so that make's output # is not polluted with repeated "-I." am__isrc=' -I$(srcdir)' # test to see if srcdir already configured if test -f $srcdir/config.status; then as_fn_error $? "source directory already configured; run \"make distclean\" there first" "$LINENO" 5 fi fi # test whether we have cygpath if test -z "$CYGPATH_W"; then if (cygpath --version) >/dev/null 2>/dev/null; then CYGPATH_W='cygpath -w' else CYGPATH_W=echo fi fi # Define the identity of the package. PACKAGE='haildb' VERSION='2.3.2' cat >>confdefs.h <<_ACEOF #define PACKAGE "$PACKAGE" _ACEOF cat >>confdefs.h <<_ACEOF #define VERSION "$VERSION" _ACEOF # Some tools Automake needs. ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"} AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"} AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"} AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"} MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"} # We need awk for the "check" target. The system "awk" is bad on # some platforms. # Always define AMTAR for backward compatibility. AMTAR=${AMTAR-"${am_missing_run}tar"} { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to create a ustar tar archive" >&5 $as_echo_n "checking how to create a ustar tar archive... " >&6; } # Loop over all known methods to create a tar archive until one works. _am_tools='gnutar plaintar pax cpio none' _am_tools=${am_cv_prog_tar_ustar-$_am_tools} # Do not fold the above two line into one, because Tru64 sh and # Solaris sh will not grok spaces in the rhs of `-'. for _am_tool in $_am_tools do case $_am_tool in gnutar) for _am_tar in tar gnutar gtar; do { echo "$as_me:$LINENO: $_am_tar --version" >&5 ($_am_tar --version) >&5 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && break done am__tar="$_am_tar --format=ustar -chf - "'"$$tardir"' am__tar_="$_am_tar --format=ustar -chf - "'"$tardir"' am__untar="$_am_tar -xf -" ;; plaintar) # Must skip GNU tar: if it does not support --format= it doesn't create # ustar tarball either. (tar --version) >/dev/null 2>&1 && continue am__tar='tar chf - "$$tardir"' am__tar_='tar chf - "$tardir"' am__untar='tar xf -' ;; pax) am__tar='pax -L -x ustar -w "$$tardir"' am__tar_='pax -L -x ustar -w "$tardir"' am__untar='pax -r' ;; cpio) am__tar='find "$$tardir" -print | cpio -o -H ustar -L' am__tar_='find "$tardir" -print | cpio -o -H ustar -L' am__untar='cpio -i -H ustar -d' ;; none) am__tar=false am__tar_=false am__untar=false ;; esac # If the value was cached, stop now. We just wanted to have am__tar # and am__untar set. test -n "${am_cv_prog_tar_ustar}" && break # tar/untar a dummy directory, and stop if the command works rm -rf conftest.dir mkdir conftest.dir echo GrepMe > conftest.dir/file { echo "$as_me:$LINENO: tardir=conftest.dir && eval $am__tar_ >conftest.tar" >&5 (tardir=conftest.dir && eval $am__tar_ >conftest.tar) >&5 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } rm -rf conftest.dir if test -s conftest.tar; then { echo "$as_me:$LINENO: $am__untar &5 ($am__untar &5 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } grep GrepMe conftest.dir/file >/dev/null 2>&1 && break fi done rm -rf conftest.dir if test "${am_cv_prog_tar_ustar+set}" = set; then : $as_echo_n "(cached) " >&6 else am_cv_prog_tar_ustar=$_am_tool fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_tar_ustar" >&5 $as_echo "$am_cv_prog_tar_ustar" >&6; } # Check whether --enable-silent-rules was given. if test "${enable_silent_rules+set}" = set; then : enableval=$enable_silent_rules; fi case $enable_silent_rules in yes) AM_DEFAULT_VERBOSITY=0;; no) AM_DEFAULT_VERBOSITY=1;; *) AM_DEFAULT_VERBOSITY=0;; esac AM_BACKSLASH='\' vc_changelog=no pandora_building_from_vc=no if test -d ".bzr" ; then pandora_building_from_bzr=yes pandora_building_from_vc=yes else pandora_building_from_bzr=no fi if test -d ".svn" ; then pandora_building_from_svn=yes pandora_building_from_vc=yes else pandora_building_from_svn=no fi if test -d ".hg" ; then pandora_building_from_hg=yes pandora_building_from_vc=yes else pandora_building_from_hg=no fi if test -d ".git" ; then pandora_building_from_git=yes pandora_building_from_vc=yes else pandora_building_from_git=no fi PANDORA_RELEASE_ID=`echo $VERSION | sed 's/[^0-9]//g'` PANDORA_RELEASE_COMMENT="" cat >>confdefs.h <<_ACEOF #define PANDORA_RELEASE_VERSION "$VERSION" _ACEOF PANDORA_HEX_VERSION=`echo $VERSION | sed 's|\-a-z0-9*$||' | \ awk -F. '{printf "0x%0.2d%0.3d%0.3d", $1, $2, $3}'` # FIXME: we rely on the cache variable name because # there is no other way. set dummy $CC am_cc=`echo $2 | sed 's/[^a-zA-Z0-9_]/_/g;s/^[0-9]/_/'` eval am_t=\$ac_cv_prog_cc_${am_cc}_c_o if test "$am_t" != yes; then # Losing compiler, so override with the script. # FIXME: It is wrong to rewrite CC. # But if we don't then we get into trouble of one sort or another. # A longer-term fix would be to have automake use am__CC in this case, # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" CC="$am_aux_dir/compile $CC" fi cat >>confdefs.h <<_ACEOF #define HOST_VENDOR "$host_vendor" _ACEOF cat >>confdefs.h <<_ACEOF #define HOST_OS "$host_os" _ACEOF cat >>confdefs.h <<_ACEOF #define HOST_CPU "$host_cpu" _ACEOF cat >>confdefs.h <<_ACEOF #define TARGET_VENDOR "$target_vendor" _ACEOF cat >>confdefs.h <<_ACEOF #define TARGET_OS "$target_os" _ACEOF cat >>confdefs.h <<_ACEOF #define TARGET_CPU "$target_cpu" _ACEOF case "$host_os" in *solaris*) if test "x${ac_cv_env_CPPFLAGS_set}" = "x"; then : CPPFLAGS="${CPPFLAGS} -I/usr/local/include" fi if test "x${ac_cv_env_LDFLAGS_set}" = "x"; then : LDFLAGS="${LDFLAGS} -L/usr/local/lib" fi ;; *freebsd*) if test "x${ac_cv_env_CPPFLAGS_set}" = "x"; then : CPPFLAGS="${CPPFLAGS} -isystem /usr/local/include" fi if test "x${ac_cv_env_LDFLAGS_set}" = "x"; then : LDFLAGS="${LDFLAGS} -L/usr/local/lib" fi ;; esac PANDORA_OPTIMIZE_BITFIELD=1 case "$target_os" in *linux*) TARGET_LINUX="true" $as_echo "#define TARGET_OS_LINUX 1" >>confdefs.h ;; *darwin*) TARGET_OSX="true" $as_echo "#define TARGET_OS_OSX 1" >>confdefs.h ;; *solaris*) TARGET_SOLARIS="true" PANDORA_OPTIMIZE_BITFIELD=0 if test "x${USE_NLS}" = "xyes"; then : LIBS="${LIBS} -lintl" fi $as_echo "#define TARGET_OS_SOLARIS 1" >>confdefs.h ;; *freebsd*) TARGET_FREEBSD="true" $as_echo "#define TARGET_OS_FREEBSD 1" >>confdefs.h $as_echo "#define __APPLE_CC__ 1" >>confdefs.h ;; *mingw32*) TARGET_WINDOWS="true" $as_echo "#define TARGET_OS_WINDOWS 1" >>confdefs.h $as_echo "#define WINVER WindowsXP" >>confdefs.h $as_echo "#define _WIN32_WINNT 0x0501" >>confdefs.h $as_echo "#define EAI_SYSTEM 11" >>confdefs.h LIBS="$LIBS -lwsock32 -lws2_32" AM_CFLAGS="${AM_CFLAGS} -I\${top_srcdir}/win32/mingw -I\${top_builddir}/win32/mingw -I\${top_srcdir}/win32 -I\${top_builddir}/win32" ;; esac if test "x${TARGET_WINDOWS}" = "xtrue"; then BUILD_WIN32_TRUE= BUILD_WIN32_FALSE='#' else BUILD_WIN32_TRUE='#' BUILD_WIN32_FALSE= fi ac_fn_c_check_decl "$LINENO" "__SUNPRO_C" "ac_cv_have_decl___SUNPRO_C" "$ac_includes_default" if test "x$ac_cv_have_decl___SUNPRO_C" = x""yes; then : SUNCC="yes" else SUNCC="no" fi ac_fn_c_check_decl "$LINENO" "__ICC" "ac_cv_have_decl___ICC" "$ac_includes_default" if test "x$ac_cv_have_decl___ICC" = x""yes; then : INTELCC="yes" else INTELCC="no" fi if test "$INTELCC" = "yes"; then : enable_rpath=no fi if test "$SUNCC" = "yes"; then : $as_echo "#define _STLP_NO_NEW_C_HEADERS 1" >>confdefs.h fi if test "x$TARGET_OSX" = "xtrue"; then : if test "x$ac_enable_fat_binaries" = "xyes"; then : AM_CFLAGS="-arch i386 -arch x86_64 -arch ppc" AM_CXXFLAGS="-arch i386 -arch x86_64 -arch ppc" AM_LDFLAGS="-arch i386 -arch x86_64 -arch ppc" fi fi pandora_have_old_libtool=no if test "$SUNCC" = "yes" -a "${pandora_have_old_libtool}" = "yes"; then : as_fn_error $? "Building ${PACKAGE} with Sun Studio requires at least libtool 2.2" "$LINENO" 5 fi if test "$GCC" = "yes"; then : if test "$ac_cv_cxx_compile_cxx0x_native" = "yes"; then : else if test "$ac_cv_cxx_compile_cxx0x_gxx" = "yes"; then : CXX_STANDARD="-std=gnu++0x" else CXX_STANDARD="-std=gnu++98" fi fi fi AM_CXXFLAGS="${CXX_STANDARD} ${AM_CXXFLAGS}" save_CXXFLAGS="${CXXFLAGS}" CXXFLAGS="${CXXFLAGS} ${CXX_STANDARD}" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ISO C++ 98 include files" >&5 $as_echo_n "checking for ISO C++ 98 include files... " >&6; } if test "${ac_cv_cxx_stdcxx_98+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_cv_cxx_stdcxx_98=yes else ac_cv_cxx_stdcxx_98=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_stdcxx_98" >&5 $as_echo "$ac_cv_cxx_stdcxx_98" >&6; } if test "$ac_cv_cxx_stdcxx_98" = yes; then $as_echo "#define STDCXX_98_HEADERS /**/" >>confdefs.h fi CXXFLAGS="${save_CXXFLAGS}" { $as_echo "$as_me:${as_lineno-$LINENO}: checking the location of cstdint" >&5 $as_echo_n "checking the location of cstdint... " >&6; } ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu save_CXXFLAGS="${CXXFLAGS}" CXXFLAGS="${CXX_STANDARD} ${CXXFLAGS}" ac_cv_cxx_cstdint="" for location in tr1/cstdint boost/cstdint cstdint; do if test -z "$ac_cv_cxx_cstdint"; then cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include <$location> int main () { uint32_t t ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_cv_cxx_cstdint="<$location>"; fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi done ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu CXXFLAGS="${save_CXXFLAGS}" if test -n "$ac_cv_cxx_cstdint"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_cstdint" >&5 $as_echo "$ac_cv_cxx_cstdint" >&6; } else $as_echo "#define __STDC_CONSTANT_MACROS 1" >>confdefs.h $as_echo "#define __STDC_FORMAT_MACROS 1" >>confdefs.h ac_cv_cxx_cstdint="" { $as_echo "$as_me:${as_lineno-$LINENO}: result: " >&5 $as_echo "" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Could not find a cstdint header." >&5 $as_echo "$as_me: WARNING: Could not find a cstdint header." >&2;} fi cat >>confdefs.h <<_ACEOF #define CSTDINT_H $ac_cv_cxx_cstdint _ACEOF { $as_echo "$as_me:${as_lineno-$LINENO}: checking the location of cinttypes" >&5 $as_echo_n "checking the location of cinttypes... " >&6; } ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu save_CXXFLAGS="${CXXFLAGS}" CXXFLAGS="${CXX_STANDARD} ${CXXFLAGS}" ac_cv_cxx_cinttypes="" for location in tr1/cinttypes boost/cinttypes cinttypes; do if test -z "$ac_cv_cxx_cinttypes"; then cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include $ac_cv_cxx_cstdint; #include <$location> int main () { uint32_t foo= UINT32_C(1) ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_cv_cxx_cinttypes="<$location>"; fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi done ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu CXXFLAGS="${save_CXXFLAGS}" if test -n "$ac_cv_cxx_cinttypes"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_cinttypes" >&5 $as_echo "$ac_cv_cxx_cinttypes" >&6; } else ac_cv_cxx_cinttypes="" { $as_echo "$as_me:${as_lineno-$LINENO}: result: " >&5 $as_echo "" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Could not find a cinttypes header." >&5 $as_echo "$as_me: WARNING: Could not find a cinttypes header." >&2;} fi $as_echo "#define __STDC_LIMIT_MACROS 1" >>confdefs.h cat >>confdefs.h <<_ACEOF #define CINTTYPES_H $ac_cv_cxx_cinttypes _ACEOF { $as_echo "$as_me:${as_lineno-$LINENO}: checking \"C Compiler version--$GCC\"" >&5 $as_echo_n "checking \"C Compiler version--$GCC\"... " >&6; } if test "$GCC" = "yes"; then : CC_VERSION=`$CC --version | sed 1q` else if test "$SUNCC" = "yes"; then : CC_VERSION=`$CC -V 2>&1 | sed 1q` else CC_VERSION="" fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: \"$CC_VERSION\"" >&5 $as_echo "\"$CC_VERSION\"" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking \"C++ Compiler version\"" >&5 $as_echo_n "checking \"C++ Compiler version\"... " >&6; } if test "$GCC" = "yes"; then : CXX_VERSION=`$CXX --version | sed 1q` else if test "$SUNCC" = "yes"; then : CXX_VERSION=`$CXX -V 2>&1 | sed 1q` else CXX_VERSION="" fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: \"$CXX_VERSION\"" >&5 $as_echo "\"$CXX_VERSION\"" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether byte ordering is bigendian" >&5 $as_echo_n "checking whether byte ordering is bigendian... " >&6; } if test "${ac_cv_c_bigendian+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_cv_c_bigendian=unknown # See if we're dealing with a universal compiler. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifndef __APPLE_CC__ not a universal capable compiler #endif typedef int dummy; _ACEOF if ac_fn_c_try_compile "$LINENO"; then : # Check for potential -arch flags. It is not universal unless # there are at least two -arch flags with different values. ac_arch= ac_prev= for ac_word in $CC $CFLAGS $CPPFLAGS $LDFLAGS; do if test -n "$ac_prev"; then case $ac_word in i?86 | x86_64 | ppc | ppc64) if test -z "$ac_arch" || test "$ac_arch" = "$ac_word"; then ac_arch=$ac_word else ac_cv_c_bigendian=universal break fi ;; esac ac_prev= elif test "x$ac_word" = "x-arch"; then ac_prev=arch fi done fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext if test $ac_cv_c_bigendian = unknown; then # See if sys/param.h defines the BYTE_ORDER macro. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main () { #if ! (defined BYTE_ORDER && defined BIG_ENDIAN \ && defined LITTLE_ENDIAN && BYTE_ORDER && BIG_ENDIAN \ && LITTLE_ENDIAN) bogus endian macros #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : # It does; now see whether it defined to BIG_ENDIAN or not. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main () { #if BYTE_ORDER != BIG_ENDIAN not big endian #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_c_bigendian=yes else ac_cv_c_bigendian=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi if test $ac_cv_c_bigendian = unknown; then # See if defines _LITTLE_ENDIAN or _BIG_ENDIAN (e.g., Solaris). cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { #if ! (defined _LITTLE_ENDIAN || defined _BIG_ENDIAN) bogus endian macros #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : # It does; now see whether it defined to _BIG_ENDIAN or not. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { #ifndef _BIG_ENDIAN not big endian #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_c_bigendian=yes else ac_cv_c_bigendian=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi if test $ac_cv_c_bigendian = unknown; then # Compile a test program. if test "$cross_compiling" = yes; then : # Try to guess by grepping values from an object file. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ short int ascii_mm[] = { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 }; short int ascii_ii[] = { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 }; int use_ascii (int i) { return ascii_mm[i] + ascii_ii[i]; } short int ebcdic_ii[] = { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 }; short int ebcdic_mm[] = { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 }; int use_ebcdic (int i) { return ebcdic_mm[i] + ebcdic_ii[i]; } extern int foo; int main () { return use_ascii (foo) == use_ebcdic (foo); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : if grep BIGenDianSyS conftest.$ac_objext >/dev/null; then ac_cv_c_bigendian=yes fi if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then if test "$ac_cv_c_bigendian" = unknown; then ac_cv_c_bigendian=no else # finding both strings is unlikely to happen, but who knows? ac_cv_c_bigendian=unknown fi fi fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default int main () { /* Are we little or big endian? From Harbison&Steele. */ union { long int l; char c[sizeof (long int)]; } u; u.l = 1; return u.c[sizeof (long int) - 1] == 1; ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : ac_cv_c_bigendian=no else ac_cv_c_bigendian=yes fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_bigendian" >&5 $as_echo "$ac_cv_c_bigendian" >&6; } case $ac_cv_c_bigendian in #( yes) $as_echo "#define WORDS_BIGENDIAN 1" >>confdefs.h ;; #( no) ;; #( universal) $as_echo "#define AC_APPLE_UNIVERSAL_BUILD 1" >>confdefs.h ;; #( *) as_fn_error $? "unknown endianness presetting ac_cv_c_bigendian=no (or yes) will help" "$LINENO" 5 ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for an ANSI C-conforming const" >&5 $as_echo_n "checking for an ANSI C-conforming const... " >&6; } if test "${ac_cv_c_const+set}" = set; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { /* FIXME: Include the comments suggested by Paul. */ #ifndef __cplusplus /* Ultrix mips cc rejects this. */ typedef int charset[2]; const charset cs; /* SunOS 4.1.1 cc rejects this. */ char const *const *pcpcc; char **ppc; /* NEC SVR4.0.2 mips cc rejects this. */ struct point {int x, y;}; static struct point const zero = {0,0}; /* AIX XL C 1.02.0.0 rejects this. It does not let you subtract one const X* pointer from another in an arm of an if-expression whose if-part is not a constant expression */ const char *g = "string"; pcpcc = &g + (g ? g-g : 0); /* HPUX 7.0 cc rejects these. */ ++pcpcc; ppc = (char**) pcpcc; pcpcc = (char const *const *) ppc; { /* SCO 3.2v4 cc rejects this. */ char *t; char const *s = 0 ? (char *) 0 : (char const *) 0; *t++ = 0; if (s) return 0; } { /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */ int x[] = {25, 17}; const int *foo = &x[0]; ++foo; } { /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */ typedef const int *iptr; iptr p = 0; ++p; } { /* AIX XL C 1.02.0.0 rejects this saying "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */ struct s { int j; const int *ap[3]; }; struct s *b; b->j = 5; } { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ const int foo = 10; if (!foo) return 0; } return !cs[0] && !zero.x; #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_c_const=yes else ac_cv_c_const=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_const" >&5 $as_echo "$ac_cv_c_const" >&6; } if test $ac_cv_c_const = no; then $as_echo "#define const /**/" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for inline" >&5 $as_echo_n "checking for inline... " >&6; } if test "${ac_cv_c_inline+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_cv_c_inline=no for ac_kw in inline __inline__ __inline; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifndef __cplusplus typedef int foo_t; static $ac_kw foo_t static_foo () {return 0; } $ac_kw foo_t foo () {return 0; } #endif _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_c_inline=$ac_kw fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext test "$ac_cv_c_inline" != no && break done fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_inline" >&5 $as_echo "$ac_cv_c_inline" >&6; } case $ac_cv_c_inline in inline | yes) ;; *) case $ac_cv_c_inline in no) ac_val=;; *) ac_val=$ac_cv_c_inline;; esac cat >>confdefs.h <<_ACEOF #ifndef __cplusplus #define inline $ac_val #endif _ACEOF ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working volatile" >&5 $as_echo_n "checking for working volatile... " >&6; } if test "${ac_cv_c_volatile+set}" = set; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { volatile int x; int * volatile y = (int *) 0; return !x && !y; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_c_volatile=yes else ac_cv_c_volatile=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_volatile" >&5 $as_echo "$ac_cv_c_volatile" >&6; } if test $ac_cv_c_volatile = no; then $as_echo "#define volatile /**/" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for C/C++ restrict keyword" >&5 $as_echo_n "checking for C/C++ restrict keyword... " >&6; } if test "${ac_cv_c_restrict+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_cv_c_restrict=no # The order here caters to the fact that C++ does not require restrict. for ac_kw in __restrict __restrict__ _Restrict restrict; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ typedef int * int_ptr; int foo (int_ptr $ac_kw ip) { return ip[0]; } int main () { int s[1]; int * $ac_kw t = s; t[0] = 0; return foo(t) ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_c_restrict=$ac_kw fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext test "$ac_cv_c_restrict" != no && break done fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_restrict" >&5 $as_echo "$ac_cv_c_restrict" >&6; } case $ac_cv_c_restrict in restrict) ;; no) $as_echo "#define restrict /**/" >>confdefs.h ;; *) cat >>confdefs.h <<_ACEOF #define restrict $ac_cv_c_restrict _ACEOF ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether time.h and sys/time.h may both be included" >&5 $as_echo_n "checking whether time.h and sys/time.h may both be included... " >&6; } if test "${ac_cv_header_time+set}" = set; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include int main () { if ((struct tm *) 0) return 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_header_time=yes else ac_cv_header_time=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_time" >&5 $as_echo "$ac_cv_header_time" >&6; } if test $ac_cv_header_time = yes; then $as_echo "#define TIME_WITH_SYS_TIME 1" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether struct tm is in sys/time.h or time.h" >&5 $as_echo_n "checking whether struct tm is in sys/time.h or time.h... " >&6; } if test "${ac_cv_struct_tm+set}" = set; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main () { struct tm tm; int *p = &tm.tm_sec; return !p; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_struct_tm=time.h else ac_cv_struct_tm=sys/time.h fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_struct_tm" >&5 $as_echo "$ac_cv_struct_tm" >&6; } if test $ac_cv_struct_tm = sys/time.h; then $as_echo "#define TM_IN_SYS_TIME 1" >>confdefs.h fi ac_fn_c_check_type "$LINENO" "size_t" "ac_cv_type_size_t" "$ac_includes_default" if test "x$ac_cv_type_size_t" = x""yes; then : else cat >>confdefs.h <<_ACEOF #define size_t unsigned int _ACEOF fi # Check whether --enable-largefile was given. if test "${enable_largefile+set}" = set; then : enableval=$enable_largefile; fi if test "$enable_largefile" != no; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for special C compiler options needed for large files" >&5 $as_echo_n "checking for special C compiler options needed for large files... " >&6; } if test "${ac_cv_sys_largefile_CC+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_cv_sys_largefile_CC=no if test "$GCC" != yes; then ac_save_CC=$CC while :; do # IRIX 6.2 and later do not support large files by default, # so use the C compiler's -n32 option if that helps. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : break fi rm -f core conftest.err conftest.$ac_objext CC="$CC -n32" if ac_fn_c_try_compile "$LINENO"; then : ac_cv_sys_largefile_CC=' -n32'; break fi rm -f core conftest.err conftest.$ac_objext break done CC=$ac_save_CC rm -f conftest.$ac_ext fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_largefile_CC" >&5 $as_echo "$ac_cv_sys_largefile_CC" >&6; } if test "$ac_cv_sys_largefile_CC" != no; then CC=$CC$ac_cv_sys_largefile_CC fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _FILE_OFFSET_BITS value needed for large files" >&5 $as_echo_n "checking for _FILE_OFFSET_BITS value needed for large files... " >&6; } if test "${ac_cv_sys_file_offset_bits+set}" = set; then : $as_echo_n "(cached) " >&6 else while :; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_sys_file_offset_bits=no; break fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define _FILE_OFFSET_BITS 64 #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_sys_file_offset_bits=64; break fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_sys_file_offset_bits=unknown break done fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_file_offset_bits" >&5 $as_echo "$ac_cv_sys_file_offset_bits" >&6; } case $ac_cv_sys_file_offset_bits in #( no | unknown) ;; *) cat >>confdefs.h <<_ACEOF #define _FILE_OFFSET_BITS $ac_cv_sys_file_offset_bits _ACEOF ;; esac rm -rf conftest* if test $ac_cv_sys_file_offset_bits = unknown; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _LARGE_FILES value needed for large files" >&5 $as_echo_n "checking for _LARGE_FILES value needed for large files... " >&6; } if test "${ac_cv_sys_large_files+set}" = set; then : $as_echo_n "(cached) " >&6 else while :; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_sys_large_files=no; break fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define _LARGE_FILES 1 #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_sys_large_files=1; break fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_sys_large_files=unknown break done fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_large_files" >&5 $as_echo "$ac_cv_sys_large_files" >&6; } case $ac_cv_sys_large_files in #( no | unknown) ;; *) cat >>confdefs.h <<_ACEOF #define _LARGE_FILES $ac_cv_sys_large_files _ACEOF ;; esac rm -rf conftest* fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing clock_gettime" >&5 $as_echo_n "checking for library containing clock_gettime... " >&6; } if test "${ac_cv_search_clock_gettime+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char clock_gettime (); int main () { return clock_gettime (); ; return 0; } _ACEOF for ac_lib in '' rt; do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO"; then : ac_cv_search_clock_gettime=$ac_res fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext if test "${ac_cv_search_clock_gettime+set}" = set; then : break fi done if test "${ac_cv_search_clock_gettime+set}" = set; then : else ac_cv_search_clock_gettime=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_clock_gettime" >&5 $as_echo "$ac_cv_search_clock_gettime" >&6; } ac_res=$ac_cv_search_clock_gettime if test "$ac_res" != no; then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi if test "x${ac_cv_search_clock_gettime}" != "xno"; then : $as_echo "#define HAVE_CLOCK_GETTIME 1" >>confdefs.h fi for ac_header in sys/socket.h do : ac_fn_c_check_header_mongrel "$LINENO" "sys/socket.h" "ac_cv_header_sys_socket_h" "$ac_includes_default" if test "x$ac_cv_header_sys_socket_h" = x""yes; then : cat >>confdefs.h <<_ACEOF #define HAVE_SYS_SOCKET_H 1 _ACEOF fi done # off_t is not a builtin type # The cast to long int works around a bug in the HP C Compiler # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects # declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. # This bug is HP SR number 8606223364. { $as_echo "$as_me:${as_lineno-$LINENO}: checking size of off_t" >&5 $as_echo_n "checking size of off_t... " >&6; } if test "${ac_cv_sizeof_off_t+set}" = set; then : $as_echo_n "(cached) " >&6 else if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (off_t))" "ac_cv_sizeof_off_t" "$ac_includes_default"; then : else if test "$ac_cv_type_off_t" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "cannot compute sizeof (off_t) See \`config.log' for more details" "$LINENO" 5 ; } else ac_cv_sizeof_off_t=0 fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_off_t" >&5 $as_echo "$ac_cv_sizeof_off_t" >&6; } cat >>confdefs.h <<_ACEOF #define SIZEOF_OFF_T $ac_cv_sizeof_off_t _ACEOF if test "$ac_cv_sizeof_off_t" -eq 0; then : as_fn_error $? "\"${PACKAGE} needs an off_t type.\"" "$LINENO" 5 fi # The cast to long int works around a bug in the HP C Compiler # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects # declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. # This bug is HP SR number 8606223364. { $as_echo "$as_me:${as_lineno-$LINENO}: checking size of size_t" >&5 $as_echo_n "checking size of size_t... " >&6; } if test "${ac_cv_sizeof_size_t+set}" = set; then : $as_echo_n "(cached) " >&6 else if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (size_t))" "ac_cv_sizeof_size_t" "$ac_includes_default"; then : else if test "$ac_cv_type_size_t" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "cannot compute sizeof (size_t) See \`config.log' for more details" "$LINENO" 5 ; } else ac_cv_sizeof_size_t=0 fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_size_t" >&5 $as_echo "$ac_cv_sizeof_size_t" >&6; } cat >>confdefs.h <<_ACEOF #define SIZEOF_SIZE_T $ac_cv_sizeof_size_t _ACEOF if test "$ac_cv_sizeof_size_t" -eq 0; then : as_fn_error $? "\"${PACKAGE} needs an size_t type.\"" "$LINENO" 5 fi cat >>confdefs.h <<_ACEOF #define SIZEOF_SIZE_T $ac_cv_sizeof_size_t _ACEOF # The cast to long int works around a bug in the HP C Compiler # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects # declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. # This bug is HP SR number 8606223364. { $as_echo "$as_me:${as_lineno-$LINENO}: checking size of long long" >&5 $as_echo_n "checking size of long long... " >&6; } if test "${ac_cv_sizeof_long_long+set}" = set; then : $as_echo_n "(cached) " >&6 else if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (long long))" "ac_cv_sizeof_long_long" "$ac_includes_default"; then : else if test "$ac_cv_type_long_long" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "cannot compute sizeof (long long) See \`config.log' for more details" "$LINENO" 5 ; } else ac_cv_sizeof_long_long=0 fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_long_long" >&5 $as_echo "$ac_cv_sizeof_long_long" >&6; } cat >>confdefs.h <<_ACEOF #define SIZEOF_LONG_LONG $ac_cv_sizeof_long_long _ACEOF cat >>confdefs.h <<_ACEOF #define SIZEOF_LONG_LONG $ac_cv_sizeof_long_long _ACEOF { $as_echo "$as_me:${as_lineno-$LINENO}: checking if time_t is unsigned" >&5 $as_echo_n "checking if time_t is unsigned... " >&6; } if test "${ac_cv_time_t_unsigned+set}" = set; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { int array[(((time_t)-1) > 0) ? 1 : -1]; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_time_t_unsigned=yes else ac_cv_time_t_unsigned=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_time_t_unsigned" >&5 $as_echo "$ac_cv_time_t_unsigned" >&6; } if test "$ac_cv_time_t_unsigned" = "yes"; then : $as_echo "#define TIME_T_UNSIGNED 1" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking if system defines RUSAGE_THREAD" >&5 $as_echo_n "checking if system defines RUSAGE_THREAD... " >&6; } if test "${ac_cv_rusage_thread+set}" = set; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main () { int x= RUSAGE_THREAD; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_rusage_thread=yes else ac_cv_rusage_thread=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_rusage_thread" >&5 $as_echo "$ac_cv_rusage_thread" >&6; } if test "$ac_cv_rusage_thread" = "no"; then : $as_echo "#define RUSAGE_THREAD RUSAGE_SELF" >>confdefs.h fi LIBM= case $host in *-*-beos* | *-*-cygwin* | *-*-pw32* | *-*-darwin*) # These system don't have libm, or don't need it ;; *-ncr-sysv4.3*) { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _mwvalidcheckl in -lmw" >&5 $as_echo_n "checking for _mwvalidcheckl in -lmw... " >&6; } if test "${ac_cv_lib_mw__mwvalidcheckl+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lmw $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char _mwvalidcheckl (); int main () { return _mwvalidcheckl (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_mw__mwvalidcheckl=yes else ac_cv_lib_mw__mwvalidcheckl=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_mw__mwvalidcheckl" >&5 $as_echo "$ac_cv_lib_mw__mwvalidcheckl" >&6; } if test "x$ac_cv_lib_mw__mwvalidcheckl" = x""yes; then : LIBM="-lmw" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for cos in -lm" >&5 $as_echo_n "checking for cos in -lm... " >&6; } if test "${ac_cv_lib_m_cos+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lm $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char cos (); int main () { return cos (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_m_cos=yes else ac_cv_lib_m_cos=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_m_cos" >&5 $as_echo "$ac_cv_lib_m_cos" >&6; } if test "x$ac_cv_lib_m_cos" = x""yes; then : LIBM="$LIBM -lm" fi ;; *) { $as_echo "$as_me:${as_lineno-$LINENO}: checking for cos in -lm" >&5 $as_echo_n "checking for cos in -lm... " >&6; } if test "${ac_cv_lib_m_cos+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lm $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char cos (); int main () { return cos (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_m_cos=yes else ac_cv_lib_m_cos=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_m_cos" >&5 $as_echo "$ac_cv_lib_m_cos" >&6; } if test "x$ac_cv_lib_m_cos" = x""yes; then : LIBM="-lm" fi ;; esac ac_fn_c_check_func "$LINENO" "setsockopt" "ac_cv_func_setsockopt" if test "x$ac_cv_func_setsockopt" = x""yes; then : else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for setsockopt in -lsocket" >&5 $as_echo_n "checking for setsockopt in -lsocket... " >&6; } if test "${ac_cv_lib_socket_setsockopt+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lsocket $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char setsockopt (); int main () { return setsockopt (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_socket_setsockopt=yes else ac_cv_lib_socket_setsockopt=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_socket_setsockopt" >&5 $as_echo "$ac_cv_lib_socket_setsockopt" >&6; } if test "x$ac_cv_lib_socket_setsockopt" = x""yes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBSOCKET 1 _ACEOF LIBS="-lsocket $LIBS" fi fi ac_fn_c_check_func "$LINENO" "bind" "ac_cv_func_bind" if test "x$ac_cv_func_bind" = x""yes; then : else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for bind in -lbind" >&5 $as_echo_n "checking for bind in -lbind... " >&6; } if test "${ac_cv_lib_bind_bind+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lbind $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char bind (); int main () { return bind (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_bind_bind=yes else ac_cv_lib_bind_bind=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_bind_bind" >&5 $as_echo "$ac_cv_lib_bind_bind" >&6; } if test "x$ac_cv_lib_bind_bind" = x""yes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBBIND 1 _ACEOF LIBS="-lbind $LIBS" fi fi if test "$GCC" = "yes" -a "$INTELCC" = "no"; then : case "$target_cpu" in *ppc* | *powerpc*) AM_CFLAGS="-mno-fused-madd ${AM_CFLAGS}" AM_CXXFLAGS="-mno-fused-madd ${AM_CXXFLAGS}" ;; esac CC="${CC} -std=gnu99" AM_CPPFLAGS="-g ${AM_CPPFLAGS}" DEBUG_CFLAGS="-O0" DEBUG_CXXFLAGS="-O0" OPTIMIZE_CFLAGS="-O2" OPTIMIZE_CXXFLAGS="-O2" fi if test "$INTELCC" = "yes"; then : AM_CPPFLAGS="-g ${AM_CPPFLAGS}" DEBUG_CFLAGS="-O0" DEBUG_CXXFLAGS="-O0" OPTIMIZE_CFLAGS="-xHOST -O2 -no-prec-div -static" OPTIMIZE_CXXFLAGS="${OPTIMIZE_CFLAGS}" fi if test "$SUNCC" = "yes"; then : CC="${CC} -xc99=all" CXX="${CXX} -xlang=c99" AM_CFLAGS="-g -mt -xstrconst -Xa ${AM_CFLAGS}" AM_CXXFLAGS="-mt -compat=5 -library=stlport4 -library=Crun -template=no%extdef ${AM_CXXFLAGS}" DEBUG_CXXFLAGS="-g" OPTIMIZE_FLAGS="-xO3 -xlibmil -xdepend -xbuiltin" OPTIMIZE_CFLAGS="${OPTIMIZE_FLAGS}" OPTIMIZE_CXXFLAGS="-g0 ${OPTIMIZE_FLAGS}" fi # Check whether --with-debug was given. if test "${with_debug+set}" = set; then : withval=$with_debug; with_debug=$withval else with_debug=no fi if test "$with_debug" = "yes"; then : # Debugging. No optimization. AM_CFLAGS="${AM_CFLAGS} ${DEBUG_CFLAGS} -DDEBUG" AM_CXXFLAGS="${AM_CXXFLAGS} ${DEBUG_CXXFLAGS} -DDEBUG" else # Optimized version. No debug AM_CFLAGS="${AM_CFLAGS} ${OPTIMIZE_CFLAGS}" AM_CXXFLAGS="${AM_CXXFLAGS} ${OPTIMIZE_CXXFLAGS}" fi ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu # Test whether madvise() is declared in C++ code -- it is not on some # systems, such as Solaris ac_fn_cxx_check_decl "$LINENO" "madvise" "ac_cv_have_decl_madvise" "$ac_includes_default #if HAVE_SYS_MMAN_H #include #include #endif " if test "x$ac_cv_have_decl_madvise" = x""yes; then : ac_have_decl=1 else ac_have_decl=0 fi cat >>confdefs.h <<_ACEOF #define HAVE_DECL_MADVISE $ac_have_decl _ACEOF ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the compiler provides atomic builtins" >&5 $as_echo_n "checking whether the compiler provides atomic builtins... " >&6; } if test "${ac_cv_gcc_atomic_builtins+set}" = set; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { int foo= -10; int bar= 10; if (!__sync_fetch_and_add(&foo, bar) || foo) return -1; bar= __sync_lock_test_and_set(&foo, bar); if (bar || foo != 10) return -1; bar= __sync_val_compare_and_swap(&bar, foo, 15); if (bar) return -1; return 0; ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_gcc_atomic_builtins=yes else ac_cv_gcc_atomic_builtins=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_gcc_atomic_builtins" >&5 $as_echo "$ac_cv_gcc_atomic_builtins" >&6; } if test "x$ac_cv_gcc_atomic_builtins" = "xyes"; then : $as_echo "#define HAVE_GCC_ATOMIC_BUILTINS 1" >>confdefs.h fi AM_CFLAGS="${AM_CFLAGS} ${CFLAG_VISIBILITY}" AM_CXXFLAGS="${AM_CXXFLAGS} ${CFLAG_VISIBILITY}" for ac_header in assert.h do : ac_fn_c_check_header_mongrel "$LINENO" "assert.h" "ac_cv_header_assert_h" "$ac_includes_default" if test "x$ac_cv_header_assert_h" = x""yes; then : cat >>confdefs.h <<_ACEOF #define HAVE_ASSERT_H 1 _ACEOF fi done { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable assertions" >&5 $as_echo_n "checking whether to enable assertions... " >&6; } # Check whether --enable-assert was given. if test "${enable_assert+set}" = set; then : enableval=$enable_assert; ac_cv_assert="no" else ac_cv_assert="yes" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_assert" >&5 $as_echo "$ac_cv_assert" >&6; } if test "$ac_cv_assert" = "no"; then : $as_echo "#define NDEBUG 1" >>confdefs.h fi if test "$pandora_building_from_vc" = "yes"; then : ac_cv_warnings_as_errors=yes else ac_cv_warnings_as_errors=no fi # Check whether --enable-gcc-profile-mode was given. if test "${enable_gcc_profile_mode+set}" = set; then : enableval=$enable_gcc_profile_mode; ac_gcc_profile_mode="$enableval" else ac_gcc_profile_mode="no" fi # Check whether --enable-profiling was given. if test "${enable_profiling+set}" = set; then : enableval=$enable_profiling; ac_profiling="$enableval" else ac_profiling="no" fi # Check whether --enable-coverage was given. if test "${enable_coverage+set}" = set; then : enableval=$enable_coverage; ac_coverage="$enableval" else ac_coverage="no" fi if test "$GCC" = "yes"; then : if test "$ac_profiling" = "yes"; then : CC_PROFILING="-pg" GCOV_LIBS="-pg -lgcov" save_LIBS="${LIBS}" LIBS="" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for read in -lc_p" >&5 $as_echo_n "checking for read in -lc_p... " >&6; } if test "${ac_cv_lib_c_p_read+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lc_p $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char read (); int main () { return read (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_c_p_read=yes else ac_cv_lib_c_p_read=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_c_p_read" >&5 $as_echo "$ac_cv_lib_c_p_read" >&6; } if test "x$ac_cv_lib_c_p_read" = x""yes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBC_P 1 _ACEOF LIBS="-lc_p $LIBS" fi LIBC_P="${LIBS}" LIBS="${save_LIBS}" else CC_PROFILING=" " fi if test "$ac_coverage" = "yes"; then : CC_COVERAGE="--coverage" GCOV_LIBS="-lgcov" fi if test "$ac_cv_warnings_as_errors" = "yes"; then : W_FAIL="-Werror" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether it is safe to use -fdiagnostics-show-option" >&5 $as_echo_n "checking whether it is safe to use -fdiagnostics-show-option... " >&6; } if test "${ac_cv_safe_to_use_fdiagnostics_show_option_+set}" = set; then : $as_echo_n "(cached) " >&6 else save_CFLAGS="$CFLAGS" CFLAGS="-fdiagnostics-show-option ${AM_CFLAGS} ${CFLAGS}" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_safe_to_use_fdiagnostics_show_option_=yes else ac_cv_safe_to_use_fdiagnostics_show_option_=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS="$save_CFLAGS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_safe_to_use_fdiagnostics_show_option_" >&5 $as_echo "$ac_cv_safe_to_use_fdiagnostics_show_option_" >&6; } if test "$ac_cv_safe_to_use_fdiagnostics_show_option_" = "yes"; then : F_DIAGNOSTICS_SHOW_OPTION="-fdiagnostics-show-option" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether it is safe to use -floop-parallelize-all" >&5 $as_echo_n "checking whether it is safe to use -floop-parallelize-all... " >&6; } if test "${ac_cv_safe_to_use_floop_parallelize_all_+set}" = set; then : $as_echo_n "(cached) " >&6 else save_CFLAGS="$CFLAGS" CFLAGS="-floop-parallelize-all ${AM_CFLAGS} ${CFLAGS}" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_safe_to_use_floop_parallelize_all_=yes else ac_cv_safe_to_use_floop_parallelize_all_=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS="$save_CFLAGS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_safe_to_use_floop_parallelize_all_" >&5 $as_echo "$ac_cv_safe_to_use_floop_parallelize_all_" >&6; } if test "$ac_cv_safe_to_use_floop_parallelize_all_" = "yes"; then : F_LOOP_PARALLELIZE_ALL="-floop-parallelize-all" fi NO_STRICT_ALIASING="-fno-strict-aliasing -Wno-strict-aliasing" NO_SHADOW="-Wno-shadow" if test "$INTELCC" = "yes"; then : BASE_WARNINGS="-w1 -Werror -Wcheck -Wp64 -Woverloaded-virtual -Wcast-qual -diag-disable 188,981,2259,2203,1683,1684" CC_WARNINGS="${BASE_WARNINGS}" CXX_WARNINGS="${BASE_WARNINGS}" PROTOSKIP_WARNINGS="-diag-disable 188,981,967,2259,1683,1684,2203" else BASE_WARNINGS_FULL="${NO_STRICT_ALIASING}" if test "${ac_cv_assert}" = "no"; then : NO_UNUSED="-Wno-unused-variable -Wno-unused-parameter" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether it is safe to use -Wextra" >&5 $as_echo_n "checking whether it is safe to use -Wextra... " >&6; } if test "${ac_cv_safe_to_use_Wextra_+set}" = set; then : $as_echo_n "(cached) " >&6 else save_CFLAGS="$CFLAGS" CFLAGS="${W_FAIL} -pedantic -Wextra ${AM_CFLAGS} ${CFLAGS}" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_safe_to_use_Wextra_=yes else ac_cv_safe_to_use_Wextra_=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS="$save_CFLAGS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_safe_to_use_Wextra_" >&5 $as_echo "$ac_cv_safe_to_use_Wextra_" >&6; } BASE_WARNINGS="${W_FAIL} -pedantic -Wall -Wundef -Wshadow ${NO_UNUSED} ${F_DIAGNOSTICS_SHOW_OPTION} ${F_LOOP_PARALLELIZE_ALL} ${BASE_WARNINGS_FULL}" if test "$ac_cv_safe_to_use_Wextra_" = "yes"; then : BASE_WARNINGS="${BASE_WARNINGS} -Wextra" else BASE_WARNINGS="${BASE_WARNINGS} -W" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether it is safe to use -Wformat" >&5 $as_echo_n "checking whether it is safe to use -Wformat... " >&6; } if test "${ac_cv_safe_to_use_wformat_+set}" = set; then : $as_echo_n "(cached) " >&6 else save_CFLAGS="$CFLAGS" CFLAGS="-Wformat -Werror -pedantic ${AM_CFLAGS} ${CFLAGS}" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include void foo(); void foo() { uint64_t test_u= 0; printf("This is a %" PRIu64 "test\n", test_u); } int main () { foo(); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_safe_to_use_wformat_=yes else ac_cv_safe_to_use_wformat_=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS="$save_CFLAGS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_safe_to_use_wformat_" >&5 $as_echo "$ac_cv_safe_to_use_wformat_" >&6; } if test "$ac_cv_safe_to_use_wformat_" = "yes"; then : BASE_WARNINGS="${BASE_WARNINGS} -Wformat -Wno-format-nonliteral -Wno-format-security" BASE_WARNINGS_FULL="${BASE_WARNINGS_FULL} -Wformat=2 -Wno-format-nonliteral -Wno-format-security" else BASE_WARNINGS="${BASE_WARNINGS} -Wno-format" BASE_WARNINGS_FULL="${BASE_WARNINGS_FULL} -Wno-format" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether it is safe to use -Wconversion" >&5 $as_echo_n "checking whether it is safe to use -Wconversion... " >&6; } if test "${ac_cv_safe_to_use_wconversion_+set}" = set; then : $as_echo_n "(cached) " >&6 else save_CFLAGS="$CFLAGS" CFLAGS="-Wconversion -Werror -pedantic ${AM_CFLAGS} ${CFLAGS}" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include void foo(bool a) { (void)a; } int main () { foo(0); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_safe_to_use_wconversion_=yes else ac_cv_safe_to_use_wconversion_=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS="$save_CFLAGS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_safe_to_use_wconversion_" >&5 $as_echo "$ac_cv_safe_to_use_wconversion_" >&6; } if test "$ac_cv_safe_to_use_wconversion_" = "yes"; then : W_CONVERSION="-Wconversion" { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether it is safe to use -Wconversion with htons" >&5 $as_echo_n "checking whether it is safe to use -Wconversion with htons... " >&6; } if test "${ac_cv_safe_to_use_Wconversion_+set}" = set; then : $as_echo_n "(cached) " >&6 else save_CFLAGS="$CFLAGS" CFLAGS="-Wconversion -Werror -pedantic ${AM_CFLAGS} ${CFLAGS}" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { uint16_t x= htons(80); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_safe_to_use_Wconversion_=yes else ac_cv_safe_to_use_Wconversion_=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS="$save_CFLAGS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_safe_to_use_Wconversion_" >&5 $as_echo "$ac_cv_safe_to_use_Wconversion_" >&6; } if test "$ac_cv_safe_to_use_Wconversion_" = "no"; then : NO_CONVERSION="-Wno-conversion" fi fi CC_WARNINGS="${BASE_WARNINGS} -Wstrict-prototypes -Wmissing-prototypes -Wredundant-decls -Wmissing-declarations -Wcast-align ${CC_WARNINGS_FULL}" CXX_WARNINGS="${BASE_WARNINGS} -Woverloaded-virtual -Wnon-virtual-dtor -Wctor-dtor-privacy -Wno-long-long ${CXX_WARNINGS_FULL}" { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether it is safe to use -Wmissing-declarations from C++" >&5 $as_echo_n "checking whether it is safe to use -Wmissing-declarations from C++... " >&6; } if test "${ac_cv_safe_to_use_Wmissing_declarations_+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu save_CXXFLAGS="$CXXFLAGS" CXXFLAGS="-Werror -pedantic -Wmissing-declarations ${AM_CXXFLAGS}" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_cv_safe_to_use_Wmissing_declarations_=yes else ac_cv_safe_to_use_Wmissing_declarations_=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CXXFLAGS="$save_CXXFLAGS" ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_safe_to_use_Wmissing_declarations_" >&5 $as_echo "$ac_cv_safe_to_use_Wmissing_declarations_" >&6; } if test "$ac_cv_safe_to_use_Wmissing_declarations_" = "yes"; then : CXX_WARNINGS="${CXX_WARNINGS} -Wmissing-declarations" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether it is safe to use -Wframe-larger-than" >&5 $as_echo_n "checking whether it is safe to use -Wframe-larger-than... " >&6; } if test "${ac_cv_safe_to_use_Wframe_larger_than_+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu save_CXXFLAGS="$CXXFLAGS" CXXFLAGS="-Werror -pedantic -Wframe-larger-than=32768 ${AM_CXXFLAGS}" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_cv_safe_to_use_Wframe_larger_than_=yes else ac_cv_safe_to_use_Wframe_larger_than_=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CXXFLAGS="$save_CXXFLAGS" ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_safe_to_use_Wframe_larger_than_" >&5 $as_echo "$ac_cv_safe_to_use_Wframe_larger_than_" >&6; } if test "$ac_cv_safe_to_use_Wframe_larger_than_" = "yes"; then : CXX_WARNINGS="${CXX_WARNINGS} -Wframe-larger-than=32768" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether it is safe to use -Wlogical-op" >&5 $as_echo_n "checking whether it is safe to use -Wlogical-op... " >&6; } if test "${ac_cv_safe_to_use_Wlogical_op_+set}" = set; then : $as_echo_n "(cached) " >&6 else save_CFLAGS="$CFLAGS" CFLAGS="${W_FAIL} -pedantic -Wlogical-op ${AM_CFLAGS} ${CFLAGS}" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_safe_to_use_Wlogical_op_=yes else ac_cv_safe_to_use_Wlogical_op_=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS="$save_CFLAGS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_safe_to_use_Wlogical_op_" >&5 $as_echo "$ac_cv_safe_to_use_Wlogical_op_" >&6; } if test "$ac_cv_safe_to_use_Wlogical_op_" = "yes"; then : CC_WARNINGS="${CC_WARNINGS} -Wlogical-op" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether it is safe to use -Wredundant-decls from C++" >&5 $as_echo_n "checking whether it is safe to use -Wredundant-decls from C++... " >&6; } if test "${ac_cv_safe_to_use_Wredundant_decls_+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu save_CXXFLAGS="${CXXFLAGS}" CXXFLAGS="${W_FAIL} -pedantic -Wredundant-decls ${AM_CXXFLAGS}" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ template struct C { void foo(); }; template void C::foo() { } template <> void C::foo(); $ac_includes_default int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_cv_safe_to_use_Wredundant_decls_=yes else ac_cv_safe_to_use_Wredundant_decls_=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CXXFLAGS="${save_CXXFLAGS}" ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_safe_to_use_Wredundant_decls_" >&5 $as_echo "$ac_cv_safe_to_use_Wredundant_decls_" >&6; } if test "$ac_cv_safe_to_use_Wredundant_decls_" = "yes"; then : CXX_WARNINGS="${CXX_WARNINGS} -Wredundant-decls" else CXX_WARNINGS="${CXX_WARNINGS} -Wno-redundant-decls" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether it is safe to use -Wattributes from C++" >&5 $as_echo_n "checking whether it is safe to use -Wattributes from C++... " >&6; } if test "${ac_cv_safe_to_use_Wattributes_+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu save_CXXFLAGS="${CXXFLAGS}" CXXFLAGS="${W_FAIL} -pedantic -Wattributes -fvisibility=hidden ${AM_CXXFLAGS}" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include const ::google::protobuf::EnumDescriptor* Table_TableOptions_RowType_descriptor(); enum Table_TableOptions_RowType { Table_TableOptions_RowType_ROW_TYPE_DEFAULT = 0, Table_TableOptions_RowType_ROW_TYPE_FIXED = 1, Table_TableOptions_RowType_ROW_TYPE_DYNAMIC = 2, Table_TableOptions_RowType_ROW_TYPE_COMPRESSED = 3, Table_TableOptions_RowType_ROW_TYPE_REDUNDANT = 4, Table_TableOptions_RowType_ROW_TYPE_COMPACT = 5, Table_TableOptions_RowType_ROW_TYPE_PAGE = 6 }; namespace google { namespace protobuf { template <> inline const EnumDescriptor* GetEnumDescriptor() { return Table_TableOptions_RowType_descriptor(); } } } int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_cv_safe_to_use_Wattributes_=yes else ac_cv_safe_to_use_Wattributes_=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CXXFLAGS="${save_CXXFLAGS}" ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_safe_to_use_Wattributes_" >&5 $as_echo "$ac_cv_safe_to_use_Wattributes_" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether it is safe to use -Wno-attributes" >&5 $as_echo_n "checking whether it is safe to use -Wno-attributes... " >&6; } if test "${ac_cv_safe_to_use_Wno_attributes_+set}" = set; then : $as_echo_n "(cached) " >&6 else save_CFLAGS="$CFLAGS" CFLAGS="${W_FAIL} -pedantic -Wno_attributes_ ${AM_CFLAGS} ${CFLAGS}" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_safe_to_use_Wno_attributes_=yes else ac_cv_safe_to_use_Wno_attributes_=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS="$save_CFLAGS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_safe_to_use_Wno_attributes_" >&5 $as_echo "$ac_cv_safe_to_use_Wno_attributes_" >&6; } if test "$ac_cv_safe_to_use_Wattributes_" != "yes"; then : if test "$ac_cv_safe_to_use_Wno_attributes_" = "yes"; then : CC_WARNINGS="${CC_WARNINGS} -Wno-attributes" NO_ATTRIBUTES="-Wno-attributes" fi fi NO_REDUNDANT_DECLS="-Wno-redundant-decls" PROTOSKIP_WARNINGS="-Wno-effc++ -Wno-shadow -Wno-missing-braces ${NO_ATTRIBUTES}" NO_WERROR="-Wno-error" PERMISSIVE_WARNINGS="-Wno-error -Wno-unused-function -fpermissive" if test "$host_vendor" = "apple"; then : BOOSTSKIP_WARNINGS="-Wno-uninitialized" fi fi fi if test "$SUNCC" = "yes"; then : if test "$ac_profiling" = "yes"; then : CC_PROFILING="-xinstrument=datarace" fi if test "$ac_cv_warnings_as_errors" = "yes"; then : W_FAIL="-errwarn=%all" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether E_PASTE_RESULT_NOT_TOKEN is usable" >&5 $as_echo_n "checking whether E_PASTE_RESULT_NOT_TOKEN is usable... " >&6; } if test "${ac_cv_paste_result+set}" = set; then : $as_echo_n "(cached) " >&6 else save_CFLAGS="${CFLAGS}" CFLAGS="-errwarn=%all -erroff=E_PASTE_RESULT_NOT_TOKEN ${CFLAGS}" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default int main () { int x= 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_paste_result=yes else ac_cv_paste_result=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS="${save_CFLAGS}" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_paste_result" >&5 $as_echo "$ac_cv_paste_result" >&6; } if test $ac_cv_paste_result = yes; then : W_PASTE_RESULT=",E_PASTE_RESULT_NOT_TOKEN" fi CC_WARNINGS_FULL="-erroff=E_ATTRIBUTE_NOT_VAR,E_STATEMENT_NOT_REACHED" CXX_WARNINGS_FULL="-erroff=attrskipunsup,doubunder,reftotemp,inllargeuse,truncwarn1,signextwarn,inllargeint" CC_WARNINGS="-v -errtags=yes ${W_FAIL} ${CC_WARNINGS_FULL}" CXX_WARNINGS="+w +w2 -xwe -xport64 -errtags=yes ${CXX_WARNINGS_FULL} ${W_FAIL}" PROTOSKIP_WARNINGS="-erroff=attrskipunsup,doubunder,reftotemp,wbadinitl,identexpected,inllargeuse,truncwarn1,signextwarn,partinit,notused,badargtype2w,wbadinit" BOOSTSKIP_WARNINGS="-erroff=attrskipunsup,doubunder,reftotemp,inllargeuse,truncwarn1,signextwarn,inllargeint,hidef,wvarhidenmem" PERMISSIVE_WARNINGS="-erroff=attrskipunsup,doubunder,reftotemp,inllargeuse,truncwarn1,signextwarn,inllargeint,hidef,wvarhidenmem,notused,badargtype2w,wunreachable" INNOBASE_SKIP_WARNINGS="-erroff=attrskipunsup,doubunder,reftotemp,wbadinitl,identexpected,inllargeuse,truncwarn1,signextwarn,partinit,notused,badargtype2w,wbadinit,wunreachable" NO_UNREACHED="-erroff=E_STATEMENT_NOT_REACHED" NO_WERROR="-errwarn=%none" fi # Check whether --enable-dtrace was given. if test "${enable_dtrace+set}" = set; then : enableval=$enable_dtrace; ac_cv_enable_dtrace="$enableval" else ac_cv_enable_dtrace="yes" fi if test "$ac_cv_enable_dtrace" = "yes"; then : for ac_prog in dtrace do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_DTRACE+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$DTRACE"; then ac_cv_prog_DTRACE="$DTRACE" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_DTRACE="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi DTRACE=$ac_cv_prog_DTRACE if test -n "$DTRACE"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DTRACE" >&5 $as_echo "$DTRACE" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$DTRACE" && break done for ac_header in sys/sdt.h do : ac_fn_c_check_header_mongrel "$LINENO" "sys/sdt.h" "ac_cv_header_sys_sdt_h" "$ac_includes_default" if test "x$ac_cv_header_sys_sdt_h" = x""yes; then : cat >>confdefs.h <<_ACEOF #define HAVE_SYS_SDT_H 1 _ACEOF fi done if test "x$ac_cv_prog_DTRACE" = "xdtrace" -a "x${ac_cv_header_sys_sdt_h}" = "xyes"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking if dtrace works" >&5 $as_echo_n "checking if dtrace works... " >&6; } if test "${ac_cv_dtrace_works+set}" = set; then : $as_echo_n "(cached) " >&6 else cat >conftest.d <<_ACEOF provider Example { probe increment(int); }; _ACEOF $DTRACE -h -o conftest.h -s conftest.d 2>/dev/zero if test $? -eq 0; then : ac_cv_dtrace_works=yes else ac_cv_dtrace_works=no fi rm -f conftest.h conftest.d fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_dtrace_works" >&5 $as_echo "$ac_cv_dtrace_works" >&6; } if test "x$ac_cv_dtrace_works" = "xyes"; then : $as_echo "#define HAVE_DTRACE 1" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking if dtrace should instrument object files" >&5 $as_echo_n "checking if dtrace should instrument object files... " >&6; } if test "${ac_cv_dtrace_needs_objects+set}" = set; then : $as_echo_n "(cached) " >&6 else cat >conftest.d <<_ACEOF provider Example { probe increment(int); }; _ACEOF $DTRACE -G -o conftest.d.o -s conftest.d 2>/dev/zero if test $? -eq 0; then : ac_cv_dtrace_needs_objects=yes else ac_cv_dtrace_needs_objects=no fi rm -f conftest.d.o conftest.d fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_dtrace_needs_objects" >&5 $as_echo "$ac_cv_dtrace_needs_objects" >&6; } ac_cv_have_dtrace=yes fi fi if test "x$ac_cv_dtrace_works" = "xyes"; then HAVE_DTRACE_TRUE= HAVE_DTRACE_FALSE='#' else HAVE_DTRACE_TRUE='#' HAVE_DTRACE_FALSE= fi if test "x$ac_cv_dtrace_needs_objects" = "xyes"; then DTRACE_NEEDS_OBJECTS_TRUE= DTRACE_NEEDS_OBJECTS_FALSE='#' else DTRACE_NEEDS_OBJECTS_TRUE='#' DTRACE_NEEDS_OBJECTS_FALSE= fi use_additional=yes acl_save_prefix="$prefix" prefix="$acl_final_prefix" acl_save_exec_prefix="$exec_prefix" exec_prefix="$acl_final_exec_prefix" eval additional_includedir=\"$includedir\" eval additional_libdir=\"$libdir\" exec_prefix="$acl_save_exec_prefix" prefix="$acl_save_prefix" # Check whether --with-lib-prefix was given. if test "${with_lib_prefix+set}" = set; then : withval=$with_lib_prefix; if test "X$withval" = "Xno"; then use_additional=no else if test "X$withval" = "X"; then acl_save_prefix="$prefix" prefix="$acl_final_prefix" acl_save_exec_prefix="$exec_prefix" exec_prefix="$acl_final_exec_prefix" eval additional_includedir=\"$includedir\" eval additional_libdir=\"$libdir\" exec_prefix="$acl_save_exec_prefix" prefix="$acl_save_prefix" else additional_includedir="$withval/include" additional_libdir="$withval/$acl_libdirstem" fi fi fi if test $use_additional = yes; then if test "X$additional_includedir" != "X/usr/include"; then haveit= for x in $CPPFLAGS; do acl_save_prefix="$prefix" prefix="$acl_final_prefix" acl_save_exec_prefix="$exec_prefix" exec_prefix="$acl_final_exec_prefix" eval x=\"$x\" exec_prefix="$acl_save_exec_prefix" prefix="$acl_save_prefix" if test "X$x" = "X-I$additional_includedir"; then haveit=yes break fi done if test -z "$haveit"; then if test "X$additional_includedir" = "X/usr/local/include"; then if test -n "$GCC"; then case $host_os in linux* | gnu* | k*bsd*-gnu) haveit=yes;; esac fi fi if test -z "$haveit"; then if test -d "$additional_includedir"; then CPPFLAGS="${CPPFLAGS}${CPPFLAGS:+ }-I$additional_includedir" fi fi fi fi if test "X$additional_libdir" != "X/usr/$acl_libdirstem"; then haveit= for x in $LDFLAGS; do acl_save_prefix="$prefix" prefix="$acl_final_prefix" acl_save_exec_prefix="$exec_prefix" exec_prefix="$acl_final_exec_prefix" eval x=\"$x\" exec_prefix="$acl_save_exec_prefix" prefix="$acl_save_prefix" if test "X$x" = "X-L$additional_libdir"; then haveit=yes break fi done if test -z "$haveit"; then if test "X$additional_libdir" = "X/usr/local/$acl_libdirstem"; then if test -n "$GCC"; then case $host_os in linux*) haveit=yes;; esac fi fi if test -z "$haveit"; then if test -d "$additional_libdir"; then LDFLAGS="${LDFLAGS}${LDFLAGS:+ }-L$additional_libdir" fi fi fi fi fi # Check whether --enable-umem was given. if test "${enable_umem+set}" = set; then : enableval=$enable_umem; ac_enable_umem="$enableval" else case "$target_os" in *solaris*) ac_enable_umem="yes" ;; *) ac_enable_umem="no" ;; esac fi # Check whether --enable-tcmalloc was given. if test "${enable_tcmalloc+set}" = set; then : enableval=$enable_tcmalloc; ac_enable_tcmalloc="$enableval" else ac_enable_tcmalloc="no" fi # Check whether --enable-mtmalloc was given. if test "${enable_mtmalloc+set}" = set; then : enableval=$enable_mtmalloc; ac_enable_mtmalloc="$enableval" else ac_enable_mtmalloc="yes" fi save_LIBS="${LIBS}" LIBS= if test "x$ac_enable_umem" = "xyes"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for malloc in -lumem" >&5 $as_echo_n "checking for malloc in -lumem... " >&6; } if test "${ac_cv_lib_umem_malloc+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lumem $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char malloc (); int main () { return malloc (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_umem_malloc=yes else ac_cv_lib_umem_malloc=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_umem_malloc" >&5 $as_echo "$ac_cv_lib_umem_malloc" >&6; } if test "x$ac_cv_lib_umem_malloc" = x""yes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBUMEM 1 _ACEOF LIBS="-lumem $LIBS" fi else case "$target_os" in *linux*) if test "x$ac_enable_tcmalloc" != "xno"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for malloc in -ltcmalloc-minimal" >&5 $as_echo_n "checking for malloc in -ltcmalloc-minimal... " >&6; } if test "${ac_cv_lib_tcmalloc_minimal_malloc+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-ltcmalloc-minimal $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char malloc (); int main () { return malloc (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_tcmalloc_minimal_malloc=yes else ac_cv_lib_tcmalloc_minimal_malloc=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_tcmalloc_minimal_malloc" >&5 $as_echo "$ac_cv_lib_tcmalloc_minimal_malloc" >&6; } if test "x$ac_cv_lib_tcmalloc_minimal_malloc" = x""yes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBTCMALLOC_MINIMAL 1 _ACEOF LIBS="-ltcmalloc-minimal $LIBS" fi if test "x$ac_cv_lib_tcmalloc_minimal_malloc" != "xyes"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for malloc in -ltcmalloc" >&5 $as_echo_n "checking for malloc in -ltcmalloc... " >&6; } if test "${ac_cv_lib_tcmalloc_malloc+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-ltcmalloc $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char malloc (); int main () { return malloc (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_tcmalloc_malloc=yes else ac_cv_lib_tcmalloc_malloc=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_tcmalloc_malloc" >&5 $as_echo "$ac_cv_lib_tcmalloc_malloc" >&6; } if test "x$ac_cv_lib_tcmalloc_malloc" = x""yes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBTCMALLOC 1 _ACEOF LIBS="-ltcmalloc $LIBS" fi fi fi ;; *solaris*) if test "x$ac_enable_mtmalloc" != "xno"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for malloc in -lmtmalloc" >&5 $as_echo_n "checking for malloc in -lmtmalloc... " >&6; } if test "${ac_cv_lib_mtmalloc_malloc+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lmtmalloc $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char malloc (); int main () { return malloc (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_mtmalloc_malloc=yes else ac_cv_lib_mtmalloc_malloc=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_mtmalloc_malloc" >&5 $as_echo "$ac_cv_lib_mtmalloc_malloc" >&6; } if test "x$ac_cv_lib_mtmalloc_malloc" = x""yes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBMTMALLOC 1 _ACEOF LIBS="-lmtmalloc $LIBS" fi fi ;; esac fi BETTER_MALLOC_LIBS="${LIBS}" LIBS="${save_LIBS}" for ac_prog in doxygen do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_DOXYGEN+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$DOXYGEN"; then ac_cv_prog_DOXYGEN="$DOXYGEN" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_DOXYGEN="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi DOXYGEN=$ac_cv_prog_DOXYGEN if test -n "$DOXYGEN"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DOXYGEN" >&5 $as_echo "$DOXYGEN" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$DOXYGEN" && break done for ac_prog in perl do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_PERL+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$PERL"; then ac_cv_prog_PERL="$PERL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_PERL="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi PERL=$ac_cv_prog_PERL if test -n "$PERL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PERL" >&5 $as_echo "$PERL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$PERL" && break done for ac_prog in dpkg-gensymbols do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_DPKG_GENSYMBOLS+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$DPKG_GENSYMBOLS"; then ac_cv_prog_DPKG_GENSYMBOLS="$DPKG_GENSYMBOLS" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_DPKG_GENSYMBOLS="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi DPKG_GENSYMBOLS=$ac_cv_prog_DPKG_GENSYMBOLS if test -n "$DPKG_GENSYMBOLS"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DPKG_GENSYMBOLS" >&5 $as_echo "$DPKG_GENSYMBOLS" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$DPKG_GENSYMBOLS" && break done test -n "$DPKG_GENSYMBOLS" || DPKG_GENSYMBOLS=":" for ac_prog in lcov do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_LCOV+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$LCOV"; then ac_cv_prog_LCOV="$LCOV" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_LCOV="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi LCOV=$ac_cv_prog_LCOV if test -n "$LCOV"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LCOV" >&5 $as_echo "$LCOV" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$LCOV" && break done test -n "$LCOV" || LCOV="echo lcov not found" for ac_prog in genhtml do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_LCOV_GENHTML+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$LCOV_GENHTML"; then ac_cv_prog_LCOV_GENHTML="$LCOV_GENHTML" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_LCOV_GENHTML="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi LCOV_GENHTML=$ac_cv_prog_LCOV_GENHTML if test -n "$LCOV_GENHTML"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LCOV_GENHTML" >&5 $as_echo "$LCOV_GENHTML" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$LCOV_GENHTML" && break done test -n "$LCOV_GENHTML" || LCOV_GENHTML="echo genhtml not found" for ac_prog in sphinx-build do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_SPHINXBUILD+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$SPHINXBUILD"; then ac_cv_prog_SPHINXBUILD="$SPHINXBUILD" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_SPHINXBUILD="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi SPHINXBUILD=$ac_cv_prog_SPHINXBUILD if test -n "$SPHINXBUILD"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $SPHINXBUILD" >&5 $as_echo "$SPHINXBUILD" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$SPHINXBUILD" && break done test -n "$SPHINXBUILD" || SPHINXBUILD=":" if test "x${SPHINXBUILD}" != "x:"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking if sphinx is new enough" >&5 $as_echo_n "checking if sphinx is new enough... " >&6; } if test "${ac_cv_recent_sphinx+set}" = set; then : $as_echo_n "(cached) " >&6 else ${SPHINXBUILD} -Q -C -b man -d conftest.d . . >/dev/null 2>&1 if test $? -eq 0; then : ac_cv_recent_sphinx=yes else ac_cv_recent_sphinx=no fi rm -rf conftest.d fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_recent_sphinx" >&5 $as_echo "$ac_cv_recent_sphinx" >&6; } fi if test "x${DPKG_GENSYMBOLS}" != "x:"; then HAVE_DPKG_GENSYMBOLS_TRUE= HAVE_DPKG_GENSYMBOLS_FALSE='#' else HAVE_DPKG_GENSYMBOLS_TRUE='#' HAVE_DPKG_GENSYMBOLS_FALSE= fi if test "x${SPHINXBUILD}" != "x:"; then HAVE_SPHINX_TRUE= HAVE_SPHINX_FALSE='#' else HAVE_SPHINX_TRUE='#' HAVE_SPHINX_FALSE= fi if test "x${ac_cv_recent_sphinx}" = "xyes"; then HAVE_RECENT_SPHINX_TRUE= HAVE_RECENT_SPHINX_FALSE='#' else HAVE_RECENT_SPHINX_TRUE='#' HAVE_RECENT_SPHINX_FALSE= fi if test "x${USE_NLS}" = "xyes" -a "x${pandora_have_intltool}" = "xyes"; then BUILD_PO_TRUE= BUILD_PO_FALSE='#' else BUILD_PO_TRUE='#' BUILD_PO_FALSE= fi if test "x${gl_LIBOBJS}" != "x"; then : if test "$GCC" = "yes"; then : AM_CPPFLAGS="-isystem \${top_srcdir}/gnulib -isystem \${top_builddir}/gnulib ${AM_CPPFLAGS}" else AM_CPPFLAGS="-I\${top_srcdir}/gnulib -I\${top_builddir}/gnulib ${AM_CPPFLAGS}" fi fi AM_CPPFLAGS="-I\$(top_srcdir) -I\$(top_builddir) ${AM_CPPFLAGS}" if test "$GCC" = "yes"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working -pipe" >&5 $as_echo_n "checking for working -pipe... " >&6; } if test "${pandora_cv_use_pipe+set}" = set; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main(int argc, char** argv) { (void) argc; (void) argv; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : pandora_cv_use_pipe=yes else pandora_cv_use_pipe=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $pandora_cv_use_pipe" >&5 $as_echo "$pandora_cv_use_pipe" >&6; } if test "$pandora_cv_use_pipe" = "yes"; then : AM_CFLAGS="-pipe ${AM_CFLAGS}" AM_CXXFLAGS="-pipe ${AM_CXXFLAGS}" fi fi mkdir -p config cat > config/top.h.stamp </dev/null 2>&1 || mv config/top.h.stamp config/top.h rm -f config/top.h.stamp AM_CFLAGS="${AM_CFLAGS} ${CC_WARNINGS} ${CC_PROFILING} ${CC_COVERAGE}" AM_CXXFLAGS="${AM_CXXFLAGS} ${CXX_WARNINGS} ${CC_PROFILING} ${CC_COVERAGE}" # Extract the first word of "uname", so it can be a program name with args. set dummy uname; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_path_uname_prog+set}" = set; then : $as_echo_n "(cached) " >&6 else case $uname_prog in [\\/]* | ?:[\\/]*) ac_cv_path_uname_prog="$uname_prog" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_uname_prog="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS test -z "$ac_cv_path_uname_prog" && ac_cv_path_uname_prog="no" ;; esac fi uname_prog=$ac_cv_path_uname_prog if test -n "$uname_prog"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $uname_prog" >&5 $as_echo "$uname_prog" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi # increment if interfaces have been added, removed or changed VERSION_CURRENT=6 # increment if source code has changed # set to zero if current is incremented VERSION_REVISION=0 # increment if interfaces have been added # set to zero if interfaces have been removed or changed VERSION_AGE=0 cat >>confdefs.h <<_ACEOF #define IB_API_VERSION_CURRENT $VERSION_CURRENT _ACEOF cat >>confdefs.h <<_ACEOF #define IB_API_VERSION_REVISION $VERSION_REVISION _ACEOF cat >>confdefs.h <<_ACEOF #define IB_API_VERSION_AGE $VERSION_AGE _ACEOF IB_API_VERSION=$VERSION_CURRENT:$VERSION_REVISION:$VERSION_AGE for ac_prog in 'bison -y' do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if test "${ac_cv_prog_YACC+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$YACC"; then ac_cv_prog_YACC="$YACC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_YACC="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi YACC=$ac_cv_prog_YACC if test -n "$YACC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $YACC" >&5 $as_echo "$YACC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$YACC" && break done test -n "$YACC" || YACC=":" if test "x$YACC" = "x:"; then : pandora_have_bison=no YACC='if test -f ""; then echo "WARNING: no proper bison binary found, ignoring changes to $<"; exit 0; else echo "ERROR: no proper bison binary found"; exit 1; fi;' else pandora_have_bison=yes fi if test "x${pandora_have_bison}" = "xyes"; then HAVE_BISON_TRUE= HAVE_BISON_FALSE='#' else HAVE_BISON_TRUE='#' HAVE_BISON_FALSE= fi if test "x${pandora_have_bison}" = "xno" -a "$pandora_building_from_bzr" = "yes"; then : as_fn_error $? "\"bison is required for ${PACKAGE} to build from a bzr branch\"" "$LINENO" 5 fi # libhaildb versioning when linked with GNU ld. if test "$lt_cv_prog_gnu_ld" = "yes" then LD_VERSION_SCRIPT="-Wl,--version-script=\$(top_srcdir)/libhaildb.ver" fi ##### ## Check for system header files used by InnoDB. ##### for ac_header in pthread.h do : ac_fn_c_check_header_mongrel "$LINENO" "pthread.h" "ac_cv_header_pthread_h" "$ac_includes_default" if test "x$ac_cv_header_pthread_h" = x""yes; then : cat >>confdefs.h <<_ACEOF #define HAVE_PTHREAD_H 1 _ACEOF fi done for ac_header in valgrind/memcheck.h do : ac_fn_c_check_header_mongrel "$LINENO" "valgrind/memcheck.h" "ac_cv_header_valgrind_memcheck_h" "$ac_includes_default" if test "x$ac_cv_header_valgrind_memcheck_h" = x""yes; then : cat >>confdefs.h <<_ACEOF #define HAVE_VALGRIND_MEMCHECK_H 1 _ACEOF fi done case "$target_os" in lin*) TARGET_LINUX="true"; CFLAGS="$CFLAGS -DUNIV_LINUX";; hpux10*) CFLAGS="$CFLAGS -DUNIV_MUST_NOT_INLINE -DUNIV_HPUX -DUNIV_HPUX10";; hp*) CFLAGS="$CFLAGS -DUNIV_MUST_NOT_INLINE -DUNIV_HPUX";; aix*) CFLAGS="$CFLAGS -DUNIV_AIX";; irix*|osf*|sysv5uw7*|openbsd*) CFLAGS="$CFLAGS -DUNIV_MUST_NOT_INLINE";; *solaris*|*SunOS*) CFLAGS="$CFLAGS -DUNIV_SOLARIS";; esac # do not reindent the following lines, they are copied from InnoDB Plugin's # plug.in and this way it is easier for maintenance { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether GCC atomic builtins are available" >&5 $as_echo_n "checking whether GCC atomic builtins are available... " >&6; } # either define HAVE_IB_GCC_ATOMIC_BUILTINS or not if test "$cross_compiling" = yes; then : { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot run test program while cross compiling See \`config.log' for more details" "$LINENO" 5 ; } else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main() { long x; long y; long res; char c; x = 10; y = 123; res = __sync_bool_compare_and_swap(&x, x, y); if (!res || x != y) { return(1); } x = 10; y = 123; res = __sync_bool_compare_and_swap(&x, x + 1, y); if (res || x != 10) { return(1); } x = 10; y = 123; res = __sync_add_and_fetch(&x, y); if (res != 123 + 10 || x != 123 + 10) { return(1); } c = 10; res = __sync_lock_test_and_set(&c, 123); if (res != 10 || c != 123) { return(1); } return(0); } _ACEOF if ac_fn_c_try_run "$LINENO"; then : $as_echo "#define HAVE_IB_GCC_ATOMIC_BUILTINS 1" >>confdefs.h { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether pthread_t can be used by GCC atomic builtins" >&5 $as_echo_n "checking whether pthread_t can be used by GCC atomic builtins... " >&6; } # either define HAVE_IB_ATOMIC_PTHREAD_T_GCC or not if test "$cross_compiling" = yes; then : { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot run test program while cross compiling See \`config.log' for more details" "$LINENO" 5 ; } else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main(int argc, char** argv) { pthread_t x1; pthread_t x2; pthread_t x3; memset(&x1, 0x0, sizeof(x1)); memset(&x2, 0x0, sizeof(x2)); memset(&x3, 0x0, sizeof(x3)); __sync_bool_compare_and_swap(&x1, x2, x3); return(0); } _ACEOF if ac_fn_c_try_run "$LINENO"; then : $as_echo "#define HAVE_IB_ATOMIC_PTHREAD_T_GCC 1" >>confdefs.h { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether Solaris libc atomic functions are available" >&5 $as_echo_n "checking whether Solaris libc atomic functions are available... " >&6; } # either define HAVE_IB_SOLARIS_ATOMICS or not for ac_func in atomic_add_long \ atomic_cas_32 \ atomic_cas_64 \ atomic_cas_ulong do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" if eval test \"x\$"$as_ac_var"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF $as_echo "#define HAVE_IB_SOLARIS_ATOMICS 1" >>confdefs.h fi done { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether pthread_t can be used by Solaris libc atomic functions" >&5 $as_echo_n "checking whether pthread_t can be used by Solaris libc atomic functions... " >&6; } # either define HAVE_IB_ATOMIC_PTHREAD_T_SOLARIS or not if test "$cross_compiling" = yes; then : { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot run test program while cross compiling See \`config.log' for more details" "$LINENO" 5 ; } else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main(int argc, char** argv) { pthread_t x1; pthread_t x2; pthread_t x3; memset(&x1, 0x0, sizeof(x1)); memset(&x2, 0x0, sizeof(x2)); memset(&x3, 0x0, sizeof(x3)); if (sizeof(pthread_t) == 4) { atomic_cas_32(&x1, x2, x3); } else if (sizeof(pthread_t) == 8) { atomic_cas_64(&x1, x2, x3); } else { return(1); } return(0); } _ACEOF if ac_fn_c_try_run "$LINENO"; then : $as_echo "#define HAVE_IB_ATOMIC_PTHREAD_T_SOLARIS 1" >>confdefs.h { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi # this is needed to know which one of atomic_cas_32() or atomic_cas_64() # to use in the source # The cast to long int works around a bug in the HP C Compiler # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects # declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. # This bug is HP SR number 8606223364. { $as_echo "$as_me:${as_lineno-$LINENO}: checking size of pthread_t" >&5 $as_echo_n "checking size of pthread_t... " >&6; } if test "${ac_cv_sizeof_pthread_t+set}" = set; then : $as_echo_n "(cached) " >&6 else if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (pthread_t))" "ac_cv_sizeof_pthread_t" "#include "; then : else if test "$ac_cv_type_pthread_t" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "cannot compute sizeof (pthread_t) See \`config.log' for more details" "$LINENO" 5 ; } else ac_cv_sizeof_pthread_t=0 fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_pthread_t" >&5 $as_echo "$ac_cv_sizeof_pthread_t" >&6; } cat >>confdefs.h <<_ACEOF #define SIZEOF_PTHREAD_T $ac_cv_sizeof_pthread_t _ACEOF # Check for x86 PAUSE instruction { $as_echo "$as_me:${as_lineno-$LINENO}: checking for x86 PAUSE instruction" >&5 $as_echo_n "checking for x86 PAUSE instruction... " >&6; } # We have to actually try running the test program, because of a bug # in Solaris on x86_64, where it wrongly reports that PAUSE is not # supported when trying to run an application. See # http://bugs.opensolaris.org/bugdatabase/printableBug.do?bug_id=6478684 # We use ib_ prefix to avoid collisoins if this code is added to # mysql's configure.in. if test "$cross_compiling" = yes; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main() { __asm__ __volatile__ ("pause"); return(0); } _ACEOF if ac_fn_c_try_run "$LINENO"; then : $as_echo "#define HAVE_IB_PAUSE_INSTRUCTION 1" >>confdefs.h { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi # end of "do not reindent the following lines" # # The behavior of --with-atomic-ops must be: # if this switch is not specified then # if gcc atomics are present, use them # else if solaris atomics are present, use them # else use innodb atomics # else the switch is specified with some value, # check if the requested atomics are present # if present, use them # else emit error during ./configure # # Check whether --with-atomic-ops was given. if test "${with_atomic_ops+set}" = set; then : withval=$with_atomic_ops; fi case "$with_atomic_ops" in "gcc_builtins") ac_fn_c_check_decl "$LINENO" "HAVE_IB_GCC_ATOMIC_BUILTINS" "ac_cv_have_decl_HAVE_IB_GCC_ATOMIC_BUILTINS" "$ac_includes_default" if test "x$ac_cv_have_decl_HAVE_IB_GCC_ATOMIC_BUILTINS" = x""yes; then : ac_have_decl=1 else ac_have_decl=0 fi cat >>confdefs.h <<_ACEOF #define HAVE_DECL_HAVE_IB_GCC_ATOMIC_BUILTINS $ac_have_decl _ACEOF if test $ac_have_decl = 1; then : { $as_echo "$as_me:${as_lineno-$LINENO}: will use the requested GCC atomic builtins" >&5 $as_echo "$as_me: will use the requested GCC atomic builtins" >&6;} else as_fn_error $? "the requested GCC atomic builtins are not available" "$LINENO" 5 fi $as_echo "#define IB_ATOMIC_MODE_GCC_ATOMIC_BUILTINS 1" >>confdefs.h ;; "solaris") ac_fn_c_check_decl "$LINENO" "HAVE_IB_SOLARIS_ATOMICS" "ac_cv_have_decl_HAVE_IB_SOLARIS_ATOMICS" "$ac_includes_default" if test "x$ac_cv_have_decl_HAVE_IB_SOLARIS_ATOMICS" = x""yes; then : ac_have_decl=1 else ac_have_decl=0 fi cat >>confdefs.h <<_ACEOF #define HAVE_DECL_HAVE_IB_SOLARIS_ATOMICS $ac_have_decl _ACEOF if test $ac_have_decl = 1; then : { $as_echo "$as_me:${as_lineno-$LINENO}: will use the requested Solaris atomic functions" >&5 $as_echo "$as_me: will use the requested Solaris atomic functions" >&6;} else as_fn_error $? "the requested Solaris atomic functions are not available" "$LINENO" 5 fi $as_echo "#define IB_ATOMIC_MODE_SOLARIS_ATOMICS 1" >>confdefs.h ;; "innodb") { $as_echo "$as_me:${as_lineno-$LINENO}: will use the requested InnoDB's own implementation of mutexes and rw_locks" >&5 $as_echo "$as_me: will use the requested InnoDB's own implementation of mutexes and rw_locks" >&6;} # this macro is not used but is here for clarity $as_echo "#define IB_ATOMIC_MODE_INNODB 1" >>confdefs.h ;; "") ac_fn_c_check_decl "$LINENO" "HAVE_IB_GCC_ATOMIC_BUILTINS" "ac_cv_have_decl_HAVE_IB_GCC_ATOMIC_BUILTINS" "$ac_includes_default" if test "x$ac_cv_have_decl_HAVE_IB_GCC_ATOMIC_BUILTINS" = x""yes; then : ac_have_decl=1 else ac_have_decl=0 fi cat >>confdefs.h <<_ACEOF #define HAVE_DECL_HAVE_IB_GCC_ATOMIC_BUILTINS $ac_have_decl _ACEOF if test $ac_have_decl = 1; then : { $as_echo "$as_me:${as_lineno-$LINENO}: will use the GCC atomic builtins (auto selected)" >&5 $as_echo "$as_me: will use the GCC atomic builtins (auto selected)" >&6;}; $as_echo "#define IB_ATOMIC_MODE_GCC_ATOMIC_BUILTINS 1" >>confdefs.h else ac_fn_c_check_decl "$LINENO" "HAVE_IB_SOLARIS_ATOMICS" "ac_cv_have_decl_HAVE_IB_SOLARIS_ATOMICS" "$ac_includes_default" if test "x$ac_cv_have_decl_HAVE_IB_SOLARIS_ATOMICS" = x""yes; then : ac_have_decl=1 else ac_have_decl=0 fi cat >>confdefs.h <<_ACEOF #define HAVE_DECL_HAVE_IB_SOLARIS_ATOMICS $ac_have_decl _ACEOF if test $ac_have_decl = 1; then : { $as_echo "$as_me:${as_lineno-$LINENO}: will use the Solaris atomic functions (auto selected)" >&5 $as_echo "$as_me: will use the Solaris atomic functions (auto selected)" >&6;}; $as_echo "#define IB_ATOMIC_MODE_SOLARIS_ATOMICS 1" >>confdefs.h else { $as_echo "$as_me:${as_lineno-$LINENO}: will use InnoDB's own implementation of mutexes and rw_locks (auto selected)" >&5 $as_echo "$as_me: will use InnoDB's own implementation of mutexes and rw_locks (auto selected)" >&6;} fi fi ;; *) as_fn_error $? "Unknown value \"$with_atomic_ops\" specified for --with-atomic-ops" "$LINENO" 5 ;; esac # For large pages support if test "$TARGET_LINUX" = "true"; then # For SHM_HUGETLB on Linux ac_fn_c_check_decl "$LINENO" "SHM_HUGETLB" "ac_cv_have_decl_SHM_HUGETLB" " #include " if test "x$ac_cv_have_decl_SHM_HUGETLB" = x""yes; then : ac_have_decl=1 else ac_have_decl=0 fi cat >>confdefs.h <<_ACEOF #define HAVE_DECL_SHM_HUGETLB $ac_have_decl _ACEOF if test $ac_have_decl = 1; then : $as_echo "#define HAVE_LARGE_PAGES 1" >>confdefs.h $as_echo "#define HUGETLB_USE_PROC_MEMINFO 1" >>confdefs.h fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for an ANSI C-conforming const" >&5 $as_echo_n "checking for an ANSI C-conforming const... " >&6; } if test "${ac_cv_c_const+set}" = set; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { /* FIXME: Include the comments suggested by Paul. */ #ifndef __cplusplus /* Ultrix mips cc rejects this. */ typedef int charset[2]; const charset cs; /* SunOS 4.1.1 cc rejects this. */ char const *const *pcpcc; char **ppc; /* NEC SVR4.0.2 mips cc rejects this. */ struct point {int x, y;}; static struct point const zero = {0,0}; /* AIX XL C 1.02.0.0 rejects this. It does not let you subtract one const X* pointer from another in an arm of an if-expression whose if-part is not a constant expression */ const char *g = "string"; pcpcc = &g + (g ? g-g : 0); /* HPUX 7.0 cc rejects these. */ ++pcpcc; ppc = (char**) pcpcc; pcpcc = (char const *const *) ppc; { /* SCO 3.2v4 cc rejects this. */ char *t; char const *s = 0 ? (char *) 0 : (char const *) 0; *t++ = 0; if (s) return 0; } { /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */ int x[] = {25, 17}; const int *foo = &x[0]; ++foo; } { /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */ typedef const int *iptr; iptr p = 0; ++p; } { /* AIX XL C 1.02.0.0 rejects this saying "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */ struct s { int j; const int *ap[3]; }; struct s *b; b->j = 5; } { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ const int foo = 10; if (!foo) return 0; } return !cs[0] && !zero.x; #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_c_const=yes else ac_cv_c_const=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_const" >&5 $as_echo "$ac_cv_c_const" >&6; } if test $ac_cv_c_const = no; then $as_echo "#define const /**/" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for inline" >&5 $as_echo_n "checking for inline... " >&6; } if test "${ac_cv_c_inline+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_cv_c_inline=no for ac_kw in inline __inline__ __inline; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifndef __cplusplus typedef int foo_t; static $ac_kw foo_t static_foo () {return 0; } $ac_kw foo_t foo () {return 0; } #endif _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_c_inline=$ac_kw fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext test "$ac_cv_c_inline" != no && break done fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_inline" >&5 $as_echo "$ac_cv_c_inline" >&6; } case $ac_cv_c_inline in inline | yes) ;; *) case $ac_cv_c_inline in no) ac_val=;; *) ac_val=$ac_cv_c_inline;; esac cat >>confdefs.h <<_ACEOF #ifndef __cplusplus #define inline $ac_val #endif _ACEOF ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working volatile" >&5 $as_echo_n "checking for working volatile... " >&6; } if test "${ac_cv_c_volatile+set}" = set; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { volatile int x; int * volatile y = (int *) 0; return !x && !y; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_c_volatile=yes else ac_cv_c_volatile=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_volatile" >&5 $as_echo "$ac_cv_c_volatile" >&6; } if test $ac_cv_c_volatile = no; then $as_echo "#define volatile /**/" >>confdefs.h fi ac_fn_c_check_type "$LINENO" "int64_t" "ac_cv_type_int64_t" "#include " if test "x$ac_cv_type_int64_t" = x""yes; then : cat >>confdefs.h <<_ACEOF #define HAVE_INT64_T 1 _ACEOF else as_fn_error $? "Need the C99 integer types from stdint.h" "$LINENO" 5 fi ac_fn_c_check_type "$LINENO" "uint64_t" "ac_cv_type_uint64_t" "#include " if test "x$ac_cv_type_uint64_t" = x""yes; then : cat >>confdefs.h <<_ACEOF #define HAVE_UINT64_T 1 _ACEOF else as_fn_error $? "Need the C99 integer types from stdint.h" "$LINENO" 5 fi ac_fn_c_check_type "$LINENO" "int32_t" "ac_cv_type_int32_t" "#include " if test "x$ac_cv_type_int32_t" = x""yes; then : cat >>confdefs.h <<_ACEOF #define HAVE_INT32_T 1 _ACEOF else as_fn_error $? "Need the C99 integer types from stdint.h" "$LINENO" 5 fi ac_fn_c_check_type "$LINENO" "uint32_t" "ac_cv_type_uint32_t" "#include " if test "x$ac_cv_type_uint32_t" = x""yes; then : cat >>confdefs.h <<_ACEOF #define HAVE_UINT32_T 1 _ACEOF else as_fn_error $? "Need the C99 integer types from stdint.h" "$LINENO" 5 fi ac_fn_c_check_type "$LINENO" "int16_t" "ac_cv_type_int16_t" "#include " if test "x$ac_cv_type_int16_t" = x""yes; then : cat >>confdefs.h <<_ACEOF #define HAVE_INT16_T 1 _ACEOF else as_fn_error $? "Need the C99 integer types from stdint.h" "$LINENO" 5 fi ac_fn_c_check_type "$LINENO" "uint16_t" "ac_cv_type_uint16_t" "#include " if test "x$ac_cv_type_uint16_t" = x""yes; then : cat >>confdefs.h <<_ACEOF #define HAVE_UINT16_T 1 _ACEOF else as_fn_error $? "Need the C99 integer types from stdint.h" "$LINENO" 5 fi ac_fn_c_check_type "$LINENO" "int8_t" "ac_cv_type_int8_t" "#include " if test "x$ac_cv_type_int8_t" = x""yes; then : cat >>confdefs.h <<_ACEOF #define HAVE_INT8_T 1 _ACEOF else as_fn_error $? "Need the C99 integer types from stdint.h" "$LINENO" 5 fi ac_fn_c_check_type "$LINENO" "uint8_t" "ac_cv_type_uint8_t" "#include " if test "x$ac_cv_type_uint8_t" = x""yes; then : cat >>confdefs.h <<_ACEOF #define HAVE_UINT8_T 1 _ACEOF else as_fn_error $? "Need the C99 integer types from stdint.h" "$LINENO" 5 fi ac_fn_c_check_type "$LINENO" "unsigned long long int" "ac_cv_type_unsigned_long_long_int" "#include " if test "x$ac_cv_type_unsigned_long_long_int" = x""yes; then : cat >>confdefs.h <<_ACEOF #define HAVE_UNSIGNED_LONG_LONG_INT 1 _ACEOF else as_fn_error $? "Need the C99 integer types from stdint.h" "$LINENO" 5 fi ac_fn_c_check_type "$LINENO" "long long int" "ac_cv_type_long_long_int" "#include " if test "x$ac_cv_type_long_long_int" = x""yes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LONG_LONG_INT 1 _ACEOF else as_fn_error $? "Need the C99 integer types from stdint.h" "$LINENO" 5 fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether struct tm is in sys/time.h or time.h" >&5 $as_echo_n "checking whether struct tm is in sys/time.h or time.h... " >&6; } if test "${ac_cv_struct_tm+set}" = set; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main () { struct tm tm; int *p = &tm.tm_sec; return !p; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_struct_tm=time.h else ac_cv_struct_tm=sys/time.h fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_struct_tm" >&5 $as_echo "$ac_cv_struct_tm" >&6; } if test $ac_cv_struct_tm = sys/time.h; then $as_echo "#define TM_IN_SYS_TIME 1" >>confdefs.h fi ac_fn_c_check_type "$LINENO" "off_t" "ac_cv_type_off_t" "$ac_includes_default" if test "x$ac_cv_type_off_t" = x""yes; then : else cat >>confdefs.h <<_ACEOF #define off_t long int _ACEOF fi ac_fn_c_check_type "$LINENO" "size_t" "ac_cv_type_size_t" "$ac_includes_default" if test "x$ac_cv_type_size_t" = x""yes; then : else cat >>confdefs.h <<_ACEOF #define size_t unsigned int _ACEOF fi ac_fn_c_check_member "$LINENO" "struct stat" "st_rdev" "ac_cv_member_struct_stat_st_rdev" "$ac_includes_default" if test "x$ac_cv_member_struct_stat_st_rdev" = x""yes; then : cat >>confdefs.h <<_ACEOF #define HAVE_STRUCT_STAT_ST_RDEV 1 _ACEOF $as_echo "#define HAVE_ST_RDEV 1" >>confdefs.h fi # The cast to long int works around a bug in the HP C Compiler # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects # declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. # This bug is HP SR number 8606223364. { $as_echo "$as_me:${as_lineno-$LINENO}: checking size of void*" >&5 $as_echo_n "checking size of void*... " >&6; } if test "${ac_cv_sizeof_voidp+set}" = set; then : $as_echo_n "(cached) " >&6 else if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (void*))" "ac_cv_sizeof_voidp" "$ac_includes_default"; then : else if test "$ac_cv_type_voidp" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "cannot compute sizeof (void*) See \`config.log' for more details" "$LINENO" 5 ; } else ac_cv_sizeof_voidp=0 fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_voidp" >&5 $as_echo "$ac_cv_sizeof_voidp" >&6; } cat >>confdefs.h <<_ACEOF #define SIZEOF_VOIDP $ac_cv_sizeof_voidp _ACEOF # The cast to long int works around a bug in the HP C Compiler # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects # declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. # This bug is HP SR number 8606223364. { $as_echo "$as_me:${as_lineno-$LINENO}: checking size of char*" >&5 $as_echo_n "checking size of char*... " >&6; } if test "${ac_cv_sizeof_charp+set}" = set; then : $as_echo_n "(cached) " >&6 else if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (char*))" "ac_cv_sizeof_charp" "$ac_includes_default"; then : else if test "$ac_cv_type_charp" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "cannot compute sizeof (char*) See \`config.log' for more details" "$LINENO" 5 ; } else ac_cv_sizeof_charp=0 fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_charp" >&5 $as_echo "$ac_cv_sizeof_charp" >&6; } cat >>confdefs.h <<_ACEOF #define SIZEOF_CHARP $ac_cv_sizeof_charp _ACEOF # The cast to long int works around a bug in the HP C Compiler # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects # declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. # This bug is HP SR number 8606223364. { $as_echo "$as_me:${as_lineno-$LINENO}: checking size of short" >&5 $as_echo_n "checking size of short... " >&6; } if test "${ac_cv_sizeof_short+set}" = set; then : $as_echo_n "(cached) " >&6 else if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (short))" "ac_cv_sizeof_short" "$ac_includes_default"; then : else if test "$ac_cv_type_short" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "cannot compute sizeof (short) See \`config.log' for more details" "$LINENO" 5 ; } else ac_cv_sizeof_short=0 fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_short" >&5 $as_echo "$ac_cv_sizeof_short" >&6; } cat >>confdefs.h <<_ACEOF #define SIZEOF_SHORT $ac_cv_sizeof_short _ACEOF # The cast to long int works around a bug in the HP C Compiler # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects # declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. # This bug is HP SR number 8606223364. { $as_echo "$as_me:${as_lineno-$LINENO}: checking size of int" >&5 $as_echo_n "checking size of int... " >&6; } if test "${ac_cv_sizeof_int+set}" = set; then : $as_echo_n "(cached) " >&6 else if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (int))" "ac_cv_sizeof_int" "$ac_includes_default"; then : else if test "$ac_cv_type_int" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "cannot compute sizeof (int) See \`config.log' for more details" "$LINENO" 5 ; } else ac_cv_sizeof_int=0 fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_int" >&5 $as_echo "$ac_cv_sizeof_int" >&6; } cat >>confdefs.h <<_ACEOF #define SIZEOF_INT $ac_cv_sizeof_int _ACEOF # The cast to long int works around a bug in the HP C Compiler # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects # declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. # This bug is HP SR number 8606223364. { $as_echo "$as_me:${as_lineno-$LINENO}: checking size of long" >&5 $as_echo_n "checking size of long... " >&6; } if test "${ac_cv_sizeof_long+set}" = set; then : $as_echo_n "(cached) " >&6 else if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (long))" "ac_cv_sizeof_long" "$ac_includes_default"; then : else if test "$ac_cv_type_long" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "cannot compute sizeof (long) See \`config.log' for more details" "$LINENO" 5 ; } else ac_cv_sizeof_long=0 fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_long" >&5 $as_echo "$ac_cv_sizeof_long" >&6; } cat >>confdefs.h <<_ACEOF #define SIZEOF_LONG $ac_cv_sizeof_long _ACEOF # The cast to long int works around a bug in the HP C Compiler # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects # declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. # This bug is HP SR number 8606223364. { $as_echo "$as_me:${as_lineno-$LINENO}: checking size of long long" >&5 $as_echo_n "checking size of long long... " >&6; } if test "${ac_cv_sizeof_long_long+set}" = set; then : $as_echo_n "(cached) " >&6 else if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (long long))" "ac_cv_sizeof_long_long" "$ac_includes_default"; then : else if test "$ac_cv_type_long_long" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "cannot compute sizeof (long long) See \`config.log' for more details" "$LINENO" 5 ; } else ac_cv_sizeof_long_long=0 fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_long_long" >&5 $as_echo "$ac_cv_sizeof_long_long" >&6; } cat >>confdefs.h <<_ACEOF #define SIZEOF_LONG_LONG $ac_cv_sizeof_long_long _ACEOF # off_t is not a builtin type # The cast to long int works around a bug in the HP C Compiler # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects # declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. # This bug is HP SR number 8606223364. { $as_echo "$as_me:${as_lineno-$LINENO}: checking size of off_t" >&5 $as_echo_n "checking size of off_t... " >&6; } if test "${ac_cv_sizeof_off_t+set}" = set; then : $as_echo_n "(cached) " >&6 else if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (off_t))" "ac_cv_sizeof_off_t" "$ac_includes_default"; then : else if test "$ac_cv_type_off_t" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "cannot compute sizeof (off_t) See \`config.log' for more details" "$LINENO" 5 ; } else ac_cv_sizeof_off_t=0 fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_off_t" >&5 $as_echo "$ac_cv_sizeof_off_t" >&6; } cat >>confdefs.h <<_ACEOF #define SIZEOF_OFF_T $ac_cv_sizeof_off_t _ACEOF ac_fn_c_check_type "$LINENO" "off_t" "ac_cv_type_off_t" "#include " if test "x$ac_cv_type_off_t" = x""yes; then : cat >>confdefs.h <<_ACEOF #define HAVE_OFF_T 1 _ACEOF fi ac_fn_c_check_type "$LINENO" "ulong" "ac_cv_type_ulong" "#include " if test "x$ac_cv_type_ulong" = x""yes; then : cat >>confdefs.h <<_ACEOF #define HAVE_ULONG 1 _ACEOF fi ac_fn_c_check_type "$LINENO" "size_t" "ac_cv_type_size_t" "#include " if test "x$ac_cv_type_size_t" = x""yes; then : cat >>confdefs.h <<_ACEOF #define HAVE_SIZE_T 1 _ACEOF fi ac_fn_c_check_type "$LINENO" "u_int32_t" "ac_cv_type_u_int32_t" "$ac_includes_default" if test "x$ac_cv_type_u_int32_t" = x""yes; then : cat >>confdefs.h <<_ACEOF #define HAVE_U_INT32_T 1 _ACEOF fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_mutex_trylock in -lpthread" >&5 $as_echo_n "checking for pthread_mutex_trylock in -lpthread... " >&6; } if test "${ac_cv_lib_pthread_pthread_mutex_trylock+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lpthread $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char pthread_mutex_trylock (); int main () { return pthread_mutex_trylock (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_pthread_pthread_mutex_trylock=yes else ac_cv_lib_pthread_pthread_mutex_trylock=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pthread_pthread_mutex_trylock" >&5 $as_echo "$ac_cv_lib_pthread_pthread_mutex_trylock" >&6; } if test "x$ac_cv_lib_pthread_pthread_mutex_trylock" = x""yes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBPTHREAD 1 _ACEOF LIBS="-lpthread $LIBS" fi # Required on Solaris ac_fn_c_check_func "$LINENO" "sched_yield" "ac_cv_func_sched_yield" if test "x$ac_cv_func_sched_yield" = x""yes; then : else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sched_yield in -lposix4" >&5 $as_echo_n "checking for sched_yield in -lposix4... " >&6; } if test "${ac_cv_lib_posix4_sched_yield+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lposix4 $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char sched_yield (); int main () { return sched_yield (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_posix4_sched_yield=yes else ac_cv_lib_posix4_sched_yield=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_posix4_sched_yield" >&5 $as_echo "$ac_cv_lib_posix4_sched_yield" >&6; } if test "x$ac_cv_lib_posix4_sched_yield" = x""yes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBPOSIX4 1 _ACEOF LIBS="-lposix4 $LIBS" fi fi for ac_func in strdup strlcpy do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" if eval test \"x\$"$as_ac_var"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF fi done ##### # Check whether the user wants to disable compressed tables functionality ##### # Check whether --enable-compression was given. if test "${enable_compression+set}" = set; then : enableval=$enable_compression; else enable_compression=yes fi if test $enable_compression = yes; then # Check for ZLib functions ac_fn_c_check_func "$LINENO" "deflate" "ac_cv_func_deflate" if test "x$ac_cv_func_deflate" = x""yes; then : else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for deflate in -lz" >&5 $as_echo_n "checking for deflate in -lz... " >&6; } if test "${ac_cv_lib_z_deflate+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lz $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char deflate (); int main () { return deflate (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_z_deflate=yes else ac_cv_lib_z_deflate=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_z_deflate" >&5 $as_echo "$ac_cv_lib_z_deflate" >&6; } if test "x$ac_cv_lib_z_deflate" = x""yes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBZ 1 _ACEOF LIBS="-lz $LIBS" else as_fn_error $? "Zlib is required for compression to work" "$LINENO" 5 fi fi ac_fn_c_check_func "$LINENO" "inflate" "ac_cv_func_inflate" if test "x$ac_cv_func_inflate" = x""yes; then : else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for inflate in -lz" >&5 $as_echo_n "checking for inflate in -lz... " >&6; } if test "${ac_cv_lib_z_inflate+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lz $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char inflate (); int main () { return inflate (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_z_inflate=yes else ac_cv_lib_z_inflate=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_z_inflate" >&5 $as_echo "$ac_cv_lib_z_inflate" >&6; } if test "x$ac_cv_lib_z_inflate" = x""yes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBZ 1 _ACEOF LIBS="-lz $LIBS" else as_fn_error $? "Zlib is required for compression to work" "$LINENO" 5 fi fi for ac_header in zlib.h do : ac_fn_c_check_header_mongrel "$LINENO" "zlib.h" "ac_cv_header_zlib_h" "$ac_includes_default" if test "x$ac_cv_header_zlib_h" = x""yes; then : cat >>confdefs.h <<_ACEOF #define HAVE_ZLIB_H 1 _ACEOF fi done $as_echo "#define WITH_ZIP 1" >>confdefs.h fi if test $enable_compression = yes; then WITH_ZIP_TRUE= WITH_ZIP_FALSE='#' else WITH_ZIP_TRUE='#' WITH_ZIP_FALSE= fi # End Zip tests ##### # Check whether the user wants to disable XA functionality ##### # Check whether --enable-xa was given. if test "${enable_xa+set}" = set; then : enableval=$enable_xa; else enable_xa=yes $as_echo "#define WITH_XOPEN 1" >>confdefs.h fi if test $enable_xa = yes; then WITH_XOPEN_TRUE= WITH_XOPEN_FALSE='#' else WITH_XOPEN_TRUE='#' WITH_XOPEN_FALSE= fi for ac_func in \ bcmp \ fcntl \ finite \ fsync \ ftruncate \ getcwd \ getrusage \ index \ localtime_r \ locking \ memcpy \ memmove \ perror \ pread \ mmap \ getpagesize \ pthread_attr_setstacksize \ pthread_barrier_destroy \ pthread_barrier_init \ pthread_barrier_wait \ pthread_setprio \ rename \ rint \ shmget \ shmat \ shmdt \ shmctl \ sleep \ snprintf \ stpcpy \ strcasecmp \ strerror \ strstr \ strtoul \ tell do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" if eval test \"x\$"$as_ac_var"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF fi done if test "$HAVE_PTHREAD_BARRIER_INIT" = yes; then HAVE_PTHREAD_BARRIER_TRUE= HAVE_PTHREAD_BARRIER_FALSE='#' else HAVE_PTHREAD_BARRIER_TRUE='#' HAVE_PTHREAD_BARRIER_FALSE= fi AM_CPPFLAGS="${AM_CPPFLAGS} -I\$(top_srcdir)/include -I\$(top_builddir)/include" AM_CFLAGS="${AM_CFLAGS} ${NO_WERROR}" ac_config_files="$ac_config_files Makefile haildb.spec" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure # scripts and configure runs, see configure's option --config-cache. # It is not useful on other systems. If it contains results you don't # want to keep, you may remove or edit it. # # config.status only pays attention to the cache file if you give it # the --recheck option to rerun configure. # # `ac_cv_env_foo' variables (set or unset) will be overridden when # loading this file, other *unset* `ac_cv_foo' will be assigned the # following values. _ACEOF # The following way of writing the cache mishandles newlines in values, # but we know of no workaround that is simple, portable, and efficient. # So, we kill variables containing newlines. # Ultrix sh set writes to stderr and can't be redirected directly, # and sets the high bit in the cache file unless we assign to the vars. ( for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space=' '; set) 2>&1` in #( *${as_nl}ac_space=\ *) # `set' does not quote correctly, so add quotes: double-quote # substitution turns \\\\ into \\, and sed turns \\ into \. sed -n \ "s/'/'\\\\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" ;; #( *) # `set' quotes correctly as required by POSIX, so do not add quotes. sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) | sed ' /^ac_cv_env_/b end t clear :clear s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ t end s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ :end' >>confcache if diff "$cache_file" confcache >/dev/null 2>&1; then :; else if test -w "$cache_file"; then test "x$cache_file" != "x/dev/null" && { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 $as_echo "$as_me: updating cache $cache_file" >&6;} cat confcache >$cache_file else { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 $as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} fi fi rm -f confcache test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' DEFS=-DHAVE_CONFIG_H ac_libobjs= ac_ltlibobjs= U= for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' ac_i=`$as_echo "$ac_i" | sed "$ac_script"` # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR # will be set to the directory where LIBOBJS objects are built. as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' done LIBOBJS=$ac_libobjs LTLIBOBJS=$ac_ltlibobjs if test -n "$EXEEXT"; then am__EXEEXT_TRUE= am__EXEEXT_FALSE='#' else am__EXEEXT_TRUE='#' am__EXEEXT_FALSE= fi if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then as_fn_error $? "conditional \"AMDEP\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then as_fn_error $? "conditional \"am__fastdepCC\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${am__fastdepCXX_TRUE}" && test -z "${am__fastdepCXX_FALSE}"; then as_fn_error $? "conditional \"am__fastdepCXX\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${BUILD_WIN32_TRUE}" && test -z "${BUILD_WIN32_FALSE}"; then as_fn_error $? "conditional \"BUILD_WIN32\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${am__fastdepCXX_TRUE}" && test -z "${am__fastdepCXX_FALSE}"; then as_fn_error $? "conditional \"am__fastdepCXX\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${HAVE_DTRACE_TRUE}" && test -z "${HAVE_DTRACE_FALSE}"; then as_fn_error $? "conditional \"HAVE_DTRACE\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${DTRACE_NEEDS_OBJECTS_TRUE}" && test -z "${DTRACE_NEEDS_OBJECTS_FALSE}"; then as_fn_error $? "conditional \"DTRACE_NEEDS_OBJECTS\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${HAVE_DPKG_GENSYMBOLS_TRUE}" && test -z "${HAVE_DPKG_GENSYMBOLS_FALSE}"; then as_fn_error $? "conditional \"HAVE_DPKG_GENSYMBOLS\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${HAVE_SPHINX_TRUE}" && test -z "${HAVE_SPHINX_FALSE}"; then as_fn_error $? "conditional \"HAVE_SPHINX\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${HAVE_RECENT_SPHINX_TRUE}" && test -z "${HAVE_RECENT_SPHINX_FALSE}"; then as_fn_error $? "conditional \"HAVE_RECENT_SPHINX\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${BUILD_PO_TRUE}" && test -z "${BUILD_PO_FALSE}"; then as_fn_error $? "conditional \"BUILD_PO\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${HAVE_BISON_TRUE}" && test -z "${HAVE_BISON_FALSE}"; then as_fn_error $? "conditional \"HAVE_BISON\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${WITH_ZIP_TRUE}" && test -z "${WITH_ZIP_FALSE}"; then as_fn_error $? "conditional \"WITH_ZIP\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${WITH_XOPEN_TRUE}" && test -z "${WITH_XOPEN_FALSE}"; then as_fn_error $? "conditional \"WITH_XOPEN\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${HAVE_PTHREAD_BARRIER_TRUE}" && test -z "${HAVE_PTHREAD_BARRIER_FALSE}"; then as_fn_error $? "conditional \"HAVE_PTHREAD_BARRIER\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi : ${CONFIG_STATUS=./config.status} ac_write_fail=0 ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files $CONFIG_STATUS" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 $as_echo "$as_me: creating $CONFIG_STATUS" >&6;} as_write_fail=0 cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 #! $SHELL # Generated by $as_me. # Run this file to recreate the current configuration. # Compiler output produced by configure, useful for debugging # configure, is in config.log if it exists. debug=false ac_cs_recheck=false ac_cs_silent=false SHELL=\${CONFIG_SHELL-$SHELL} export SHELL _ASEOF cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -p'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -p' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -p' fi else as_ln_s='cp -p' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi if test -x / >/dev/null 2>&1; then as_test_x='test -x' else if ls -dL / >/dev/null 2>&1; then as_ls_L_option=L else as_ls_L_option= fi as_test_x=' eval sh -c '\'' if test -d "$1"; then test -d "$1/."; else case $1 in #( -*)set "./$1";; esac; case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #(( ???[sx]*):;;*)false;;esac;fi '\'' sh ' fi as_executable_p=$as_test_x # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" exec 6>&1 ## ----------------------------------- ## ## Main body of $CONFIG_STATUS script. ## ## ----------------------------------- ## _ASEOF test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Save the log message, to keep $0 and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" This file was extended by haildb $as_me 2.3.2, which was generated by GNU Autoconf 2.67. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ on `(hostname || uname -n) 2>/dev/null | sed 1q` " _ACEOF case $ac_config_files in *" "*) set x $ac_config_files; shift; ac_config_files=$*;; esac case $ac_config_headers in *" "*) set x $ac_config_headers; shift; ac_config_headers=$*;; esac cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # Files that config.status was made for. config_files="$ac_config_files" config_headers="$ac_config_headers" config_commands="$ac_config_commands" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 ac_cs_usage="\ \`$as_me' instantiates files and other configuration actions from templates according to the current configuration. Unless the files and actions are specified as TAGs, all are instantiated by default. Usage: $0 [OPTION]... [TAG]... -h, --help print this help, then exit -V, --version print version number and configuration settings, then exit --config print configuration, then exit -q, --quiet, --silent do not print progress messages -d, --debug don't remove temporary files --recheck update $as_me by reconfiguring in the same conditions --file=FILE[:TEMPLATE] instantiate the configuration file FILE --header=FILE[:TEMPLATE] instantiate the configuration header FILE Configuration files: $config_files Configuration headers: $config_headers Configuration commands: $config_commands Report bugs to ." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ haildb config.status 2.3.2 configured by $0, generated by GNU Autoconf 2.67, with options \\"\$ac_cs_config\\" Copyright (C) 2010 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." ac_pwd='$ac_pwd' srcdir='$srcdir' INSTALL='$INSTALL' MKDIR_P='$MKDIR_P' AWK='$AWK' test -n "\$AWK" || AWK=awk _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # The default lists apply if the user does not specify any file. ac_need_defaults=: while test $# != 0 do case $1 in --*=?*) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` ac_shift=: ;; --*=) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg= ac_shift=: ;; *) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; esac case $ac_option in # Handling of the options. -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) ac_cs_recheck=: ;; --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) $as_echo "$ac_cs_version"; exit ;; --config | --confi | --conf | --con | --co | --c ) $as_echo "$ac_cs_config"; exit ;; --debug | --debu | --deb | --de | --d | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; '') as_fn_error $? "missing file argument" ;; esac as_fn_append CONFIG_FILES " '$ac_optarg'" ac_need_defaults=false;; --header | --heade | --head | --hea ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; esac as_fn_append CONFIG_HEADERS " '$ac_optarg'" ac_need_defaults=false;; --he | --h) # Conflict between --help and --header as_fn_error $? "ambiguous option: \`$1' Try \`$0 --help' for more information.";; --help | --hel | -h ) $as_echo "$ac_cs_usage"; exit ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil | --si | --s) ac_cs_silent=: ;; # This is an error. -*) as_fn_error $? "unrecognized option: \`$1' Try \`$0 --help' for more information." ;; *) as_fn_append ac_config_targets " $1" ac_need_defaults=false ;; esac shift done ac_configure_extra_args= if $ac_cs_silent; then exec 6>/dev/null ac_configure_extra_args="$ac_configure_extra_args --silent" fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 if \$ac_cs_recheck; then set X '$SHELL' '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion shift \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 CONFIG_SHELL='$SHELL' export CONFIG_SHELL exec "\$@" fi _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 exec 5>>config.log { echo sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX ## Running $as_me. ## _ASBOX $as_echo "$ac_log" } >&5 _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # # INIT-COMMANDS # AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir" # The HP-UX ksh and POSIX shell print the target directory to stdout # if CDPATH is set. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH sed_quote_subst='$sed_quote_subst' double_quote_subst='$double_quote_subst' delay_variable_subst='$delay_variable_subst' enable_static='`$ECHO "X$enable_static" | $Xsed -e "$delay_single_quote_subst"`' macro_version='`$ECHO "X$macro_version" | $Xsed -e "$delay_single_quote_subst"`' macro_revision='`$ECHO "X$macro_revision" | $Xsed -e "$delay_single_quote_subst"`' enable_shared='`$ECHO "X$enable_shared" | $Xsed -e "$delay_single_quote_subst"`' pic_mode='`$ECHO "X$pic_mode" | $Xsed -e "$delay_single_quote_subst"`' enable_fast_install='`$ECHO "X$enable_fast_install" | $Xsed -e "$delay_single_quote_subst"`' host_alias='`$ECHO "X$host_alias" | $Xsed -e "$delay_single_quote_subst"`' host='`$ECHO "X$host" | $Xsed -e "$delay_single_quote_subst"`' host_os='`$ECHO "X$host_os" | $Xsed -e "$delay_single_quote_subst"`' build_alias='`$ECHO "X$build_alias" | $Xsed -e "$delay_single_quote_subst"`' build='`$ECHO "X$build" | $Xsed -e "$delay_single_quote_subst"`' build_os='`$ECHO "X$build_os" | $Xsed -e "$delay_single_quote_subst"`' SED='`$ECHO "X$SED" | $Xsed -e "$delay_single_quote_subst"`' Xsed='`$ECHO "X$Xsed" | $Xsed -e "$delay_single_quote_subst"`' GREP='`$ECHO "X$GREP" | $Xsed -e "$delay_single_quote_subst"`' EGREP='`$ECHO "X$EGREP" | $Xsed -e "$delay_single_quote_subst"`' FGREP='`$ECHO "X$FGREP" | $Xsed -e "$delay_single_quote_subst"`' LD='`$ECHO "X$LD" | $Xsed -e "$delay_single_quote_subst"`' NM='`$ECHO "X$NM" | $Xsed -e "$delay_single_quote_subst"`' LN_S='`$ECHO "X$LN_S" | $Xsed -e "$delay_single_quote_subst"`' max_cmd_len='`$ECHO "X$max_cmd_len" | $Xsed -e "$delay_single_quote_subst"`' ac_objext='`$ECHO "X$ac_objext" | $Xsed -e "$delay_single_quote_subst"`' exeext='`$ECHO "X$exeext" | $Xsed -e "$delay_single_quote_subst"`' lt_unset='`$ECHO "X$lt_unset" | $Xsed -e "$delay_single_quote_subst"`' lt_SP2NL='`$ECHO "X$lt_SP2NL" | $Xsed -e "$delay_single_quote_subst"`' lt_NL2SP='`$ECHO "X$lt_NL2SP" | $Xsed -e "$delay_single_quote_subst"`' reload_flag='`$ECHO "X$reload_flag" | $Xsed -e "$delay_single_quote_subst"`' reload_cmds='`$ECHO "X$reload_cmds" | $Xsed -e "$delay_single_quote_subst"`' OBJDUMP='`$ECHO "X$OBJDUMP" | $Xsed -e "$delay_single_quote_subst"`' deplibs_check_method='`$ECHO "X$deplibs_check_method" | $Xsed -e "$delay_single_quote_subst"`' file_magic_cmd='`$ECHO "X$file_magic_cmd" | $Xsed -e "$delay_single_quote_subst"`' AR='`$ECHO "X$AR" | $Xsed -e "$delay_single_quote_subst"`' AR_FLAGS='`$ECHO "X$AR_FLAGS" | $Xsed -e "$delay_single_quote_subst"`' STRIP='`$ECHO "X$STRIP" | $Xsed -e "$delay_single_quote_subst"`' RANLIB='`$ECHO "X$RANLIB" | $Xsed -e "$delay_single_quote_subst"`' old_postinstall_cmds='`$ECHO "X$old_postinstall_cmds" | $Xsed -e "$delay_single_quote_subst"`' old_postuninstall_cmds='`$ECHO "X$old_postuninstall_cmds" | $Xsed -e "$delay_single_quote_subst"`' old_archive_cmds='`$ECHO "X$old_archive_cmds" | $Xsed -e "$delay_single_quote_subst"`' CC='`$ECHO "X$CC" | $Xsed -e "$delay_single_quote_subst"`' CFLAGS='`$ECHO "X$CFLAGS" | $Xsed -e "$delay_single_quote_subst"`' compiler='`$ECHO "X$compiler" | $Xsed -e "$delay_single_quote_subst"`' GCC='`$ECHO "X$GCC" | $Xsed -e "$delay_single_quote_subst"`' lt_cv_sys_global_symbol_pipe='`$ECHO "X$lt_cv_sys_global_symbol_pipe" | $Xsed -e "$delay_single_quote_subst"`' lt_cv_sys_global_symbol_to_cdecl='`$ECHO "X$lt_cv_sys_global_symbol_to_cdecl" | $Xsed -e "$delay_single_quote_subst"`' lt_cv_sys_global_symbol_to_c_name_address='`$ECHO "X$lt_cv_sys_global_symbol_to_c_name_address" | $Xsed -e "$delay_single_quote_subst"`' lt_cv_sys_global_symbol_to_c_name_address_lib_prefix='`$ECHO "X$lt_cv_sys_global_symbol_to_c_name_address_lib_prefix" | $Xsed -e "$delay_single_quote_subst"`' objdir='`$ECHO "X$objdir" | $Xsed -e "$delay_single_quote_subst"`' SHELL='`$ECHO "X$SHELL" | $Xsed -e "$delay_single_quote_subst"`' ECHO='`$ECHO "X$ECHO" | $Xsed -e "$delay_single_quote_subst"`' MAGIC_CMD='`$ECHO "X$MAGIC_CMD" | $Xsed -e "$delay_single_quote_subst"`' lt_prog_compiler_no_builtin_flag='`$ECHO "X$lt_prog_compiler_no_builtin_flag" | $Xsed -e "$delay_single_quote_subst"`' lt_prog_compiler_wl='`$ECHO "X$lt_prog_compiler_wl" | $Xsed -e "$delay_single_quote_subst"`' lt_prog_compiler_pic='`$ECHO "X$lt_prog_compiler_pic" | $Xsed -e "$delay_single_quote_subst"`' lt_prog_compiler_static='`$ECHO "X$lt_prog_compiler_static" | $Xsed -e "$delay_single_quote_subst"`' lt_cv_prog_compiler_c_o='`$ECHO "X$lt_cv_prog_compiler_c_o" | $Xsed -e "$delay_single_quote_subst"`' need_locks='`$ECHO "X$need_locks" | $Xsed -e "$delay_single_quote_subst"`' DSYMUTIL='`$ECHO "X$DSYMUTIL" | $Xsed -e "$delay_single_quote_subst"`' NMEDIT='`$ECHO "X$NMEDIT" | $Xsed -e "$delay_single_quote_subst"`' LIPO='`$ECHO "X$LIPO" | $Xsed -e "$delay_single_quote_subst"`' OTOOL='`$ECHO "X$OTOOL" | $Xsed -e "$delay_single_quote_subst"`' OTOOL64='`$ECHO "X$OTOOL64" | $Xsed -e "$delay_single_quote_subst"`' libext='`$ECHO "X$libext" | $Xsed -e "$delay_single_quote_subst"`' shrext_cmds='`$ECHO "X$shrext_cmds" | $Xsed -e "$delay_single_quote_subst"`' extract_expsyms_cmds='`$ECHO "X$extract_expsyms_cmds" | $Xsed -e "$delay_single_quote_subst"`' archive_cmds_need_lc='`$ECHO "X$archive_cmds_need_lc" | $Xsed -e "$delay_single_quote_subst"`' enable_shared_with_static_runtimes='`$ECHO "X$enable_shared_with_static_runtimes" | $Xsed -e "$delay_single_quote_subst"`' export_dynamic_flag_spec='`$ECHO "X$export_dynamic_flag_spec" | $Xsed -e "$delay_single_quote_subst"`' whole_archive_flag_spec='`$ECHO "X$whole_archive_flag_spec" | $Xsed -e "$delay_single_quote_subst"`' compiler_needs_object='`$ECHO "X$compiler_needs_object" | $Xsed -e "$delay_single_quote_subst"`' old_archive_from_new_cmds='`$ECHO "X$old_archive_from_new_cmds" | $Xsed -e "$delay_single_quote_subst"`' old_archive_from_expsyms_cmds='`$ECHO "X$old_archive_from_expsyms_cmds" | $Xsed -e "$delay_single_quote_subst"`' archive_cmds='`$ECHO "X$archive_cmds" | $Xsed -e "$delay_single_quote_subst"`' archive_expsym_cmds='`$ECHO "X$archive_expsym_cmds" | $Xsed -e "$delay_single_quote_subst"`' module_cmds='`$ECHO "X$module_cmds" | $Xsed -e "$delay_single_quote_subst"`' module_expsym_cmds='`$ECHO "X$module_expsym_cmds" | $Xsed -e "$delay_single_quote_subst"`' with_gnu_ld='`$ECHO "X$with_gnu_ld" | $Xsed -e "$delay_single_quote_subst"`' allow_undefined_flag='`$ECHO "X$allow_undefined_flag" | $Xsed -e "$delay_single_quote_subst"`' no_undefined_flag='`$ECHO "X$no_undefined_flag" | $Xsed -e "$delay_single_quote_subst"`' hardcode_libdir_flag_spec='`$ECHO "X$hardcode_libdir_flag_spec" | $Xsed -e "$delay_single_quote_subst"`' hardcode_libdir_flag_spec_ld='`$ECHO "X$hardcode_libdir_flag_spec_ld" | $Xsed -e "$delay_single_quote_subst"`' hardcode_libdir_separator='`$ECHO "X$hardcode_libdir_separator" | $Xsed -e "$delay_single_quote_subst"`' hardcode_direct='`$ECHO "X$hardcode_direct" | $Xsed -e "$delay_single_quote_subst"`' hardcode_direct_absolute='`$ECHO "X$hardcode_direct_absolute" | $Xsed -e "$delay_single_quote_subst"`' hardcode_minus_L='`$ECHO "X$hardcode_minus_L" | $Xsed -e "$delay_single_quote_subst"`' hardcode_shlibpath_var='`$ECHO "X$hardcode_shlibpath_var" | $Xsed -e "$delay_single_quote_subst"`' hardcode_automatic='`$ECHO "X$hardcode_automatic" | $Xsed -e "$delay_single_quote_subst"`' inherit_rpath='`$ECHO "X$inherit_rpath" | $Xsed -e "$delay_single_quote_subst"`' link_all_deplibs='`$ECHO "X$link_all_deplibs" | $Xsed -e "$delay_single_quote_subst"`' fix_srcfile_path='`$ECHO "X$fix_srcfile_path" | $Xsed -e "$delay_single_quote_subst"`' always_export_symbols='`$ECHO "X$always_export_symbols" | $Xsed -e "$delay_single_quote_subst"`' export_symbols_cmds='`$ECHO "X$export_symbols_cmds" | $Xsed -e "$delay_single_quote_subst"`' exclude_expsyms='`$ECHO "X$exclude_expsyms" | $Xsed -e "$delay_single_quote_subst"`' include_expsyms='`$ECHO "X$include_expsyms" | $Xsed -e "$delay_single_quote_subst"`' prelink_cmds='`$ECHO "X$prelink_cmds" | $Xsed -e "$delay_single_quote_subst"`' file_list_spec='`$ECHO "X$file_list_spec" | $Xsed -e "$delay_single_quote_subst"`' variables_saved_for_relink='`$ECHO "X$variables_saved_for_relink" | $Xsed -e "$delay_single_quote_subst"`' need_lib_prefix='`$ECHO "X$need_lib_prefix" | $Xsed -e "$delay_single_quote_subst"`' need_version='`$ECHO "X$need_version" | $Xsed -e "$delay_single_quote_subst"`' version_type='`$ECHO "X$version_type" | $Xsed -e "$delay_single_quote_subst"`' runpath_var='`$ECHO "X$runpath_var" | $Xsed -e "$delay_single_quote_subst"`' shlibpath_var='`$ECHO "X$shlibpath_var" | $Xsed -e "$delay_single_quote_subst"`' shlibpath_overrides_runpath='`$ECHO "X$shlibpath_overrides_runpath" | $Xsed -e "$delay_single_quote_subst"`' libname_spec='`$ECHO "X$libname_spec" | $Xsed -e "$delay_single_quote_subst"`' library_names_spec='`$ECHO "X$library_names_spec" | $Xsed -e "$delay_single_quote_subst"`' soname_spec='`$ECHO "X$soname_spec" | $Xsed -e "$delay_single_quote_subst"`' postinstall_cmds='`$ECHO "X$postinstall_cmds" | $Xsed -e "$delay_single_quote_subst"`' postuninstall_cmds='`$ECHO "X$postuninstall_cmds" | $Xsed -e "$delay_single_quote_subst"`' finish_cmds='`$ECHO "X$finish_cmds" | $Xsed -e "$delay_single_quote_subst"`' finish_eval='`$ECHO "X$finish_eval" | $Xsed -e "$delay_single_quote_subst"`' hardcode_into_libs='`$ECHO "X$hardcode_into_libs" | $Xsed -e "$delay_single_quote_subst"`' sys_lib_search_path_spec='`$ECHO "X$sys_lib_search_path_spec" | $Xsed -e "$delay_single_quote_subst"`' sys_lib_dlsearch_path_spec='`$ECHO "X$sys_lib_dlsearch_path_spec" | $Xsed -e "$delay_single_quote_subst"`' hardcode_action='`$ECHO "X$hardcode_action" | $Xsed -e "$delay_single_quote_subst"`' enable_dlopen='`$ECHO "X$enable_dlopen" | $Xsed -e "$delay_single_quote_subst"`' enable_dlopen_self='`$ECHO "X$enable_dlopen_self" | $Xsed -e "$delay_single_quote_subst"`' enable_dlopen_self_static='`$ECHO "X$enable_dlopen_self_static" | $Xsed -e "$delay_single_quote_subst"`' old_striplib='`$ECHO "X$old_striplib" | $Xsed -e "$delay_single_quote_subst"`' striplib='`$ECHO "X$striplib" | $Xsed -e "$delay_single_quote_subst"`' compiler_lib_search_dirs='`$ECHO "X$compiler_lib_search_dirs" | $Xsed -e "$delay_single_quote_subst"`' predep_objects='`$ECHO "X$predep_objects" | $Xsed -e "$delay_single_quote_subst"`' postdep_objects='`$ECHO "X$postdep_objects" | $Xsed -e "$delay_single_quote_subst"`' predeps='`$ECHO "X$predeps" | $Xsed -e "$delay_single_quote_subst"`' postdeps='`$ECHO "X$postdeps" | $Xsed -e "$delay_single_quote_subst"`' compiler_lib_search_path='`$ECHO "X$compiler_lib_search_path" | $Xsed -e "$delay_single_quote_subst"`' LD_CXX='`$ECHO "X$LD_CXX" | $Xsed -e "$delay_single_quote_subst"`' old_archive_cmds_CXX='`$ECHO "X$old_archive_cmds_CXX" | $Xsed -e "$delay_single_quote_subst"`' compiler_CXX='`$ECHO "X$compiler_CXX" | $Xsed -e "$delay_single_quote_subst"`' GCC_CXX='`$ECHO "X$GCC_CXX" | $Xsed -e "$delay_single_quote_subst"`' lt_prog_compiler_no_builtin_flag_CXX='`$ECHO "X$lt_prog_compiler_no_builtin_flag_CXX" | $Xsed -e "$delay_single_quote_subst"`' lt_prog_compiler_wl_CXX='`$ECHO "X$lt_prog_compiler_wl_CXX" | $Xsed -e "$delay_single_quote_subst"`' lt_prog_compiler_pic_CXX='`$ECHO "X$lt_prog_compiler_pic_CXX" | $Xsed -e "$delay_single_quote_subst"`' lt_prog_compiler_static_CXX='`$ECHO "X$lt_prog_compiler_static_CXX" | $Xsed -e "$delay_single_quote_subst"`' lt_cv_prog_compiler_c_o_CXX='`$ECHO "X$lt_cv_prog_compiler_c_o_CXX" | $Xsed -e "$delay_single_quote_subst"`' archive_cmds_need_lc_CXX='`$ECHO "X$archive_cmds_need_lc_CXX" | $Xsed -e "$delay_single_quote_subst"`' enable_shared_with_static_runtimes_CXX='`$ECHO "X$enable_shared_with_static_runtimes_CXX" | $Xsed -e "$delay_single_quote_subst"`' export_dynamic_flag_spec_CXX='`$ECHO "X$export_dynamic_flag_spec_CXX" | $Xsed -e "$delay_single_quote_subst"`' whole_archive_flag_spec_CXX='`$ECHO "X$whole_archive_flag_spec_CXX" | $Xsed -e "$delay_single_quote_subst"`' compiler_needs_object_CXX='`$ECHO "X$compiler_needs_object_CXX" | $Xsed -e "$delay_single_quote_subst"`' old_archive_from_new_cmds_CXX='`$ECHO "X$old_archive_from_new_cmds_CXX" | $Xsed -e "$delay_single_quote_subst"`' old_archive_from_expsyms_cmds_CXX='`$ECHO "X$old_archive_from_expsyms_cmds_CXX" | $Xsed -e "$delay_single_quote_subst"`' archive_cmds_CXX='`$ECHO "X$archive_cmds_CXX" | $Xsed -e "$delay_single_quote_subst"`' archive_expsym_cmds_CXX='`$ECHO "X$archive_expsym_cmds_CXX" | $Xsed -e "$delay_single_quote_subst"`' module_cmds_CXX='`$ECHO "X$module_cmds_CXX" | $Xsed -e "$delay_single_quote_subst"`' module_expsym_cmds_CXX='`$ECHO "X$module_expsym_cmds_CXX" | $Xsed -e "$delay_single_quote_subst"`' with_gnu_ld_CXX='`$ECHO "X$with_gnu_ld_CXX" | $Xsed -e "$delay_single_quote_subst"`' allow_undefined_flag_CXX='`$ECHO "X$allow_undefined_flag_CXX" | $Xsed -e "$delay_single_quote_subst"`' no_undefined_flag_CXX='`$ECHO "X$no_undefined_flag_CXX" | $Xsed -e "$delay_single_quote_subst"`' hardcode_libdir_flag_spec_CXX='`$ECHO "X$hardcode_libdir_flag_spec_CXX" | $Xsed -e "$delay_single_quote_subst"`' hardcode_libdir_flag_spec_ld_CXX='`$ECHO "X$hardcode_libdir_flag_spec_ld_CXX" | $Xsed -e "$delay_single_quote_subst"`' hardcode_libdir_separator_CXX='`$ECHO "X$hardcode_libdir_separator_CXX" | $Xsed -e "$delay_single_quote_subst"`' hardcode_direct_CXX='`$ECHO "X$hardcode_direct_CXX" | $Xsed -e "$delay_single_quote_subst"`' hardcode_direct_absolute_CXX='`$ECHO "X$hardcode_direct_absolute_CXX" | $Xsed -e "$delay_single_quote_subst"`' hardcode_minus_L_CXX='`$ECHO "X$hardcode_minus_L_CXX" | $Xsed -e "$delay_single_quote_subst"`' hardcode_shlibpath_var_CXX='`$ECHO "X$hardcode_shlibpath_var_CXX" | $Xsed -e "$delay_single_quote_subst"`' hardcode_automatic_CXX='`$ECHO "X$hardcode_automatic_CXX" | $Xsed -e "$delay_single_quote_subst"`' inherit_rpath_CXX='`$ECHO "X$inherit_rpath_CXX" | $Xsed -e "$delay_single_quote_subst"`' link_all_deplibs_CXX='`$ECHO "X$link_all_deplibs_CXX" | $Xsed -e "$delay_single_quote_subst"`' fix_srcfile_path_CXX='`$ECHO "X$fix_srcfile_path_CXX" | $Xsed -e "$delay_single_quote_subst"`' always_export_symbols_CXX='`$ECHO "X$always_export_symbols_CXX" | $Xsed -e "$delay_single_quote_subst"`' export_symbols_cmds_CXX='`$ECHO "X$export_symbols_cmds_CXX" | $Xsed -e "$delay_single_quote_subst"`' exclude_expsyms_CXX='`$ECHO "X$exclude_expsyms_CXX" | $Xsed -e "$delay_single_quote_subst"`' include_expsyms_CXX='`$ECHO "X$include_expsyms_CXX" | $Xsed -e "$delay_single_quote_subst"`' prelink_cmds_CXX='`$ECHO "X$prelink_cmds_CXX" | $Xsed -e "$delay_single_quote_subst"`' file_list_spec_CXX='`$ECHO "X$file_list_spec_CXX" | $Xsed -e "$delay_single_quote_subst"`' hardcode_action_CXX='`$ECHO "X$hardcode_action_CXX" | $Xsed -e "$delay_single_quote_subst"`' compiler_lib_search_dirs_CXX='`$ECHO "X$compiler_lib_search_dirs_CXX" | $Xsed -e "$delay_single_quote_subst"`' predep_objects_CXX='`$ECHO "X$predep_objects_CXX" | $Xsed -e "$delay_single_quote_subst"`' postdep_objects_CXX='`$ECHO "X$postdep_objects_CXX" | $Xsed -e "$delay_single_quote_subst"`' predeps_CXX='`$ECHO "X$predeps_CXX" | $Xsed -e "$delay_single_quote_subst"`' postdeps_CXX='`$ECHO "X$postdeps_CXX" | $Xsed -e "$delay_single_quote_subst"`' compiler_lib_search_path_CXX='`$ECHO "X$compiler_lib_search_path_CXX" | $Xsed -e "$delay_single_quote_subst"`' LTCC='$LTCC' LTCFLAGS='$LTCFLAGS' compiler='$compiler_DEFAULT' # Quote evaled strings. for var in SED \ GREP \ EGREP \ FGREP \ LD \ NM \ LN_S \ lt_SP2NL \ lt_NL2SP \ reload_flag \ OBJDUMP \ deplibs_check_method \ file_magic_cmd \ AR \ AR_FLAGS \ STRIP \ RANLIB \ CC \ CFLAGS \ compiler \ lt_cv_sys_global_symbol_pipe \ lt_cv_sys_global_symbol_to_cdecl \ lt_cv_sys_global_symbol_to_c_name_address \ lt_cv_sys_global_symbol_to_c_name_address_lib_prefix \ SHELL \ ECHO \ lt_prog_compiler_no_builtin_flag \ lt_prog_compiler_wl \ lt_prog_compiler_pic \ lt_prog_compiler_static \ lt_cv_prog_compiler_c_o \ need_locks \ DSYMUTIL \ NMEDIT \ LIPO \ OTOOL \ OTOOL64 \ shrext_cmds \ export_dynamic_flag_spec \ whole_archive_flag_spec \ compiler_needs_object \ with_gnu_ld \ allow_undefined_flag \ no_undefined_flag \ hardcode_libdir_flag_spec \ hardcode_libdir_flag_spec_ld \ hardcode_libdir_separator \ fix_srcfile_path \ exclude_expsyms \ include_expsyms \ file_list_spec \ variables_saved_for_relink \ libname_spec \ library_names_spec \ soname_spec \ finish_eval \ old_striplib \ striplib \ compiler_lib_search_dirs \ predep_objects \ postdep_objects \ predeps \ postdeps \ compiler_lib_search_path \ LD_CXX \ compiler_CXX \ lt_prog_compiler_no_builtin_flag_CXX \ lt_prog_compiler_wl_CXX \ lt_prog_compiler_pic_CXX \ lt_prog_compiler_static_CXX \ lt_cv_prog_compiler_c_o_CXX \ export_dynamic_flag_spec_CXX \ whole_archive_flag_spec_CXX \ compiler_needs_object_CXX \ with_gnu_ld_CXX \ allow_undefined_flag_CXX \ no_undefined_flag_CXX \ hardcode_libdir_flag_spec_CXX \ hardcode_libdir_flag_spec_ld_CXX \ hardcode_libdir_separator_CXX \ fix_srcfile_path_CXX \ exclude_expsyms_CXX \ include_expsyms_CXX \ file_list_spec_CXX \ compiler_lib_search_dirs_CXX \ predep_objects_CXX \ postdep_objects_CXX \ predeps_CXX \ postdeps_CXX \ compiler_lib_search_path_CXX; do case \`eval \\\\\$ECHO "X\\\\\$\$var"\` in *[\\\\\\\`\\"\\\$]*) eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"X\\\$\$var\\" | \\\$Xsed -e \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" ;; *) eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" ;; esac done # Double-quote double-evaled strings. for var in reload_cmds \ old_postinstall_cmds \ old_postuninstall_cmds \ old_archive_cmds \ extract_expsyms_cmds \ old_archive_from_new_cmds \ old_archive_from_expsyms_cmds \ archive_cmds \ archive_expsym_cmds \ module_cmds \ module_expsym_cmds \ export_symbols_cmds \ prelink_cmds \ postinstall_cmds \ postuninstall_cmds \ finish_cmds \ sys_lib_search_path_spec \ sys_lib_dlsearch_path_spec \ old_archive_cmds_CXX \ old_archive_from_new_cmds_CXX \ old_archive_from_expsyms_cmds_CXX \ archive_cmds_CXX \ archive_expsym_cmds_CXX \ module_cmds_CXX \ module_expsym_cmds_CXX \ export_symbols_cmds_CXX \ prelink_cmds_CXX; do case \`eval \\\\\$ECHO "X\\\\\$\$var"\` in *[\\\\\\\`\\"\\\$]*) eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"X\\\$\$var\\" | \\\$Xsed -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" ;; *) eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" ;; esac done # Fix-up fallback echo if it was mangled by the above quoting rules. case \$lt_ECHO in *'\\\$0 --fallback-echo"') lt_ECHO=\`\$ECHO "X\$lt_ECHO" | \$Xsed -e 's/\\\\\\\\\\\\\\\$0 --fallback-echo"\$/\$0 --fallback-echo"/'\` ;; esac ac_aux_dir='$ac_aux_dir' xsi_shell='$xsi_shell' lt_shell_append='$lt_shell_append' # See if we are running on zsh, and set the options which allow our # commands through without removal of \ escapes INIT. if test -n "\${ZSH_VERSION+set}" ; then setopt NO_GLOB_SUBST fi PACKAGE='$PACKAGE' VERSION='$VERSION' TIMESTAMP='$TIMESTAMP' RM='$RM' ofile='$ofile' _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Handling of arguments. for ac_config_target in $ac_config_targets do case $ac_config_target in "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;; "libtool") CONFIG_COMMANDS="$CONFIG_COMMANDS libtool" ;; "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; "haildb.spec") CONFIG_FILES="$CONFIG_FILES haildb.spec" ;; *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5 ;; esac done # If the user did not use the arguments to specify the items to instantiate, # then the envvar interface is used. Set only those that are not. # We use the long form for the default assignment because of an extremely # bizarre bug on SunOS 4.1.3. if $ac_need_defaults; then test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands fi # Have a temporary directory for convenience. Make it in the build tree # simply because there is no reason against having it here, and in addition, # creating and moving files from /tmp can sometimes cause problems. # Hook for its removal unless debugging. # Note that there is a small window in which the directory will not be cleaned: # after its creation but before its name has been assigned to `$tmp'. $debug || { tmp= trap 'exit_status=$? { test -z "$tmp" || test ! -d "$tmp" || rm -fr "$tmp"; } && exit $exit_status ' 0 trap 'as_fn_exit 1' 1 2 13 15 } # Create a (secure) tmp directory for tmp files. { tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" } || { tmp=./conf$$-$RANDOM (umask 077 && mkdir "$tmp") } || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 # Set up the scripts for CONFIG_FILES section. # No need to generate them if there are no CONFIG_FILES. # This happens for instance with `./config.status config.h'. if test -n "$CONFIG_FILES"; then ac_cr=`echo X | tr X '\015'` # On cygwin, bash can eat \r inside `` if the user requested igncr. # But we know of no other shell where ac_cr would be empty at this # point, so we can use a bashism as a fallback. if test "x$ac_cr" = x; then eval ac_cr=\$\'\\r\' fi ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then ac_cs_awk_cr='\\r' else ac_cs_awk_cr=$ac_cr fi echo 'BEGIN {' >"$tmp/subs1.awk" && _ACEOF { echo "cat >conf$$subs.awk <<_ACEOF" && echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && echo "_ACEOF" } >conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` ac_delim='%!_!# ' for ac_last_try in false false false false false :; do . ./conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` if test $ac_delim_n = $ac_delim_num; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done rm -f conf$$subs.sh cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 cat >>"\$tmp/subs1.awk" <<\\_ACAWK && _ACEOF sed -n ' h s/^/S["/; s/!.*/"]=/ p g s/^[^!]*!// :repl t repl s/'"$ac_delim"'$// t delim :nl h s/\(.\{148\}\)..*/\1/ t more1 s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ p n b repl :more1 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t nl :delim h s/\(.\{148\}\)..*/\1/ t more2 s/["\\]/\\&/g; s/^/"/; s/$/"/ p b :more2 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t delim ' >$CONFIG_STATUS || ac_write_fail=1 rm -f conf$$subs.awk cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACAWK cat >>"\$tmp/subs1.awk" <<_ACAWK && for (key in S) S_is_set[key] = 1 FS = "" } { line = $ 0 nfields = split(line, field, "@") substed = 0 len = length(field[1]) for (i = 2; i < nfields; i++) { key = field[i] keylen = length(key) if (S_is_set[key]) { value = S[key] line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) len += length(value) + length(field[++i]) substed = 1 } else len += 1 + keylen } print line } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" else cat fi < "$tmp/subs1.awk" > "$tmp/subs.awk" \ || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 _ACEOF # VPATH may cause trouble with some makes, so we remove sole $(srcdir), # ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and # trailing colons and then remove the whole line if VPATH becomes empty # (actually we leave an empty line to preserve line numbers). if test "x$srcdir" = x.; then ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ h s/// s/^/:/ s/[ ]*$/:/ s/:\$(srcdir):/:/g s/:\${srcdir}:/:/g s/:@srcdir@:/:/g s/^:*// s/:*$// x s/\(=[ ]*\).*/\1/ G s/\n// s/^[^=]*=[ ]*$// }' fi cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 fi # test -n "$CONFIG_FILES" # Set up the scripts for CONFIG_HEADERS section. # No need to generate them if there are no CONFIG_HEADERS. # This happens for instance with `./config.status Makefile'. if test -n "$CONFIG_HEADERS"; then cat >"$tmp/defines.awk" <<\_ACAWK || BEGIN { _ACEOF # Transform confdefs.h into an awk script `defines.awk', embedded as # here-document in config.status, that substitutes the proper values into # config.h.in to produce config.h. # Create a delimiter string that does not exist in confdefs.h, to ease # handling of long lines. ac_delim='%!_!# ' for ac_last_try in false false :; do ac_t=`sed -n "/$ac_delim/p" confdefs.h` if test -z "$ac_t"; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done # For the awk script, D is an array of macro values keyed by name, # likewise P contains macro parameters if any. Preserve backslash # newline sequences. ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* sed -n ' s/.\{148\}/&'"$ac_delim"'/g t rset :rset s/^[ ]*#[ ]*define[ ][ ]*/ / t def d :def s/\\$// t bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3"/p s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p d :bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3\\\\\\n"\\/p t cont s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p t cont d :cont n s/.\{148\}/&'"$ac_delim"'/g t clear :clear s/\\$// t bsnlc s/["\\]/\\&/g; s/^/"/; s/$/"/p d :bsnlc s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p b cont ' >$CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 for (key in D) D_is_set[key] = 1 FS = "" } /^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { line = \$ 0 split(line, arg, " ") if (arg[1] == "#") { defundef = arg[2] mac1 = arg[3] } else { defundef = substr(arg[1], 2) mac1 = arg[2] } split(mac1, mac2, "(") #) macro = mac2[1] prefix = substr(line, 1, index(line, defundef) - 1) if (D_is_set[macro]) { # Preserve the white space surrounding the "#". print prefix "define", macro P[macro] D[macro] next } else { # Replace #undef with comments. This is necessary, for example, # in the case of _POSIX_SOURCE, which is predefined and required # on some systems where configure will not decide to define it. if (defundef == "undef") { print "/*", prefix defundef, macro, "*/" next } } } { print } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 fi # test -n "$CONFIG_HEADERS" eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS :C $CONFIG_COMMANDS" shift for ac_tag do case $ac_tag in :[FHLC]) ac_mode=$ac_tag; continue;; esac case $ac_mode$ac_tag in :[FHL]*:*);; :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5 ;; :[FH]-) ac_tag=-:-;; :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; esac ac_save_IFS=$IFS IFS=: set x $ac_tag IFS=$ac_save_IFS shift ac_file=$1 shift case $ac_mode in :L) ac_source=$1;; :[FH]) ac_file_inputs= for ac_f do case $ac_f in -) ac_f="$tmp/stdin";; *) # Look for the file first in the build tree, then in the source tree # (if the path is not absolute). The absolute path cannot be DOS-style, # because $ac_f cannot contain `:'. test -f "$ac_f" || case $ac_f in [\\/$]*) false;; *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; esac || as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5 ;; esac case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac as_fn_append ac_file_inputs " '$ac_f'" done # Let's still pretend it is `configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ configure_input='Generated from '` $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' `' by configure.' if test x"$ac_file" != x-; then configure_input="$ac_file. $configure_input" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 $as_echo "$as_me: creating $ac_file" >&6;} fi # Neutralize special characters interpreted by sed in replacement strings. case $configure_input in #( *\&* | *\|* | *\\* ) ac_sed_conf_input=`$as_echo "$configure_input" | sed 's/[\\\\&|]/\\\\&/g'`;; #( *) ac_sed_conf_input=$configure_input;; esac case $ac_tag in *:-:* | *:-) cat >"$tmp/stdin" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; esac ;; esac ac_dir=`$as_dirname -- "$ac_file" || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` as_dir="$ac_dir"; as_fn_mkdir_p ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix case $ac_mode in :F) # # CONFIG_FILE # case $INSTALL in [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; esac ac_MKDIR_P=$MKDIR_P case $MKDIR_P in [\\/$]* | ?:[\\/]* ) ;; */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;; esac _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # If the template does not know about datarootdir, expand it. # FIXME: This hack should be removed a few years after 2.60. ac_datarootdir_hack=; ac_datarootdir_seen= ac_sed_dataroot=' /datarootdir/ { p q } /@datadir@/p /@docdir@/p /@infodir@/p /@localedir@/p /@mandir@/p' case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in *datarootdir*) ac_datarootdir_seen=yes;; *@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 $as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_datarootdir_hack=' s&@datadir@&$datadir&g s&@docdir@&$docdir&g s&@infodir@&$infodir&g s&@localedir@&$localedir&g s&@mandir@&$mandir&g s&\\\${datarootdir}&$datarootdir&g' ;; esac _ACEOF # Neutralize VPATH when `$srcdir' = `.'. # Shell code in configure.ac might set extrasub. # FIXME: do we really want to maintain this feature? cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_sed_extra="$ac_vpsub $extrasub _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 :t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b s|@configure_input@|$ac_sed_conf_input|;t t s&@top_builddir@&$ac_top_builddir_sub&;t t s&@top_build_prefix@&$ac_top_build_prefix&;t t s&@srcdir@&$ac_srcdir&;t t s&@abs_srcdir@&$ac_abs_srcdir&;t t s&@top_srcdir@&$ac_top_srcdir&;t t s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t s&@builddir@&$ac_builddir&;t t s&@abs_builddir@&$ac_abs_builddir&;t t s&@abs_top_builddir@&$ac_abs_top_builddir&;t t s&@INSTALL@&$ac_INSTALL&;t t s&@MKDIR_P@&$ac_MKDIR_P&;t t $ac_datarootdir_hack " eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$tmp/subs.awk" >$tmp/out \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && { ac_out=`sed -n '/\${datarootdir}/p' "$tmp/out"`; test -n "$ac_out"; } && { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' "$tmp/out"`; test -z "$ac_out"; } && { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&5 $as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&2;} rm -f "$tmp/stdin" case $ac_file in -) cat "$tmp/out" && rm -f "$tmp/out";; *) rm -f "$ac_file" && mv "$tmp/out" "$ac_file";; esac \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; :H) # # CONFIG_HEADER # if test x"$ac_file" != x-; then { $as_echo "/* $configure_input */" \ && eval '$AWK -f "$tmp/defines.awk"' "$ac_file_inputs" } >"$tmp/config.h" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 if diff "$ac_file" "$tmp/config.h" >/dev/null 2>&1; then { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 $as_echo "$as_me: $ac_file is unchanged" >&6;} else rm -f "$ac_file" mv "$tmp/config.h" "$ac_file" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 fi else $as_echo "/* $configure_input */" \ && eval '$AWK -f "$tmp/defines.awk"' "$ac_file_inputs" \ || as_fn_error $? "could not create -" "$LINENO" 5 fi # Compute "$ac_file"'s index in $config_headers. _am_arg="$ac_file" _am_stamp_count=1 for _am_header in $config_headers :; do case $_am_header in $_am_arg | $_am_arg:* ) break ;; * ) _am_stamp_count=`expr $_am_stamp_count + 1` ;; esac done echo "timestamp for $_am_arg" >`$as_dirname -- "$_am_arg" || $as_expr X"$_am_arg" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$_am_arg" : 'X\(//\)[^/]' \| \ X"$_am_arg" : 'X\(//\)$' \| \ X"$_am_arg" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$_am_arg" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'`/stamp-h$_am_stamp_count ;; :C) { $as_echo "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5 $as_echo "$as_me: executing $ac_file commands" >&6;} ;; esac case $ac_file$ac_mode in "depfiles":C) test x"$AMDEP_TRUE" != x"" || { # Autoconf 2.62 quotes --file arguments for eval, but not when files # are listed without --file. Let's play safe and only enable the eval # if we detect the quoting. case $CONFIG_FILES in *\'*) eval set x "$CONFIG_FILES" ;; *) set x $CONFIG_FILES ;; esac shift for mf do # Strip MF so we end up with the name of the file. mf=`echo "$mf" | sed -e 's/:.*$//'` # Check whether this is an Automake generated Makefile or not. # We used to match only the files named `Makefile.in', but # some people rename them; so instead we look at the file content. # Grep'ing the first line is not enough: some people post-process # each Makefile.in and add a new line on top of each file to say so. # Grep'ing the whole file is not good either: AIX grep has a line # limit of 2048, but all sed's we know have understand at least 4000. if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then dirpart=`$as_dirname -- "$mf" || $as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$mf" : 'X\(//\)[^/]' \| \ X"$mf" : 'X\(//\)$' \| \ X"$mf" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$mf" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` else continue fi # Extract the definition of DEPDIR, am__include, and am__quote # from the Makefile without running `make'. DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` test -z "$DEPDIR" && continue am__include=`sed -n 's/^am__include = //p' < "$mf"` test -z "am__include" && continue am__quote=`sed -n 's/^am__quote = //p' < "$mf"` # When using ansi2knr, U may be empty or an underscore; expand it U=`sed -n 's/^U = //p' < "$mf"` # Find all dependency output files, they are included files with # $(DEPDIR) in their names. We invoke sed twice because it is the # simplest approach to changing $(DEPDIR) to its actual value in the # expansion. for file in `sed -n " s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do # Make sure the directory exists. test -f "$dirpart/$file" && continue fdir=`$as_dirname -- "$file" || $as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$file" : 'X\(//\)[^/]' \| \ X"$file" : 'X\(//\)$' \| \ X"$file" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` as_dir=$dirpart/$fdir; as_fn_mkdir_p # echo "creating $dirpart/$file" echo '# dummy' > "$dirpart/$file" done done } ;; "libtool":C) # See if we are running on zsh, and set the options which allow our # commands through without removal of \ escapes. if test -n "${ZSH_VERSION+set}" ; then setopt NO_GLOB_SUBST fi cfgfile="${ofile}T" trap "$RM \"$cfgfile\"; exit 1" 1 2 15 $RM "$cfgfile" cat <<_LT_EOF >> "$cfgfile" #! $SHELL # `$ECHO "$ofile" | sed 's%^.*/%%'` - Provide generalized library-building support services. # Generated automatically by $as_me ($PACKAGE$TIMESTAMP) $VERSION # Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`: # NOTE: Changes made to this file will be lost: look at ltmain.sh. # # Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, # 2006, 2007, 2008 Free Software Foundation, Inc. # Written by Gordon Matzigkeit, 1996 # # This file is part of GNU Libtool. # # GNU Libtool is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. # # As a special exception to the GNU General Public License, # if you distribute this file as part of a program or library that # is built using GNU Libtool, you may include this file under the # same distribution terms that you use for the rest of that program. # # GNU Libtool 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNU Libtool; see the file COPYING. If not, a copy # can be downloaded from http://www.gnu.org/licenses/gpl.html, or # obtained by writing to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # The names of the tagged configurations supported by this script. available_tags="CXX " # ### BEGIN LIBTOOL CONFIG # Whether or not to build static libraries. build_old_libs=$enable_static # Which release of libtool.m4 was used? macro_version=$macro_version macro_revision=$macro_revision # Whether or not to build shared libraries. build_libtool_libs=$enable_shared # What type of objects to build. pic_mode=$pic_mode # Whether or not to optimize for fast installation. fast_install=$enable_fast_install # The host system. host_alias=$host_alias host=$host host_os=$host_os # The build system. build_alias=$build_alias build=$build build_os=$build_os # A sed program that does not truncate output. SED=$lt_SED # Sed that helps us avoid accidentally triggering echo(1) options like -n. Xsed="\$SED -e 1s/^X//" # A grep program that handles long lines. GREP=$lt_GREP # An ERE matcher. EGREP=$lt_EGREP # A literal string matcher. FGREP=$lt_FGREP # A BSD- or MS-compatible name lister. NM=$lt_NM # Whether we need soft or hard links. LN_S=$lt_LN_S # What is the maximum length of a command? max_cmd_len=$max_cmd_len # Object file suffix (normally "o"). objext=$ac_objext # Executable file suffix (normally ""). exeext=$exeext # whether the shell understands "unset". lt_unset=$lt_unset # turn spaces into newlines. SP2NL=$lt_lt_SP2NL # turn newlines into spaces. NL2SP=$lt_lt_NL2SP # How to create reloadable object files. reload_flag=$lt_reload_flag reload_cmds=$lt_reload_cmds # An object symbol dumper. OBJDUMP=$lt_OBJDUMP # Method to check whether dependent libraries are shared objects. deplibs_check_method=$lt_deplibs_check_method # Command to use when deplibs_check_method == "file_magic". file_magic_cmd=$lt_file_magic_cmd # The archiver. AR=$lt_AR AR_FLAGS=$lt_AR_FLAGS # A symbol stripping program. STRIP=$lt_STRIP # Commands used to install an old-style archive. RANLIB=$lt_RANLIB old_postinstall_cmds=$lt_old_postinstall_cmds old_postuninstall_cmds=$lt_old_postuninstall_cmds # A C compiler. LTCC=$lt_CC # LTCC compiler flags. LTCFLAGS=$lt_CFLAGS # Take the output of nm and produce a listing of raw symbols and C names. global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe # Transform the output of nm in a proper C declaration. global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl # Transform the output of nm in a C name address pair. global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address # Transform the output of nm in a C name address pair when lib prefix is needed. global_symbol_to_c_name_address_lib_prefix=$lt_lt_cv_sys_global_symbol_to_c_name_address_lib_prefix # The name of the directory that contains temporary libtool files. objdir=$objdir # Shell to use when invoking shell scripts. SHELL=$lt_SHELL # An echo program that does not interpret backslashes. ECHO=$lt_ECHO # Used to examine libraries when file_magic_cmd begins with "file". MAGIC_CMD=$MAGIC_CMD # Must we lock files when doing compilation? need_locks=$lt_need_locks # Tool to manipulate archived DWARF debug symbol files on Mac OS X. DSYMUTIL=$lt_DSYMUTIL # Tool to change global to local symbols on Mac OS X. NMEDIT=$lt_NMEDIT # Tool to manipulate fat objects and archives on Mac OS X. LIPO=$lt_LIPO # ldd/readelf like tool for Mach-O binaries on Mac OS X. OTOOL=$lt_OTOOL # ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4. OTOOL64=$lt_OTOOL64 # Old archive suffix (normally "a"). libext=$libext # Shared library suffix (normally ".so"). shrext_cmds=$lt_shrext_cmds # The commands to extract the exported symbol list from a shared archive. extract_expsyms_cmds=$lt_extract_expsyms_cmds # Variables whose values should be saved in libtool wrapper scripts and # restored at link time. variables_saved_for_relink=$lt_variables_saved_for_relink # Do we need the "lib" prefix for modules? need_lib_prefix=$need_lib_prefix # Do we need a version for libraries? need_version=$need_version # Library versioning type. version_type=$version_type # Shared library runtime path variable. runpath_var=$runpath_var # Shared library path variable. shlibpath_var=$shlibpath_var # Is shlibpath searched before the hard-coded library search path? shlibpath_overrides_runpath=$shlibpath_overrides_runpath # Format of library name prefix. libname_spec=$lt_libname_spec # List of archive names. First name is the real one, the rest are links. # The last name is the one that the linker finds with -lNAME library_names_spec=$lt_library_names_spec # The coded name of the library, if different from the real name. soname_spec=$lt_soname_spec # Command to use after installation of a shared archive. postinstall_cmds=$lt_postinstall_cmds # Command to use after uninstallation of a shared archive. postuninstall_cmds=$lt_postuninstall_cmds # Commands used to finish a libtool library installation in a directory. finish_cmds=$lt_finish_cmds # As "finish_cmds", except a single script fragment to be evaled but # not shown. finish_eval=$lt_finish_eval # Whether we should hardcode library paths into libraries. hardcode_into_libs=$hardcode_into_libs # Compile-time system search path for libraries. sys_lib_search_path_spec=$lt_sys_lib_search_path_spec # Run-time system search path for libraries. sys_lib_dlsearch_path_spec=$lt_sys_lib_dlsearch_path_spec # Whether dlopen is supported. dlopen_support=$enable_dlopen # Whether dlopen of programs is supported. dlopen_self=$enable_dlopen_self # Whether dlopen of statically linked programs is supported. dlopen_self_static=$enable_dlopen_self_static # Commands to strip libraries. old_striplib=$lt_old_striplib striplib=$lt_striplib # The linker used to build libraries. LD=$lt_LD # Commands used to build an old-style archive. old_archive_cmds=$lt_old_archive_cmds # A language specific compiler. CC=$lt_compiler # Is the compiler the GNU compiler? with_gcc=$GCC # Compiler flag to turn off builtin functions. no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag # How to pass a linker flag through the compiler. wl=$lt_lt_prog_compiler_wl # Additional compiler flags for building library objects. pic_flag=$lt_lt_prog_compiler_pic # Compiler flag to prevent dynamic linking. link_static_flag=$lt_lt_prog_compiler_static # Does compiler simultaneously support -c and -o options? compiler_c_o=$lt_lt_cv_prog_compiler_c_o # Whether or not to add -lc for building shared libraries. build_libtool_need_lc=$archive_cmds_need_lc # Whether or not to disallow shared libs when runtime libs are static. allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes # Compiler flag to allow reflexive dlopens. export_dynamic_flag_spec=$lt_export_dynamic_flag_spec # Compiler flag to generate shared objects directly from archives. whole_archive_flag_spec=$lt_whole_archive_flag_spec # Whether the compiler copes with passing no objects directly. compiler_needs_object=$lt_compiler_needs_object # Create an old-style archive from a shared archive. old_archive_from_new_cmds=$lt_old_archive_from_new_cmds # Create a temporary old-style archive to link instead of a shared archive. old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds # Commands used to build a shared archive. archive_cmds=$lt_archive_cmds archive_expsym_cmds=$lt_archive_expsym_cmds # Commands used to build a loadable module if different from building # a shared archive. module_cmds=$lt_module_cmds module_expsym_cmds=$lt_module_expsym_cmds # Whether we are building with GNU ld or not. with_gnu_ld=$lt_with_gnu_ld # Flag that allows shared libraries with undefined symbols to be built. allow_undefined_flag=$lt_allow_undefined_flag # Flag that enforces no undefined symbols. no_undefined_flag=$lt_no_undefined_flag # Flag to hardcode \$libdir into a binary during linking. # This must work even if \$libdir does not exist hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec # If ld is used when linking, flag to hardcode \$libdir into a binary # during linking. This must work even if \$libdir does not exist. hardcode_libdir_flag_spec_ld=$lt_hardcode_libdir_flag_spec_ld # Whether we need a single "-rpath" flag with a separated argument. hardcode_libdir_separator=$lt_hardcode_libdir_separator # Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes # DIR into the resulting binary. hardcode_direct=$hardcode_direct # Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes # DIR into the resulting binary and the resulting library dependency is # "absolute",i.e impossible to change by setting \${shlibpath_var} if the # library is relocated. hardcode_direct_absolute=$hardcode_direct_absolute # Set to "yes" if using the -LDIR flag during linking hardcodes DIR # into the resulting binary. hardcode_minus_L=$hardcode_minus_L # Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR # into the resulting binary. hardcode_shlibpath_var=$hardcode_shlibpath_var # Set to "yes" if building a shared library automatically hardcodes DIR # into the library and all subsequent libraries and executables linked # against it. hardcode_automatic=$hardcode_automatic # Set to yes if linker adds runtime paths of dependent libraries # to runtime path list. inherit_rpath=$inherit_rpath # Whether libtool must link a program against all its dependency libraries. link_all_deplibs=$link_all_deplibs # Fix the shell variable \$srcfile for the compiler. fix_srcfile_path=$lt_fix_srcfile_path # Set to "yes" if exported symbols are required. always_export_symbols=$always_export_symbols # The commands to list exported symbols. export_symbols_cmds=$lt_export_symbols_cmds # Symbols that should not be listed in the preloaded symbols. exclude_expsyms=$lt_exclude_expsyms # Symbols that must always be exported. include_expsyms=$lt_include_expsyms # Commands necessary for linking programs (against libraries) with templates. prelink_cmds=$lt_prelink_cmds # Specify filename containing input files. file_list_spec=$lt_file_list_spec # How to hardcode a shared library path into an executable. hardcode_action=$hardcode_action # The directories searched by this compiler when creating a shared library. compiler_lib_search_dirs=$lt_compiler_lib_search_dirs # Dependencies to place before and after the objects being linked to # create a shared library. predep_objects=$lt_predep_objects postdep_objects=$lt_postdep_objects predeps=$lt_predeps postdeps=$lt_postdeps # The library search path used internally by the compiler when linking # a shared library. compiler_lib_search_path=$lt_compiler_lib_search_path # ### END LIBTOOL CONFIG _LT_EOF case $host_os in aix3*) cat <<\_LT_EOF >> "$cfgfile" # AIX sometimes has problems with the GCC collect2 program. For some # reason, if we set the COLLECT_NAMES environment variable, the problems # vanish in a puff of smoke. if test "X${COLLECT_NAMES+set}" != Xset; then COLLECT_NAMES= export COLLECT_NAMES fi _LT_EOF ;; esac ltmain="$ac_aux_dir/ltmain.sh" # We use sed instead of cat because bash on DJGPP gets confused if # if finds mixed CR/LF and LF-only lines. Since sed operates in # text mode, it properly converts lines to CR/LF. This bash problem # is reportedly fixed, but why not run on old versions too? sed '/^# Generated shell functions inserted here/q' "$ltmain" >> "$cfgfile" \ || (rm -f "$cfgfile"; exit 1) case $xsi_shell in yes) cat << \_LT_EOF >> "$cfgfile" # func_dirname file append nondir_replacement # Compute the dirname of FILE. If nonempty, add APPEND to the result, # otherwise set result to NONDIR_REPLACEMENT. func_dirname () { case ${1} in */*) func_dirname_result="${1%/*}${2}" ;; * ) func_dirname_result="${3}" ;; esac } # func_basename file func_basename () { func_basename_result="${1##*/}" } # func_dirname_and_basename file append nondir_replacement # perform func_basename and func_dirname in a single function # call: # dirname: Compute the dirname of FILE. If nonempty, # add APPEND to the result, otherwise set result # to NONDIR_REPLACEMENT. # value returned in "$func_dirname_result" # basename: Compute filename of FILE. # value retuned in "$func_basename_result" # Implementation must be kept synchronized with func_dirname # and func_basename. For efficiency, we do not delegate to # those functions but instead duplicate the functionality here. func_dirname_and_basename () { case ${1} in */*) func_dirname_result="${1%/*}${2}" ;; * ) func_dirname_result="${3}" ;; esac func_basename_result="${1##*/}" } # func_stripname prefix suffix name # strip PREFIX and SUFFIX off of NAME. # PREFIX and SUFFIX must not contain globbing or regex special # characters, hashes, percent signs, but SUFFIX may contain a leading # dot (in which case that matches only a dot). func_stripname () { # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are # positional parameters, so assign one to ordinary parameter first. func_stripname_result=${3} func_stripname_result=${func_stripname_result#"${1}"} func_stripname_result=${func_stripname_result%"${2}"} } # func_opt_split func_opt_split () { func_opt_split_opt=${1%%=*} func_opt_split_arg=${1#*=} } # func_lo2o object func_lo2o () { case ${1} in *.lo) func_lo2o_result=${1%.lo}.${objext} ;; *) func_lo2o_result=${1} ;; esac } # func_xform libobj-or-source func_xform () { func_xform_result=${1%.*}.lo } # func_arith arithmetic-term... func_arith () { func_arith_result=$(( $* )) } # func_len string # STRING may not start with a hyphen. func_len () { func_len_result=${#1} } _LT_EOF ;; *) # Bourne compatible functions. cat << \_LT_EOF >> "$cfgfile" # func_dirname file append nondir_replacement # Compute the dirname of FILE. If nonempty, add APPEND to the result, # otherwise set result to NONDIR_REPLACEMENT. func_dirname () { # Extract subdirectory from the argument. func_dirname_result=`$ECHO "X${1}" | $Xsed -e "$dirname"` if test "X$func_dirname_result" = "X${1}"; then func_dirname_result="${3}" else func_dirname_result="$func_dirname_result${2}" fi } # func_basename file func_basename () { func_basename_result=`$ECHO "X${1}" | $Xsed -e "$basename"` } # func_stripname prefix suffix name # strip PREFIX and SUFFIX off of NAME. # PREFIX and SUFFIX must not contain globbing or regex special # characters, hashes, percent signs, but SUFFIX may contain a leading # dot (in which case that matches only a dot). # func_strip_suffix prefix name func_stripname () { case ${2} in .*) func_stripname_result=`$ECHO "X${3}" \ | $Xsed -e "s%^${1}%%" -e "s%\\\\${2}\$%%"`;; *) func_stripname_result=`$ECHO "X${3}" \ | $Xsed -e "s%^${1}%%" -e "s%${2}\$%%"`;; esac } # sed scripts: my_sed_long_opt='1s/^\(-[^=]*\)=.*/\1/;q' my_sed_long_arg='1s/^-[^=]*=//' # func_opt_split func_opt_split () { func_opt_split_opt=`$ECHO "X${1}" | $Xsed -e "$my_sed_long_opt"` func_opt_split_arg=`$ECHO "X${1}" | $Xsed -e "$my_sed_long_arg"` } # func_lo2o object func_lo2o () { func_lo2o_result=`$ECHO "X${1}" | $Xsed -e "$lo2o"` } # func_xform libobj-or-source func_xform () { func_xform_result=`$ECHO "X${1}" | $Xsed -e 's/\.[^.]*$/.lo/'` } # func_arith arithmetic-term... func_arith () { func_arith_result=`expr "$@"` } # func_len string # STRING may not start with a hyphen. func_len () { func_len_result=`expr "$1" : ".*" 2>/dev/null || echo $max_cmd_len` } _LT_EOF esac case $lt_shell_append in yes) cat << \_LT_EOF >> "$cfgfile" # func_append var value # Append VALUE to the end of shell variable VAR. func_append () { eval "$1+=\$2" } _LT_EOF ;; *) cat << \_LT_EOF >> "$cfgfile" # func_append var value # Append VALUE to the end of shell variable VAR. func_append () { eval "$1=\$$1\$2" } _LT_EOF ;; esac sed -n '/^# Generated shell functions inserted here/,$p' "$ltmain" >> "$cfgfile" \ || (rm -f "$cfgfile"; exit 1) mv -f "$cfgfile" "$ofile" || (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile") chmod +x "$ofile" cat <<_LT_EOF >> "$ofile" # ### BEGIN LIBTOOL TAG CONFIG: CXX # The linker used to build libraries. LD=$lt_LD_CXX # Commands used to build an old-style archive. old_archive_cmds=$lt_old_archive_cmds_CXX # A language specific compiler. CC=$lt_compiler_CXX # Is the compiler the GNU compiler? with_gcc=$GCC_CXX # Compiler flag to turn off builtin functions. no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag_CXX # How to pass a linker flag through the compiler. wl=$lt_lt_prog_compiler_wl_CXX # Additional compiler flags for building library objects. pic_flag=$lt_lt_prog_compiler_pic_CXX # Compiler flag to prevent dynamic linking. link_static_flag=$lt_lt_prog_compiler_static_CXX # Does compiler simultaneously support -c and -o options? compiler_c_o=$lt_lt_cv_prog_compiler_c_o_CXX # Whether or not to add -lc for building shared libraries. build_libtool_need_lc=$archive_cmds_need_lc_CXX # Whether or not to disallow shared libs when runtime libs are static. allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes_CXX # Compiler flag to allow reflexive dlopens. export_dynamic_flag_spec=$lt_export_dynamic_flag_spec_CXX # Compiler flag to generate shared objects directly from archives. whole_archive_flag_spec=$lt_whole_archive_flag_spec_CXX # Whether the compiler copes with passing no objects directly. compiler_needs_object=$lt_compiler_needs_object_CXX # Create an old-style archive from a shared archive. old_archive_from_new_cmds=$lt_old_archive_from_new_cmds_CXX # Create a temporary old-style archive to link instead of a shared archive. old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds_CXX # Commands used to build a shared archive. archive_cmds=$lt_archive_cmds_CXX archive_expsym_cmds=$lt_archive_expsym_cmds_CXX # Commands used to build a loadable module if different from building # a shared archive. module_cmds=$lt_module_cmds_CXX module_expsym_cmds=$lt_module_expsym_cmds_CXX # Whether we are building with GNU ld or not. with_gnu_ld=$lt_with_gnu_ld_CXX # Flag that allows shared libraries with undefined symbols to be built. allow_undefined_flag=$lt_allow_undefined_flag_CXX # Flag that enforces no undefined symbols. no_undefined_flag=$lt_no_undefined_flag_CXX # Flag to hardcode \$libdir into a binary during linking. # This must work even if \$libdir does not exist hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec_CXX # If ld is used when linking, flag to hardcode \$libdir into a binary # during linking. This must work even if \$libdir does not exist. hardcode_libdir_flag_spec_ld=$lt_hardcode_libdir_flag_spec_ld_CXX # Whether we need a single "-rpath" flag with a separated argument. hardcode_libdir_separator=$lt_hardcode_libdir_separator_CXX # Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes # DIR into the resulting binary. hardcode_direct=$hardcode_direct_CXX # Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes # DIR into the resulting binary and the resulting library dependency is # "absolute",i.e impossible to change by setting \${shlibpath_var} if the # library is relocated. hardcode_direct_absolute=$hardcode_direct_absolute_CXX # Set to "yes" if using the -LDIR flag during linking hardcodes DIR # into the resulting binary. hardcode_minus_L=$hardcode_minus_L_CXX # Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR # into the resulting binary. hardcode_shlibpath_var=$hardcode_shlibpath_var_CXX # Set to "yes" if building a shared library automatically hardcodes DIR # into the library and all subsequent libraries and executables linked # against it. hardcode_automatic=$hardcode_automatic_CXX # Set to yes if linker adds runtime paths of dependent libraries # to runtime path list. inherit_rpath=$inherit_rpath_CXX # Whether libtool must link a program against all its dependency libraries. link_all_deplibs=$link_all_deplibs_CXX # Fix the shell variable \$srcfile for the compiler. fix_srcfile_path=$lt_fix_srcfile_path_CXX # Set to "yes" if exported symbols are required. always_export_symbols=$always_export_symbols_CXX # The commands to list exported symbols. export_symbols_cmds=$lt_export_symbols_cmds_CXX # Symbols that should not be listed in the preloaded symbols. exclude_expsyms=$lt_exclude_expsyms_CXX # Symbols that must always be exported. include_expsyms=$lt_include_expsyms_CXX # Commands necessary for linking programs (against libraries) with templates. prelink_cmds=$lt_prelink_cmds_CXX # Specify filename containing input files. file_list_spec=$lt_file_list_spec_CXX # How to hardcode a shared library path into an executable. hardcode_action=$hardcode_action_CXX # The directories searched by this compiler when creating a shared library. compiler_lib_search_dirs=$lt_compiler_lib_search_dirs_CXX # Dependencies to place before and after the objects being linked to # create a shared library. predep_objects=$lt_predep_objects_CXX postdep_objects=$lt_postdep_objects_CXX predeps=$lt_predeps_CXX postdeps=$lt_postdeps_CXX # The library search path used internally by the compiler when linking # a shared library. compiler_lib_search_path=$lt_compiler_lib_search_path_CXX # ### END LIBTOOL TAG CONFIG: CXX _LT_EOF ;; esac done # for ac_tag as_fn_exit 0 _ACEOF ac_clean_files=$ac_clean_files_save test $ac_write_fail = 0 || as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 # configure is writing to config.log, and then calls config.status. # config.status does its own redirection, appending to config.log. # Unfortunately, on DOS this fails, as config.log is still kept open # by configure, so config.status won't be able to write to it; its # output is simply discarded. So we exec the FD to /dev/null, # effectively closing config.log, so it can be properly (re)opened and # appended to by config.status. When coming back to configure, we # need to make the FD available again. if test "$no_create" != yes; then ac_cs_success=: ac_config_status_args= test "$silent" = yes && ac_config_status_args="$ac_config_status_args --quiet" exec 5>/dev/null $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false exec 5>>config.log # Use ||, not &&, to avoid exiting from the if with $? = 1, which # would make configure fail if this is the last instruction. $ac_cs_success || as_fn_exit 1 fi if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} fi haildb-2.3.2/page/0000755000175000017500000000000011513177437014556 5ustar00pcrewspcrews00000000000000haildb-2.3.2/page/page0zip.c0000644000175000017500000041327311513177357016454 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 2005, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file page/page0zip.c Compressed page interface Created June 2005 by Marko Makela *******************************************************/ #include "univ.i" #define THIS_MODULE #include "page0zip.h" #ifdef UNIV_NONINL # include "page0zip.ic" #endif #undef THIS_MODULE #include "page0page.h" #include "mtr0log.h" #include "ut0sort.h" #include "dict0dict.h" #include "btr0cur.h" #include "page0types.h" #include "log0recv.h" #ifndef UNIV_HOTBACKUP # include "buf0lru.h" # include "btr0sea.h" # include "dict0boot.h" # include "lock0lock.h" #else /* !UNIV_HOTBACKUP */ # define lock_move_reorganize_page(block, temp_block) ((void) 0) # define buf_LRU_stat_inc_unzip() ((void) 0) #endif /* !UNIV_HOTBACKUP */ #include #ifndef UNIV_HOTBACKUP /** Statistics on compression, indexed by page_zip_des_t::ssize - 1 */ UNIV_INTERN page_zip_stat_t page_zip_stat[PAGE_ZIP_NUM_SSIZE - 1]; #endif /* !UNIV_HOTBACKUP */ /* Please refer to ../include/page0zip.ic for a description of the compressed page format. */ /* The infimum and supremum records are omitted from the compressed page. On compress, we compare that the records are there, and on uncompress we restore the records. */ /** Extra bytes of an infimum record */ static const byte infimum_extra[] = { 0x01, /* info_bits=0, n_owned=1 */ 0x00, 0x02 /* heap_no=0, status=2 */ /* ?, ? */ /* next=(first user rec, or supremum) */ }; /** Data bytes of an infimum record */ static const byte infimum_data[] = { 0x69, 0x6e, 0x66, 0x69, 0x6d, 0x75, 0x6d, 0x00 /* "infimum\0" */ }; /** Extra bytes and data bytes of a supremum record */ static const byte supremum_extra_data[] = { /* 0x0?, */ /* info_bits=0, n_owned=1..8 */ 0x00, 0x0b, /* heap_no=1, status=3 */ 0x00, 0x00, /* next=0 */ 0x73, 0x75, 0x70, 0x72, 0x65, 0x6d, 0x75, 0x6d /* "supremum" */ }; /** Assert that a block of memory is filled with zero bytes. Compare at most sizeof(field_ref_zero) bytes. @param b in: memory block @param s in: size of the memory block, in bytes */ #define ASSERT_ZERO(b, s) \ ut_ad(!memcmp(b, field_ref_zero, ut_min(s, sizeof field_ref_zero))) /** Assert that a BLOB pointer is filled with zero bytes. @param b in: BLOB pointer */ #define ASSERT_ZERO_BLOB(b) \ ut_ad(!memcmp(b, field_ref_zero, sizeof field_ref_zero)) /* Enable some extra debugging output. This code can be enabled independently of any UNIV_ debugging conditions. */ #if defined UNIV_DEBUG || defined UNIV_ZIP_DEBUG # define page_zip_fail(s, ...) \ { \ ib_logger(s, " InnoDB: "); \ ib_logger(s, __VA_ARGS__); \ } #else /* UNIV_DEBUG || UNIV_ZIP_DEBUG */ # define page_zip_fail(s, ...) /* empty */ #endif /* UNIV_DEBUG || UNIV_ZIP_DEBUG */ #ifndef UNIV_HOTBACKUP /**********************************************************************//** Reset variables. */ UNIV_INTERN void page_zip_var_init(void) /*===================*/ { #ifdef PAGE_ZIP_COMPRESS_DBG page_zip_compress_dbg = FALSE; page_zip_compress_log = 0; #endif memset(page_zip_stat, 0x0, sizeof(page_zip_stat)); } /************************************************************************** Determine the guaranteed free space on an empty page. @return minimum payload size on the page */ UNIV_INTERN ulint page_zip_empty_size( /*================*/ ulint n_fields, /*!< in: number of columns in the index */ ulint zip_size) /*!< in: compressed page size in bytes */ { lint size = zip_size /* subtract the page header and the longest uncompressed data needed for one record */ - (PAGE_DATA + PAGE_ZIP_DIR_SLOT_SIZE + DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN + 1/* encoded heap_no==2 in page_zip_write_rec() */ + 1/* end of modification log */ - REC_N_NEW_EXTRA_BYTES/* omitted bytes */) /* subtract the space for page_zip_fields_encode() */ - compressBound(2 * (n_fields + 1)); return(size > 0 ? (ulint) size : 0); } #endif /* !UNIV_HOTBACKUP */ /*************************************************************//** Gets the size of the compressed page trailer (the dense page directory), including deleted records (the free list). @return length of dense page directory, in bytes */ UNIV_INLINE ulint page_zip_dir_size( /*==============*/ const page_zip_des_t* page_zip) /*!< in: compressed page */ { /* Exclude the page infimum and supremum from the record count. */ ulint size = PAGE_ZIP_DIR_SLOT_SIZE * (page_dir_get_n_heap(page_zip->data) - PAGE_HEAP_NO_USER_LOW); return(size); } /*************************************************************//** Gets the size of the compressed page trailer (the dense page directory), only including user records (excluding the free list). @return length of dense page directory comprising existing records, in bytes */ UNIV_INLINE ulint page_zip_dir_user_size( /*===================*/ const page_zip_des_t* page_zip) /*!< in: compressed page */ { ulint size = PAGE_ZIP_DIR_SLOT_SIZE * page_get_n_recs(page_zip->data); ut_ad(size <= page_zip_dir_size(page_zip)); return(size); } /*************************************************************//** Find the slot of the given record in the dense page directory. @return dense directory slot, or NULL if record not found */ UNIV_INLINE byte* page_zip_dir_find_low( /*==================*/ byte* slot, /*!< in: start of records */ byte* end, /*!< in: end of records */ ulint offset) /*!< in: offset of user record */ { ut_ad(slot <= end); for (; slot < end; slot += PAGE_ZIP_DIR_SLOT_SIZE) { if ((mach_read_from_2(slot) & PAGE_ZIP_DIR_SLOT_MASK) == offset) { return(slot); } } return(NULL); } /*************************************************************//** Find the slot of the given non-free record in the dense page directory. @return dense directory slot, or NULL if record not found */ UNIV_INLINE byte* page_zip_dir_find( /*==============*/ page_zip_des_t* page_zip, /*!< in: compressed page */ ulint offset) /*!< in: offset of user record */ { byte* end = page_zip->data + page_zip_get_size(page_zip); ut_ad(page_zip_simple_validate(page_zip)); return(page_zip_dir_find_low(end - page_zip_dir_user_size(page_zip), end, offset)); } /*************************************************************//** Find the slot of the given free record in the dense page directory. @return dense directory slot, or NULL if record not found */ UNIV_INLINE byte* page_zip_dir_find_free( /*===================*/ page_zip_des_t* page_zip, /*!< in: compressed page */ ulint offset) /*!< in: offset of user record */ { byte* end = page_zip->data + page_zip_get_size(page_zip); ut_ad(page_zip_simple_validate(page_zip)); return(page_zip_dir_find_low(end - page_zip_dir_size(page_zip), end - page_zip_dir_user_size(page_zip), offset)); } /*************************************************************//** Read a given slot in the dense page directory. @return record offset on the uncompressed page, possibly ORed with PAGE_ZIP_DIR_SLOT_DEL or PAGE_ZIP_DIR_SLOT_OWNED */ UNIV_INLINE ulint page_zip_dir_get( /*=============*/ const page_zip_des_t* page_zip, /*!< in: compressed page */ ulint slot) /*!< in: slot (0=first user record) */ { ut_ad(page_zip_simple_validate(page_zip)); ut_ad(slot < page_zip_dir_size(page_zip) / PAGE_ZIP_DIR_SLOT_SIZE); return(mach_read_from_2(page_zip->data + page_zip_get_size(page_zip) - PAGE_ZIP_DIR_SLOT_SIZE * (slot + 1))); } #ifndef UNIV_HOTBACKUP /**********************************************************************//** Write a log record of compressing an index page. */ static void page_zip_compress_write_log( /*========================*/ const page_zip_des_t* page_zip,/*!< in: compressed page */ const page_t* page, /*!< in: uncompressed page */ dict_index_t* index, /*!< in: index of the B-tree node */ mtr_t* mtr) /*!< in: mini-transaction */ { byte* log_ptr; ulint trailer_size; ut_ad(!dict_index_is_ibuf(index)); log_ptr = mlog_open(mtr, 11 + 2 + 2); if (!log_ptr) { return; } /* Read the number of user records. */ trailer_size = page_dir_get_n_heap(page_zip->data) - PAGE_HEAP_NO_USER_LOW; /* Multiply by uncompressed of size stored per record */ if (!page_is_leaf(page)) { trailer_size *= PAGE_ZIP_DIR_SLOT_SIZE + REC_NODE_PTR_SIZE; } else if (dict_index_is_clust(index)) { trailer_size *= PAGE_ZIP_DIR_SLOT_SIZE + DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN; } else { trailer_size *= PAGE_ZIP_DIR_SLOT_SIZE; } /* Add the space occupied by BLOB pointers. */ trailer_size += page_zip->n_blobs * BTR_EXTERN_FIELD_REF_SIZE; ut_a(page_zip->m_end > PAGE_DATA); #if FIL_PAGE_DATA > PAGE_DATA # error "FIL_PAGE_DATA > PAGE_DATA" #endif ut_a(page_zip->m_end + trailer_size <= page_zip_get_size(page_zip)); log_ptr = mlog_write_initial_log_record_fast((page_t*) page, MLOG_ZIP_PAGE_COMPRESS, log_ptr, mtr); mach_write_to_2(log_ptr, page_zip->m_end - FIL_PAGE_TYPE); log_ptr += 2; mach_write_to_2(log_ptr, trailer_size); log_ptr += 2; mlog_close(mtr, log_ptr); /* Write FIL_PAGE_PREV and FIL_PAGE_NEXT */ mlog_catenate_string(mtr, page_zip->data + FIL_PAGE_PREV, 4); mlog_catenate_string(mtr, page_zip->data + FIL_PAGE_NEXT, 4); /* Write most of the page header, the compressed stream and the modification log. */ mlog_catenate_string(mtr, page_zip->data + FIL_PAGE_TYPE, page_zip->m_end - FIL_PAGE_TYPE); /* Write the uncompressed trailer of the compressed page. */ mlog_catenate_string(mtr, page_zip->data + page_zip_get_size(page_zip) - trailer_size, trailer_size); } #endif /* !UNIV_HOTBACKUP */ /******************************************************//** Determine how many externally stored columns are contained in existing records with smaller heap_no than rec. */ static ulint page_zip_get_n_prev_extern( /*=======================*/ const page_zip_des_t* page_zip,/*!< in: dense page directory on compressed page */ const rec_t* rec, /*!< in: compact physical record on a B-tree leaf page */ dict_index_t* index) /*!< in: record descriptor */ { const page_t* page = page_align(rec); ulint n_ext = 0; ulint i; ulint left; ulint heap_no; ulint n_recs = page_get_n_recs(page_zip->data); ut_ad(page_is_leaf(page)); ut_ad(page_is_comp(page)); ut_ad(dict_table_is_comp(index->table)); ut_ad(dict_index_is_clust(index)); ut_ad(!dict_index_is_ibuf(index)); heap_no = rec_get_heap_no_new(rec); ut_ad(heap_no >= PAGE_HEAP_NO_USER_LOW); left = heap_no - PAGE_HEAP_NO_USER_LOW; if (UNIV_UNLIKELY(!left)) { return(0); } for (i = 0; i < n_recs; i++) { const rec_t* r = page + (page_zip_dir_get(page_zip, i) & PAGE_ZIP_DIR_SLOT_MASK); if (rec_get_heap_no_new(r) < heap_no) { n_ext += rec_get_n_extern_new(r, index, ULINT_UNDEFINED); if (!--left) { break; } } } return(n_ext); } /**********************************************************************//** Encode the length of a fixed-length column. @return buf + length of encoded val */ static byte* page_zip_fixed_field_encode( /*========================*/ byte* buf, /*!< in: pointer to buffer where to write */ ulint val) /*!< in: value to write */ { ut_ad(val >= 2); if (UNIV_LIKELY(val < 126)) { /* 0 = nullable variable field of at most 255 bytes length; 1 = not null variable field of at most 255 bytes length; 126 = nullable variable field with maximum length >255; 127 = not null variable field with maximum length >255 */ *buf++ = (byte) val; } else { *buf++ = (byte) (0x80 | val >> 8); *buf++ = (byte) val; } return(buf); } /**********************************************************************//** Write the index information for the compressed page. @return used size of buf */ static ulint page_zip_fields_encode( /*===================*/ ulint n, /*!< in: number of fields to compress */ dict_index_t* index, /*!< in: index comprising at least n fields */ ulint trx_id_pos,/*!< in: position of the trx_id column in the index, or ULINT_UNDEFINED if this is a non-leaf page */ byte* buf) /*!< out: buffer of (n + 1) * 2 bytes */ { const byte* buf_start = buf; ulint i; ulint col; ulint trx_id_col = 0; /* sum of lengths of preceding non-nullable fixed fields, or 0 */ ulint fixed_sum = 0; ut_ad(trx_id_pos == ULINT_UNDEFINED || trx_id_pos < n); for (i = col = 0; i < n; i++) { dict_field_t* field = dict_index_get_nth_field(index, i); ulint val; if (dict_field_get_col(field)->prtype & DATA_NOT_NULL) { val = 1; /* set the "not nullable" flag */ } else { val = 0; /* nullable field */ } if (!field->fixed_len) { /* variable-length field */ const dict_col_t* column = dict_field_get_col(field); if (UNIV_UNLIKELY(column->len > 255) || UNIV_UNLIKELY(column->mtype == DATA_BLOB)) { val |= 0x7e; /* max > 255 bytes */ } if (fixed_sum) { /* write out the length of any preceding non-nullable fields */ buf = page_zip_fixed_field_encode( buf, fixed_sum << 1 | 1); fixed_sum = 0; col++; } *buf++ = (byte) val; col++; } else if (val) { /* fixed-length non-nullable field */ if (fixed_sum && UNIV_UNLIKELY (fixed_sum + field->fixed_len > DICT_MAX_INDEX_COL_LEN)) { /* Write out the length of the preceding non-nullable fields, to avoid exceeding the maximum length of a fixed-length column. */ buf = page_zip_fixed_field_encode( buf, fixed_sum << 1 | 1); fixed_sum = 0; col++; } if (i && UNIV_UNLIKELY(i == trx_id_pos)) { if (fixed_sum) { /* Write out the length of any preceding non-nullable fields, and start a new trx_id column. */ buf = page_zip_fixed_field_encode( buf, fixed_sum << 1 | 1); col++; } trx_id_col = col; fixed_sum = field->fixed_len; } else { /* add to the sum */ fixed_sum += field->fixed_len; } } else { /* fixed-length nullable field */ if (fixed_sum) { /* write out the length of any preceding non-nullable fields */ buf = page_zip_fixed_field_encode( buf, fixed_sum << 1 | 1); fixed_sum = 0; col++; } buf = page_zip_fixed_field_encode( buf, field->fixed_len << 1); col++; } } if (fixed_sum) { /* Write out the lengths of last fixed-length columns. */ buf = page_zip_fixed_field_encode(buf, fixed_sum << 1 | 1); } if (trx_id_pos != ULINT_UNDEFINED) { /* Write out the position of the trx_id column */ i = trx_id_col; } else { /* Write out the number of nullable fields */ i = index->n_nullable; } if (i < 128) { *buf++ = (byte) i; } else { *buf++ = (byte) (0x80 | i >> 8); *buf++ = (byte) i; } ut_ad((ulint) (buf - buf_start) <= (n + 2) * 2); return((ulint) (buf - buf_start)); } /**********************************************************************//** Populate the dense page directory from the sparse directory. */ static void page_zip_dir_encode( /*================*/ const page_t* page, /*!< in: compact page */ byte* buf, /*!< in: pointer to dense page directory[-1]; out: dense directory on compressed page */ const rec_t** recs) /*!< in: pointer to an array of 0, or NULL; out: dense page directory sorted by ascending address (and heap_no) */ { const byte* rec; ulint status; ulint min_mark; ulint heap_no; ulint i; ulint n_heap; ulint offs; min_mark = 0; if (page_is_leaf(page)) { status = REC_STATUS_ORDINARY; } else { status = REC_STATUS_NODE_PTR; if (UNIV_UNLIKELY (mach_read_from_4(page + FIL_PAGE_PREV) == FIL_NULL)) { min_mark = REC_INFO_MIN_REC_FLAG; } } n_heap = page_dir_get_n_heap(page); /* Traverse the list of stored records in the collation order, starting from the first user record. */ rec = page + PAGE_NEW_INFIMUM, TRUE; i = 0; for (;;) { ulint info_bits; offs = rec_get_next_offs(rec, TRUE); if (UNIV_UNLIKELY(offs == PAGE_NEW_SUPREMUM)) { break; } rec = page + offs; heap_no = rec_get_heap_no_new(rec); ut_a(heap_no >= PAGE_HEAP_NO_USER_LOW); ut_a(heap_no < n_heap); ut_a(offs < UNIV_PAGE_SIZE - PAGE_DIR); ut_a(offs >= PAGE_ZIP_START); #if PAGE_ZIP_DIR_SLOT_MASK & (PAGE_ZIP_DIR_SLOT_MASK + 1) # error "PAGE_ZIP_DIR_SLOT_MASK is not 1 less than a power of 2" #endif #if PAGE_ZIP_DIR_SLOT_MASK < UNIV_PAGE_SIZE - 1 # error "PAGE_ZIP_DIR_SLOT_MASK < UNIV_PAGE_SIZE - 1" #endif if (UNIV_UNLIKELY(rec_get_n_owned_new(rec))) { offs |= PAGE_ZIP_DIR_SLOT_OWNED; } info_bits = rec_get_info_bits(rec, TRUE); if (UNIV_UNLIKELY(info_bits & REC_INFO_DELETED_FLAG)) { info_bits &= ~REC_INFO_DELETED_FLAG; offs |= PAGE_ZIP_DIR_SLOT_DEL; } ut_a(info_bits == min_mark); /* Only the smallest user record can have REC_INFO_MIN_REC_FLAG set. */ min_mark = 0; mach_write_to_2(buf - PAGE_ZIP_DIR_SLOT_SIZE * ++i, offs); if (UNIV_LIKELY_NULL(recs)) { /* Ensure that each heap_no occurs at most once. */ ut_a(!recs[heap_no - PAGE_HEAP_NO_USER_LOW]); /* exclude infimum and supremum */ recs[heap_no - PAGE_HEAP_NO_USER_LOW] = rec; } ut_a(rec_get_status(rec) == status); } offs = page_header_get_field(page, PAGE_FREE); /* Traverse the free list (of deleted records). */ while (offs) { ut_ad(!(offs & ~PAGE_ZIP_DIR_SLOT_MASK)); rec = page + offs; heap_no = rec_get_heap_no_new(rec); ut_a(heap_no >= PAGE_HEAP_NO_USER_LOW); ut_a(heap_no < n_heap); ut_a(!rec[-REC_N_NEW_EXTRA_BYTES]); /* info_bits and n_owned */ ut_a(rec_get_status(rec) == status); mach_write_to_2(buf - PAGE_ZIP_DIR_SLOT_SIZE * ++i, offs); if (UNIV_LIKELY_NULL(recs)) { /* Ensure that each heap_no occurs at most once. */ ut_a(!recs[heap_no - PAGE_HEAP_NO_USER_LOW]); /* exclude infimum and supremum */ recs[heap_no - PAGE_HEAP_NO_USER_LOW] = rec; } offs = rec_get_next_offs(rec, TRUE); } /* Ensure that each heap no occurs at least once. */ ut_a(i + PAGE_HEAP_NO_USER_LOW == n_heap); } /**********************************************************************//** Allocate memory for zlib. */ static void* page_zip_malloc( /*============*/ void* opaque, /*!< in/out: memory heap */ uInt items, /*!< in: number of items to allocate */ uInt size) /*!< in: size of an item in bytes */ { return(mem_heap_alloc(opaque, items * size)); } /**********************************************************************//** Deallocate memory for zlib. */ static void page_zip_free( /*==========*/ void* opaque __attribute__((unused)), /*!< in: memory heap */ void* address __attribute__((unused)))/*!< in: object to free */ { } /**********************************************************************//** Configure the zlib allocator to use the given memory heap. */ UNIV_INTERN void page_zip_set_alloc( /*===============*/ void* stream, /*!< in/out: zlib stream */ mem_heap_t* heap) /*!< in: memory heap to use */ { z_stream* strm = stream; strm->zalloc = page_zip_malloc; strm->zfree = page_zip_free; strm->opaque = heap; } #if 0 || defined UNIV_DEBUG || defined UNIV_ZIP_DEBUG /** Symbol for enabling compression and decompression diagnostics */ # define PAGE_ZIP_COMPRESS_DBG #endif #ifdef PAGE_ZIP_COMPRESS_DBG /** Set this variable in a debugger to enable excessive logging in page_zip_compress(). */ UNIV_INTERN ibool page_zip_compress_dbg; /** Set this variable in a debugger to enable binary logging of the data passed to deflate(). When this variable is nonzero, it will act as a log file name generator. */ UNIV_INTERN unsigned page_zip_compress_log; /**********************************************************************//** Wrapper for deflate(). Log the operation if page_zip_compress_dbg is set. @return deflate() status: Z_OK, Z_BUF_ERROR, ... */ static int page_zip_compress_deflate( /*======================*/ FILE* logfile,/*!< in: log file, or NULL */ z_streamp strm, /*!< in/out: compressed stream for deflate() */ int flush) /*!< in: deflate() flushing method */ { int status; if (UNIV_UNLIKELY(page_zip_compress_dbg)) { ut_print_buf(ib_stream, strm->next_in, strm->avail_in); } if (UNIV_LIKELY_NULL(logfile)) { fwrite(strm->next_in, 1, strm->avail_in, logfile); } status = deflate(strm, flush); if (UNIV_UNLIKELY(page_zip_compress_dbg)) { ib_logger(ib_stream, " -> %d\n", status); } return(status); } /* Redefine deflate(). */ # undef deflate /** Debug wrapper for the zlib compression routine deflate(). Log the operation if page_zip_compress_dbg is set. @param strm in/out: compressed stream @param flush in: flushing method @return deflate() status: Z_OK, Z_BUF_ERROR, ... */ # define deflate(strm, flush) page_zip_compress_deflate(logfile, strm, flush) /** Declaration of the logfile parameter */ # define FILE_LOGFILE FILE* logfile, /** The logfile parameter */ # define LOGFILE logfile, #else /* PAGE_ZIP_COMPRESS_DBG */ /** Empty declaration of the logfile parameter */ # define FILE_LOGFILE /** Missing logfile parameter */ # define LOGFILE #endif /* PAGE_ZIP_COMPRESS_DBG */ /**********************************************************************//** Compress the records of a node pointer page. @return Z_OK, or a zlib error code */ static int page_zip_compress_node_ptrs( /*========================*/ FILE_LOGFILE z_stream* c_stream, /*!< in/out: compressed page stream */ const rec_t** recs, /*!< in: dense page directory sorted by address */ ulint n_dense, /*!< in: size of recs[] */ dict_index_t* index, /*!< in: the index of the page */ byte* storage, /*!< in: end of dense page directory */ mem_heap_t* heap) /*!< in: temporary memory heap */ { int err = Z_OK; ulint* offsets = NULL; do { const rec_t* rec = *recs++; offsets = rec_get_offsets(rec, index, offsets, ULINT_UNDEFINED, &heap); /* Only leaf nodes may contain externally stored columns. */ ut_ad(!rec_offs_any_extern(offsets)); UNIV_MEM_ASSERT_RW(rec, rec_offs_data_size(offsets)); UNIV_MEM_ASSERT_RW(rec - rec_offs_extra_size(offsets), rec_offs_extra_size(offsets)); /* Compress the extra bytes. */ c_stream->avail_in = rec - REC_N_NEW_EXTRA_BYTES - c_stream->next_in; if (c_stream->avail_in) { err = deflate(c_stream, Z_NO_FLUSH); if (UNIV_UNLIKELY(err != Z_OK)) { break; } } ut_ad(!c_stream->avail_in); /* Compress the data bytes, except node_ptr. */ c_stream->next_in = (byte*) rec; c_stream->avail_in = rec_offs_data_size(offsets) - REC_NODE_PTR_SIZE; ut_ad(c_stream->avail_in); err = deflate(c_stream, Z_NO_FLUSH); if (UNIV_UNLIKELY(err != Z_OK)) { break; } ut_ad(!c_stream->avail_in); memcpy(storage - REC_NODE_PTR_SIZE * (rec_get_heap_no_new(rec) - 1), c_stream->next_in, REC_NODE_PTR_SIZE); c_stream->next_in += REC_NODE_PTR_SIZE; } while (--n_dense); return(err); } /**********************************************************************//** Compress the records of a leaf node of a secondary index. @return Z_OK, or a zlib error code */ static int page_zip_compress_sec( /*==================*/ FILE_LOGFILE z_stream* c_stream, /*!< in/out: compressed page stream */ const rec_t** recs, /*!< in: dense page directory sorted by address */ ulint n_dense) /*!< in: size of recs[] */ { int err = Z_OK; ut_ad(n_dense > 0); do { const rec_t* rec = *recs++; /* Compress everything up to this record. */ c_stream->avail_in = rec - REC_N_NEW_EXTRA_BYTES - c_stream->next_in; if (UNIV_LIKELY(c_stream->avail_in)) { UNIV_MEM_ASSERT_RW(c_stream->next_in, c_stream->avail_in); err = deflate(c_stream, Z_NO_FLUSH); if (UNIV_UNLIKELY(err != Z_OK)) { break; } } ut_ad(!c_stream->avail_in); ut_ad(c_stream->next_in == rec - REC_N_NEW_EXTRA_BYTES); /* Skip the REC_N_NEW_EXTRA_BYTES. */ c_stream->next_in = (byte*) rec; } while (--n_dense); return(err); } /**********************************************************************//** Compress a record of a leaf node of a clustered index that contains externally stored columns. @return Z_OK, or a zlib error code */ static int page_zip_compress_clust_ext( /*========================*/ FILE_LOGFILE z_stream* c_stream, /*!< in/out: compressed page stream */ const rec_t* rec, /*!< in: record */ const ulint* offsets, /*!< in: rec_get_offsets(rec) */ ulint trx_id_col, /*!< in: position of of DB_TRX_ID */ byte* deleted, /*!< in: dense directory entry pointing to the head of the free list */ byte* storage, /*!< in: end of dense page directory */ byte** externs, /*!< in/out: pointer to the next available BLOB pointer */ ulint* n_blobs) /*!< in/out: number of externally stored columns */ { int err; ulint i; UNIV_MEM_ASSERT_RW(rec, rec_offs_data_size(offsets)); UNIV_MEM_ASSERT_RW(rec - rec_offs_extra_size(offsets), rec_offs_extra_size(offsets)); for (i = 0; i < rec_offs_n_fields(offsets); i++) { ulint len; const byte* src; if (UNIV_UNLIKELY(i == trx_id_col)) { ut_ad(!rec_offs_nth_extern(offsets, i)); /* Store trx_id and roll_ptr in uncompressed form. */ src = rec_get_nth_field(rec, offsets, i, &len); ut_ad(src + DATA_TRX_ID_LEN == rec_get_nth_field(rec, offsets, i + 1, &len)); ut_ad(len == DATA_ROLL_PTR_LEN); /* Compress any preceding bytes. */ c_stream->avail_in = src - c_stream->next_in; if (c_stream->avail_in) { err = deflate(c_stream, Z_NO_FLUSH); if (UNIV_UNLIKELY(err != Z_OK)) { return(err); } } ut_ad(!c_stream->avail_in); ut_ad(c_stream->next_in == src); memcpy(storage - (DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN) * (rec_get_heap_no_new(rec) - 1), c_stream->next_in, DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN); c_stream->next_in += DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN; /* Skip also roll_ptr */ i++; } else if (rec_offs_nth_extern(offsets, i)) { src = rec_get_nth_field(rec, offsets, i, &len); ut_ad(len >= BTR_EXTERN_FIELD_REF_SIZE); src += len - BTR_EXTERN_FIELD_REF_SIZE; c_stream->avail_in = src - c_stream->next_in; if (UNIV_LIKELY(c_stream->avail_in)) { err = deflate(c_stream, Z_NO_FLUSH); if (UNIV_UNLIKELY(err != Z_OK)) { return(err); } } ut_ad(!c_stream->avail_in); ut_ad(c_stream->next_in == src); /* Reserve space for the data at the end of the space reserved for the compressed data and the page modification log. */ if (UNIV_UNLIKELY (c_stream->avail_out <= BTR_EXTERN_FIELD_REF_SIZE)) { /* out of space */ return(Z_BUF_ERROR); } ut_ad(*externs == c_stream->next_out + c_stream->avail_out + 1/* end of modif. log */); c_stream->next_in += BTR_EXTERN_FIELD_REF_SIZE; /* Skip deleted records. */ if (UNIV_LIKELY_NULL (page_zip_dir_find_low( storage, deleted, page_offset(rec)))) { continue; } (*n_blobs)++; c_stream->avail_out -= BTR_EXTERN_FIELD_REF_SIZE; *externs -= BTR_EXTERN_FIELD_REF_SIZE; /* Copy the BLOB pointer */ memcpy(*externs, c_stream->next_in - BTR_EXTERN_FIELD_REF_SIZE, BTR_EXTERN_FIELD_REF_SIZE); } } return(Z_OK); } /**********************************************************************//** Compress the records of a leaf node of a clustered index. @return Z_OK, or a zlib error code */ static int page_zip_compress_clust( /*====================*/ FILE_LOGFILE z_stream* c_stream, /*!< in/out: compressed page stream */ const rec_t** recs, /*!< in: dense page directory sorted by address */ ulint n_dense, /*!< in: size of recs[] */ dict_index_t* index, /*!< in: the index of the page */ ulint* n_blobs, /*!< in: 0; out: number of externally stored columns */ ulint trx_id_col, /*!< index of the trx_id column */ byte* deleted, /*!< in: dense directory entry pointing to the head of the free list */ byte* storage, /*!< in: end of dense page directory */ mem_heap_t* heap) /*!< in: temporary memory heap */ { int err = Z_OK; ulint* offsets = NULL; /* BTR_EXTERN_FIELD_REF storage */ byte* externs = storage - n_dense * (DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN); ut_ad(*n_blobs == 0); do { const rec_t* rec = *recs++; offsets = rec_get_offsets(rec, index, offsets, ULINT_UNDEFINED, &heap); ut_ad(rec_offs_n_fields(offsets) == dict_index_get_n_fields(index)); UNIV_MEM_ASSERT_RW(rec, rec_offs_data_size(offsets)); UNIV_MEM_ASSERT_RW(rec - rec_offs_extra_size(offsets), rec_offs_extra_size(offsets)); /* Compress the extra bytes. */ c_stream->avail_in = rec - REC_N_NEW_EXTRA_BYTES - c_stream->next_in; if (c_stream->avail_in) { err = deflate(c_stream, Z_NO_FLUSH); if (UNIV_UNLIKELY(err != Z_OK)) { goto func_exit; } } ut_ad(!c_stream->avail_in); ut_ad(c_stream->next_in == rec - REC_N_NEW_EXTRA_BYTES); /* Compress the data bytes. */ c_stream->next_in = (byte*) rec; /* Check if there are any externally stored columns. For each externally stored column, store the BTR_EXTERN_FIELD_REF separately. */ if (UNIV_UNLIKELY(rec_offs_any_extern(offsets))) { ut_ad(dict_index_is_clust(index)); err = page_zip_compress_clust_ext( LOGFILE c_stream, rec, offsets, trx_id_col, deleted, storage, &externs, n_blobs); if (UNIV_UNLIKELY(err != Z_OK)) { goto func_exit; } } else { ulint len; const byte* src; /* Store trx_id and roll_ptr in uncompressed form. */ src = rec_get_nth_field(rec, offsets, trx_id_col, &len); ut_ad(src + DATA_TRX_ID_LEN == rec_get_nth_field(rec, offsets, trx_id_col + 1, &len)); ut_ad(len == DATA_ROLL_PTR_LEN); UNIV_MEM_ASSERT_RW(rec, rec_offs_data_size(offsets)); UNIV_MEM_ASSERT_RW(rec - rec_offs_extra_size(offsets), rec_offs_extra_size(offsets)); /* Compress any preceding bytes. */ c_stream->avail_in = src - c_stream->next_in; if (c_stream->avail_in) { err = deflate(c_stream, Z_NO_FLUSH); if (UNIV_UNLIKELY(err != Z_OK)) { return(err); } } ut_ad(!c_stream->avail_in); ut_ad(c_stream->next_in == src); memcpy(storage - (DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN) * (rec_get_heap_no_new(rec) - 1), c_stream->next_in, DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN); c_stream->next_in += DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN; /* Skip also roll_ptr */ ut_ad(trx_id_col + 1 < rec_offs_n_fields(offsets)); } /* Compress the last bytes of the record. */ c_stream->avail_in = rec + rec_offs_data_size(offsets) - c_stream->next_in; if (c_stream->avail_in) { err = deflate(c_stream, Z_NO_FLUSH); if (UNIV_UNLIKELY(err != Z_OK)) { goto func_exit; } } ut_ad(!c_stream->avail_in); } while (--n_dense); func_exit: return(err); } /**********************************************************************//** Compress a page. @return TRUE on success, FALSE on failure; page_zip will be left intact on failure. */ UNIV_INTERN ibool page_zip_compress( /*==============*/ page_zip_des_t* page_zip,/*!< in: size; out: data, n_blobs, m_start, m_end, m_nonempty */ const page_t* page, /*!< in: uncompressed page */ dict_index_t* index, /*!< in: index of the B-tree node */ mtr_t* mtr) /*!< in: mini-transaction, or NULL */ { z_stream c_stream; int err; ulint n_fields;/* number of index fields needed */ byte* fields; /*!< index field information */ byte* buf; /*!< compressed payload of the page */ byte* buf_end;/* end of buf */ ulint n_dense; ulint slot_size;/* amount of uncompressed bytes per record */ const rec_t** recs; /*!< dense page directory, sorted by address */ mem_heap_t* heap; ulint trx_id_col; ulint* offsets = NULL; ulint n_blobs = 0; byte* storage;/* storage of uncompressed columns */ #ifndef UNIV_HOTBACKUP ib_uint64_t usec = ut_time_us(NULL); #endif /* !UNIV_HOTBACKUP */ #ifdef PAGE_ZIP_COMPRESS_DBG FILE* logfile = NULL; #endif ut_a(page_is_comp(page)); ut_a(fil_page_get_type(page) == FIL_PAGE_INDEX); ut_ad(page_simple_validate_new((page_t*) page)); ut_ad(page_zip_simple_validate(page_zip)); ut_ad(dict_table_is_comp(index->table)); ut_ad(!dict_index_is_ibuf(index)); UNIV_MEM_ASSERT_RW(page, UNIV_PAGE_SIZE); /* Check the data that will be omitted. */ ut_a(!memcmp(page + (PAGE_NEW_INFIMUM - REC_N_NEW_EXTRA_BYTES), infimum_extra, sizeof infimum_extra)); ut_a(!memcmp(page + PAGE_NEW_INFIMUM, infimum_data, sizeof infimum_data)); ut_a(page[PAGE_NEW_SUPREMUM - REC_N_NEW_EXTRA_BYTES] /* info_bits == 0, n_owned <= max */ <= PAGE_DIR_SLOT_MAX_N_OWNED); ut_a(!memcmp(page + (PAGE_NEW_SUPREMUM - REC_N_NEW_EXTRA_BYTES + 1), supremum_extra_data, sizeof supremum_extra_data)); if (UNIV_UNLIKELY(!page_get_n_recs(page))) { ut_a(rec_get_next_offs(page + PAGE_NEW_INFIMUM, TRUE) == PAGE_NEW_SUPREMUM); } if (page_is_leaf(page)) { n_fields = dict_index_get_n_fields(index); } else { n_fields = dict_index_get_n_unique_in_tree(index); } /* The dense directory excludes the infimum and supremum records. */ n_dense = page_dir_get_n_heap(page) - PAGE_HEAP_NO_USER_LOW; #ifdef PAGE_ZIP_COMPRESS_DBG if (UNIV_UNLIKELY(page_zip_compress_dbg)) { ib_logger(ib_stream, "compress %p %p %lu %lu %lu\n", (void*) page_zip, (void*) page, page_is_leaf(page), n_fields, n_dense); } if (UNIV_UNLIKELY(page_zip_compress_log)) { /* Create a log file for every compression attempt. */ char logfilename[9]; ut_snprintf(logfilename, sizeof logfilename, "%08x", page_zip_compress_log++); logfile = fopen(logfilename, "wb"); if (logfile) { /* Write the uncompressed page to the log. */ fwrite(page, 1, UNIV_PAGE_SIZE, logfile); /* Record the compressed size as zero. This will be overwritten at successful exit. */ fputc(0, logfile); fputc(0, logfile); fputc(0, logfile); fputc(0, logfile); } } #endif /* PAGE_ZIP_COMPRESS_DBG */ #ifndef UNIV_HOTBACKUP page_zip_stat[page_zip->ssize - 1].compressed++; #endif /* !UNIV_HOTBACKUP */ if (UNIV_UNLIKELY(n_dense * PAGE_ZIP_DIR_SLOT_SIZE >= page_zip_get_size(page_zip))) { goto err_exit; } heap = mem_heap_create(page_zip_get_size(page_zip) + n_fields * (2 + sizeof *offsets) + n_dense * ((sizeof *recs) - PAGE_ZIP_DIR_SLOT_SIZE) + UNIV_PAGE_SIZE * 4 + (512 << MAX_MEM_LEVEL)); recs = mem_heap_zalloc(heap, n_dense * sizeof *recs); fields = mem_heap_alloc(heap, (n_fields + 1) * 2); buf = mem_heap_alloc(heap, page_zip_get_size(page_zip) - PAGE_DATA); buf_end = buf + page_zip_get_size(page_zip) - PAGE_DATA; /* Compress the data payload. */ page_zip_set_alloc(&c_stream, heap); err = deflateInit2(&c_stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, UNIV_PAGE_SIZE_SHIFT, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY); ut_a(err == Z_OK); c_stream.next_out = buf; /* Subtract the space reserved for uncompressed data. */ /* Page header and the end marker of the modification log */ c_stream.avail_out = buf_end - buf - 1; /* Dense page directory and uncompressed columns, if any */ if (page_is_leaf(page)) { if (dict_index_is_clust(index)) { trx_id_col = dict_index_get_sys_col_pos( index, DATA_TRX_ID); ut_ad(trx_id_col > 0); ut_ad(trx_id_col != ULINT_UNDEFINED); slot_size = PAGE_ZIP_DIR_SLOT_SIZE + DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN; } else { /* Signal the absence of trx_id in page_zip_fields_encode() */ ut_ad(dict_index_get_sys_col_pos(index, DATA_TRX_ID) == ULINT_UNDEFINED); trx_id_col = 0; slot_size = PAGE_ZIP_DIR_SLOT_SIZE; } } else { slot_size = PAGE_ZIP_DIR_SLOT_SIZE + REC_NODE_PTR_SIZE; trx_id_col = ULINT_UNDEFINED; } if (UNIV_UNLIKELY(c_stream.avail_out <= n_dense * slot_size + 6/* sizeof(zlib header and footer) */)) { goto zlib_error; } c_stream.avail_out -= n_dense * slot_size; c_stream.avail_in = page_zip_fields_encode(n_fields, index, trx_id_col, fields); c_stream.next_in = fields; if (UNIV_LIKELY(!trx_id_col)) { trx_id_col = ULINT_UNDEFINED; } UNIV_MEM_ASSERT_RW(c_stream.next_in, c_stream.avail_in); err = deflate(&c_stream, Z_FULL_FLUSH); if (err != Z_OK) { goto zlib_error; } ut_ad(!c_stream.avail_in); page_zip_dir_encode(page, buf_end, recs); c_stream.next_in = (byte*) page + PAGE_ZIP_START; storage = buf_end - n_dense * PAGE_ZIP_DIR_SLOT_SIZE; /* Compress the records in heap_no order. */ if (UNIV_UNLIKELY(!n_dense)) { } else if (!page_is_leaf(page)) { /* This is a node pointer page. */ err = page_zip_compress_node_ptrs(LOGFILE &c_stream, recs, n_dense, index, storage, heap); if (UNIV_UNLIKELY(err != Z_OK)) { goto zlib_error; } } else if (UNIV_LIKELY(trx_id_col == ULINT_UNDEFINED)) { /* This is a leaf page in a secondary index. */ err = page_zip_compress_sec(LOGFILE &c_stream, recs, n_dense); if (UNIV_UNLIKELY(err != Z_OK)) { goto zlib_error; } } else { /* This is a leaf page in a clustered index. */ err = page_zip_compress_clust(LOGFILE &c_stream, recs, n_dense, index, &n_blobs, trx_id_col, buf_end - PAGE_ZIP_DIR_SLOT_SIZE * page_get_n_recs(page), storage, heap); if (UNIV_UNLIKELY(err != Z_OK)) { goto zlib_error; } } /* Finish the compression. */ ut_ad(!c_stream.avail_in); /* Compress any trailing garbage, in case the last record was allocated from an originally longer space on the free list, or the data of the last record from page_zip_compress_sec(). */ c_stream.avail_in = page_header_get_field(page, PAGE_HEAP_TOP) - (c_stream.next_in - page); ut_a(c_stream.avail_in <= UNIV_PAGE_SIZE - PAGE_ZIP_START - PAGE_DIR); UNIV_MEM_ASSERT_RW(c_stream.next_in, c_stream.avail_in); err = deflate(&c_stream, Z_FINISH); if (UNIV_UNLIKELY(err != Z_STREAM_END)) { zlib_error: deflateEnd(&c_stream); mem_heap_free(heap); err_exit: #ifdef PAGE_ZIP_COMPRESS_DBG if (logfile) { fclose(logfile); } #endif /* PAGE_ZIP_COMPRESS_DBG */ #ifndef UNIV_HOTBACKUP page_zip_stat[page_zip->ssize - 1].compressed_usec += ut_time_us(NULL) - usec; #endif /* !UNIV_HOTBACKUP */ return(FALSE); } err = deflateEnd(&c_stream); ut_a(err == Z_OK); ut_ad(buf + c_stream.total_out == c_stream.next_out); ut_ad((ulint) (storage - c_stream.next_out) >= c_stream.avail_out); /* Valgrind believes that zlib does not initialize some bits in the last 7 or 8 bytes of the stream. Make Valgrind happy. */ UNIV_MEM_VALID(buf, c_stream.total_out); /* Zero out the area reserved for the modification log. Space for the end marker of the modification log is not included in avail_out. */ memset(c_stream.next_out, 0, c_stream.avail_out + 1/* end marker */); #ifdef UNIV_DEBUG page_zip->m_start = #endif /* UNIV_DEBUG */ page_zip->m_end = PAGE_DATA + c_stream.total_out; page_zip->m_nonempty = FALSE; page_zip->n_blobs = n_blobs; /* Copy those header fields that will not be written in buf_flush_init_for_writing() */ memcpy(page_zip->data + FIL_PAGE_PREV, page + FIL_PAGE_PREV, FIL_PAGE_LSN - FIL_PAGE_PREV); memcpy(page_zip->data + FIL_PAGE_TYPE, page + FIL_PAGE_TYPE, 2); memcpy(page_zip->data + FIL_PAGE_DATA, page + FIL_PAGE_DATA, PAGE_DATA - FIL_PAGE_DATA); /* Copy the rest of the compressed page */ memcpy(page_zip->data + PAGE_DATA, buf, page_zip_get_size(page_zip) - PAGE_DATA); mem_heap_free(heap); #ifdef UNIV_ZIP_DEBUG ut_a(page_zip_validate(page_zip, page)); #endif /* UNIV_ZIP_DEBUG */ if (mtr) { #ifndef UNIV_HOTBACKUP page_zip_compress_write_log(page_zip, page, index, mtr); #endif /* !UNIV_HOTBACKUP */ } UNIV_MEM_ASSERT_RW(page_zip->data, page_zip_get_size(page_zip)); #ifdef PAGE_ZIP_COMPRESS_DBG if (logfile) { /* Record the compressed size of the block. */ byte sz[4]; mach_write_to_4(sz, c_stream.total_out); fseek(logfile, UNIV_PAGE_SIZE, SEEK_SET); fwrite(sz, 1, sizeof sz, logfile); fclose(logfile); } #endif /* PAGE_ZIP_COMPRESS_DBG */ #ifndef UNIV_HOTBACKUP { page_zip_stat_t* zip_stat = &page_zip_stat[page_zip->ssize - 1]; zip_stat->compressed_ok++; zip_stat->compressed_usec += ut_time_us(NULL) - usec; } #endif /* !UNIV_HOTBACKUP */ return(TRUE); } /**********************************************************************//** Compare two page directory entries. @return positive if rec1 > rec2 */ UNIV_INLINE ibool page_zip_dir_cmp( /*=============*/ void* ignored __attribute__((unused)), const rec_t* rec1, /*!< in: rec1 */ const rec_t* rec2) /*!< in: rec2 */ { return(rec1 > rec2); } /**********************************************************************//** Sort the dense page directory by address (heap_no). */ static void page_zip_dir_sort( /*==============*/ void* ignored __attribute__((unused)), rec_t** arr, /*!< in/out: dense page directory */ rec_t** aux_arr,/*!< in/out: work area */ ulint low, /*!< in: lower bound of the sorting area, inclusive */ ulint high) /*!< in: upper bound of the sorting area, exclusive */ { /* Pass in a NULL compare context as it's not required. */ UT_SORT_FUNCTION_BODY(NULL, page_zip_dir_sort, arr, aux_arr, low, high, page_zip_dir_cmp); } /**********************************************************************//** Deallocate the index information initialized by page_zip_fields_decode(). */ static void page_zip_fields_free( /*=================*/ dict_index_t* index) /*!< in: dummy index to be freed */ { if (index) { dict_table_t* table = index->table; mem_heap_free(index->heap); mem_heap_free(table->heap); } } /**********************************************************************//** Read the index information for the compressed page. @return own: dummy index describing the page, or NULL on error */ static dict_index_t* page_zip_fields_decode( /*===================*/ const byte* buf, /*!< in: index information */ const byte* end, /*!< in: end of buf */ ulint* trx_id_col)/*!< in: NULL for non-leaf pages; for leaf pages, pointer to where to store the position of the trx_id column */ { const byte* b; ulint n; ulint i; ulint val; dict_table_t* table; dict_index_t* index; /* Determine the number of fields. */ for (b = buf, n = 0; b < end; n++) { if (*b++ & 0x80) { b++; /* skip the second byte */ } } n--; /* n_nullable or trx_id */ if (UNIV_UNLIKELY(n > REC_MAX_N_FIELDS)) { page_zip_fail(ib_stream, "page_zip_fields_decode: n = %lu\n", (ulong) n); return(NULL); } if (UNIV_UNLIKELY(b > end)) { page_zip_fail(ib_stream, "page_zip_fields_decode: %p > %p\n", (const void*) b, (const void*) end); return(NULL); } table = dict_mem_table_create("ZIP_DUMMY", DICT_HDR_SPACE, n, DICT_TF_COMPACT); index = dict_mem_index_create("ZIP_DUMMY", "ZIP_DUMMY", DICT_HDR_SPACE, 0, n); index->table = table; index->n_uniq = n; /* avoid ut_ad(index->cached) in dict_index_get_n_unique_in_tree */ index->cached = TRUE; /* Initialize the fields. */ for (b = buf, i = 0; i < n; i++) { ulint mtype; ulint len; val = *b++; if (UNIV_UNLIKELY(val & 0x80)) { /* fixed length > 62 bytes */ val = (val & 0x7f) << 8 | *b++; len = val >> 1; mtype = DATA_FIXBINARY; } else if (UNIV_UNLIKELY(val >= 126)) { /* variable length with max > 255 bytes */ len = 0x7fff; mtype = DATA_BINARY; } else if (val <= 1) { /* variable length with max <= 255 bytes */ len = 0; mtype = DATA_BINARY; } else { /* fixed length < 62 bytes */ len = val >> 1; mtype = DATA_FIXBINARY; } dict_mem_table_add_col(table, NULL, NULL, mtype, val & 1 ? DATA_NOT_NULL : 0, len); dict_index_add_col(index, table, dict_table_get_nth_col(table, i), 0); } val = *b++; if (UNIV_UNLIKELY(val & 0x80)) { val = (val & 0x7f) << 8 | *b++; } /* Decode the position of the trx_id column. */ if (trx_id_col) { if (!val) { val = ULINT_UNDEFINED; } else if (UNIV_UNLIKELY(val >= n)) { page_zip_fields_free(index); index = NULL; } else { index->type = DICT_CLUSTERED; } *trx_id_col = val; } else { /* Decode the number of nullable fields. */ if (UNIV_UNLIKELY(index->n_nullable > val)) { page_zip_fields_free(index); index = NULL; } else { index->n_nullable = val; } } ut_ad(b == end); return(index); } /**********************************************************************//** Populate the sparse page directory from the dense directory. @return TRUE on success, FALSE on failure */ static ibool page_zip_dir_decode( /*================*/ const page_zip_des_t* page_zip,/*!< in: dense page directory on compressed page */ page_t* page, /*!< in: compact page with valid header; out: trailer and sparse page directory filled in */ rec_t** recs, /*!< out: dense page directory sorted by ascending address (and heap_no) */ rec_t** recs_aux,/*!< in/out: scratch area */ ulint n_dense)/*!< in: number of user records, and size of recs[] and recs_aux[] */ { ulint i; ulint n_recs; byte* slot; n_recs = page_get_n_recs(page); if (UNIV_UNLIKELY(n_recs > n_dense)) { page_zip_fail(ib_stream, "page_zip_dir_decode 1: %lu > %lu\n", (ulong) n_recs, (ulong) n_dense); return(FALSE); } /* Traverse the list of stored records in the sorting order, starting from the first user record. */ slot = page + (UNIV_PAGE_SIZE - PAGE_DIR - PAGE_DIR_SLOT_SIZE); UNIV_PREFETCH_RW(slot); /* Zero out the page trailer. */ memset(slot + PAGE_DIR_SLOT_SIZE, 0, PAGE_DIR); mach_write_to_2(slot, PAGE_NEW_INFIMUM); slot -= PAGE_DIR_SLOT_SIZE; UNIV_PREFETCH_RW(slot); /* Initialize the sparse directory and copy the dense directory. */ for (i = 0; i < n_recs; i++) { ulint offs = page_zip_dir_get(page_zip, i); if (offs & PAGE_ZIP_DIR_SLOT_OWNED) { mach_write_to_2(slot, offs & PAGE_ZIP_DIR_SLOT_MASK); slot -= PAGE_DIR_SLOT_SIZE; UNIV_PREFETCH_RW(slot); } if (UNIV_UNLIKELY((offs & PAGE_ZIP_DIR_SLOT_MASK) < PAGE_ZIP_START + REC_N_NEW_EXTRA_BYTES)) { page_zip_fail(ib_stream, "page_zip_dir_decode 2: %u %u %lx\n", (unsigned) i, (unsigned) n_recs, (ulong) offs); return(FALSE); } recs[i] = page + (offs & PAGE_ZIP_DIR_SLOT_MASK); } mach_write_to_2(slot, PAGE_NEW_SUPREMUM); { const page_dir_slot_t* last_slot = page_dir_get_nth_slot( page, page_dir_get_n_slots(page) - 1); if (UNIV_UNLIKELY(slot != last_slot)) { page_zip_fail(ib_stream, "page_zip_dir_decode 3: %p != %p\n", (const void*) slot, (const void*) last_slot); return(FALSE); } } /* Copy the rest of the dense directory. */ for (; i < n_dense; i++) { ulint offs = page_zip_dir_get(page_zip, i); if (UNIV_UNLIKELY(offs & ~PAGE_ZIP_DIR_SLOT_MASK)) { page_zip_fail(ib_stream, "page_zip_dir_decode 4: %u %u %lx\n", (unsigned) i, (unsigned) n_dense, (ulong) offs); return(FALSE); } recs[i] = page + offs; } if (UNIV_LIKELY(n_dense > 1)) { /* Set the compare context to NULL as it's not required. */ page_zip_dir_sort(NULL, recs, recs_aux, 0, n_dense); } return(TRUE); } /**********************************************************************//** Initialize the REC_N_NEW_EXTRA_BYTES of each record. @return TRUE on success, FALSE on failure */ static ibool page_zip_set_extra_bytes( /*=====================*/ const page_zip_des_t* page_zip,/*!< in: compressed page */ page_t* page, /*!< in/out: uncompressed page */ ulint info_bits)/*!< in: REC_INFO_MIN_REC_FLAG or 0 */ { ulint n; ulint i; ulint n_owned = 1; ulint offs; rec_t* rec; n = page_get_n_recs(page); rec = page + PAGE_NEW_INFIMUM; for (i = 0; i < n; i++) { offs = page_zip_dir_get(page_zip, i); if (UNIV_UNLIKELY(offs & PAGE_ZIP_DIR_SLOT_DEL)) { info_bits |= REC_INFO_DELETED_FLAG; } if (UNIV_UNLIKELY(offs & PAGE_ZIP_DIR_SLOT_OWNED)) { info_bits |= n_owned; n_owned = 1; } else { n_owned++; } offs &= PAGE_ZIP_DIR_SLOT_MASK; if (UNIV_UNLIKELY(offs < PAGE_ZIP_START + REC_N_NEW_EXTRA_BYTES)) { page_zip_fail(ib_stream, "page_zip_set_extra_bytes 1:" " %u %u %lx\n", (unsigned) i, (unsigned) n, (ulong) offs); return(FALSE); } rec_set_next_offs_new(rec, offs); rec = page + offs; rec[-REC_N_NEW_EXTRA_BYTES] = (byte) info_bits; info_bits = 0; } /* Set the next pointer of the last user record. */ rec_set_next_offs_new(rec, PAGE_NEW_SUPREMUM); /* Set n_owned of the supremum record. */ page[PAGE_NEW_SUPREMUM - REC_N_NEW_EXTRA_BYTES] = (byte) n_owned; /* The dense directory excludes the infimum and supremum records. */ n = page_dir_get_n_heap(page) - PAGE_HEAP_NO_USER_LOW; if (i >= n) { if (UNIV_LIKELY(i == n)) { return(TRUE); } page_zip_fail(ib_stream, "page_zip_set_extra_bytes 2: %u != %u\n", (unsigned) i, (unsigned) n); return(FALSE); } offs = page_zip_dir_get(page_zip, i); /* Set the extra bytes of deleted records on the free list. */ for (;;) { if (UNIV_UNLIKELY(!offs) || UNIV_UNLIKELY(offs & ~PAGE_ZIP_DIR_SLOT_MASK)) { page_zip_fail(ib_stream, "page_zip_set_extra_bytes 3: %lx\n", (ulong) offs); return(FALSE); } rec = page + offs; rec[-REC_N_NEW_EXTRA_BYTES] = 0; /* info_bits and n_owned */ if (++i == n) { break; } offs = page_zip_dir_get(page_zip, i); rec_set_next_offs_new(rec, offs); } /* Terminate the free list. */ rec[-REC_N_NEW_EXTRA_BYTES] = 0; /* info_bits and n_owned */ rec_set_next_offs_new(rec, 0); return(TRUE); } /**********************************************************************//** Apply the modification log to a record containing externally stored columns. Do not copy the fields that are stored separately. @return pointer to modification log, or NULL on failure */ static const byte* page_zip_apply_log_ext( /*===================*/ rec_t* rec, /*!< in/out: record */ const ulint* offsets, /*!< in: rec_get_offsets(rec) */ ulint trx_id_col, /*!< in: position of of DB_TRX_ID */ const byte* data, /*!< in: modification log */ const byte* end) /*!< in: end of modification log */ { ulint i; ulint len; byte* next_out = rec; /* Check if there are any externally stored columns. For each externally stored column, skip the BTR_EXTERN_FIELD_REF. */ for (i = 0; i < rec_offs_n_fields(offsets); i++) { byte* dst; if (UNIV_UNLIKELY(i == trx_id_col)) { /* Skip trx_id and roll_ptr */ dst = rec_get_nth_field(rec, offsets, i, &len); if (UNIV_UNLIKELY(dst - next_out >= end - data) || UNIV_UNLIKELY (len < (DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN)) || rec_offs_nth_extern(offsets, i)) { page_zip_fail(ib_stream, "page_zip_apply_log_ext:" " trx_id len %lu," " %p - %p >= %p - %p\n", (ulong) len, (const void*) dst, (const void*) next_out, (const void*) end, (const void*) data); return(NULL); } memcpy(next_out, data, dst - next_out); data += dst - next_out; next_out = dst + (DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN); } else if (rec_offs_nth_extern(offsets, i)) { dst = rec_get_nth_field(rec, offsets, i, &len); ut_ad(len >= BTR_EXTERN_FIELD_REF_SIZE); len += dst - next_out - BTR_EXTERN_FIELD_REF_SIZE; if (UNIV_UNLIKELY(data + len >= end)) { page_zip_fail(ib_stream, "page_zip_apply_log_ext: " "ext %p+%lu >= %p\n", (const void*) data, (ulong) len, (const void*) end); return(NULL); } memcpy(next_out, data, len); data += len; next_out += len + BTR_EXTERN_FIELD_REF_SIZE; } } /* Copy the last bytes of the record. */ len = rec_get_end(rec, offsets) - next_out; if (UNIV_UNLIKELY(data + len >= end)) { page_zip_fail(ib_stream, "page_zip_apply_log_ext: last %p+%lu >= %p\n", (const void*) data, (ulong) len, (const void*) end); return(NULL); } memcpy(next_out, data, len); data += len; return(data); } /**********************************************************************//** Apply the modification log to an uncompressed page. Do not copy the fields that are stored separately. @return pointer to end of modification log, or NULL on failure */ static const byte* page_zip_apply_log( /*===============*/ const byte* data, /*!< in: modification log */ ulint size, /*!< in: maximum length of the log, in bytes */ rec_t** recs, /*!< in: dense page directory, sorted by address (indexed by heap_no - PAGE_HEAP_NO_USER_LOW) */ ulint n_dense,/*!< in: size of recs[] */ ulint trx_id_col,/*!< in: column number of trx_id in the index, or ULINT_UNDEFINED if none */ ulint heap_status, /*!< in: heap_no and status bits for the next record to uncompress */ dict_index_t* index, /*!< in: index of the page */ ulint* offsets)/*!< in/out: work area for rec_get_offsets_reverse() */ { const byte* const end = data + size; for (;;) { ulint val; rec_t* rec; ulint len; ulint hs; val = *data++; if (UNIV_UNLIKELY(!val)) { return(data - 1); } if (val & 0x80) { val = (val & 0x7f) << 8 | *data++; if (UNIV_UNLIKELY(!val)) { page_zip_fail(ib_stream, "page_zip_apply_log:" " invalid val %x%x\n", data[-2], data[-1]); return(NULL); } } if (UNIV_UNLIKELY(data >= end)) { page_zip_fail(ib_stream, "page_zip_apply_log: %p >= %p\n", (const void*) data, (const void*) end); return(NULL); } if (UNIV_UNLIKELY((val >> 1) > n_dense)) { page_zip_fail(ib_stream, "page_zip_apply_log: %lu>>1 > %lu\n", (ulong) val, (ulong) n_dense); return(NULL); } /* Determine the heap number and status bits of the record. */ rec = recs[(val >> 1) - 1]; hs = ((val >> 1) + 1) << REC_HEAP_NO_SHIFT; hs |= heap_status & ((1 << REC_HEAP_NO_SHIFT) - 1); /* This may either be an old record that is being overwritten (updated in place, or allocated from the free list), or a new record, with the next available_heap_no. */ if (UNIV_UNLIKELY(hs > heap_status)) { page_zip_fail(ib_stream, "page_zip_apply_log: %lu > %lu\n", (ulong) hs, (ulong) heap_status); return(NULL); } else if (hs == heap_status) { /* A new record was allocated from the heap. */ if (UNIV_UNLIKELY(val & 1)) { /* Only existing records may be cleared. */ page_zip_fail(ib_stream, "page_zip_apply_log:" " attempting to create" " deleted rec %lu\n", (ulong) hs); return(NULL); } heap_status += 1 << REC_HEAP_NO_SHIFT; } mach_write_to_2(rec - REC_NEW_HEAP_NO, hs); if (val & 1) { /* Clear the data bytes of the record. */ mem_heap_t* heap = NULL; ulint* offs; offs = rec_get_offsets(rec, index, offsets, ULINT_UNDEFINED, &heap); memset(rec, 0, rec_offs_data_size(offs)); if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } continue; } #if REC_STATUS_NODE_PTR != TRUE # error "REC_STATUS_NODE_PTR != TRUE" #endif rec_get_offsets_reverse(data, index, hs & REC_STATUS_NODE_PTR, offsets); rec_offs_make_valid(rec, index, offsets); /* Copy the extra bytes (backwards). */ { byte* start = rec_get_start(rec, offsets); byte* b = rec - REC_N_NEW_EXTRA_BYTES; while (b != start) { *--b = *data++; } } /* Copy the data bytes. */ if (UNIV_UNLIKELY(rec_offs_any_extern(offsets))) { /* Non-leaf nodes should not contain any externally stored columns. */ if (UNIV_UNLIKELY(hs & REC_STATUS_NODE_PTR)) { page_zip_fail(ib_stream, "page_zip_apply_log: " "%lu&REC_STATUS_NODE_PTR\n", (ulong) hs); return(NULL); } data = page_zip_apply_log_ext( rec, offsets, trx_id_col, data, end); if (UNIV_UNLIKELY(!data)) { return(NULL); } } else if (UNIV_UNLIKELY(hs & REC_STATUS_NODE_PTR)) { len = rec_offs_data_size(offsets) - REC_NODE_PTR_SIZE; /* Copy the data bytes, except node_ptr. */ if (UNIV_UNLIKELY(data + len >= end)) { page_zip_fail(ib_stream, "page_zip_apply_log: " "node_ptr %p+%lu >= %p\n", (const void*) data, (ulong) len, (const void*) end); return(NULL); } memcpy(rec, data, len); data += len; } else if (UNIV_LIKELY(trx_id_col == ULINT_UNDEFINED)) { len = rec_offs_data_size(offsets); /* Copy all data bytes of a record in a secondary index. */ if (UNIV_UNLIKELY(data + len >= end)) { page_zip_fail(ib_stream, "page_zip_apply_log: " "sec %p+%lu >= %p\n", (const void*) data, (ulong) len, (const void*) end); return(NULL); } memcpy(rec, data, len); data += len; } else { /* Skip DB_TRX_ID and DB_ROLL_PTR. */ ulint l = rec_get_nth_field_offs(offsets, trx_id_col, &len); byte* b; if (UNIV_UNLIKELY(data + l >= end) || UNIV_UNLIKELY(len < (DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN))) { page_zip_fail(ib_stream, "page_zip_apply_log: " "trx_id %p+%lu >= %p\n", (const void*) data, (ulong) l, (const void*) end); return(NULL); } /* Copy any preceding data bytes. */ memcpy(rec, data, l); data += l; /* Copy any bytes following DB_TRX_ID, DB_ROLL_PTR. */ b = rec + l + (DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN); len = rec_get_end(rec, offsets) - b; if (UNIV_UNLIKELY(data + len >= end)) { page_zip_fail(ib_stream, "page_zip_apply_log: " "clust %p+%lu >= %p\n", (const void*) data, (ulong) len, (const void*) end); return(NULL); } memcpy(b, data, len); data += len; } } } /**********************************************************************//** Decompress the records of a node pointer page. @return TRUE on success, FALSE on failure */ static ibool page_zip_decompress_node_ptrs( /*==========================*/ page_zip_des_t* page_zip, /*!< in/out: compressed page */ z_stream* d_stream, /*!< in/out: compressed page stream */ rec_t** recs, /*!< in: dense page directory sorted by address */ ulint n_dense, /*!< in: size of recs[] */ dict_index_t* index, /*!< in: the index of the page */ ulint* offsets, /*!< in/out: temporary offsets */ mem_heap_t* heap) /*!< in: temporary memory heap */ { ulint heap_status = REC_STATUS_NODE_PTR | PAGE_HEAP_NO_USER_LOW << REC_HEAP_NO_SHIFT; ulint slot; const byte* storage; /* Subtract the space reserved for uncompressed data. */ d_stream->avail_in -= n_dense * (PAGE_ZIP_DIR_SLOT_SIZE + REC_NODE_PTR_SIZE); /* Decompress the records in heap_no order. */ for (slot = 0; slot < n_dense; slot++) { rec_t* rec = recs[slot]; d_stream->avail_out = rec - REC_N_NEW_EXTRA_BYTES - d_stream->next_out; ut_ad(d_stream->avail_out < UNIV_PAGE_SIZE - PAGE_ZIP_START - PAGE_DIR); switch (inflate(d_stream, Z_SYNC_FLUSH)) { case Z_STREAM_END: /* Apparently, n_dense has grown since the time the page was last compressed. */ goto zlib_done; case Z_OK: case Z_BUF_ERROR: if (!d_stream->avail_out) { break; } /* fall through */ default: page_zip_fail(ib_stream, "page_zip_decompress_node_ptrs:" " 1 inflate(Z_SYNC_FLUSH)=%s\n", d_stream->msg); goto zlib_error; } ut_ad(d_stream->next_out == rec - REC_N_NEW_EXTRA_BYTES); /* Prepare to decompress the data bytes. */ d_stream->next_out = rec; /* Set heap_no and the status bits. */ mach_write_to_2(rec - REC_NEW_HEAP_NO, heap_status); heap_status += 1 << REC_HEAP_NO_SHIFT; /* Read the offsets. The status bits are needed here. */ offsets = rec_get_offsets(rec, index, offsets, ULINT_UNDEFINED, &heap); /* Non-leaf nodes should not have any externally stored columns. */ ut_ad(!rec_offs_any_extern(offsets)); /* Decompress the data bytes, except node_ptr. */ d_stream->avail_out = rec_offs_data_size(offsets) - REC_NODE_PTR_SIZE; switch (inflate(d_stream, Z_SYNC_FLUSH)) { case Z_STREAM_END: goto zlib_done; case Z_OK: case Z_BUF_ERROR: if (!d_stream->avail_out) { break; } /* fall through */ default: page_zip_fail(ib_stream, "page_zip_decompress_node_ptrs:" " 2 inflate(Z_SYNC_FLUSH)=%s\n", d_stream->msg); goto zlib_error; } /* Clear the node pointer in case the record will be deleted and the space will be reallocated to a smaller record. */ memset(d_stream->next_out, 0, REC_NODE_PTR_SIZE); d_stream->next_out += REC_NODE_PTR_SIZE; ut_ad(d_stream->next_out == rec_get_end(rec, offsets)); } /* Decompress any trailing garbage, in case the last record was allocated from an originally longer space on the free list. */ d_stream->avail_out = page_header_get_field(page_zip->data, PAGE_HEAP_TOP) - page_offset(d_stream->next_out); if (UNIV_UNLIKELY(d_stream->avail_out > UNIV_PAGE_SIZE - PAGE_ZIP_START - PAGE_DIR)) { page_zip_fail(ib_stream, "page_zip_decompress_node_ptrs:" " avail_out = %u\n", d_stream->avail_out); goto zlib_error; } if (UNIV_UNLIKELY(inflate(d_stream, Z_FINISH) != Z_STREAM_END)) { page_zip_fail(ib_stream, "page_zip_decompress_node_ptrs:" " inflate(Z_FINISH)=%s\n", d_stream->msg); zlib_error: inflateEnd(d_stream); return(FALSE); } /* Note that d_stream->avail_out > 0 may hold here if the modification log is nonempty. */ zlib_done: if (UNIV_UNLIKELY(inflateEnd(d_stream) != Z_OK)) { ut_error; } { page_t* page = page_align(d_stream->next_out); /* Clear the unused heap space on the uncompressed page. */ memset(d_stream->next_out, 0, page_dir_get_nth_slot(page, page_dir_get_n_slots(page) - 1) - d_stream->next_out); } #ifdef UNIV_DEBUG page_zip->m_start = PAGE_DATA + d_stream->total_in; #endif /* UNIV_DEBUG */ /* Apply the modification log. */ { const byte* mod_log_ptr; mod_log_ptr = page_zip_apply_log(d_stream->next_in, d_stream->avail_in + 1, recs, n_dense, ULINT_UNDEFINED, heap_status, index, offsets); if (UNIV_UNLIKELY(!mod_log_ptr)) { return(FALSE); } page_zip->m_end = mod_log_ptr - page_zip->data; page_zip->m_nonempty = mod_log_ptr != d_stream->next_in; } if (UNIV_UNLIKELY (page_zip_get_trailer_len(page_zip, dict_index_is_clust(index), NULL) + page_zip->m_end >= page_zip_get_size(page_zip))) { page_zip_fail(ib_stream, "page_zip_decompress_node_ptrs:" " %lu + %lu >= %lu, %lu\n", (ulong) page_zip_get_trailer_len( page_zip, dict_index_is_clust(index), NULL), (ulong) page_zip->m_end, (ulong) page_zip_get_size(page_zip), (ulong) dict_index_is_clust(index)); return(FALSE); } /* Restore the uncompressed columns in heap_no order. */ storage = page_zip->data + page_zip_get_size(page_zip) - n_dense * PAGE_ZIP_DIR_SLOT_SIZE; for (slot = 0; slot < n_dense; slot++) { rec_t* rec = recs[slot]; offsets = rec_get_offsets(rec, index, offsets, ULINT_UNDEFINED, &heap); /* Non-leaf nodes should not have any externally stored columns. */ ut_ad(!rec_offs_any_extern(offsets)); storage -= REC_NODE_PTR_SIZE; memcpy(rec_get_end(rec, offsets) - REC_NODE_PTR_SIZE, storage, REC_NODE_PTR_SIZE); } return(TRUE); } /**********************************************************************//** Decompress the records of a leaf node of a secondary index. @return TRUE on success, FALSE on failure */ static ibool page_zip_decompress_sec( /*====================*/ page_zip_des_t* page_zip, /*!< in/out: compressed page */ z_stream* d_stream, /*!< in/out: compressed page stream */ rec_t** recs, /*!< in: dense page directory sorted by address */ ulint n_dense, /*!< in: size of recs[] */ dict_index_t* index, /*!< in: the index of the page */ ulint* offsets) /*!< in/out: temporary offsets */ { ulint heap_status = REC_STATUS_ORDINARY | PAGE_HEAP_NO_USER_LOW << REC_HEAP_NO_SHIFT; ulint slot; ut_a(!dict_index_is_clust(index)); /* Subtract the space reserved for uncompressed data. */ d_stream->avail_in -= n_dense * PAGE_ZIP_DIR_SLOT_SIZE; for (slot = 0; slot < n_dense; slot++) { rec_t* rec = recs[slot]; /* Decompress everything up to this record. */ d_stream->avail_out = rec - REC_N_NEW_EXTRA_BYTES - d_stream->next_out; if (UNIV_LIKELY(d_stream->avail_out)) { switch (inflate(d_stream, Z_SYNC_FLUSH)) { case Z_STREAM_END: /* Apparently, n_dense has grown since the time the page was last compressed. */ goto zlib_done; case Z_OK: case Z_BUF_ERROR: if (!d_stream->avail_out) { break; } /* fall through */ default: page_zip_fail(ib_stream, "page_zip_decompress_sec:" " inflate(Z_SYNC_FLUSH)=%s\n", d_stream->msg); goto zlib_error; } } ut_ad(d_stream->next_out == rec - REC_N_NEW_EXTRA_BYTES); /* Skip the REC_N_NEW_EXTRA_BYTES. */ d_stream->next_out = rec; /* Set heap_no and the status bits. */ mach_write_to_2(rec - REC_NEW_HEAP_NO, heap_status); heap_status += 1 << REC_HEAP_NO_SHIFT; } /* Decompress the data of the last record and any trailing garbage, in case the last record was allocated from an originally longer space on the free list. */ d_stream->avail_out = page_header_get_field(page_zip->data, PAGE_HEAP_TOP) - page_offset(d_stream->next_out); if (UNIV_UNLIKELY(d_stream->avail_out > UNIV_PAGE_SIZE - PAGE_ZIP_START - PAGE_DIR)) { page_zip_fail(ib_stream, "page_zip_decompress_sec:" " avail_out = %u\n", d_stream->avail_out); goto zlib_error; } if (UNIV_UNLIKELY(inflate(d_stream, Z_FINISH) != Z_STREAM_END)) { page_zip_fail(ib_stream, "page_zip_decompress_sec:" " inflate(Z_FINISH)=%s\n", d_stream->msg); zlib_error: inflateEnd(d_stream); return(FALSE); } /* Note that d_stream->avail_out > 0 may hold here if the modification log is nonempty. */ zlib_done: if (UNIV_UNLIKELY(inflateEnd(d_stream) != Z_OK)) { ut_error; } { page_t* page = page_align(d_stream->next_out); /* Clear the unused heap space on the uncompressed page. */ memset(d_stream->next_out, 0, page_dir_get_nth_slot(page, page_dir_get_n_slots(page) - 1) - d_stream->next_out); } #ifdef UNIV_DEBUG page_zip->m_start = PAGE_DATA + d_stream->total_in; #endif /* UNIV_DEBUG */ /* Apply the modification log. */ { const byte* mod_log_ptr; mod_log_ptr = page_zip_apply_log(d_stream->next_in, d_stream->avail_in + 1, recs, n_dense, ULINT_UNDEFINED, heap_status, index, offsets); if (UNIV_UNLIKELY(!mod_log_ptr)) { return(FALSE); } page_zip->m_end = mod_log_ptr - page_zip->data; page_zip->m_nonempty = mod_log_ptr != d_stream->next_in; } if (UNIV_UNLIKELY(page_zip_get_trailer_len(page_zip, FALSE, NULL) + page_zip->m_end >= page_zip_get_size(page_zip))) { page_zip_fail(ib_stream, "page_zip_decompress_sec: %lu + %lu >= %lu\n", (ulong) page_zip_get_trailer_len( page_zip, FALSE, NULL), (ulong) page_zip->m_end, (ulong) page_zip_get_size(page_zip)); return(FALSE); } /* There are no uncompressed columns on leaf pages of secondary indexes. */ return(TRUE); } /**********************************************************************//** Decompress a record of a leaf node of a clustered index that contains externally stored columns. @return TRUE on success */ static ibool page_zip_decompress_clust_ext( /*==========================*/ z_stream* d_stream, /*!< in/out: compressed page stream */ rec_t* rec, /*!< in/out: record */ const ulint* offsets, /*!< in: rec_get_offsets(rec) */ ulint trx_id_col) /*!< in: position of of DB_TRX_ID */ { ulint i; for (i = 0; i < rec_offs_n_fields(offsets); i++) { ulint len; byte* dst; if (UNIV_UNLIKELY(i == trx_id_col)) { /* Skip trx_id and roll_ptr */ dst = rec_get_nth_field(rec, offsets, i, &len); if (UNIV_UNLIKELY(len < DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN)) { page_zip_fail(ib_stream, "page_zip_decompress_clust_ext:" " len[%lu] = %lu\n", (ulong) i, (ulong) len); return(FALSE); } if (rec_offs_nth_extern(offsets, i)) { page_zip_fail(ib_stream, "page_zip_decompress_clust_ext:" " DB_TRX_ID at %lu is ext\n", (ulong) i); return(FALSE); } d_stream->avail_out = dst - d_stream->next_out; switch (inflate(d_stream, Z_SYNC_FLUSH)) { case Z_STREAM_END: case Z_OK: case Z_BUF_ERROR: if (!d_stream->avail_out) { break; } /* fall through */ default: page_zip_fail(ib_stream, "page_zip_decompress_clust_ext:" " 1 inflate(Z_SYNC_FLUSH)=%s\n", d_stream->msg); return(FALSE); } ut_ad(d_stream->next_out == dst); /* Clear DB_TRX_ID and DB_ROLL_PTR in order to avoid uninitialized bytes in case the record is affected by page_zip_apply_log(). */ memset(dst, 0, DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN); d_stream->next_out += DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN; } else if (rec_offs_nth_extern(offsets, i)) { dst = rec_get_nth_field(rec, offsets, i, &len); ut_ad(len >= BTR_EXTERN_FIELD_REF_SIZE); dst += len - BTR_EXTERN_FIELD_REF_SIZE; d_stream->avail_out = dst - d_stream->next_out; switch (inflate(d_stream, Z_SYNC_FLUSH)) { case Z_STREAM_END: case Z_OK: case Z_BUF_ERROR: if (!d_stream->avail_out) { break; } /* fall through */ default: page_zip_fail(ib_stream, "page_zip_decompress_clust_ext:" " 2 inflate(Z_SYNC_FLUSH)=%s\n", d_stream->msg); return(FALSE); } ut_ad(d_stream->next_out == dst); /* Clear the BLOB pointer in case the record will be deleted and the space will not be reused. Note that the final initialization of the BLOB pointers (copying from "externs" or clearing) will have to take place only after the page modification log has been applied. Otherwise, we could end up with an uninitialized BLOB pointer when a record is deleted, reallocated and deleted. */ memset(d_stream->next_out, 0, BTR_EXTERN_FIELD_REF_SIZE); d_stream->next_out += BTR_EXTERN_FIELD_REF_SIZE; } } return(TRUE); } /**********************************************************************//** Compress the records of a leaf node of a clustered index. @return TRUE on success, FALSE on failure */ static ibool page_zip_decompress_clust( /*======================*/ page_zip_des_t* page_zip, /*!< in/out: compressed page */ z_stream* d_stream, /*!< in/out: compressed page stream */ rec_t** recs, /*!< in: dense page directory sorted by address */ ulint n_dense, /*!< in: size of recs[] */ dict_index_t* index, /*!< in: the index of the page */ ulint trx_id_col, /*!< index of the trx_id column */ ulint* offsets, /*!< in/out: temporary offsets */ mem_heap_t* heap) /*!< in: temporary memory heap */ { int err; ulint slot; ulint heap_status = REC_STATUS_ORDINARY | PAGE_HEAP_NO_USER_LOW << REC_HEAP_NO_SHIFT; const byte* storage; const byte* externs; ut_a(dict_index_is_clust(index)); /* Subtract the space reserved for uncompressed data. */ d_stream->avail_in -= n_dense * (PAGE_ZIP_DIR_SLOT_SIZE + DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN); /* Decompress the records in heap_no order. */ for (slot = 0; slot < n_dense; slot++) { rec_t* rec = recs[slot]; d_stream->avail_out = rec - REC_N_NEW_EXTRA_BYTES - d_stream->next_out; ut_ad(d_stream->avail_out < UNIV_PAGE_SIZE - PAGE_ZIP_START - PAGE_DIR); err = inflate(d_stream, Z_SYNC_FLUSH); switch (err) { case Z_STREAM_END: /* Apparently, n_dense has grown since the time the page was last compressed. */ goto zlib_done; case Z_OK: case Z_BUF_ERROR: if (UNIV_LIKELY(!d_stream->avail_out)) { break; } /* fall through */ default: page_zip_fail(ib_stream, "page_zip_decompress_clust:" " 1 inflate(Z_SYNC_FLUSH)=%s\n", d_stream->msg); goto zlib_error; } ut_ad(d_stream->next_out == rec - REC_N_NEW_EXTRA_BYTES); /* Prepare to decompress the data bytes. */ d_stream->next_out = rec; /* Set heap_no and the status bits. */ mach_write_to_2(rec - REC_NEW_HEAP_NO, heap_status); heap_status += 1 << REC_HEAP_NO_SHIFT; /* Read the offsets. The status bits are needed here. */ offsets = rec_get_offsets(rec, index, offsets, ULINT_UNDEFINED, &heap); /* This is a leaf page in a clustered index. */ /* Check if there are any externally stored columns. For each externally stored column, restore the BTR_EXTERN_FIELD_REF separately. */ if (UNIV_UNLIKELY(rec_offs_any_extern(offsets))) { if (UNIV_UNLIKELY (!page_zip_decompress_clust_ext( d_stream, rec, offsets, trx_id_col))) { goto zlib_error; } } else { /* Skip trx_id and roll_ptr */ ulint len; byte* dst = rec_get_nth_field(rec, offsets, trx_id_col, &len); if (UNIV_UNLIKELY(len < DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN)) { page_zip_fail(ib_stream, "page_zip_decompress_clust:" " len = %lu\n", (ulong) len); goto zlib_error; } d_stream->avail_out = dst - d_stream->next_out; switch (inflate(d_stream, Z_SYNC_FLUSH)) { case Z_STREAM_END: case Z_OK: case Z_BUF_ERROR: if (!d_stream->avail_out) { break; } /* fall through */ default: page_zip_fail(ib_stream, "page_zip_decompress_clust:" " 2 inflate(Z_SYNC_FLUSH)=%s\n", d_stream->msg); goto zlib_error; } ut_ad(d_stream->next_out == dst); /* Clear DB_TRX_ID and DB_ROLL_PTR in order to avoid uninitialized bytes in case the record is affected by page_zip_apply_log(). */ memset(dst, 0, DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN); d_stream->next_out += DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN; } /* Decompress the last bytes of the record. */ d_stream->avail_out = rec_get_end(rec, offsets) - d_stream->next_out; switch (inflate(d_stream, Z_SYNC_FLUSH)) { case Z_STREAM_END: case Z_OK: case Z_BUF_ERROR: if (!d_stream->avail_out) { break; } /* fall through */ default: page_zip_fail(ib_stream, "page_zip_decompress_clust:" " 3 inflate(Z_SYNC_FLUSH)=%s\n", d_stream->msg); goto zlib_error; } } /* Decompress any trailing garbage, in case the last record was allocated from an originally longer space on the free list. */ d_stream->avail_out = page_header_get_field(page_zip->data, PAGE_HEAP_TOP) - page_offset(d_stream->next_out); if (UNIV_UNLIKELY(d_stream->avail_out > UNIV_PAGE_SIZE - PAGE_ZIP_START - PAGE_DIR)) { page_zip_fail(ib_stream, "page_zip_decompress_clust:" " avail_out = %u\n", d_stream->avail_out); goto zlib_error; } if (UNIV_UNLIKELY(inflate(d_stream, Z_FINISH) != Z_STREAM_END)) { page_zip_fail(ib_stream, "page_zip_decompress_clust:" " inflate(Z_FINISH)=%s\n", d_stream->msg); zlib_error: inflateEnd(d_stream); return(FALSE); } /* Note that d_stream->avail_out > 0 may hold here if the modification log is nonempty. */ zlib_done: if (UNIV_UNLIKELY(inflateEnd(d_stream) != Z_OK)) { ut_error; } { page_t* page = page_align(d_stream->next_out); /* Clear the unused heap space on the uncompressed page. */ memset(d_stream->next_out, 0, page_dir_get_nth_slot(page, page_dir_get_n_slots(page) - 1) - d_stream->next_out); } #ifdef UNIV_DEBUG page_zip->m_start = PAGE_DATA + d_stream->total_in; #endif /* UNIV_DEBUG */ /* Apply the modification log. */ { const byte* mod_log_ptr; mod_log_ptr = page_zip_apply_log(d_stream->next_in, d_stream->avail_in + 1, recs, n_dense, trx_id_col, heap_status, index, offsets); if (UNIV_UNLIKELY(!mod_log_ptr)) { return(FALSE); } page_zip->m_end = mod_log_ptr - page_zip->data; page_zip->m_nonempty = mod_log_ptr != d_stream->next_in; } if (UNIV_UNLIKELY(page_zip_get_trailer_len(page_zip, TRUE, NULL) + page_zip->m_end >= page_zip_get_size(page_zip))) { page_zip_fail(ib_stream, "page_zip_decompress_clust: %lu + %lu >= %lu\n", (ulong) page_zip_get_trailer_len( page_zip, TRUE, NULL), (ulong) page_zip->m_end, (ulong) page_zip_get_size(page_zip)); return(FALSE); } storage = page_zip->data + page_zip_get_size(page_zip) - n_dense * PAGE_ZIP_DIR_SLOT_SIZE; externs = storage - n_dense * (DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN); /* Restore the uncompressed columns in heap_no order. */ for (slot = 0; slot < n_dense; slot++) { ulint i; ulint len; byte* dst; rec_t* rec = recs[slot]; ibool exists = !page_zip_dir_find_free( page_zip, page_offset(rec)); offsets = rec_get_offsets(rec, index, offsets, ULINT_UNDEFINED, &heap); dst = rec_get_nth_field(rec, offsets, trx_id_col, &len); ut_ad(len >= DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN); storage -= DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN; memcpy(dst, storage, DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN); /* Check if there are any externally stored columns in this record. For each externally stored column, restore or clear the BTR_EXTERN_FIELD_REF. */ if (!rec_offs_any_extern(offsets)) { continue; } for (i = 0; i < rec_offs_n_fields(offsets); i++) { if (!rec_offs_nth_extern(offsets, i)) { continue; } dst = rec_get_nth_field(rec, offsets, i, &len); if (UNIV_UNLIKELY(len < BTR_EXTERN_FIELD_REF_SIZE)) { page_zip_fail(ib_stream, "page_zip_decompress_clust:" " %lu < 20\n", (ulong) len); return(FALSE); } dst += len - BTR_EXTERN_FIELD_REF_SIZE; if (UNIV_LIKELY(exists)) { /* Existing record: restore the BLOB pointer */ externs -= BTR_EXTERN_FIELD_REF_SIZE; if (UNIV_UNLIKELY (externs < page_zip->data + page_zip->m_end)) { page_zip_fail(ib_stream, "page_zip_" "decompress_clust: " "%p < %p + %lu\n", (const void*) externs, (const void*) page_zip->data, (ulong) page_zip->m_end); return(FALSE); } memcpy(dst, externs, BTR_EXTERN_FIELD_REF_SIZE); page_zip->n_blobs++; } else { /* Deleted record: clear the BLOB pointer */ memset(dst, 0, BTR_EXTERN_FIELD_REF_SIZE); } } } return(TRUE); } /**********************************************************************//** Decompress a page. This function should tolerate errors on the compressed page. Instead of letting assertions fail, it will return FALSE if an inconsistency is detected. @return TRUE on success, FALSE on failure */ UNIV_INTERN ibool page_zip_decompress( /*================*/ page_zip_des_t* page_zip,/*!< in: data, ssize; out: m_start, m_end, m_nonempty, n_blobs */ page_t* page, /*!< out: uncompressed page, may be trashed */ ibool all) /*!< in: TRUE=decompress the whole page; FALSE=verify but do not copy some page header fields that should not change after page creation */ { z_stream d_stream; dict_index_t* index = NULL; rec_t** recs; /*!< dense page directory, sorted by address */ ulint n_dense;/* number of user records on the page */ ulint trx_id_col = ULINT_UNDEFINED; mem_heap_t* heap; ulint* offsets; #ifndef UNIV_HOTBACKUP ib_uint64_t usec = ut_time_us(NULL); #endif /* !UNIV_HOTBACKUP */ ut_ad(page_zip_simple_validate(page_zip)); UNIV_MEM_ASSERT_W(page, UNIV_PAGE_SIZE); UNIV_MEM_ASSERT_RW(page_zip->data, page_zip_get_size(page_zip)); /* The dense directory excludes the infimum and supremum records. */ n_dense = page_dir_get_n_heap(page_zip->data) - PAGE_HEAP_NO_USER_LOW; if (UNIV_UNLIKELY(n_dense * PAGE_ZIP_DIR_SLOT_SIZE >= page_zip_get_size(page_zip))) { page_zip_fail(ib_stream, "page_zip_decompress 1: %lu %lu\n", (ulong) n_dense, (ulong) page_zip_get_size(page_zip)); return(FALSE); } heap = mem_heap_create(n_dense * (3 * sizeof *recs) + UNIV_PAGE_SIZE); recs = mem_heap_alloc(heap, n_dense * (2 * sizeof *recs)); if (all) { /* Copy the page header. */ memcpy(page, page_zip->data, PAGE_DATA); } else { /* Check that the bytes that we skip are identical. */ #if defined UNIV_DEBUG || defined UNIV_ZIP_DEBUG ut_a(!memcmp(FIL_PAGE_TYPE + page, FIL_PAGE_TYPE + page_zip->data, PAGE_HEADER - FIL_PAGE_TYPE)); ut_a(!memcmp(PAGE_HEADER + PAGE_LEVEL + page, PAGE_HEADER + PAGE_LEVEL + page_zip->data, PAGE_DATA - (PAGE_HEADER + PAGE_LEVEL))); #endif /* UNIV_DEBUG || UNIV_ZIP_DEBUG */ /* Copy the mutable parts of the page header. */ memcpy(page, page_zip->data, FIL_PAGE_TYPE); memcpy(PAGE_HEADER + page, PAGE_HEADER + page_zip->data, PAGE_LEVEL - PAGE_N_DIR_SLOTS); #if defined UNIV_DEBUG || defined UNIV_ZIP_DEBUG /* Check that the page headers match after copying. */ ut_a(!memcmp(page, page_zip->data, PAGE_DATA)); #endif /* UNIV_DEBUG || UNIV_ZIP_DEBUG */ } #ifdef UNIV_ZIP_DEBUG /* Clear the uncompressed page, except the header. */ memset(PAGE_DATA + page, 0x55, UNIV_PAGE_SIZE - PAGE_DATA); #endif /* UNIV_ZIP_DEBUG */ UNIV_MEM_INVALID(PAGE_DATA + page, UNIV_PAGE_SIZE - PAGE_DATA); /* Copy the page directory. */ if (UNIV_UNLIKELY(!page_zip_dir_decode(page_zip, page, recs, recs + n_dense, n_dense))) { zlib_error: mem_heap_free(heap); return(FALSE); } /* Copy the infimum and supremum records. */ memcpy(page + (PAGE_NEW_INFIMUM - REC_N_NEW_EXTRA_BYTES), infimum_extra, sizeof infimum_extra); if (UNIV_UNLIKELY(!page_get_n_recs(page))) { rec_set_next_offs_new(page + PAGE_NEW_INFIMUM, PAGE_NEW_SUPREMUM); } else { rec_set_next_offs_new(page + PAGE_NEW_INFIMUM, page_zip_dir_get(page_zip, 0) & PAGE_ZIP_DIR_SLOT_MASK); } memcpy(page + PAGE_NEW_INFIMUM, infimum_data, sizeof infimum_data); memcpy(page + (PAGE_NEW_SUPREMUM - REC_N_NEW_EXTRA_BYTES + 1), supremum_extra_data, sizeof supremum_extra_data); page_zip_set_alloc(&d_stream, heap); if (UNIV_UNLIKELY(inflateInit2(&d_stream, UNIV_PAGE_SIZE_SHIFT) != Z_OK)) { ut_error; } d_stream.next_in = page_zip->data + PAGE_DATA; /* Subtract the space reserved for the page header and the end marker of the modification log. */ d_stream.avail_in = page_zip_get_size(page_zip) - (PAGE_DATA + 1); d_stream.next_out = page + PAGE_ZIP_START; d_stream.avail_out = UNIV_PAGE_SIZE - PAGE_ZIP_START; /* Decode the zlib header and the index information. */ if (UNIV_UNLIKELY(inflate(&d_stream, Z_BLOCK) != Z_OK)) { page_zip_fail(ib_stream, "page_zip_decompress:" " 1 inflate(Z_BLOCK)=%s\n", d_stream.msg); goto zlib_error; } if (UNIV_UNLIKELY(inflate(&d_stream, Z_BLOCK) != Z_OK)) { page_zip_fail(ib_stream, "page_zip_decompress:" " 2 inflate(Z_BLOCK)=%s\n", d_stream.msg); goto zlib_error; } index = page_zip_fields_decode( page + PAGE_ZIP_START, d_stream.next_out, page_is_leaf(page) ? &trx_id_col : NULL); if (UNIV_UNLIKELY(!index)) { goto zlib_error; } /* Decompress the user records. */ page_zip->n_blobs = 0; d_stream.next_out = page + PAGE_ZIP_START; { /* Pre-allocate the offsets for rec_get_offsets_reverse(). */ ulint n = 1 + 1/* node ptr */ + REC_OFFS_HEADER_SIZE + dict_index_get_n_fields(index); offsets = mem_heap_alloc(heap, n * sizeof(ulint)); *offsets = n; } /* Decompress the records in heap_no order. */ if (!page_is_leaf(page)) { /* This is a node pointer page. */ ulint info_bits; if (UNIV_UNLIKELY (!page_zip_decompress_node_ptrs(page_zip, &d_stream, recs, n_dense, index, offsets, heap))) { goto err_exit; } info_bits = mach_read_from_4(page + FIL_PAGE_PREV) == FIL_NULL ? REC_INFO_MIN_REC_FLAG : 0; if (UNIV_UNLIKELY(!page_zip_set_extra_bytes(page_zip, page, info_bits))) { goto err_exit; } } else if (UNIV_LIKELY(trx_id_col == ULINT_UNDEFINED)) { /* This is a leaf page in a secondary index. */ if (UNIV_UNLIKELY(!page_zip_decompress_sec(page_zip, &d_stream, recs, n_dense, index, offsets))) { goto err_exit; } if (UNIV_UNLIKELY(!page_zip_set_extra_bytes(page_zip, page, 0))) { err_exit: page_zip_fields_free(index); mem_heap_free(heap); return(FALSE); } } else { /* This is a leaf page in a clustered index. */ if (UNIV_UNLIKELY(!page_zip_decompress_clust(page_zip, &d_stream, recs, n_dense, index, trx_id_col, offsets, heap))) { goto err_exit; } if (UNIV_UNLIKELY(!page_zip_set_extra_bytes(page_zip, page, 0))) { goto err_exit; } } ut_a(page_is_comp(page)); UNIV_MEM_ASSERT_RW(page, UNIV_PAGE_SIZE); page_zip_fields_free(index); mem_heap_free(heap); #ifndef UNIV_HOTBACKUP { page_zip_stat_t* zip_stat = &page_zip_stat[page_zip->ssize - 1]; zip_stat->decompressed++; zip_stat->decompressed_usec += ut_time_us(NULL) - usec; } #endif /* !UNIV_HOTBACKUP */ /* Update the stat counter for LRU policy. */ buf_LRU_stat_inc_unzip(); return(TRUE); } #ifdef UNIV_ZIP_DEBUG /**********************************************************************//** Dump a block of memory on the standard error stream. */ static void page_zip_hexdump_func( /*==================*/ const char* name, /*!< in: name of the data structure */ const void* buf, /*!< in: data */ ulint size) /*!< in: length of the data, in bytes */ { const byte* s = buf; ulint addr; const ulint width = 32; /* bytes per line */ ib_logger(ib_stream, "%s:\n", name); for (addr = 0; addr < size; addr += width) { ulint i; ib_logger(ib_stream, "%04lx ", (ulong) addr); i = ut_min(width, size - addr); while (i--) { ib_logger(ib_stream, "%02x", *s++); } ib_logger(ib_stream, "\n"); } } /** Dump a block of memory on the standard error stream. @param buf in: data @param size in: length of the data, in bytes */ #define page_zip_hexdump(buf, size) page_zip_hexdump_func(#buf, buf, size) /** Flag: make page_zip_validate() compare page headers only */ UNIV_INTERN ibool page_zip_validate_header_only = FALSE; /**********************************************************************//** Check that the compressed and decompressed pages match. @return TRUE if valid, FALSE if not */ UNIV_INTERN ibool page_zip_validate_low( /*==================*/ const page_zip_des_t* page_zip,/*!< in: compressed page */ const page_t* page, /*!< in: uncompressed page */ ibool sloppy) /*!< in: FALSE=strict, TRUE=ignore the MIN_REC_FLAG */ { page_zip_des_t temp_page_zip; byte* temp_page_buf; page_t* temp_page; ibool valid; if (memcmp(page_zip->data + FIL_PAGE_PREV, page + FIL_PAGE_PREV, FIL_PAGE_LSN - FIL_PAGE_PREV) || memcmp(page_zip->data + FIL_PAGE_TYPE, page + FIL_PAGE_TYPE, 2) || memcmp(page_zip->data + FIL_PAGE_DATA, page + FIL_PAGE_DATA, PAGE_DATA - FIL_PAGE_DATA)) { page_zip_fail(ib_stream, "page_zip_validate: page header\n"); page_zip_hexdump(page_zip, sizeof *page_zip); page_zip_hexdump(page_zip->data, page_zip_get_size(page_zip)); page_zip_hexdump(page, UNIV_PAGE_SIZE); return(FALSE); } ut_a(page_is_comp(page)); if (page_zip_validate_header_only) { return(TRUE); } /* page_zip_decompress() expects the uncompressed page to be UNIV_PAGE_SIZE aligned. */ temp_page_buf = ut_malloc(2 * UNIV_PAGE_SIZE); temp_page = ut_align(temp_page_buf, UNIV_PAGE_SIZE); #ifdef UNIV_DEBUG_VALGRIND /* Get detailed information on the valid bits in case the UNIV_MEM_ASSERT_RW() checks fail. The v-bits of page[], page_zip->data[] or page_zip could be viewed at temp_page[] or temp_page_zip in a debugger when running valgrind --db-attach. */ VALGRIND_GET_VBITS(page, temp_page, UNIV_PAGE_SIZE); UNIV_MEM_ASSERT_RW(page, UNIV_PAGE_SIZE); VALGRIND_GET_VBITS(page_zip, &temp_page_zip, sizeof temp_page_zip); UNIV_MEM_ASSERT_RW(page_zip, sizeof *page_zip); VALGRIND_GET_VBITS(page_zip->data, temp_page, page_zip_get_size(page_zip)); UNIV_MEM_ASSERT_RW(page_zip->data, page_zip_get_size(page_zip)); #endif /* UNIV_DEBUG_VALGRIND */ temp_page_zip = *page_zip; valid = page_zip_decompress(&temp_page_zip, temp_page, TRUE); if (!valid) { ib_logger(ib_stream, "page_zip_validate(): failed to decompress\n"); goto func_exit; } if (page_zip->n_blobs != temp_page_zip.n_blobs) { page_zip_fail(ib_stream, "page_zip_validate: n_blobs: %u!=%u\n", page_zip->n_blobs, temp_page_zip.n_blobs); valid = FALSE; } #ifdef UNIV_DEBUG if (page_zip->m_start != temp_page_zip.m_start) { page_zip_fail(ib_stream, "page_zip_validate: m_start: %u!=%u\n", page_zip->m_start, temp_page_zip.m_start); valid = FALSE; } #endif /* UNIV_DEBUG */ if (page_zip->m_end != temp_page_zip.m_end) { page_zip_fail(ib_stream, "page_zip_validate: m_end: %u!=%u\n", page_zip->m_end, temp_page_zip.m_end); valid = FALSE; } if (page_zip->m_nonempty != temp_page_zip.m_nonempty) { page_zip_fail(ib_stream, "page_zip_validate(): m_nonempty: %u!=%u\n", page_zip->m_nonempty, temp_page_zip.m_nonempty); valid = FALSE; } if (memcmp(page + PAGE_HEADER, temp_page + PAGE_HEADER, UNIV_PAGE_SIZE - PAGE_HEADER - FIL_PAGE_DATA_END)) { /* In crash recovery, the "minimum record" flag may be set incorrectly until the mini-transaction is committed. Let us tolerate that difference when we are performing a sloppy validation. */ if (sloppy) { byte info_bits_diff; ulint offset = rec_get_next_offs(page + PAGE_NEW_INFIMUM, TRUE); ut_a(offset >= PAGE_NEW_SUPREMUM); offset -= 5 /* REC_NEW_INFO_BITS */; info_bits_diff = page[offset] ^ temp_page[offset]; if (info_bits_diff == REC_INFO_MIN_REC_FLAG) { temp_page[offset] = page[offset]; if (!memcmp(page + PAGE_HEADER, temp_page + PAGE_HEADER, UNIV_PAGE_SIZE - PAGE_HEADER - FIL_PAGE_DATA_END)) { /* Only the minimum record flag differed. Let us ignore it. */ page_zip_fail(ib_stream, "page_zip_validate: " "min_rec_flag " "(ignored, " "%lu,%lu,0x%02lx)\n", page_get_space_id(page), page_get_page_no(page), (ulong) page[offset]); goto func_exit; } } } page_zip_fail(ib_stream, "page_zip_validate: content\n"); valid = FALSE; } func_exit: if (!valid) { page_zip_hexdump(page_zip, sizeof *page_zip); page_zip_hexdump(page_zip->data, page_zip_get_size(page_zip)); page_zip_hexdump(page, UNIV_PAGE_SIZE); page_zip_hexdump(temp_page, UNIV_PAGE_SIZE); } ut_free(temp_page_buf); return(valid); } /**********************************************************************//** Check that the compressed and decompressed pages match. @return TRUE if valid, FALSE if not */ UNIV_INTERN ibool page_zip_validate( /*==============*/ const page_zip_des_t* page_zip,/*!< in: compressed page */ const page_t* page) /*!< in: uncompressed page */ { return(page_zip_validate_low(page_zip, page, recv_recovery_is_on())); } #endif /* UNIV_ZIP_DEBUG */ #ifdef UNIV_DEBUG /**********************************************************************//** Assert that the compressed and decompressed page headers match. @return TRUE */ static ibool page_zip_header_cmp( /*================*/ const page_zip_des_t* page_zip,/*!< in: compressed page */ const byte* page) /*!< in: uncompressed page */ { ut_ad(!memcmp(page_zip->data + FIL_PAGE_PREV, page + FIL_PAGE_PREV, FIL_PAGE_LSN - FIL_PAGE_PREV)); ut_ad(!memcmp(page_zip->data + FIL_PAGE_TYPE, page + FIL_PAGE_TYPE, 2)); ut_ad(!memcmp(page_zip->data + FIL_PAGE_DATA, page + FIL_PAGE_DATA, PAGE_DATA - FIL_PAGE_DATA)); return(TRUE); } #endif /* UNIV_DEBUG */ /**********************************************************************//** Write a record on the compressed page that contains externally stored columns. The data must already have been written to the uncompressed page. @return end of modification log */ static byte* page_zip_write_rec_ext( /*===================*/ page_zip_des_t* page_zip, /*!< in/out: compressed page */ const page_t* page, /*!< in: page containing rec */ const byte* rec, /*!< in: record being written */ dict_index_t* index, /*!< in: record descriptor */ const ulint* offsets, /*!< in: rec_get_offsets(rec, index) */ ulint create, /*!< in: nonzero=insert, zero=update */ ulint trx_id_col, /*!< in: position of DB_TRX_ID */ ulint heap_no, /*!< in: heap number of rec */ byte* storage, /*!< in: end of dense page directory */ byte* data) /*!< in: end of modification log */ { const byte* start = rec; ulint i; ulint len; byte* externs = storage; ulint n_ext = rec_offs_n_extern(offsets); ut_ad(rec_offs_validate(rec, index, offsets)); UNIV_MEM_ASSERT_RW(rec, rec_offs_data_size(offsets)); UNIV_MEM_ASSERT_RW(rec - rec_offs_extra_size(offsets), rec_offs_extra_size(offsets)); externs -= (DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN) * (page_dir_get_n_heap(page) - PAGE_HEAP_NO_USER_LOW); /* Note that this will not take into account the BLOB columns of rec if create==TRUE. */ ut_ad(data + rec_offs_data_size(offsets) - (DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN) - n_ext * BTR_EXTERN_FIELD_REF_SIZE < externs - BTR_EXTERN_FIELD_REF_SIZE * page_zip->n_blobs); { ulint blob_no = page_zip_get_n_prev_extern( page_zip, rec, index); byte* ext_end = externs - page_zip->n_blobs * BTR_EXTERN_FIELD_REF_SIZE; ut_ad(blob_no <= page_zip->n_blobs); externs -= blob_no * BTR_EXTERN_FIELD_REF_SIZE; if (create) { page_zip->n_blobs += n_ext; ASSERT_ZERO_BLOB(ext_end - n_ext * BTR_EXTERN_FIELD_REF_SIZE); memmove(ext_end - n_ext * BTR_EXTERN_FIELD_REF_SIZE, ext_end, externs - ext_end); } ut_a(blob_no + n_ext <= page_zip->n_blobs); } for (i = 0; i < rec_offs_n_fields(offsets); i++) { const byte* src; if (UNIV_UNLIKELY(i == trx_id_col)) { ut_ad(!rec_offs_nth_extern(offsets, i)); ut_ad(!rec_offs_nth_extern(offsets, i + 1)); /* Locate trx_id and roll_ptr. */ src = rec_get_nth_field(rec, offsets, i, &len); ut_ad(len == DATA_TRX_ID_LEN); ut_ad(src + DATA_TRX_ID_LEN == rec_get_nth_field( rec, offsets, i + 1, &len)); ut_ad(len == DATA_ROLL_PTR_LEN); /* Log the preceding fields. */ ASSERT_ZERO(data, src - start); memcpy(data, start, src - start); data += src - start; start = src + (DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN); /* Store trx_id and roll_ptr. */ memcpy(storage - (DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN) * (heap_no - 1), src, DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN); i++; /* skip also roll_ptr */ } else if (rec_offs_nth_extern(offsets, i)) { src = rec_get_nth_field(rec, offsets, i, &len); ut_ad(dict_index_is_clust(index)); ut_ad(len >= BTR_EXTERN_FIELD_REF_SIZE); src += len - BTR_EXTERN_FIELD_REF_SIZE; ASSERT_ZERO(data, src - start); memcpy(data, start, src - start); data += src - start; start = src + BTR_EXTERN_FIELD_REF_SIZE; /* Store the BLOB pointer. */ externs -= BTR_EXTERN_FIELD_REF_SIZE; ut_ad(data < externs); memcpy(externs, src, BTR_EXTERN_FIELD_REF_SIZE); } } /* Log the last bytes of the record. */ len = rec_offs_data_size(offsets) - (start - rec); ASSERT_ZERO(data, len); memcpy(data, start, len); data += len; return(data); } /**********************************************************************//** Write an entire record on the compressed page. The data must already have been written to the uncompressed page. */ UNIV_INTERN void page_zip_write_rec( /*===============*/ page_zip_des_t* page_zip,/*!< in/out: compressed page */ const byte* rec, /*!< in: record being written */ dict_index_t* index, /*!< in: the index the record belongs to */ const ulint* offsets,/*!< in: rec_get_offsets(rec, index) */ ulint create) /*!< in: nonzero=insert, zero=update */ { const page_t* page; byte* data; byte* storage; ulint heap_no; byte* slot; ut_ad(PAGE_ZIP_MATCH(rec, page_zip)); ut_ad(page_zip_simple_validate(page_zip)); ut_ad(page_zip_get_size(page_zip) > PAGE_DATA + page_zip_dir_size(page_zip)); ut_ad(rec_offs_comp(offsets)); ut_ad(rec_offs_validate(rec, index, offsets)); ut_ad(page_zip->m_start >= PAGE_DATA); page = page_align(rec); ut_ad(page_zip_header_cmp(page_zip, page)); ut_ad(page_simple_validate_new((page_t*) page)); UNIV_MEM_ASSERT_RW(page_zip->data, page_zip_get_size(page_zip)); UNIV_MEM_ASSERT_RW(rec, rec_offs_data_size(offsets)); UNIV_MEM_ASSERT_RW(rec - rec_offs_extra_size(offsets), rec_offs_extra_size(offsets)); slot = page_zip_dir_find(page_zip, page_offset(rec)); ut_a(slot); /* Copy the delete mark. */ if (rec_get_deleted_flag(rec, TRUE)) { *slot |= PAGE_ZIP_DIR_SLOT_DEL >> 8; } else { *slot &= ~(PAGE_ZIP_DIR_SLOT_DEL >> 8); } ut_ad(rec_get_start((rec_t*) rec, offsets) >= page + PAGE_ZIP_START); ut_ad(rec_get_end((rec_t*) rec, offsets) <= page + UNIV_PAGE_SIZE - PAGE_DIR - PAGE_DIR_SLOT_SIZE * page_dir_get_n_slots(page)); heap_no = rec_get_heap_no_new(rec); ut_ad(heap_no >= PAGE_HEAP_NO_USER_LOW); /* not infimum or supremum */ ut_ad(heap_no < page_dir_get_n_heap(page)); /* Append to the modification log. */ data = page_zip->data + page_zip->m_end; ut_ad(!*data); /* Identify the record by writing its heap number - 1. 0 is reserved to indicate the end of the modification log. */ if (UNIV_UNLIKELY(heap_no - 1 >= 64)) { *data++ = (byte) (0x80 | (heap_no - 1) >> 7); ut_ad(!*data); } *data++ = (byte) ((heap_no - 1) << 1); ut_ad(!*data); { const byte* start = rec - rec_offs_extra_size(offsets); const byte* b = rec - REC_N_NEW_EXTRA_BYTES; /* Write the extra bytes backwards, so that rec_offs_extra_size() can be easily computed in page_zip_apply_log() by invoking rec_get_offsets_reverse(). */ while (b != start) { *data++ = *--b; ut_ad(!*data); } } /* Write the data bytes. Store the uncompressed bytes separately. */ storage = page_zip->data + page_zip_get_size(page_zip) - (page_dir_get_n_heap(page) - PAGE_HEAP_NO_USER_LOW) * PAGE_ZIP_DIR_SLOT_SIZE; if (page_is_leaf(page)) { ulint len; if (dict_index_is_clust(index)) { ulint trx_id_col; trx_id_col = dict_index_get_sys_col_pos(index, DATA_TRX_ID); ut_ad(trx_id_col != ULINT_UNDEFINED); /* Store separately trx_id, roll_ptr and the BTR_EXTERN_FIELD_REF of each BLOB column. */ if (rec_offs_any_extern(offsets)) { data = page_zip_write_rec_ext( page_zip, page, rec, index, offsets, create, trx_id_col, heap_no, storage, data); } else { /* Locate trx_id and roll_ptr. */ const byte* src = rec_get_nth_field(rec, offsets, trx_id_col, &len); ut_ad(len == DATA_TRX_ID_LEN); ut_ad(src + DATA_TRX_ID_LEN == rec_get_nth_field( rec, offsets, trx_id_col + 1, &len)); ut_ad(len == DATA_ROLL_PTR_LEN); /* Log the preceding fields. */ ASSERT_ZERO(data, src - rec); memcpy(data, rec, src - rec); data += src - rec; /* Store trx_id and roll_ptr. */ memcpy(storage - (DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN) * (heap_no - 1), src, DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN); src += DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN; /* Log the last bytes of the record. */ len = rec_offs_data_size(offsets) - (src - rec); ASSERT_ZERO(data, len); memcpy(data, src, len); data += len; } } else { /* Leaf page of a secondary index: no externally stored columns */ ut_ad(dict_index_get_sys_col_pos(index, DATA_TRX_ID) == ULINT_UNDEFINED); ut_ad(!rec_offs_any_extern(offsets)); /* Log the entire record. */ len = rec_offs_data_size(offsets); ASSERT_ZERO(data, len); memcpy(data, rec, len); data += len; } } else { /* This is a node pointer page. */ ulint len; /* Non-leaf nodes should not have any externally stored columns. */ ut_ad(!rec_offs_any_extern(offsets)); /* Copy the data bytes, except node_ptr. */ len = rec_offs_data_size(offsets) - REC_NODE_PTR_SIZE; ut_ad(data + len < storage - REC_NODE_PTR_SIZE * (page_dir_get_n_heap(page) - PAGE_HEAP_NO_USER_LOW)); ASSERT_ZERO(data, len); memcpy(data, rec, len); data += len; /* Copy the node pointer to the uncompressed area. */ memcpy(storage - REC_NODE_PTR_SIZE * (heap_no - 1), rec + len, REC_NODE_PTR_SIZE); } ut_a(!*data); ut_ad((ulint) (data - page_zip->data) < page_zip_get_size(page_zip)); page_zip->m_end = data - page_zip->data; page_zip->m_nonempty = TRUE; #ifdef UNIV_ZIP_DEBUG ut_a(page_zip_validate(page_zip, page_align(rec))); #endif /* UNIV_ZIP_DEBUG */ } /***********************************************************//** Parses a log record of writing a BLOB pointer of a record. @return end of log record or NULL */ UNIV_INTERN byte* page_zip_parse_write_blob_ptr( /*==========================*/ byte* ptr, /*!< in: redo log buffer */ byte* end_ptr,/*!< in: redo log buffer end */ page_t* page, /*!< in/out: uncompressed page */ page_zip_des_t* page_zip)/*!< in/out: compressed page */ { ulint offset; ulint z_offset; ut_ad(!page == !page_zip); if (UNIV_UNLIKELY (end_ptr < ptr + (2 + 2 + BTR_EXTERN_FIELD_REF_SIZE))) { return(NULL); } offset = mach_read_from_2(ptr); z_offset = mach_read_from_2(ptr + 2); if (UNIV_UNLIKELY(offset < PAGE_ZIP_START) || UNIV_UNLIKELY(offset >= UNIV_PAGE_SIZE) || UNIV_UNLIKELY(z_offset >= UNIV_PAGE_SIZE)) { corrupt: recv_sys->found_corrupt_log = TRUE; return(NULL); } if (page) { if (UNIV_UNLIKELY(!page_zip) || UNIV_UNLIKELY(!page_is_leaf(page))) { goto corrupt; } #ifdef UNIV_ZIP_DEBUG ut_a(page_zip_validate(page_zip, page)); #endif /* UNIV_ZIP_DEBUG */ memcpy(page + offset, ptr + 4, BTR_EXTERN_FIELD_REF_SIZE); memcpy(page_zip->data + z_offset, ptr + 4, BTR_EXTERN_FIELD_REF_SIZE); #ifdef UNIV_ZIP_DEBUG ut_a(page_zip_validate(page_zip, page)); #endif /* UNIV_ZIP_DEBUG */ } return(ptr + (2 + 2 + BTR_EXTERN_FIELD_REF_SIZE)); } /**********************************************************************//** Write a BLOB pointer of a record on the leaf page of a clustered index. The information must already have been updated on the uncompressed page. */ UNIV_INTERN void page_zip_write_blob_ptr( /*====================*/ page_zip_des_t* page_zip,/*!< in/out: compressed page */ const byte* rec, /*!< in/out: record whose data is being written */ dict_index_t* index, /*!< in: index of the page */ const ulint* offsets,/*!< in: rec_get_offsets(rec, index) */ ulint n, /*!< in: column index */ mtr_t* mtr) /*!< in: mini-transaction handle, or NULL if no logging is needed */ { const byte* field; byte* externs; const page_t* page = page_align(rec); ulint blob_no; ulint len; ut_ad(PAGE_ZIP_MATCH(rec, page_zip)); ut_ad(page_simple_validate_new((page_t*) page)); ut_ad(page_zip_simple_validate(page_zip)); ut_ad(page_zip_get_size(page_zip) > PAGE_DATA + page_zip_dir_size(page_zip)); ut_ad(rec_offs_comp(offsets)); ut_ad(rec_offs_validate(rec, NULL, offsets)); ut_ad(rec_offs_any_extern(offsets)); ut_ad(rec_offs_nth_extern(offsets, n)); ut_ad(page_zip->m_start >= PAGE_DATA); ut_ad(page_zip_header_cmp(page_zip, page)); ut_ad(page_is_leaf(page)); ut_ad(dict_index_is_clust(index)); UNIV_MEM_ASSERT_RW(page_zip->data, page_zip_get_size(page_zip)); UNIV_MEM_ASSERT_RW(rec, rec_offs_data_size(offsets)); UNIV_MEM_ASSERT_RW(rec - rec_offs_extra_size(offsets), rec_offs_extra_size(offsets)); blob_no = page_zip_get_n_prev_extern(page_zip, rec, index) + rec_get_n_extern_new(rec, index, n); ut_a(blob_no < page_zip->n_blobs); externs = page_zip->data + page_zip_get_size(page_zip) - (page_dir_get_n_heap(page) - PAGE_HEAP_NO_USER_LOW) * (PAGE_ZIP_DIR_SLOT_SIZE + DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN); field = rec_get_nth_field(rec, offsets, n, &len); externs -= (blob_no + 1) * BTR_EXTERN_FIELD_REF_SIZE; field += len - BTR_EXTERN_FIELD_REF_SIZE; memcpy(externs, field, BTR_EXTERN_FIELD_REF_SIZE); #ifdef UNIV_ZIP_DEBUG ut_a(page_zip_validate(page_zip, page)); #endif /* UNIV_ZIP_DEBUG */ if (mtr) { #ifndef UNIV_HOTBACKUP byte* log_ptr = mlog_open( mtr, 11 + 2 + 2 + BTR_EXTERN_FIELD_REF_SIZE); if (UNIV_UNLIKELY(!log_ptr)) { return; } log_ptr = mlog_write_initial_log_record_fast( (byte*) field, MLOG_ZIP_WRITE_BLOB_PTR, log_ptr, mtr); mach_write_to_2(log_ptr, page_offset(field)); log_ptr += 2; mach_write_to_2(log_ptr, externs - page_zip->data); log_ptr += 2; memcpy(log_ptr, externs, BTR_EXTERN_FIELD_REF_SIZE); log_ptr += BTR_EXTERN_FIELD_REF_SIZE; mlog_close(mtr, log_ptr); #endif /* !UNIV_HOTBACKUP */ } } /***********************************************************//** Parses a log record of writing the node pointer of a record. @return end of log record or NULL */ UNIV_INTERN byte* page_zip_parse_write_node_ptr( /*==========================*/ byte* ptr, /*!< in: redo log buffer */ byte* end_ptr,/*!< in: redo log buffer end */ page_t* page, /*!< in/out: uncompressed page */ page_zip_des_t* page_zip)/*!< in/out: compressed page */ { ulint offset; ulint z_offset; ut_ad(!page == !page_zip); if (UNIV_UNLIKELY(end_ptr < ptr + (2 + 2 + REC_NODE_PTR_SIZE))) { return(NULL); } offset = mach_read_from_2(ptr); z_offset = mach_read_from_2(ptr + 2); if (UNIV_UNLIKELY(offset < PAGE_ZIP_START) || UNIV_UNLIKELY(offset >= UNIV_PAGE_SIZE) || UNIV_UNLIKELY(z_offset >= UNIV_PAGE_SIZE)) { corrupt: recv_sys->found_corrupt_log = TRUE; return(NULL); } if (page) { byte* storage_end; byte* field; byte* storage; ulint heap_no; if (UNIV_UNLIKELY(!page_zip) || UNIV_UNLIKELY(page_is_leaf(page))) { goto corrupt; } #ifdef UNIV_ZIP_DEBUG ut_a(page_zip_validate(page_zip, page)); #endif /* UNIV_ZIP_DEBUG */ field = page + offset; storage = page_zip->data + z_offset; storage_end = page_zip->data + page_zip_get_size(page_zip) - (page_dir_get_n_heap(page) - PAGE_HEAP_NO_USER_LOW) * PAGE_ZIP_DIR_SLOT_SIZE; heap_no = 1 + (storage_end - storage) / REC_NODE_PTR_SIZE; if (UNIV_UNLIKELY((storage_end - storage) % REC_NODE_PTR_SIZE) || UNIV_UNLIKELY(heap_no < PAGE_HEAP_NO_USER_LOW) || UNIV_UNLIKELY(heap_no >= page_dir_get_n_heap(page))) { goto corrupt; } memcpy(field, ptr + 4, REC_NODE_PTR_SIZE); memcpy(storage, ptr + 4, REC_NODE_PTR_SIZE); #ifdef UNIV_ZIP_DEBUG ut_a(page_zip_validate(page_zip, page)); #endif /* UNIV_ZIP_DEBUG */ } return(ptr + (2 + 2 + REC_NODE_PTR_SIZE)); } /**********************************************************************//** Write the node pointer of a record on a non-leaf compressed page. */ UNIV_INTERN void page_zip_write_node_ptr( /*====================*/ page_zip_des_t* page_zip,/*!< in/out: compressed page */ byte* rec, /*!< in/out: record */ ulint size, /*!< in: data size of rec */ ulint ptr, /*!< in: node pointer */ mtr_t* mtr) /*!< in: mini-transaction, or NULL */ { byte* field; byte* storage; page_t* page = page_align(rec); ut_ad(PAGE_ZIP_MATCH(rec, page_zip)); ut_ad(page_simple_validate_new(page)); ut_ad(page_zip_simple_validate(page_zip)); ut_ad(page_zip_get_size(page_zip) > PAGE_DATA + page_zip_dir_size(page_zip)); ut_ad(page_rec_is_comp(rec)); ut_ad(page_zip->m_start >= PAGE_DATA); ut_ad(page_zip_header_cmp(page_zip, page)); ut_ad(!page_is_leaf(page)); UNIV_MEM_ASSERT_RW(page_zip->data, page_zip_get_size(page_zip)); UNIV_MEM_ASSERT_RW(rec, size); storage = page_zip->data + page_zip_get_size(page_zip) - (page_dir_get_n_heap(page) - PAGE_HEAP_NO_USER_LOW) * PAGE_ZIP_DIR_SLOT_SIZE - (rec_get_heap_no_new(rec) - 1) * REC_NODE_PTR_SIZE; field = rec + size - REC_NODE_PTR_SIZE; #if defined UNIV_DEBUG || defined UNIV_ZIP_DEBUG ut_a(!memcmp(storage, field, REC_NODE_PTR_SIZE)); #endif /* UNIV_DEBUG || UNIV_ZIP_DEBUG */ #if REC_NODE_PTR_SIZE != 4 # error "REC_NODE_PTR_SIZE != 4" #endif mach_write_to_4(field, ptr); memcpy(storage, field, REC_NODE_PTR_SIZE); if (mtr) { #ifndef UNIV_HOTBACKUP byte* log_ptr = mlog_open(mtr, 11 + 2 + 2 + REC_NODE_PTR_SIZE); if (UNIV_UNLIKELY(!log_ptr)) { return; } log_ptr = mlog_write_initial_log_record_fast( field, MLOG_ZIP_WRITE_NODE_PTR, log_ptr, mtr); mach_write_to_2(log_ptr, page_offset(field)); log_ptr += 2; mach_write_to_2(log_ptr, storage - page_zip->data); log_ptr += 2; memcpy(log_ptr, field, REC_NODE_PTR_SIZE); log_ptr += REC_NODE_PTR_SIZE; mlog_close(mtr, log_ptr); #endif /* !UNIV_HOTBACKUP */ } } /**********************************************************************//** Write the trx_id and roll_ptr of a record on a B-tree leaf node page. */ UNIV_INTERN void page_zip_write_trx_id_and_roll_ptr( /*===============================*/ page_zip_des_t* page_zip,/*!< in/out: compressed page */ byte* rec, /*!< in/out: record */ const ulint* offsets,/*!< in: rec_get_offsets(rec, index) */ ulint trx_id_col,/*!< in: column number of TRX_ID in rec */ trx_id_t trx_id, /*!< in: transaction identifier */ roll_ptr_t roll_ptr)/*!< in: roll_ptr */ { byte* field; byte* storage; page_t* page = page_align(rec); ulint len; ut_ad(PAGE_ZIP_MATCH(rec, page_zip)); ut_ad(page_simple_validate_new(page)); ut_ad(page_zip_simple_validate(page_zip)); ut_ad(page_zip_get_size(page_zip) > PAGE_DATA + page_zip_dir_size(page_zip)); ut_ad(rec_offs_validate(rec, NULL, offsets)); ut_ad(rec_offs_comp(offsets)); ut_ad(page_zip->m_start >= PAGE_DATA); ut_ad(page_zip_header_cmp(page_zip, page)); ut_ad(page_is_leaf(page)); UNIV_MEM_ASSERT_RW(page_zip->data, page_zip_get_size(page_zip)); storage = page_zip->data + page_zip_get_size(page_zip) - (page_dir_get_n_heap(page) - PAGE_HEAP_NO_USER_LOW) * PAGE_ZIP_DIR_SLOT_SIZE - (rec_get_heap_no_new(rec) - 1) * (DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN); #if DATA_TRX_ID + 1 != DATA_ROLL_PTR # error "DATA_TRX_ID + 1 != DATA_ROLL_PTR" #endif field = rec_get_nth_field(rec, offsets, trx_id_col, &len); ut_ad(len == DATA_TRX_ID_LEN); ut_ad(field + DATA_TRX_ID_LEN == rec_get_nth_field(rec, offsets, trx_id_col + 1, &len)); ut_ad(len == DATA_ROLL_PTR_LEN); #if defined UNIV_DEBUG || defined UNIV_ZIP_DEBUG ut_a(!memcmp(storage, field, DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN)); #endif /* UNIV_DEBUG || UNIV_ZIP_DEBUG */ #if DATA_TRX_ID_LEN != 6 # error "DATA_TRX_ID_LEN != 6" #endif mach_write_to_6(field, trx_id); #if DATA_ROLL_PTR_LEN != 7 # error "DATA_ROLL_PTR_LEN != 7" #endif mach_write_to_7(field + DATA_TRX_ID_LEN, roll_ptr); memcpy(storage, field, DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN); UNIV_MEM_ASSERT_RW(rec, rec_offs_data_size(offsets)); UNIV_MEM_ASSERT_RW(rec - rec_offs_extra_size(offsets), rec_offs_extra_size(offsets)); UNIV_MEM_ASSERT_RW(page_zip->data, page_zip_get_size(page_zip)); } #ifdef UNIV_ZIP_DEBUG /** Set this variable in a debugger to disable page_zip_clear_rec(). The only observable effect should be the compression ratio due to deleted records not being zeroed out. In rare cases, there can be page_zip_validate() failures on the node_ptr, trx_id and roll_ptr columns if the space is reallocated for a smaller record. */ UNIV_INTERN ibool page_zip_clear_rec_disable; #endif /* UNIV_ZIP_DEBUG */ /**********************************************************************//** Clear an area on the uncompressed and compressed page, if possible. */ static void page_zip_clear_rec( /*===============*/ page_zip_des_t* page_zip,/*!< in/out: compressed page */ byte* rec, /*!< in: record to clear */ dict_index_t* index, /*!< in: index of rec */ const ulint* offsets)/*!< in: rec_get_offsets(rec, index) */ { ulint heap_no; page_t* page = page_align(rec); /* page_zip_validate() would fail here if a record containing externally stored columns is being deleted. */ ut_ad(rec_offs_validate(rec, index, offsets)); ut_ad(!page_zip_dir_find(page_zip, page_offset(rec))); ut_ad(page_zip_dir_find_free(page_zip, page_offset(rec))); ut_ad(page_zip_header_cmp(page_zip, page)); heap_no = rec_get_heap_no_new(rec); ut_ad(heap_no >= PAGE_HEAP_NO_USER_LOW); UNIV_MEM_ASSERT_RW(page_zip->data, page_zip_get_size(page_zip)); UNIV_MEM_ASSERT_RW(rec, rec_offs_data_size(offsets)); UNIV_MEM_ASSERT_RW(rec - rec_offs_extra_size(offsets), rec_offs_extra_size(offsets)); if ( #ifdef UNIV_ZIP_DEBUG !page_zip_clear_rec_disable && #endif /* UNIV_ZIP_DEBUG */ page_zip->m_end + 1 + ((heap_no - 1) >= 64)/* size of the log entry */ + page_zip_get_trailer_len(page_zip, dict_index_is_clust(index), NULL) < page_zip_get_size(page_zip)) { byte* data; /* Clear only the data bytes, because the allocator and the decompressor depend on the extra bytes. */ memset(rec, 0, rec_offs_data_size(offsets)); if (!page_is_leaf(page)) { /* Clear node_ptr on the compressed page. */ byte* storage = page_zip->data + page_zip_get_size(page_zip) - (page_dir_get_n_heap(page) - PAGE_HEAP_NO_USER_LOW) * PAGE_ZIP_DIR_SLOT_SIZE; memset(storage - (heap_no - 1) * REC_NODE_PTR_SIZE, 0, REC_NODE_PTR_SIZE); } else if (dict_index_is_clust(index)) { /* Clear trx_id and roll_ptr on the compressed page. */ byte* storage = page_zip->data + page_zip_get_size(page_zip) - (page_dir_get_n_heap(page) - PAGE_HEAP_NO_USER_LOW) * PAGE_ZIP_DIR_SLOT_SIZE; memset(storage - (heap_no - 1) * (DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN), 0, DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN); } /* Log that the data was zeroed out. */ data = page_zip->data + page_zip->m_end; ut_ad(!*data); if (UNIV_UNLIKELY(heap_no - 1 >= 64)) { *data++ = (byte) (0x80 | (heap_no - 1) >> 7); ut_ad(!*data); } *data++ = (byte) ((heap_no - 1) << 1 | 1); ut_ad(!*data); ut_ad((ulint) (data - page_zip->data) < page_zip_get_size(page_zip)); page_zip->m_end = data - page_zip->data; page_zip->m_nonempty = TRUE; } else if (page_is_leaf(page) && dict_index_is_clust(index)) { /* Do not clear the record, because there is not enough space to log the operation. */ if (rec_offs_any_extern(offsets)) { ulint i; for (i = rec_offs_n_fields(offsets); i--; ) { /* Clear all BLOB pointers in order to make page_zip_validate() pass. */ if (rec_offs_nth_extern(offsets, i)) { ulint len; byte* field = rec_get_nth_field( rec, offsets, i, &len); memset(field + len - BTR_EXTERN_FIELD_REF_SIZE, 0, BTR_EXTERN_FIELD_REF_SIZE); } } } } #ifdef UNIV_ZIP_DEBUG ut_a(page_zip_validate(page_zip, page)); #endif /* UNIV_ZIP_DEBUG */ } /**********************************************************************//** Write the "deleted" flag of a record on a compressed page. The flag must already have been written on the uncompressed page. */ UNIV_INTERN void page_zip_rec_set_deleted( /*=====================*/ page_zip_des_t* page_zip,/*!< in/out: compressed page */ const byte* rec, /*!< in: record on the uncompressed page */ ulint flag) /*!< in: the deleted flag (nonzero=TRUE) */ { byte* slot = page_zip_dir_find(page_zip, page_offset(rec)); ut_a(slot); UNIV_MEM_ASSERT_RW(page_zip->data, page_zip_get_size(page_zip)); if (flag) { *slot |= (PAGE_ZIP_DIR_SLOT_DEL >> 8); } else { *slot &= ~(PAGE_ZIP_DIR_SLOT_DEL >> 8); } #ifdef UNIV_ZIP_DEBUG ut_a(page_zip_validate(page_zip, page_align(rec))); #endif /* UNIV_ZIP_DEBUG */ } /**********************************************************************//** Write the "owned" flag of a record on a compressed page. The n_owned field must already have been written on the uncompressed page. */ UNIV_INTERN void page_zip_rec_set_owned( /*===================*/ page_zip_des_t* page_zip,/*!< in/out: compressed page */ const byte* rec, /*!< in: record on the uncompressed page */ ulint flag) /*!< in: the owned flag (nonzero=TRUE) */ { byte* slot = page_zip_dir_find(page_zip, page_offset(rec)); ut_a(slot); UNIV_MEM_ASSERT_RW(page_zip->data, page_zip_get_size(page_zip)); if (flag) { *slot |= (PAGE_ZIP_DIR_SLOT_OWNED >> 8); } else { *slot &= ~(PAGE_ZIP_DIR_SLOT_OWNED >> 8); } } /**********************************************************************//** Insert a record to the dense page directory. */ UNIV_INTERN void page_zip_dir_insert( /*================*/ page_zip_des_t* page_zip,/*!< in/out: compressed page */ const byte* prev_rec,/*!< in: record after which to insert */ const byte* free_rec,/*!< in: record from which rec was allocated, or NULL */ byte* rec) /*!< in: record to insert */ { ulint n_dense; byte* slot_rec; byte* slot_free; ut_ad(prev_rec != rec); ut_ad(page_rec_get_next((rec_t*) prev_rec) == rec); ut_ad(page_zip_simple_validate(page_zip)); UNIV_MEM_ASSERT_RW(page_zip->data, page_zip_get_size(page_zip)); if (page_rec_is_infimum(prev_rec)) { /* Use the first slot. */ slot_rec = page_zip->data + page_zip_get_size(page_zip); } else { byte* end = page_zip->data + page_zip_get_size(page_zip); byte* start = end - page_zip_dir_user_size(page_zip); if (UNIV_LIKELY(!free_rec)) { /* PAGE_N_RECS was already incremented in page_cur_insert_rec_zip(), but the dense directory slot at that position contains garbage. Skip it. */ start += PAGE_ZIP_DIR_SLOT_SIZE; } slot_rec = page_zip_dir_find_low(start, end, page_offset(prev_rec)); ut_a(slot_rec); } /* Read the old n_dense (n_heap may have been incremented). */ n_dense = page_dir_get_n_heap(page_zip->data) - (PAGE_HEAP_NO_USER_LOW + 1); if (UNIV_LIKELY_NULL(free_rec)) { /* The record was allocated from the free list. Shift the dense directory only up to that slot. Note that in this case, n_dense is actually off by one, because page_cur_insert_rec_zip() did not increment n_heap. */ ut_ad(rec_get_heap_no_new(rec) < n_dense + 1 + PAGE_HEAP_NO_USER_LOW); ut_ad(rec >= free_rec); slot_free = page_zip_dir_find(page_zip, page_offset(free_rec)); ut_ad(slot_free); slot_free += PAGE_ZIP_DIR_SLOT_SIZE; } else { /* The record was allocated from the heap. Shift the entire dense directory. */ ut_ad(rec_get_heap_no_new(rec) == n_dense + PAGE_HEAP_NO_USER_LOW); /* Shift to the end of the dense page directory. */ slot_free = page_zip->data + page_zip_get_size(page_zip) - PAGE_ZIP_DIR_SLOT_SIZE * n_dense; } /* Shift the dense directory to allocate place for rec. */ memmove(slot_free - PAGE_ZIP_DIR_SLOT_SIZE, slot_free, slot_rec - slot_free); /* Write the entry for the inserted record. The "owned" and "deleted" flags must be zero. */ mach_write_to_2(slot_rec - PAGE_ZIP_DIR_SLOT_SIZE, page_offset(rec)); } /**********************************************************************//** Shift the dense page directory and the array of BLOB pointers when a record is deleted. */ UNIV_INTERN void page_zip_dir_delete( /*================*/ page_zip_des_t* page_zip,/*!< in/out: compressed page */ byte* rec, /*!< in: record to delete */ dict_index_t* index, /*!< in: index of rec */ const ulint* offsets,/*!< in: rec_get_offsets(rec) */ const byte* free) /*!< in: previous start of the free list */ { byte* slot_rec; byte* slot_free; ulint n_ext; page_t* page = page_align(rec); ut_ad(rec_offs_validate(rec, index, offsets)); ut_ad(rec_offs_comp(offsets)); UNIV_MEM_ASSERT_RW(page_zip->data, page_zip_get_size(page_zip)); UNIV_MEM_ASSERT_RW(rec, rec_offs_data_size(offsets)); UNIV_MEM_ASSERT_RW(rec - rec_offs_extra_size(offsets), rec_offs_extra_size(offsets)); slot_rec = page_zip_dir_find(page_zip, page_offset(rec)); ut_a(slot_rec); /* This could not be done before page_zip_dir_find(). */ page_header_set_field(page, page_zip, PAGE_N_RECS, (ulint)(page_get_n_recs(page) - 1)); if (UNIV_UNLIKELY(!free)) { /* Make the last slot the start of the free list. */ slot_free = page_zip->data + page_zip_get_size(page_zip) - PAGE_ZIP_DIR_SLOT_SIZE * (page_dir_get_n_heap(page_zip->data) - PAGE_HEAP_NO_USER_LOW); } else { slot_free = page_zip_dir_find_free(page_zip, page_offset(free)); ut_a(slot_free < slot_rec); /* Grow the free list by one slot by moving the start. */ slot_free += PAGE_ZIP_DIR_SLOT_SIZE; } if (UNIV_LIKELY(slot_rec > slot_free)) { memmove(slot_free + PAGE_ZIP_DIR_SLOT_SIZE, slot_free, slot_rec - slot_free); } /* Write the entry for the deleted record. The "owned" and "deleted" flags will be cleared. */ mach_write_to_2(slot_free, page_offset(rec)); if (!page_is_leaf(page) || !dict_index_is_clust(index)) { ut_ad(!rec_offs_any_extern(offsets)); goto skip_blobs; } n_ext = rec_offs_n_extern(offsets); if (UNIV_UNLIKELY(n_ext)) { /* Shift and zero fill the array of BLOB pointers. */ ulint blob_no; byte* externs; byte* ext_end; blob_no = page_zip_get_n_prev_extern(page_zip, rec, index); ut_a(blob_no + n_ext <= page_zip->n_blobs); externs = page_zip->data + page_zip_get_size(page_zip) - (page_dir_get_n_heap(page) - PAGE_HEAP_NO_USER_LOW) * (PAGE_ZIP_DIR_SLOT_SIZE + DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN); ext_end = externs - page_zip->n_blobs * BTR_EXTERN_FIELD_REF_SIZE; externs -= blob_no * BTR_EXTERN_FIELD_REF_SIZE; page_zip->n_blobs -= n_ext; /* Shift and zero fill the array. */ memmove(ext_end + n_ext * BTR_EXTERN_FIELD_REF_SIZE, ext_end, (page_zip->n_blobs - blob_no) * BTR_EXTERN_FIELD_REF_SIZE); memset(ext_end, 0, n_ext * BTR_EXTERN_FIELD_REF_SIZE); } skip_blobs: /* The compression algorithm expects info_bits and n_owned to be 0 for deleted records. */ rec[-REC_N_NEW_EXTRA_BYTES] = 0; /* info_bits and n_owned */ page_zip_clear_rec(page_zip, rec, index, offsets); } /**********************************************************************//** Add a slot to the dense page directory. */ UNIV_INTERN void page_zip_dir_add_slot( /*==================*/ page_zip_des_t* page_zip, /*!< in/out: compressed page */ ulint is_clustered) /*!< in: nonzero for clustered index, zero for others */ { ulint n_dense; byte* dir; byte* stored; ut_ad(page_is_comp(page_zip->data)); UNIV_MEM_ASSERT_RW(page_zip->data, page_zip_get_size(page_zip)); /* Read the old n_dense (n_heap has already been incremented). */ n_dense = page_dir_get_n_heap(page_zip->data) - (PAGE_HEAP_NO_USER_LOW + 1); dir = page_zip->data + page_zip_get_size(page_zip) - PAGE_ZIP_DIR_SLOT_SIZE * n_dense; if (!page_is_leaf(page_zip->data)) { ut_ad(!page_zip->n_blobs); stored = dir - n_dense * REC_NODE_PTR_SIZE; } else if (UNIV_UNLIKELY(is_clustered)) { /* Move the BLOB pointer array backwards to make space for the roll_ptr and trx_id columns and the dense directory slot. */ byte* externs; stored = dir - n_dense * (DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN); externs = stored - page_zip->n_blobs * BTR_EXTERN_FIELD_REF_SIZE; ASSERT_ZERO(externs - (PAGE_ZIP_DIR_SLOT_SIZE + DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN), PAGE_ZIP_DIR_SLOT_SIZE + DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN); memmove(externs - (PAGE_ZIP_DIR_SLOT_SIZE + DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN), externs, stored - externs); } else { stored = dir - page_zip->n_blobs * BTR_EXTERN_FIELD_REF_SIZE; ASSERT_ZERO(stored - PAGE_ZIP_DIR_SLOT_SIZE, PAGE_ZIP_DIR_SLOT_SIZE); } /* Move the uncompressed area backwards to make space for one directory slot. */ memmove(stored - PAGE_ZIP_DIR_SLOT_SIZE, stored, dir - stored); } /***********************************************************//** Parses a log record of writing to the header of a page. @return end of log record or NULL */ UNIV_INTERN byte* page_zip_parse_write_header( /*========================*/ byte* ptr, /*!< in: redo log buffer */ byte* end_ptr,/*!< in: redo log buffer end */ page_t* page, /*!< in/out: uncompressed page */ page_zip_des_t* page_zip)/*!< in/out: compressed page */ { ulint offset; ulint len; ut_ad(ptr && end_ptr); ut_ad(!page == !page_zip); if (UNIV_UNLIKELY(end_ptr < ptr + (1 + 1))) { return(NULL); } offset = (ulint) *ptr++; len = (ulint) *ptr++; if (UNIV_UNLIKELY(!len) || UNIV_UNLIKELY(offset + len >= PAGE_DATA)) { corrupt: recv_sys->found_corrupt_log = TRUE; return(NULL); } if (UNIV_UNLIKELY(end_ptr < ptr + len)) { return(NULL); } if (page) { if (UNIV_UNLIKELY(!page_zip)) { goto corrupt; } #ifdef UNIV_ZIP_DEBUG ut_a(page_zip_validate(page_zip, page)); #endif /* UNIV_ZIP_DEBUG */ memcpy(page + offset, ptr, len); memcpy(page_zip->data + offset, ptr, len); #ifdef UNIV_ZIP_DEBUG ut_a(page_zip_validate(page_zip, page)); #endif /* UNIV_ZIP_DEBUG */ } return(ptr + len); } #ifndef UNIV_HOTBACKUP /**********************************************************************//** Write a log record of writing to the uncompressed header portion of a page. */ UNIV_INTERN void page_zip_write_header_log( /*======================*/ const byte* data, /*!< in: data on the uncompressed page */ ulint length, /*!< in: length of the data */ mtr_t* mtr) /*!< in: mini-transaction */ { byte* log_ptr = mlog_open(mtr, 11 + 1 + 1); ulint offset = page_offset(data); ut_ad(offset < PAGE_DATA); ut_ad(offset + length < PAGE_DATA); #if PAGE_DATA > 255 # error "PAGE_DATA > 255" #endif ut_ad(length < 256); /* If no logging is requested, we may return now */ if (UNIV_UNLIKELY(!log_ptr)) { return; } log_ptr = mlog_write_initial_log_record_fast( (byte*) data, MLOG_ZIP_WRITE_HEADER, log_ptr, mtr); *log_ptr++ = (byte) offset; *log_ptr++ = (byte) length; mlog_close(mtr, log_ptr); mlog_catenate_string(mtr, data, length); } #endif /* !UNIV_HOTBACKUP */ /**********************************************************************//** Reorganize and compress a page. This is a low-level operation for compressed pages, to be used when page_zip_compress() fails. On success, a redo log entry MLOG_ZIP_PAGE_COMPRESS will be written. The function btr_page_reorganize() should be preferred whenever possible. IMPORTANT: if page_zip_reorganize() is invoked on a leaf page of a non-clustered index, the caller must update the insert buffer free bits in the same mini-transaction in such a way that the modification will be redo-logged. @return TRUE on success, FALSE on failure; page_zip will be left intact on failure, but page will be overwritten. */ UNIV_INTERN ibool page_zip_reorganize( /*================*/ buf_block_t* block, /*!< in/out: page with compressed page; on the compressed page, in: size; out: data, n_blobs, m_start, m_end, m_nonempty */ dict_index_t* index, /*!< in: index of the B-tree node */ mtr_t* mtr) /*!< in: mini-transaction */ { page_zip_des_t* page_zip = buf_block_get_page_zip(block); page_t* page = buf_block_get_frame(block); buf_block_t* temp_block; page_t* temp_page; ulint log_mode; ut_ad(mtr_memo_contains(mtr, block, MTR_MEMO_PAGE_X_FIX)); ut_ad(page_is_comp(page)); ut_ad(!dict_index_is_ibuf(index)); /* Note that page_zip_validate(page_zip, page) may fail here. */ UNIV_MEM_ASSERT_RW(page, UNIV_PAGE_SIZE); UNIV_MEM_ASSERT_RW(page_zip->data, page_zip_get_size(page_zip)); /* Disable logging */ log_mode = mtr_set_log_mode(mtr, MTR_LOG_NONE); #ifndef UNIV_HOTBACKUP temp_block = buf_block_alloc(0); btr_search_drop_page_hash_index(block); block->check_index_page_at_flush = TRUE; #else /* !UNIV_HOTBACKUP */ ut_ad(block == back_block1); temp_block = back_block2; #endif /* !UNIV_HOTBACKUP */ temp_page = temp_block->frame; /* Copy the old page to temporary space */ buf_frame_copy(temp_page, page); /* Recreate the page: note that global data on page (possible segment headers, next page-field, etc.) is preserved intact */ page_create(block, mtr, TRUE); /* Copy the records from the temporary space to the recreated page; do not copy the lock bits yet */ page_copy_rec_list_end_no_locks(block, temp_block, page_get_infimum_rec(temp_page), index, mtr); if (!dict_index_is_clust(index) && page_is_leaf(temp_page)) { /* Copy max trx id to recreated page */ trx_id_t max_trx_id = page_get_max_trx_id(temp_page); page_set_max_trx_id(block, NULL, max_trx_id, NULL); ut_ad(!ut_dulint_is_zero(max_trx_id)); } /* Restore logging. */ mtr_set_log_mode(mtr, log_mode); if (UNIV_UNLIKELY(!page_zip_compress(page_zip, page, index, mtr))) { #ifndef UNIV_HOTBACKUP buf_block_free(temp_block); #endif /* !UNIV_HOTBACKUP */ return(FALSE); } lock_move_reorganize_page(block, temp_block); #ifndef UNIV_HOTBACKUP buf_block_free(temp_block); #endif /* !UNIV_HOTBACKUP */ return(TRUE); } #ifndef UNIV_HOTBACKUP /**********************************************************************//** Copy the records of a page byte for byte. Do not copy the page header or trailer, except those B-tree header fields that are directly related to the storage of records. Also copy PAGE_MAX_TRX_ID. NOTE: The caller must update the lock table and the adaptive hash index. */ UNIV_INTERN void page_zip_copy_recs( /*===============*/ page_zip_des_t* page_zip, /*!< out: copy of src_zip (n_blobs, m_start, m_end, m_nonempty, data[0..size-1]) */ page_t* page, /*!< out: copy of src */ const page_zip_des_t* src_zip, /*!< in: compressed page */ const page_t* src, /*!< in: page */ dict_index_t* index, /*!< in: index of the B-tree */ mtr_t* mtr) /*!< in: mini-transaction */ { ut_ad(mtr_memo_contains_page(mtr, page, MTR_MEMO_PAGE_X_FIX)); ut_ad(mtr_memo_contains_page(mtr, (page_t*) src, MTR_MEMO_PAGE_X_FIX)); ut_ad(!dict_index_is_ibuf(index)); #ifdef UNIV_ZIP_DEBUG /* The B-tree operations that call this function may set FIL_PAGE_PREV or PAGE_LEVEL, causing a temporary min_rec_flag mismatch. A strict page_zip_validate() will be executed later during the B-tree operations. */ ut_a(page_zip_validate_low(src_zip, src, TRUE)); #endif /* UNIV_ZIP_DEBUG */ ut_a(page_zip_get_size(page_zip) == page_zip_get_size(src_zip)); if (UNIV_UNLIKELY(src_zip->n_blobs)) { ut_a(page_is_leaf(src)); ut_a(dict_index_is_clust(index)); } /* The PAGE_MAX_TRX_ID must be set on leaf pages of secondary indexes. It does not matter on other pages. */ ut_a(dict_index_is_clust(index) || !page_is_leaf(src) || !ut_dulint_is_zero(page_get_max_trx_id(src))); UNIV_MEM_ASSERT_W(page, UNIV_PAGE_SIZE); UNIV_MEM_ASSERT_W(page_zip->data, page_zip_get_size(page_zip)); UNIV_MEM_ASSERT_RW(src, UNIV_PAGE_SIZE); UNIV_MEM_ASSERT_RW(src_zip->data, page_zip_get_size(page_zip)); /* Copy those B-tree page header fields that are related to the records stored in the page. Also copy the field PAGE_MAX_TRX_ID. Skip the rest of the page header and trailer. On the compressed page, there is no trailer. */ #if PAGE_MAX_TRX_ID + 8 != PAGE_HEADER_PRIV_END # error "PAGE_MAX_TRX_ID + 8 != PAGE_HEADER_PRIV_END" #endif memcpy(PAGE_HEADER + page, PAGE_HEADER + src, PAGE_HEADER_PRIV_END); memcpy(PAGE_DATA + page, PAGE_DATA + src, UNIV_PAGE_SIZE - PAGE_DATA - FIL_PAGE_DATA_END); memcpy(PAGE_HEADER + page_zip->data, PAGE_HEADER + src_zip->data, PAGE_HEADER_PRIV_END); memcpy(PAGE_DATA + page_zip->data, PAGE_DATA + src_zip->data, page_zip_get_size(page_zip) - PAGE_DATA); /* Copy all fields of src_zip to page_zip, except the pointer to the compressed data page. */ { page_zip_t* data = page_zip->data; memcpy(page_zip, src_zip, sizeof *page_zip); page_zip->data = data; } ut_ad(page_zip_get_trailer_len(page_zip, dict_index_is_clust(index), NULL) + page_zip->m_end < page_zip_get_size(page_zip)); if (!page_is_leaf(src) && UNIV_UNLIKELY(mach_read_from_4(src + FIL_PAGE_PREV) == FIL_NULL) && UNIV_LIKELY(mach_read_from_4(page + FIL_PAGE_PREV) != FIL_NULL)) { /* Clear the REC_INFO_MIN_REC_FLAG of the first user record. */ ulint offs = rec_get_next_offs(page + PAGE_NEW_INFIMUM, TRUE); if (UNIV_LIKELY(offs != PAGE_NEW_SUPREMUM)) { rec_t* rec = page + offs; ut_a(rec[-REC_N_NEW_EXTRA_BYTES] & REC_INFO_MIN_REC_FLAG); rec[-REC_N_NEW_EXTRA_BYTES] &= ~ REC_INFO_MIN_REC_FLAG; } } #ifdef UNIV_ZIP_DEBUG ut_a(page_zip_validate(page_zip, page)); #endif /* UNIV_ZIP_DEBUG */ page_zip_compress_write_log(page_zip, page, index, mtr); } #endif /* !UNIV_HOTBACKUP */ /**********************************************************************//** Parses a log record of compressing an index page. @return end of log record or NULL */ UNIV_INTERN byte* page_zip_parse_compress( /*====================*/ byte* ptr, /*!< in: buffer */ byte* end_ptr,/*!< in: buffer end */ page_t* page, /*!< out: uncompressed page */ page_zip_des_t* page_zip)/*!< out: compressed page */ { ulint size; ulint trailer_size; ut_ad(ptr && end_ptr); ut_ad(!page == !page_zip); if (UNIV_UNLIKELY(ptr + (2 + 2) > end_ptr)) { return(NULL); } size = mach_read_from_2(ptr); ptr += 2; trailer_size = mach_read_from_2(ptr); ptr += 2; if (UNIV_UNLIKELY(ptr + 8 + size + trailer_size > end_ptr)) { return(NULL); } if (page) { if (UNIV_UNLIKELY(!page_zip) || UNIV_UNLIKELY(page_zip_get_size(page_zip) < size)) { corrupt: recv_sys->found_corrupt_log = TRUE; return(NULL); } memcpy(page_zip->data + FIL_PAGE_PREV, ptr, 4); memcpy(page_zip->data + FIL_PAGE_NEXT, ptr + 4, 4); memcpy(page_zip->data + FIL_PAGE_TYPE, ptr + 8, size); memset(page_zip->data + FIL_PAGE_TYPE + size, 0, page_zip_get_size(page_zip) - trailer_size - (FIL_PAGE_TYPE + size)); memcpy(page_zip->data + page_zip_get_size(page_zip) - trailer_size, ptr + 8 + size, trailer_size); if (UNIV_UNLIKELY(!page_zip_decompress(page_zip, page, TRUE))) { goto corrupt; } } return(ptr + 8 + size + trailer_size); } /**********************************************************************//** Calculate the compressed page checksum. @return page checksum */ UNIV_INTERN ulint page_zip_calc_checksum( /*===================*/ const void* data, /*!< in: compressed page */ ulint size) /*!< in: size of compressed page */ { /* Exclude FIL_PAGE_SPACE_OR_CHKSUM, FIL_PAGE_LSN, and FIL_PAGE_FILE_FLUSH_LSN from the checksum. */ const Bytef* s = data; uLong adler; ut_ad(size > FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID); adler = adler32(0L, s + FIL_PAGE_OFFSET, FIL_PAGE_LSN - FIL_PAGE_OFFSET); adler = adler32(adler, s + FIL_PAGE_TYPE, 2); adler = adler32(adler, s + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID, size - FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID); return((ulint) adler); } haildb-2.3.2/page/page0cur.c0000644000175000017500000015313411513177357016440 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1994, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /********************************************************************//** @file page/page0cur.c The page cursor Created 10/4/1994 Heikki Tuuri *************************************************************************/ #include "univ.i" #include "page0zip.h" #include "page0cur.h" #ifdef UNIV_NONINL #include "page0cur.ic" #endif #include "mtr0log.h" #include "log0recv.h" #include "ut0ut.h" #ifndef UNIV_HOTBACKUP #include "rem0cmp.h" #ifdef PAGE_CUR_ADAPT # ifdef UNIV_SEARCH_PERF_STAT static ulint page_cur_short_succ = 0; # endif /* UNIV_SEARCH_PERF_STAT */ /*******************************************************************//** This is a linear congruential generator PRNG. Returns a pseudo random number between 0 and 2^64-1 inclusive. The formula and the constants being used are: X[n+1] = (a * X[n] + c) mod m where: X[0] = ut_time_us(NULL) a = 1103515245 (3^5 * 5 * 7 * 129749) c = 12345 (3 * 5 * 823) m = 18446744073709551616 (2^64) @return number between 0 and 2^64-1 */ static ib_uint64_t page_cur_lcg_prng(void) /*===================*/ { #define LCG_a 1103515245 #define LCG_c 12345 static ib_uint64_t lcg_current = 0; static ibool initialized = FALSE; if (!initialized) { lcg_current = (ib_uint64_t) ut_time_us(NULL); initialized = TRUE; } /* no need to "% 2^64" explicitly because lcg_current is 64 bit and this will be done anyway */ lcg_current = LCG_a * lcg_current + LCG_c; return(lcg_current); } /****************************************************************//** Tries a search shortcut based on the last insert. @return TRUE on success */ UNIV_INLINE ibool page_cur_try_search_shortcut( /*=========================*/ const buf_block_t* block, /*!< in: index page */ const dict_index_t* index, /*!< in: record descriptor */ const dtuple_t* tuple, /*!< in: data tuple */ ulint* iup_matched_fields, /*!< in/out: already matched fields in upper limit record */ ulint* iup_matched_bytes, /*!< in/out: already matched bytes in a field not yet completely matched */ ulint* ilow_matched_fields, /*!< in/out: already matched fields in lower limit record */ ulint* ilow_matched_bytes, /*!< in/out: already matched bytes in a field not yet completely matched */ page_cur_t* cursor) /*!< out: page cursor */ { const rec_t* rec; const rec_t* next_rec; ulint low_match; ulint low_bytes; ulint up_match; ulint up_bytes; #ifdef UNIV_SEARCH_DEBUG page_cur_t cursor2; #endif ibool success = FALSE; const page_t* page = buf_block_get_frame(block); mem_heap_t* heap = NULL; ulint offsets_[REC_OFFS_NORMAL_SIZE]; ulint* offsets = offsets_; rec_offs_init(offsets_); ut_ad(dtuple_check_typed(tuple)); rec = page_header_get_ptr(page, PAGE_LAST_INSERT); offsets = rec_get_offsets(rec, index, offsets, dtuple_get_n_fields(tuple), &heap); ut_ad(rec); ut_ad(page_rec_is_user_rec(rec)); ut_pair_min(&low_match, &low_bytes, *ilow_matched_fields, *ilow_matched_bytes, *iup_matched_fields, *iup_matched_bytes); up_match = low_match; up_bytes = low_bytes; if (page_cmp_dtuple_rec_with_match( index->cmp_ctx, tuple, rec, offsets, &low_match, &low_bytes) < 0) { goto exit_func; } next_rec = page_rec_get_next_const(rec); offsets = rec_get_offsets(next_rec, index, offsets, dtuple_get_n_fields(tuple), &heap); if (page_cmp_dtuple_rec_with_match( index->cmp_ctx, tuple, next_rec, offsets, &up_match, &up_bytes) >= 0) { goto exit_func; } page_cur_position(rec, block, cursor); #ifdef UNIV_SEARCH_DEBUG page_cur_search_with_match(block, index, tuple, PAGE_CUR_DBG, iup_matched_fields, iup_matched_bytes, ilow_matched_fields, ilow_matched_bytes, &cursor2); ut_a(cursor2.rec == cursor->rec); if (!page_rec_is_supremum(next_rec)) { ut_a(*iup_matched_fields == up_match); ut_a(*iup_matched_bytes == up_bytes); } ut_a(*ilow_matched_fields == low_match); ut_a(*ilow_matched_bytes == low_bytes); #endif if (!page_rec_is_supremum(next_rec)) { *iup_matched_fields = up_match; *iup_matched_bytes = up_bytes; } *ilow_matched_fields = low_match; *ilow_matched_bytes = low_bytes; #ifdef UNIV_SEARCH_PERF_STAT page_cur_short_succ++; #endif success = TRUE; exit_func: if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } return(success); } #endif #ifdef PAGE_CUR_LE_OR_EXTENDS /****************************************************************//** Checks if the nth field in a record is a character type field which extends the nth field in tuple, i.e., the field is longer or equal in length and has common first characters. @return TRUE if rec field extends tuple field */ static ibool page_cur_rec_field_extends( /*=======================*/ const dtuple_t* tuple, /*!< in: data tuple */ const rec_t* rec, /*!< in: record */ const ulint* offsets,/*!< in: array returned by rec_get_offsets() */ ulint n) /*!< in: compare nth field */ { const dtype_t* type; const dfield_t* dfield; const byte* rec_f; ulint rec_f_len; ut_ad(rec_offs_validate(rec, NULL, offsets)); dfield = dtuple_get_nth_field(tuple, n); type = dfield_get_type(dfield); rec_f = rec_get_nth_field(rec, offsets, n, &rec_f_len); if (type->mtype == DATA_VARCHAR || type->mtype == DATA_CHAR || type->mtype == DATA_FIXBINARY || type->mtype == DATA_BINARY || type->mtype == DATA_BLOB || type->mtype == DATA_VARCLIENT || type->mtype == DATA_CLIENT) { if (dfield_get_len(dfield) != UNIV_SQL_NULL && rec_f_len != UNIV_SQL_NULL && rec_f_len >= dfield_get_len(dfield) && !cmp_data_data_slow(type->mtype, type->prtype, dfield_get_data(dfield), dfield_get_len(dfield), rec_f, dfield_get_len(dfield))) { return(TRUE); } } return(FALSE); } #endif /* PAGE_CUR_LE_OR_EXTENDS */ /****************************************************************//** Searches the right position for a page cursor. */ UNIV_INTERN void page_cur_search_with_match( /*=======================*/ const buf_block_t* block, /*!< in: buffer block */ const dict_index_t* index, /*!< in: record descriptor */ const dtuple_t* tuple, /*!< in: data tuple */ ulint mode, /*!< in: PAGE_CUR_L, PAGE_CUR_LE, PAGE_CUR_G, or PAGE_CUR_GE */ ulint* iup_matched_fields, /*!< in/out: already matched fields in upper limit record */ ulint* iup_matched_bytes, /*!< in/out: already matched bytes in a field not yet completely matched */ ulint* ilow_matched_fields, /*!< in/out: already matched fields in lower limit record */ ulint* ilow_matched_bytes, /*!< in/out: already matched bytes in a field not yet completely matched */ page_cur_t* cursor) /*!< out: page cursor */ { ulint up; ulint low; ulint mid; const page_t* page; const page_dir_slot_t* slot; const rec_t* up_rec; const rec_t* low_rec; const rec_t* mid_rec; ulint up_matched_fields; ulint up_matched_bytes; ulint low_matched_fields; ulint low_matched_bytes; ulint cur_matched_fields; ulint cur_matched_bytes; int cmp; #ifdef UNIV_SEARCH_DEBUG int dbg_cmp; ulint dbg_matched_fields; ulint dbg_matched_bytes; #endif #ifdef UNIV_ZIP_DEBUG const page_zip_des_t* page_zip = buf_block_get_page_zip(block); #endif /* UNIV_ZIP_DEBUG */ mem_heap_t* heap = NULL; ulint offsets_[REC_OFFS_NORMAL_SIZE]; ulint* offsets = offsets_; rec_offs_init(offsets_); ut_ad(block && tuple && iup_matched_fields && iup_matched_bytes && ilow_matched_fields && ilow_matched_bytes && cursor); ut_ad(dtuple_validate(tuple)); #ifdef UNIV_DEBUG # ifdef PAGE_CUR_DBG if (mode != PAGE_CUR_DBG) # endif /* PAGE_CUR_DBG */ # ifdef PAGE_CUR_LE_OR_EXTENDS if (mode != PAGE_CUR_LE_OR_EXTENDS) # endif /* PAGE_CUR_LE_OR_EXTENDS */ ut_ad(mode == PAGE_CUR_L || mode == PAGE_CUR_LE || mode == PAGE_CUR_G || mode == PAGE_CUR_GE); #endif /* UNIV_DEBUG */ page = buf_block_get_frame(block); #ifdef UNIV_ZIP_DEBUG ut_a(!page_zip || page_zip_validate(page_zip, page)); #endif /* UNIV_ZIP_DEBUG */ page_check_dir(page); #ifdef PAGE_CUR_ADAPT if (page_is_leaf(page) && (mode == PAGE_CUR_LE) && (page_header_get_field(page, PAGE_N_DIRECTION) > 3) && (page_header_get_ptr(page, PAGE_LAST_INSERT)) && (page_header_get_field(page, PAGE_DIRECTION) == PAGE_RIGHT)) { if (page_cur_try_search_shortcut( block, index, tuple, iup_matched_fields, iup_matched_bytes, ilow_matched_fields, ilow_matched_bytes, cursor)) { return; } } # ifdef PAGE_CUR_DBG if (mode == PAGE_CUR_DBG) { mode = PAGE_CUR_LE; } # endif #endif /* The following flag does not work for non-latin1 char sets because cmp_full_field does not tell how many bytes matched */ #ifdef PAGE_CUR_LE_OR_EXTENDS ut_a(mode != PAGE_CUR_LE_OR_EXTENDS); #endif /* PAGE_CUR_LE_OR_EXTENDS */ /* If mode PAGE_CUR_G is specified, we are trying to position the cursor to answer a query of the form "tuple < X", where tuple is the input parameter, and X denotes an arbitrary physical record on the page. We want to position the cursor on the first X which satisfies the condition. */ up_matched_fields = *iup_matched_fields; up_matched_bytes = *iup_matched_bytes; low_matched_fields = *ilow_matched_fields; low_matched_bytes = *ilow_matched_bytes; /* Perform binary search. First the search is done through the page directory, after that as a linear search in the list of records owned by the upper limit directory slot. */ low = 0; up = page_dir_get_n_slots(page) - 1; /* Perform binary search until the lower and upper limit directory slots come to the distance 1 of each other */ while (up - low > 1) { mid = (low + up) / 2; slot = page_dir_get_nth_slot(page, mid); mid_rec = page_dir_slot_get_rec(slot); ut_pair_min(&cur_matched_fields, &cur_matched_bytes, low_matched_fields, low_matched_bytes, up_matched_fields, up_matched_bytes); offsets = rec_get_offsets(mid_rec, index, offsets, dtuple_get_n_fields_cmp(tuple), &heap); cmp = cmp_dtuple_rec_with_match( index->cmp_ctx, tuple, mid_rec, offsets, &cur_matched_fields, &cur_matched_bytes); if (UNIV_LIKELY(cmp > 0)) { low_slot_match: low = mid; low_matched_fields = cur_matched_fields; low_matched_bytes = cur_matched_bytes; } else if (UNIV_EXPECT(cmp, -1)) { #ifdef PAGE_CUR_LE_OR_EXTENDS if (mode == PAGE_CUR_LE_OR_EXTENDS && page_cur_rec_field_extends( tuple, mid_rec, offsets, cur_matched_fields)) { goto low_slot_match; } #endif /* PAGE_CUR_LE_OR_EXTENDS */ up_slot_match: up = mid; up_matched_fields = cur_matched_fields; up_matched_bytes = cur_matched_bytes; } else if (mode == PAGE_CUR_G || mode == PAGE_CUR_LE #ifdef PAGE_CUR_LE_OR_EXTENDS || mode == PAGE_CUR_LE_OR_EXTENDS #endif /* PAGE_CUR_LE_OR_EXTENDS */ ) { goto low_slot_match; } else { goto up_slot_match; } } slot = page_dir_get_nth_slot(page, low); low_rec = page_dir_slot_get_rec(slot); slot = page_dir_get_nth_slot(page, up); up_rec = page_dir_slot_get_rec(slot); /* Perform linear search until the upper and lower records come to distance 1 of each other. */ while (page_rec_get_next_const(low_rec) != up_rec) { mid_rec = page_rec_get_next_const(low_rec); ut_pair_min(&cur_matched_fields, &cur_matched_bytes, low_matched_fields, low_matched_bytes, up_matched_fields, up_matched_bytes); offsets = rec_get_offsets(mid_rec, index, offsets, dtuple_get_n_fields_cmp(tuple), &heap); cmp = cmp_dtuple_rec_with_match( index->cmp_ctx, tuple, mid_rec, offsets, &cur_matched_fields, &cur_matched_bytes); if (UNIV_LIKELY(cmp > 0)) { low_rec_match: low_rec = mid_rec; low_matched_fields = cur_matched_fields; low_matched_bytes = cur_matched_bytes; } else if (UNIV_EXPECT(cmp, -1)) { #ifdef PAGE_CUR_LE_OR_EXTENDS if (mode == PAGE_CUR_LE_OR_EXTENDS && page_cur_rec_field_extends( tuple, mid_rec, offsets, cur_matched_fields)) { goto low_rec_match; } #endif /* PAGE_CUR_LE_OR_EXTENDS */ up_rec_match: up_rec = mid_rec; up_matched_fields = cur_matched_fields; up_matched_bytes = cur_matched_bytes; } else if (mode == PAGE_CUR_G || mode == PAGE_CUR_LE #ifdef PAGE_CUR_LE_OR_EXTENDS || mode == PAGE_CUR_LE_OR_EXTENDS #endif /* PAGE_CUR_LE_OR_EXTENDS */ ) { goto low_rec_match; } else { goto up_rec_match; } } #ifdef UNIV_SEARCH_DEBUG /* Check that the lower and upper limit records have the right alphabetical order compared to tuple. */ dbg_matched_fields = 0; dbg_matched_bytes = 0; offsets = rec_get_offsets(low_rec, index, offsets, ULINT_UNDEFINED, &heap); dbg_cmp = page_cmp_dtuple_rec_with_match( index->cmp_ctx, tuple, low_rec, offsets, &dbg_matched_fields, &dbg_matched_bytes); if (mode == PAGE_CUR_G) { ut_a(dbg_cmp >= 0); } else if (mode == PAGE_CUR_GE) { ut_a(dbg_cmp == 1); } else if (mode == PAGE_CUR_L) { ut_a(dbg_cmp == 1); } else if (mode == PAGE_CUR_LE) { ut_a(dbg_cmp >= 0); } if (!page_rec_is_infimum(low_rec)) { ut_a(low_matched_fields == dbg_matched_fields); ut_a(low_matched_bytes == dbg_matched_bytes); } dbg_matched_fields = 0; dbg_matched_bytes = 0; offsets = rec_get_offsets(up_rec, index, offsets, ULINT_UNDEFINED, &heap); dbg_cmp = page_cmp_dtuple_rec_with_match( index->cmp_ctx, tuple, up_rec, offsets, &dbg_matched_fields, &dbg_matched_bytes); if (mode == PAGE_CUR_G) { ut_a(dbg_cmp == -1); } else if (mode == PAGE_CUR_GE) { ut_a(dbg_cmp <= 0); } else if (mode == PAGE_CUR_L) { ut_a(dbg_cmp <= 0); } else if (mode == PAGE_CUR_LE) { ut_a(dbg_cmp == -1); } if (!page_rec_is_supremum(up_rec)) { ut_a(up_matched_fields == dbg_matched_fields); ut_a(up_matched_bytes == dbg_matched_bytes); } #endif if (mode <= PAGE_CUR_GE) { page_cur_position(up_rec, block, cursor); } else { page_cur_position(low_rec, block, cursor); } *iup_matched_fields = up_matched_fields; *iup_matched_bytes = up_matched_bytes; *ilow_matched_fields = low_matched_fields; *ilow_matched_bytes = low_matched_bytes; if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } } /***********************************************************//** Positions a page cursor on a randomly chosen user record on a page. If there are no user records, sets the cursor on the infimum record. */ UNIV_INTERN void page_cur_open_on_rnd_user_rec( /*==========================*/ buf_block_t* block, /*!< in: page */ page_cur_t* cursor) /*!< out: page cursor */ { ulint rnd; ulint n_recs = page_get_n_recs(buf_block_get_frame(block)); page_cur_set_before_first(block, cursor); if (UNIV_UNLIKELY(n_recs == 0)) { return; } rnd = (ulint) (page_cur_lcg_prng() % n_recs); do { page_cur_move_to_next(cursor); } while (rnd--); } /***********************************************************//** Writes the log record of a record insert on a page. */ static void page_cur_insert_rec_write_log( /*==========================*/ rec_t* insert_rec, /*!< in: inserted physical record */ ulint rec_size, /*!< in: insert_rec size */ rec_t* cursor_rec, /*!< in: record the cursor is pointing to */ dict_index_t* index, /*!< in: record descriptor */ mtr_t* mtr) /*!< in: mini-transaction handle */ { ulint cur_rec_size; ulint extra_size; ulint cur_extra_size; const byte* ins_ptr; byte* log_ptr; const byte* log_end; ulint i; ut_a(rec_size < UNIV_PAGE_SIZE); ut_ad(page_align(insert_rec) == page_align(cursor_rec)); ut_ad(!page_rec_is_comp(insert_rec) == !dict_table_is_comp(index->table)); { mem_heap_t* heap = NULL; ulint cur_offs_[REC_OFFS_NORMAL_SIZE]; ulint ins_offs_[REC_OFFS_NORMAL_SIZE]; ulint* cur_offs; ulint* ins_offs; rec_offs_init(cur_offs_); rec_offs_init(ins_offs_); cur_offs = rec_get_offsets(cursor_rec, index, cur_offs_, ULINT_UNDEFINED, &heap); ins_offs = rec_get_offsets(insert_rec, index, ins_offs_, ULINT_UNDEFINED, &heap); extra_size = rec_offs_extra_size(ins_offs); cur_extra_size = rec_offs_extra_size(cur_offs); ut_ad(rec_size == rec_offs_size(ins_offs)); cur_rec_size = rec_offs_size(cur_offs); if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } } ins_ptr = insert_rec - extra_size; i = 0; if (cur_extra_size == extra_size) { ulint min_rec_size = ut_min(cur_rec_size, rec_size); const byte* cur_ptr = cursor_rec - cur_extra_size; /* Find out the first byte in insert_rec which differs from cursor_rec; skip the bytes in the record info */ do { if (*ins_ptr == *cur_ptr) { i++; ins_ptr++; cur_ptr++; } else if ((i < extra_size) && (i >= extra_size - page_rec_get_base_extra_size (insert_rec))) { i = extra_size; ins_ptr = insert_rec; cur_ptr = cursor_rec; } else { break; } } while (i < min_rec_size); } if (mtr_get_log_mode(mtr) != MTR_LOG_SHORT_INSERTS) { if (page_rec_is_comp(insert_rec)) { log_ptr = mlog_open_and_write_index( mtr, insert_rec, index, MLOG_COMP_REC_INSERT, 2 + 5 + 1 + 5 + 5 + MLOG_BUF_MARGIN); if (UNIV_UNLIKELY(!log_ptr)) { /* Logging in mtr is switched off during crash recovery: in that case mlog_open returns NULL */ return; } } else { log_ptr = mlog_open(mtr, 11 + 2 + 5 + 1 + 5 + 5 + MLOG_BUF_MARGIN); if (UNIV_UNLIKELY(!log_ptr)) { /* Logging in mtr is switched off during crash recovery: in that case mlog_open returns NULL */ return; } log_ptr = mlog_write_initial_log_record_fast( insert_rec, MLOG_REC_INSERT, log_ptr, mtr); } log_end = &log_ptr[2 + 5 + 1 + 5 + 5 + MLOG_BUF_MARGIN]; /* Write the cursor rec offset as a 2-byte ulint */ mach_write_to_2(log_ptr, page_offset(cursor_rec)); log_ptr += 2; } else { log_ptr = mlog_open(mtr, 5 + 1 + 5 + 5 + MLOG_BUF_MARGIN); if (!log_ptr) { /* Logging in mtr is switched off during crash recovery: in that case mlog_open returns NULL */ return; } log_end = &log_ptr[5 + 1 + 5 + 5 + MLOG_BUF_MARGIN]; } if (page_rec_is_comp(insert_rec)) { if (UNIV_UNLIKELY (rec_get_info_and_status_bits(insert_rec, TRUE) != rec_get_info_and_status_bits(cursor_rec, TRUE))) { goto need_extra_info; } } else { if (UNIV_UNLIKELY (rec_get_info_and_status_bits(insert_rec, FALSE) != rec_get_info_and_status_bits(cursor_rec, FALSE))) { goto need_extra_info; } } if (extra_size != cur_extra_size || rec_size != cur_rec_size) { need_extra_info: /* Write the record end segment length and the extra info storage flag */ log_ptr += mach_write_compressed(log_ptr, 2 * (rec_size - i) + 1); /* Write the info bits */ mach_write_to_1(log_ptr, rec_get_info_and_status_bits( insert_rec, page_rec_is_comp(insert_rec))); log_ptr++; /* Write the record origin offset */ log_ptr += mach_write_compressed(log_ptr, extra_size); /* Write the mismatch index */ log_ptr += mach_write_compressed(log_ptr, i); ut_a(i < UNIV_PAGE_SIZE); ut_a(extra_size < UNIV_PAGE_SIZE); } else { /* Write the record end segment length and the extra info storage flag */ log_ptr += mach_write_compressed(log_ptr, 2 * (rec_size - i)); } /* Write to the log the inserted index record end segment which differs from the cursor record */ rec_size -= i; if (log_ptr + rec_size <= log_end) { memcpy(log_ptr, ins_ptr, rec_size); mlog_close(mtr, log_ptr + rec_size); } else { mlog_close(mtr, log_ptr); ut_a(rec_size < UNIV_PAGE_SIZE); mlog_catenate_string(mtr, ins_ptr, rec_size); } } #else /* !UNIV_HOTBACKUP */ # define page_cur_insert_rec_write_log(ins_rec,size,cur,index,mtr) ((void) 0) #endif /* !UNIV_HOTBACKUP */ /***********************************************************//** Parses a log record of a record insert on a page. @return end of log record or NULL */ UNIV_INTERN byte* page_cur_parse_insert_rec( /*======================*/ ibool is_short,/*!< in: TRUE if short inserts */ byte* ptr, /*!< in: buffer */ byte* end_ptr,/*!< in: buffer end */ buf_block_t* block, /*!< in: page or NULL */ dict_index_t* index, /*!< in: record descriptor */ mtr_t* mtr) /*!< in: mtr or NULL */ { ulint origin_offset; ulint end_seg_len; ulint mismatch_index; page_t* page; rec_t* cursor_rec; byte buf1[1024]; byte* buf; byte* ptr2 = ptr; ulint info_and_status_bits = 0; /* remove warning */ page_cur_t cursor; mem_heap_t* heap = NULL; ulint offsets_[REC_OFFS_NORMAL_SIZE]; ulint* offsets = offsets_; rec_offs_init(offsets_); page = block ? buf_block_get_frame(block) : NULL; if (is_short) { cursor_rec = page_rec_get_prev(page_get_supremum_rec(page)); } else { ulint offset; /* Read the cursor rec offset as a 2-byte ulint */ if (UNIV_UNLIKELY(end_ptr < ptr + 2)) { return(NULL); } offset = mach_read_from_2(ptr); ptr += 2; cursor_rec = page + offset; if (UNIV_UNLIKELY(offset >= UNIV_PAGE_SIZE)) { recv_sys->found_corrupt_log = TRUE; return(NULL); } } ptr = mach_parse_compressed(ptr, end_ptr, &end_seg_len); if (ptr == NULL) { return(NULL); } if (UNIV_UNLIKELY(end_seg_len >= UNIV_PAGE_SIZE << 1)) { recv_sys->found_corrupt_log = TRUE; return(NULL); } if (end_seg_len & 0x1UL) { /* Read the info bits */ if (end_ptr < ptr + 1) { return(NULL); } info_and_status_bits = mach_read_from_1(ptr); ptr++; ptr = mach_parse_compressed(ptr, end_ptr, &origin_offset); if (ptr == NULL) { return(NULL); } ut_a(origin_offset < UNIV_PAGE_SIZE); ptr = mach_parse_compressed(ptr, end_ptr, &mismatch_index); if (ptr == NULL) { return(NULL); } ut_a(mismatch_index < UNIV_PAGE_SIZE); } if (UNIV_UNLIKELY(end_ptr < ptr + (end_seg_len >> 1))) { return(NULL); } if (!block) { return(ptr + (end_seg_len >> 1)); } ut_ad(!!page_is_comp(page) == dict_table_is_comp(index->table)); ut_ad(!buf_block_get_page_zip(block) || page_is_comp(page)); /* Read from the log the inserted index record end segment which differs from the cursor record */ offsets = rec_get_offsets(cursor_rec, index, offsets, ULINT_UNDEFINED, &heap); if (!(end_seg_len & 0x1UL)) { info_and_status_bits = rec_get_info_and_status_bits( cursor_rec, page_is_comp(page)); origin_offset = rec_offs_extra_size(offsets); mismatch_index = rec_offs_size(offsets) - (end_seg_len >> 1); } end_seg_len >>= 1; if (mismatch_index + end_seg_len < sizeof buf1) { buf = buf1; } else { buf = mem_alloc(mismatch_index + end_seg_len); } /* Build the inserted record to buf */ if (UNIV_UNLIKELY(mismatch_index >= UNIV_PAGE_SIZE)) { ib_logger(ib_stream, "Is short %lu, info_and_status_bits %lu, offset %lu, " "o_offset %lu\n" "mismatch index %lu, end_seg_len %lu\n" "parsed len %lu\n", (ulong) is_short, (ulong) info_and_status_bits, (ulong) page_offset(cursor_rec), (ulong) origin_offset, (ulong) mismatch_index, (ulong) end_seg_len, (ulong) (ptr - ptr2)); ib_logger(ib_stream, "Dump of 300 bytes of log:\n"); ut_print_buf(ib_stream, ptr2, 300); ib_logger(ib_stream, "\n"); buf_page_print(page, 0); ut_error; } ut_memcpy(buf, rec_get_start(cursor_rec, offsets), mismatch_index); ut_memcpy(buf + mismatch_index, ptr, end_seg_len); if (page_is_comp(page)) { rec_set_info_and_status_bits(buf + origin_offset, info_and_status_bits); } else { rec_set_info_bits_old(buf + origin_offset, info_and_status_bits); } page_cur_position(cursor_rec, block, &cursor); offsets = rec_get_offsets(buf + origin_offset, index, offsets, ULINT_UNDEFINED, &heap); if (UNIV_UNLIKELY(!page_cur_rec_insert(&cursor, buf + origin_offset, index, offsets, mtr))) { /* The redo log record should only have been written after the write was successful. */ ut_error; } if (buf != buf1) { mem_free(buf); } if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } return(ptr + end_seg_len); } /***********************************************************//** Inserts a record next to page cursor on an uncompressed page. Returns pointer to inserted record if succeed, i.e., enough space available, NULL otherwise. The cursor stays at the same position. @return pointer to record if succeed, NULL otherwise */ UNIV_INTERN rec_t* page_cur_insert_rec_low( /*====================*/ rec_t* current_rec,/*!< in: pointer to current record after which the new record is inserted */ dict_index_t* index, /*!< in: record descriptor */ const rec_t* rec, /*!< in: pointer to a physical record */ ulint* offsets,/*!< in/out: rec_get_offsets(rec, index) */ mtr_t* mtr) /*!< in: mini-transaction handle, or NULL */ { byte* insert_buf; ulint rec_size; page_t* page; /*!< the relevant page */ rec_t* last_insert; /*!< cursor position at previous insert */ rec_t* free_rec; /*!< a free record that was reused, or NULL */ rec_t* insert_rec; /*!< inserted record */ ulint heap_no; /*!< heap number of the inserted record */ ut_ad(rec_offs_validate(rec, index, offsets)); page = page_align(current_rec); ut_ad(dict_table_is_comp(index->table) == (ibool) !!page_is_comp(page)); ut_ad(!page_rec_is_supremum(current_rec)); /* 1. Get the size of the physical record in the page */ rec_size = rec_offs_size(offsets); #ifdef UNIV_DEBUG_VALGRIND { const void* rec_start = rec - rec_offs_extra_size(offsets); ulint extra_size = rec_offs_extra_size(offsets) - (rec_offs_comp(offsets) ? REC_N_NEW_EXTRA_BYTES : REC_N_OLD_EXTRA_BYTES); /* All data bytes of the record must be valid. */ UNIV_MEM_ASSERT_RW(rec, rec_offs_data_size(offsets)); /* The variable-length header must be valid. */ UNIV_MEM_ASSERT_RW(rec_start, extra_size); } #endif /* UNIV_DEBUG_VALGRIND */ /* 2. Try to find suitable space from page memory management */ free_rec = page_header_get_ptr(page, PAGE_FREE); if (UNIV_LIKELY_NULL(free_rec)) { /* Try to allocate from the head of the free list. */ ulint foffsets_[REC_OFFS_NORMAL_SIZE]; ulint* foffsets = foffsets_; mem_heap_t* heap = NULL; rec_offs_init(foffsets_); foffsets = rec_get_offsets(free_rec, index, foffsets, ULINT_UNDEFINED, &heap); if (rec_offs_size(foffsets) < rec_size) { if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } goto use_heap; } insert_buf = free_rec - rec_offs_extra_size(foffsets); if (page_is_comp(page)) { heap_no = rec_get_heap_no_new(free_rec); page_mem_alloc_free(page, NULL, rec_get_next_ptr(free_rec, TRUE), rec_size); } else { heap_no = rec_get_heap_no_old(free_rec); page_mem_alloc_free(page, NULL, rec_get_next_ptr(free_rec, FALSE), rec_size); } if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } } else { use_heap: free_rec = NULL; insert_buf = page_mem_alloc_heap(page, NULL, rec_size, &heap_no); if (UNIV_UNLIKELY(insert_buf == NULL)) { return(NULL); } } /* 3. Create the record */ insert_rec = rec_copy(insert_buf, rec, offsets); rec_offs_make_valid(insert_rec, index, offsets); /* 4. Insert the record in the linked list of records */ ut_ad(current_rec != insert_rec); { /* next record after current before the insertion */ rec_t* next_rec = page_rec_get_next(current_rec); #ifdef UNIV_DEBUG if (page_is_comp(page)) { ut_ad(rec_get_status(current_rec) <= REC_STATUS_INFIMUM); ut_ad(rec_get_status(insert_rec) < REC_STATUS_INFIMUM); ut_ad(rec_get_status(next_rec) != REC_STATUS_INFIMUM); } #endif page_rec_set_next(insert_rec, next_rec); page_rec_set_next(current_rec, insert_rec); } page_header_set_field(page, NULL, PAGE_N_RECS, 1 + page_get_n_recs(page)); /* 5. Set the n_owned field in the inserted record to zero, and set the heap_no field */ if (page_is_comp(page)) { rec_set_n_owned_new(insert_rec, NULL, 0); rec_set_heap_no_new(insert_rec, heap_no); } else { rec_set_n_owned_old(insert_rec, 0); rec_set_heap_no_old(insert_rec, heap_no); } UNIV_MEM_ASSERT_RW(rec_get_start(insert_rec, offsets), rec_offs_size(offsets)); /* 6. Update the last insertion info in page header */ last_insert = page_header_get_ptr(page, PAGE_LAST_INSERT); ut_ad(!last_insert || !page_is_comp(page) || rec_get_node_ptr_flag(last_insert) == rec_get_node_ptr_flag(insert_rec)); if (UNIV_UNLIKELY(last_insert == NULL)) { page_header_set_field(page, NULL, PAGE_DIRECTION, PAGE_NO_DIRECTION); page_header_set_field(page, NULL, PAGE_N_DIRECTION, 0); } else if ((last_insert == current_rec) && (page_header_get_field(page, PAGE_DIRECTION) != PAGE_LEFT)) { page_header_set_field(page, NULL, PAGE_DIRECTION, PAGE_RIGHT); page_header_set_field(page, NULL, PAGE_N_DIRECTION, page_header_get_field( page, PAGE_N_DIRECTION) + 1); } else if ((page_rec_get_next(insert_rec) == last_insert) && (page_header_get_field(page, PAGE_DIRECTION) != PAGE_RIGHT)) { page_header_set_field(page, NULL, PAGE_DIRECTION, PAGE_LEFT); page_header_set_field(page, NULL, PAGE_N_DIRECTION, page_header_get_field( page, PAGE_N_DIRECTION) + 1); } else { page_header_set_field(page, NULL, PAGE_DIRECTION, PAGE_NO_DIRECTION); page_header_set_field(page, NULL, PAGE_N_DIRECTION, 0); } page_header_set_ptr(page, NULL, PAGE_LAST_INSERT, insert_rec); /* 7. It remains to update the owner record. */ { rec_t* owner_rec = page_rec_find_owner_rec(insert_rec); ulint n_owned; if (page_is_comp(page)) { n_owned = rec_get_n_owned_new(owner_rec); rec_set_n_owned_new(owner_rec, NULL, n_owned + 1); } else { n_owned = rec_get_n_owned_old(owner_rec); rec_set_n_owned_old(owner_rec, n_owned + 1); } /* 8. Now we have incremented the n_owned field of the owner record. If the number exceeds PAGE_DIR_SLOT_MAX_N_OWNED, we have to split the corresponding directory slot in two. */ if (UNIV_UNLIKELY(n_owned == PAGE_DIR_SLOT_MAX_N_OWNED)) { page_dir_split_slot( page, NULL, page_dir_find_owner_slot(owner_rec)); } } /* 9. Write log record of the insert */ if (UNIV_LIKELY(mtr != NULL)) { page_cur_insert_rec_write_log(insert_rec, rec_size, current_rec, index, mtr); } return(insert_rec); } /***********************************************************//** Compresses or reorganizes a page after an optimistic insert. @return rec if succeed, NULL otherwise */ static rec_t* page_cur_insert_rec_zip_reorg( /*==========================*/ rec_t** current_rec,/*!< in/out: pointer to current record after which the new record is inserted */ buf_block_t* block, /*!< in: buffer block */ dict_index_t* index, /*!< in: record descriptor */ rec_t* rec, /*!< in: inserted record */ page_t* page, /*!< in: uncompressed page */ page_zip_des_t* page_zip,/*!< in: compressed page */ mtr_t* mtr) /*!< in: mini-transaction, or NULL */ { ulint pos; /* Recompress or reorganize and recompress the page. */ if (UNIV_LIKELY(page_zip_compress(page_zip, page, index, mtr))) { return(rec); } /* Before trying to reorganize the page, store the number of preceding records on the page. */ pos = page_rec_get_n_recs_before(rec); if (page_zip_reorganize(block, index, mtr)) { /* The page was reorganized: Find rec by seeking to pos, and update *current_rec. */ rec = page + PAGE_NEW_INFIMUM; while (--pos) { rec = page + rec_get_next_offs(rec, TRUE); } *current_rec = rec; rec = page + rec_get_next_offs(rec, TRUE); return(rec); } /* Out of space: restore the page */ if (!page_zip_decompress(page_zip, page, FALSE)) { ut_error; /* Memory corrupted? */ } ut_ad(page_validate(page, index)); return(NULL); } /***********************************************************//** Inserts a record next to page cursor on a compressed and uncompressed page. Returns pointer to inserted record if succeed, i.e., enough space available, NULL otherwise. The cursor stays at the same position. @return pointer to record if succeed, NULL otherwise */ UNIV_INTERN rec_t* page_cur_insert_rec_zip( /*====================*/ rec_t** current_rec,/*!< in/out: pointer to current record after which the new record is inserted */ buf_block_t* block, /*!< in: buffer block of *current_rec */ dict_index_t* index, /*!< in: record descriptor */ const rec_t* rec, /*!< in: pointer to a physical record */ ulint* offsets,/*!< in/out: rec_get_offsets(rec, index) */ mtr_t* mtr) /*!< in: mini-transaction handle, or NULL */ { byte* insert_buf; ulint rec_size; page_t* page; /*!< the relevant page */ rec_t* last_insert; /*!< cursor position at previous insert */ rec_t* free_rec; /*!< a free record that was reused, or NULL */ rec_t* insert_rec; /*!< inserted record */ ulint heap_no; /*!< heap number of the inserted record */ page_zip_des_t* page_zip; page_zip = buf_block_get_page_zip(block); ut_ad(page_zip); ut_ad(rec_offs_validate(rec, index, offsets)); page = page_align(*current_rec); ut_ad(dict_table_is_comp(index->table)); ut_ad(page_is_comp(page)); ut_ad(!page_rec_is_supremum(*current_rec)); #ifdef UNIV_ZIP_DEBUG ut_a(page_zip_validate(page_zip, page)); #endif /* UNIV_ZIP_DEBUG */ /* 1. Get the size of the physical record in the page */ rec_size = rec_offs_size(offsets); #ifdef UNIV_DEBUG_VALGRIND { const void* rec_start = rec - rec_offs_extra_size(offsets); ulint extra_size = rec_offs_extra_size(offsets) - (rec_offs_comp(offsets) ? REC_N_NEW_EXTRA_BYTES : REC_N_OLD_EXTRA_BYTES); /* All data bytes of the record must be valid. */ UNIV_MEM_ASSERT_RW(rec, rec_offs_data_size(offsets)); /* The variable-length header must be valid. */ UNIV_MEM_ASSERT_RW(rec_start, extra_size); } #endif /* UNIV_DEBUG_VALGRIND */ /* 2. Try to find suitable space from page memory management */ if (!page_zip_available(page_zip, dict_index_is_clust(index), rec_size, 1)) { /* Try compressing the whole page afterwards. */ insert_rec = page_cur_insert_rec_low(*current_rec, index, rec, offsets, NULL); if (UNIV_LIKELY(insert_rec != NULL)) { insert_rec = page_cur_insert_rec_zip_reorg( current_rec, block, index, insert_rec, page, page_zip, mtr); } return(insert_rec); } free_rec = page_header_get_ptr(page, PAGE_FREE); if (UNIV_LIKELY_NULL(free_rec)) { /* Try to allocate from the head of the free list. */ lint extra_size_diff; ulint foffsets_[REC_OFFS_NORMAL_SIZE]; ulint* foffsets = foffsets_; mem_heap_t* heap = NULL; rec_offs_init(foffsets_); foffsets = rec_get_offsets(free_rec, index, foffsets, ULINT_UNDEFINED, &heap); if (rec_offs_size(foffsets) < rec_size) { too_small: if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } goto use_heap; } insert_buf = free_rec - rec_offs_extra_size(foffsets); /* On compressed pages, do not relocate records from the free list. If extra_size would grow, use the heap. */ extra_size_diff = rec_offs_extra_size(offsets) - rec_offs_extra_size(foffsets); if (UNIV_UNLIKELY(extra_size_diff < 0)) { /* Add an offset to the extra_size. */ if (rec_offs_size(foffsets) < rec_size - extra_size_diff) { goto too_small; } insert_buf -= extra_size_diff; } else if (UNIV_UNLIKELY(extra_size_diff)) { /* Do not allow extra_size to grow */ goto too_small; } heap_no = rec_get_heap_no_new(free_rec); page_mem_alloc_free(page, page_zip, rec_get_next_ptr(free_rec, TRUE), rec_size); if (!page_is_leaf(page)) { /* Zero out the node pointer of free_rec, in case it will not be overwritten by insert_rec. */ ut_ad(rec_size > REC_NODE_PTR_SIZE); if (rec_offs_extra_size(foffsets) + rec_offs_data_size(foffsets) > rec_size) { memset(rec_get_end(free_rec, foffsets) - REC_NODE_PTR_SIZE, 0, REC_NODE_PTR_SIZE); } } else if (dict_index_is_clust(index)) { /* Zero out the DB_TRX_ID and DB_ROLL_PTR columns of free_rec, in case it will not be overwritten by insert_rec. */ ulint trx_id_col; ulint trx_id_offs; ulint len; trx_id_col = dict_index_get_sys_col_pos(index, DATA_TRX_ID); ut_ad(trx_id_col > 0); ut_ad(trx_id_col != ULINT_UNDEFINED); trx_id_offs = rec_get_nth_field_offs(foffsets, trx_id_col, &len); ut_ad(len == DATA_TRX_ID_LEN); if (DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN + trx_id_offs + rec_offs_extra_size(foffsets) > rec_size) { /* We will have to zero out the DB_TRX_ID and DB_ROLL_PTR, because they will not be fully overwritten by insert_rec. */ memset(free_rec + trx_id_offs, 0, DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN); } ut_ad(free_rec + trx_id_offs + DATA_TRX_ID_LEN == rec_get_nth_field(free_rec, foffsets, trx_id_col + 1, &len)); ut_ad(len == DATA_ROLL_PTR_LEN); } if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } } else { use_heap: free_rec = NULL; insert_buf = page_mem_alloc_heap(page, page_zip, rec_size, &heap_no); if (UNIV_UNLIKELY(insert_buf == NULL)) { return(NULL); } page_zip_dir_add_slot(page_zip, dict_index_is_clust(index)); } /* 3. Create the record */ insert_rec = rec_copy(insert_buf, rec, offsets); rec_offs_make_valid(insert_rec, index, offsets); /* 4. Insert the record in the linked list of records */ ut_ad(*current_rec != insert_rec); { /* next record after current before the insertion */ rec_t* next_rec = page_rec_get_next(*current_rec); ut_ad(rec_get_status(*current_rec) <= REC_STATUS_INFIMUM); ut_ad(rec_get_status(insert_rec) < REC_STATUS_INFIMUM); ut_ad(rec_get_status(next_rec) != REC_STATUS_INFIMUM); page_rec_set_next(insert_rec, next_rec); page_rec_set_next(*current_rec, insert_rec); } page_header_set_field(page, page_zip, PAGE_N_RECS, 1 + page_get_n_recs(page)); /* 5. Set the n_owned field in the inserted record to zero, and set the heap_no field */ rec_set_n_owned_new(insert_rec, NULL, 0); rec_set_heap_no_new(insert_rec, heap_no); UNIV_MEM_ASSERT_RW(rec_get_start(insert_rec, offsets), rec_offs_size(offsets)); page_zip_dir_insert(page_zip, *current_rec, free_rec, insert_rec); /* 6. Update the last insertion info in page header */ last_insert = page_header_get_ptr(page, PAGE_LAST_INSERT); ut_ad(!last_insert || rec_get_node_ptr_flag(last_insert) == rec_get_node_ptr_flag(insert_rec)); if (UNIV_UNLIKELY(last_insert == NULL)) { page_header_set_field(page, page_zip, PAGE_DIRECTION, PAGE_NO_DIRECTION); page_header_set_field(page, page_zip, PAGE_N_DIRECTION, 0); } else if ((last_insert == *current_rec) && (page_header_get_field(page, PAGE_DIRECTION) != PAGE_LEFT)) { page_header_set_field(page, page_zip, PAGE_DIRECTION, PAGE_RIGHT); page_header_set_field(page, page_zip, PAGE_N_DIRECTION, page_header_get_field( page, PAGE_N_DIRECTION) + 1); } else if ((page_rec_get_next(insert_rec) == last_insert) && (page_header_get_field(page, PAGE_DIRECTION) != PAGE_RIGHT)) { page_header_set_field(page, page_zip, PAGE_DIRECTION, PAGE_LEFT); page_header_set_field(page, page_zip, PAGE_N_DIRECTION, page_header_get_field( page, PAGE_N_DIRECTION) + 1); } else { page_header_set_field(page, page_zip, PAGE_DIRECTION, PAGE_NO_DIRECTION); page_header_set_field(page, page_zip, PAGE_N_DIRECTION, 0); } page_header_set_ptr(page, page_zip, PAGE_LAST_INSERT, insert_rec); /* 7. It remains to update the owner record. */ { rec_t* owner_rec = page_rec_find_owner_rec(insert_rec); ulint n_owned; n_owned = rec_get_n_owned_new(owner_rec); rec_set_n_owned_new(owner_rec, page_zip, n_owned + 1); /* 8. Now we have incremented the n_owned field of the owner record. If the number exceeds PAGE_DIR_SLOT_MAX_N_OWNED, we have to split the corresponding directory slot in two. */ if (UNIV_UNLIKELY(n_owned == PAGE_DIR_SLOT_MAX_N_OWNED)) { page_dir_split_slot( page, page_zip, page_dir_find_owner_slot(owner_rec)); } } page_zip_write_rec(page_zip, insert_rec, index, offsets, 1); /* 9. Write log record of the insert */ if (UNIV_LIKELY(mtr != NULL)) { page_cur_insert_rec_write_log(insert_rec, rec_size, *current_rec, index, mtr); } return(insert_rec); } #ifndef UNIV_HOTBACKUP /**********************************************************//** Writes a log record of copying a record list end to a new created page. @return 4-byte field where to write the log data length, or NULL if logging is disabled */ UNIV_INLINE byte* page_copy_rec_list_to_created_page_write_log( /*=========================================*/ page_t* page, /*!< in: index page */ dict_index_t* index, /*!< in: record descriptor */ mtr_t* mtr) /*!< in: mtr */ { byte* log_ptr; ut_ad(!!page_is_comp(page) == dict_table_is_comp(index->table)); log_ptr = mlog_open_and_write_index(mtr, page, index, page_is_comp(page) ? MLOG_COMP_LIST_END_COPY_CREATED : MLOG_LIST_END_COPY_CREATED, 4); if (UNIV_LIKELY(log_ptr != NULL)) { mlog_close(mtr, log_ptr + 4); } return(log_ptr); } #endif /* !UNIV_HOTBACKUP */ /**********************************************************//** Parses a log record of copying a record list end to a new created page. @return end of log record or NULL */ UNIV_INTERN byte* page_parse_copy_rec_list_to_created_page( /*=====================================*/ byte* ptr, /*!< in: buffer */ byte* end_ptr,/*!< in: buffer end */ buf_block_t* block, /*!< in: page or NULL */ dict_index_t* index, /*!< in: record descriptor */ mtr_t* mtr) /*!< in: mtr or NULL */ { byte* rec_end; ulint log_data_len; page_t* page; page_zip_des_t* page_zip; if (ptr + 4 > end_ptr) { return(NULL); } log_data_len = mach_read_from_4(ptr); ptr += 4; rec_end = ptr + log_data_len; if (rec_end > end_ptr) { return(NULL); } if (!block) { return(rec_end); } while (ptr < rec_end) { ptr = page_cur_parse_insert_rec(TRUE, ptr, end_ptr, block, index, mtr); } ut_a(ptr == rec_end); page = buf_block_get_frame(block); page_zip = buf_block_get_page_zip(block); page_header_set_ptr(page, page_zip, PAGE_LAST_INSERT, NULL); page_header_set_field(page, page_zip, PAGE_DIRECTION, PAGE_NO_DIRECTION); page_header_set_field(page, page_zip, PAGE_N_DIRECTION, 0); return(rec_end); } #ifndef UNIV_HOTBACKUP /*************************************************************//** Copies records from page to a newly created page, from a given record onward, including that record. Infimum and supremum records are not copied. */ UNIV_INTERN void page_copy_rec_list_end_to_created_page( /*===================================*/ page_t* new_page, /*!< in/out: index page to copy to */ rec_t* rec, /*!< in: first record to copy */ dict_index_t* index, /*!< in: record descriptor */ mtr_t* mtr) /*!< in: mtr */ { page_dir_slot_t* slot = 0; /* remove warning */ byte* heap_top; rec_t* insert_rec = 0; /* remove warning */ rec_t* prev_rec; ulint count; ulint n_recs; ulint slot_index; ulint rec_size; ulint log_mode; byte* log_ptr; ulint log_data_len; mem_heap_t* heap = NULL; ulint offsets_[REC_OFFS_NORMAL_SIZE]; ulint* offsets = offsets_; rec_offs_init(offsets_); ut_ad(page_dir_get_n_heap(new_page) == PAGE_HEAP_NO_USER_LOW); ut_ad(page_align(rec) != new_page); ut_ad(page_rec_is_comp(rec) == page_is_comp(new_page)); if (page_rec_is_infimum(rec)) { rec = page_rec_get_next(rec); } if (page_rec_is_supremum(rec)) { return; } #ifdef UNIV_DEBUG /* To pass the debug tests we have to set these dummy values in the debug version */ page_dir_set_n_slots(new_page, NULL, UNIV_PAGE_SIZE / 2); page_header_set_ptr(new_page, NULL, PAGE_HEAP_TOP, new_page + UNIV_PAGE_SIZE - 1); #endif log_ptr = page_copy_rec_list_to_created_page_write_log(new_page, index, mtr); log_data_len = dyn_array_get_data_size(&(mtr->log)); /* Individual inserts are logged in a shorter form */ log_mode = mtr_set_log_mode(mtr, MTR_LOG_SHORT_INSERTS); prev_rec = page_get_infimum_rec(new_page); if (page_is_comp(new_page)) { heap_top = new_page + PAGE_NEW_SUPREMUM_END; } else { heap_top = new_page + PAGE_OLD_SUPREMUM_END; } count = 0; slot_index = 0; n_recs = 0; do { offsets = rec_get_offsets(rec, index, offsets, ULINT_UNDEFINED, &heap); insert_rec = rec_copy(heap_top, rec, offsets); if (page_is_comp(new_page)) { rec_set_next_offs_new(prev_rec, page_offset(insert_rec)); rec_set_n_owned_new(insert_rec, NULL, 0); rec_set_heap_no_new(insert_rec, PAGE_HEAP_NO_USER_LOW + n_recs); } else { rec_set_next_offs_old(prev_rec, page_offset(insert_rec)); rec_set_n_owned_old(insert_rec, 0); rec_set_heap_no_old(insert_rec, PAGE_HEAP_NO_USER_LOW + n_recs); } count++; n_recs++; if (UNIV_UNLIKELY (count == (PAGE_DIR_SLOT_MAX_N_OWNED + 1) / 2)) { slot_index++; slot = page_dir_get_nth_slot(new_page, slot_index); page_dir_slot_set_rec(slot, insert_rec); page_dir_slot_set_n_owned(slot, NULL, count); count = 0; } rec_size = rec_offs_size(offsets); ut_ad(heap_top < new_page + UNIV_PAGE_SIZE); heap_top += rec_size; page_cur_insert_rec_write_log(insert_rec, rec_size, prev_rec, index, mtr); prev_rec = insert_rec; rec = page_rec_get_next(rec); } while (!page_rec_is_supremum(rec)); if ((slot_index > 0) && (count + 1 + (PAGE_DIR_SLOT_MAX_N_OWNED + 1) / 2 <= PAGE_DIR_SLOT_MAX_N_OWNED)) { /* We can merge the two last dir slots. This operation is here to make this function imitate exactly the equivalent task made using page_cur_insert_rec, which we use in database recovery to reproduce the task performed by this function. To be able to check the correctness of recovery, it is good that it imitates exactly. */ count += (PAGE_DIR_SLOT_MAX_N_OWNED + 1) / 2; page_dir_slot_set_n_owned(slot, NULL, 0); slot_index--; } if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } log_data_len = dyn_array_get_data_size(&(mtr->log)) - log_data_len; ut_a(log_data_len < 100 * UNIV_PAGE_SIZE); if (UNIV_LIKELY(log_ptr != NULL)) { mach_write_to_4(log_ptr, log_data_len); } if (page_is_comp(new_page)) { rec_set_next_offs_new(insert_rec, PAGE_NEW_SUPREMUM); } else { rec_set_next_offs_old(insert_rec, PAGE_OLD_SUPREMUM); } slot = page_dir_get_nth_slot(new_page, 1 + slot_index); page_dir_slot_set_rec(slot, page_get_supremum_rec(new_page)); page_dir_slot_set_n_owned(slot, NULL, count + 1); page_dir_set_n_slots(new_page, NULL, 2 + slot_index); page_header_set_ptr(new_page, NULL, PAGE_HEAP_TOP, heap_top); page_dir_set_n_heap(new_page, NULL, PAGE_HEAP_NO_USER_LOW + n_recs); page_header_set_field(new_page, NULL, PAGE_N_RECS, n_recs); page_header_set_ptr(new_page, NULL, PAGE_LAST_INSERT, NULL); page_header_set_field(new_page, NULL, PAGE_DIRECTION, PAGE_NO_DIRECTION); page_header_set_field(new_page, NULL, PAGE_N_DIRECTION, 0); /* Restore the log mode */ mtr_set_log_mode(mtr, log_mode); } /***********************************************************//** Writes log record of a record delete on a page. */ UNIV_INLINE void page_cur_delete_rec_write_log( /*==========================*/ rec_t* rec, /*!< in: record to be deleted */ dict_index_t* index, /*!< in: record descriptor */ mtr_t* mtr) /*!< in: mini-transaction handle */ { byte* log_ptr; ut_ad(!!page_rec_is_comp(rec) == dict_table_is_comp(index->table)); log_ptr = mlog_open_and_write_index(mtr, rec, index, page_rec_is_comp(rec) ? MLOG_COMP_REC_DELETE : MLOG_REC_DELETE, 2); if (!log_ptr) { /* Logging in mtr is switched off during crash recovery: in that case mlog_open returns NULL */ return; } /* Write the cursor rec offset as a 2-byte ulint */ mach_write_to_2(log_ptr, page_offset(rec)); mlog_close(mtr, log_ptr + 2); } #else /* !UNIV_HOTBACKUP */ # define page_cur_delete_rec_write_log(rec,index,mtr) ((void) 0) #endif /* !UNIV_HOTBACKUP */ /***********************************************************//** Parses log record of a record delete on a page. @return pointer to record end or NULL */ UNIV_INTERN byte* page_cur_parse_delete_rec( /*======================*/ byte* ptr, /*!< in: buffer */ byte* end_ptr,/*!< in: buffer end */ buf_block_t* block, /*!< in: page or NULL */ dict_index_t* index, /*!< in: record descriptor */ mtr_t* mtr) /*!< in: mtr or NULL */ { ulint offset; page_cur_t cursor; if (end_ptr < ptr + 2) { return(NULL); } /* Read the cursor rec offset as a 2-byte ulint */ offset = mach_read_from_2(ptr); ptr += 2; ut_a(offset <= UNIV_PAGE_SIZE); if (block) { page_t* page = buf_block_get_frame(block); mem_heap_t* heap = NULL; ulint offsets_[REC_OFFS_NORMAL_SIZE]; rec_t* rec = page + offset; rec_offs_init(offsets_); page_cur_position(rec, block, &cursor); ut_ad(!buf_block_get_page_zip(block) || page_is_comp(page)); page_cur_delete_rec(&cursor, index, rec_get_offsets(rec, index, offsets_, ULINT_UNDEFINED, &heap), mtr); if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } } return(ptr); } /***********************************************************//** Deletes a record at the page cursor. The cursor is moved to the next record after the deleted one. */ UNIV_INTERN void page_cur_delete_rec( /*================*/ page_cur_t* cursor, /*!< in/out: a page cursor */ dict_index_t* index, /*!< in: record descriptor */ const ulint* offsets,/*!< in: rec_get_offsets(cursor->rec, index) */ mtr_t* mtr) /*!< in: mini-transaction handle */ { page_dir_slot_t* cur_dir_slot; page_dir_slot_t* prev_slot; page_t* page; page_zip_des_t* page_zip; rec_t* current_rec; rec_t* prev_rec = NULL; rec_t* next_rec; ulint cur_slot_no; ulint cur_n_owned; rec_t* rec; ut_ad(cursor && mtr); page = page_cur_get_page(cursor); page_zip = page_cur_get_page_zip(cursor); /* page_zip_validate() will fail here when btr_cur_pessimistic_delete() invokes btr_set_min_rec_mark(). Then, both "page_zip" and "page" would have the min-rec-mark set on the smallest user record, but "page" would additionally have it set on the smallest-but-one record. Because sloppy page_zip_validate_low() only ignores min-rec-flag differences in the smallest user record, it cannot be used here either. */ current_rec = cursor->rec; ut_ad(rec_offs_validate(current_rec, index, offsets)); ut_ad(!!page_is_comp(page) == dict_table_is_comp(index->table)); /* The record must not be the supremum or infimum record. */ ut_ad(page_rec_is_user_rec(current_rec)); /* Save to local variables some data associated with current_rec */ cur_slot_no = page_dir_find_owner_slot(current_rec); cur_dir_slot = page_dir_get_nth_slot(page, cur_slot_no); cur_n_owned = page_dir_slot_get_n_owned(cur_dir_slot); /* 0. Write the log record */ page_cur_delete_rec_write_log(current_rec, index, mtr); /* 1. Reset the last insert info in the page header and increment the modify clock for the frame */ page_header_set_ptr(page, page_zip, PAGE_LAST_INSERT, NULL); /* The page gets invalid for optimistic searches: increment the frame modify clock */ buf_block_modify_clock_inc(page_cur_get_block(cursor)); /* 2. Find the next and the previous record. Note that the cursor is left at the next record. */ ut_ad(cur_slot_no > 0); prev_slot = page_dir_get_nth_slot(page, cur_slot_no - 1); rec = (rec_t*) page_dir_slot_get_rec(prev_slot); /* rec now points to the record of the previous directory slot. Look for the immediate predecessor of current_rec in a loop. */ while(current_rec != rec) { prev_rec = rec; rec = page_rec_get_next(rec); } page_cur_move_to_next(cursor); next_rec = cursor->rec; /* 3. Remove the record from the linked list of records */ page_rec_set_next(prev_rec, next_rec); /* 4. If the deleted record is pointed to by a dir slot, update the record pointer in slot. In the following if-clause we assume that prev_rec is owned by the same slot, i.e., PAGE_DIR_SLOT_MIN_N_OWNED >= 2. */ #if PAGE_DIR_SLOT_MIN_N_OWNED < 2 # error "PAGE_DIR_SLOT_MIN_N_OWNED < 2" #endif ut_ad(cur_n_owned > 1); if (current_rec == page_dir_slot_get_rec(cur_dir_slot)) { page_dir_slot_set_rec(cur_dir_slot, prev_rec); } /* 5. Update the number of owned records of the slot */ page_dir_slot_set_n_owned(cur_dir_slot, page_zip, cur_n_owned - 1); /* 6. Free the memory occupied by the record */ page_mem_free(page, page_zip, current_rec, index, offsets); /* 7. Now we have decremented the number of owned records of the slot. If the number drops below PAGE_DIR_SLOT_MIN_N_OWNED, we balance the slots. */ if (UNIV_UNLIKELY(cur_n_owned <= PAGE_DIR_SLOT_MIN_N_OWNED)) { page_dir_balance_slot(page, page_zip, cur_slot_no); } #ifdef UNIV_ZIP_DEBUG ut_a(!page_zip || page_zip_validate(page_zip, page)); #endif /* UNIV_ZIP_DEBUG */ } #ifdef UNIV_COMPILE_TEST_FUNCS /*******************************************************************//** Print the first n numbers, generated by page_cur_lcg_prng() to make sure (visually) that it works properly. */ void test_page_cur_lcg_prng( /*===================*/ int n) /*!< in: print first n numbers */ { int i; unsigned long long rnd; for (i = 0; i < n; i++) { rnd = page_cur_lcg_prng(); printf("%llu\t%%2=%llu %%3=%llu %%5=%llu %%7=%llu %%11=%llu\n", rnd, rnd % 2, rnd % 3, rnd % 5, rnd % 7, rnd % 11); } } #endif /* UNIV_COMPILE_TEST_FUNCS */ haildb-2.3.2/page/page0page.c0000644000175000017500000021122111513177357016553 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1994, 2010, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file page/page0page.c Index page routines Created 2/2/1994 Heikki Tuuri *******************************************************/ #define THIS_MODULE #include "page0page.h" #ifdef UNIV_NONINL #include "page0page.ic" #endif #undef THIS_MODULE #include "page0cur.h" #include "buf0buf.h" #include "btr0btr.h" #ifndef UNIV_HOTBACKUP # include "srv0srv.h" # include "lock0lock.h" # include "fut0lst.h" # include "btr0sea.h" #endif /* !UNIV_HOTBACKUP */ /* THE INDEX PAGE ============== The index page consists of a page header which contains the page's id and other information. On top of it are the index records in a heap linked into a one way linear list according to alphabetic order. Just below page end is an array of pointers which we call page directory, to about every sixth record in the list. The pointers are placed in the directory in the alphabetical order of the records pointed to, enabling us to make binary search using the array. Each slot n:o I in the directory points to a record, where a 4-bit field contains a count of those records which are in the linear list between pointer I and the pointer I - 1 in the directory, including the record pointed to by pointer I and not including the record pointed to by I - 1. We say that the record pointed to by slot I, or that slot I, owns these records. The count is always kept in the range 4 to 8, with the exception that it is 1 for the first slot, and 1--8 for the second slot. An essentially binary search can be performed in the list of index records, like we could do if we had pointer to every record in the page directory. The data structure is, however, more efficient when we are doing inserts, because most inserts are just pushed on a heap. Only every 8th insert requires block move in the directory pointer table, which itself is quite small. A record is deleted from the page by just taking it off the linear list and updating the number of owned records-field of the record which owns it, and updating the page directory, if necessary. A special case is the one when the record owns itself. Because the overhead of inserts is so small, we may also increase the page size from the projected default of 8 kB to 64 kB without too much loss of efficiency in inserts. Bigger page becomes actual when the disk transfer rate compared to seek and latency time rises. On the present system, the page size is set so that the page transfer time (3 ms) is 20 % of the disk random access time (15 ms). When the page is split, merged, or becomes full but contains deleted records, we have to reorganize the page. Assuming a page size of 8 kB, a typical index page of a secondary index contains 300 index entries, and the size of the page directory is 50 x 4 bytes = 200 bytes. */ /***************************************************************//** Looks for the directory slot which owns the given record. @return the directory slot number */ UNIV_INTERN ulint page_dir_find_owner_slot( /*=====================*/ const rec_t* rec) /*!< in: the physical record */ { const page_t* page; register ib_uint16_t rec_offs_bytes; register const page_dir_slot_t* slot; register const page_dir_slot_t* first_slot; register const rec_t* r = rec; ut_ad(page_rec_check(rec)); page = page_align(rec); first_slot = page_dir_get_nth_slot(page, 0); slot = page_dir_get_nth_slot(page, page_dir_get_n_slots(page) - 1); if (page_is_comp(page)) { while (rec_get_n_owned_new(r) == 0) { r = rec_get_next_ptr_const(r, TRUE); ut_ad(r >= page + PAGE_NEW_SUPREMUM); ut_ad(r < page + (UNIV_PAGE_SIZE - PAGE_DIR)); } } else { while (rec_get_n_owned_old(r) == 0) { r = rec_get_next_ptr_const(r, FALSE); ut_ad(r >= page + PAGE_OLD_SUPREMUM); ut_ad(r < page + (UNIV_PAGE_SIZE - PAGE_DIR)); } } rec_offs_bytes = mach_encode_2(r - page); while (UNIV_LIKELY(*(ib_uint16_t*) slot != rec_offs_bytes)) { if (UNIV_UNLIKELY(slot == first_slot)) { ib_logger(ib_stream, "InnoDB: Probable data corruption on" " page %lu\n" "InnoDB: Original record ", (ulong) page_get_page_no(page)); if (page_is_comp(page)) { ib_logger(ib_stream, "(compact record)"); } else { rec_print_old(ib_stream, rec); } ib_logger(ib_stream, "\n" "InnoDB: on that page.\n" "InnoDB: Cannot find the dir slot for record "); if (page_is_comp(page)) { ib_logger(ib_stream, "(compact record)"); } else { rec_print_old(ib_stream, page + mach_decode_2(rec_offs_bytes)); } ib_logger(ib_stream, "\n" "InnoDB: on that page!\n"); buf_page_print(page, 0); ut_error; } slot += PAGE_DIR_SLOT_SIZE; } return(((ulint) (first_slot - slot)) / PAGE_DIR_SLOT_SIZE); } /**************************************************************//** Used to check the consistency of a directory slot. @return TRUE if succeed */ static ibool page_dir_slot_check( /*================*/ page_dir_slot_t* slot) /*!< in: slot */ { page_t* page; ulint n_slots; ulint n_owned; ut_a(slot); page = page_align(slot); n_slots = page_dir_get_n_slots(page); ut_a(slot <= page_dir_get_nth_slot(page, 0)); ut_a(slot >= page_dir_get_nth_slot(page, n_slots - 1)); ut_a(page_rec_check(page_dir_slot_get_rec(slot))); if (page_is_comp(page)) { n_owned = rec_get_n_owned_new(page_dir_slot_get_rec(slot)); } else { n_owned = rec_get_n_owned_old(page_dir_slot_get_rec(slot)); } if (slot == page_dir_get_nth_slot(page, 0)) { ut_a(n_owned == 1); } else if (slot == page_dir_get_nth_slot(page, n_slots - 1)) { ut_a(n_owned >= 1); ut_a(n_owned <= PAGE_DIR_SLOT_MAX_N_OWNED); } else { ut_a(n_owned >= PAGE_DIR_SLOT_MIN_N_OWNED); ut_a(n_owned <= PAGE_DIR_SLOT_MAX_N_OWNED); } return(TRUE); } /*************************************************************//** Sets the max trx id field value. */ UNIV_INTERN void page_set_max_trx_id( /*================*/ buf_block_t* block, /*!< in/out: page */ page_zip_des_t* page_zip,/*!< in/out: compressed page, or NULL */ trx_id_t trx_id, /*!< in: transaction id */ mtr_t* mtr) /*!< in/out: mini-transaction, or NULL */ { page_t* page = buf_block_get_frame(block); const ibool is_hashed = block->is_hashed; if (is_hashed) { rw_lock_x_lock(&btr_search_latch); } ut_ad(!mtr || mtr_memo_contains(mtr, block, MTR_MEMO_PAGE_X_FIX)); /* It is not necessary to write this change to the redo log, as during a database recovery we assume that the max trx id of every page is the maximum trx id assigned before the crash. */ if (UNIV_LIKELY_NULL(page_zip)) { mach_write_to_8(page + (PAGE_HEADER + PAGE_MAX_TRX_ID), trx_id); page_zip_write_header(page_zip, page + (PAGE_HEADER + PAGE_MAX_TRX_ID), 8, mtr); } else if (mtr) { mlog_write_dulint(page + (PAGE_HEADER + PAGE_MAX_TRX_ID), trx_id, mtr); } else { mach_write_to_8(page + (PAGE_HEADER + PAGE_MAX_TRX_ID), trx_id); } if (is_hashed) { rw_lock_x_unlock(&btr_search_latch); } } /************************************************************//** Allocates a block of memory from the heap of an index page. @return pointer to start of allocated buffer, or NULL if allocation fails */ UNIV_INTERN byte* page_mem_alloc_heap( /*================*/ page_t* page, /*!< in/out: index page */ page_zip_des_t* page_zip,/*!< in/out: compressed page with enough space available for inserting the record, or NULL */ ulint need, /*!< in: total number of bytes needed */ ulint* heap_no)/*!< out: this contains the heap number of the allocated record if allocation succeeds */ { byte* block; ulint avl_space; ut_ad(page && heap_no); avl_space = page_get_max_insert_size(page, 1); if (avl_space >= need) { block = page_header_get_ptr(page, PAGE_HEAP_TOP); page_header_set_ptr(page, page_zip, PAGE_HEAP_TOP, block + need); *heap_no = page_dir_get_n_heap(page); page_dir_set_n_heap(page, page_zip, 1 + *heap_no); return(block); } return(NULL); } #ifndef UNIV_HOTBACKUP /**********************************************************//** Writes a log record of page creation. */ UNIV_INLINE void page_create_write_log( /*==================*/ buf_frame_t* frame, /*!< in: a buffer frame where the page is created */ mtr_t* mtr, /*!< in: mini-transaction handle */ ibool comp) /*!< in: TRUE=compact page format */ { mlog_write_initial_log_record(frame, comp ? MLOG_COMP_PAGE_CREATE : MLOG_PAGE_CREATE, mtr); } #else /* !UNIV_HOTBACKUP */ # define page_create_write_log(frame,mtr,comp) ((void) 0) #endif /* !UNIV_HOTBACKUP */ /***********************************************************//** Parses a redo log record of creating a page. @return end of log record or NULL */ UNIV_INTERN byte* page_parse_create( /*==============*/ byte* ptr, /*!< in: buffer */ byte* end_ptr __attribute__((unused)), /*!< in: buffer end */ ulint comp, /*!< in: nonzero=compact page format */ buf_block_t* block, /*!< in: block or NULL */ mtr_t* mtr) /*!< in: mtr or NULL */ { ut_ad(ptr && end_ptr); /* The record is empty, except for the record initial part */ if (block) { page_create(block, mtr, comp); } return(ptr); } /**********************************************************//** The index page creation function. @return pointer to the page */ static page_t* page_create_low( /*============*/ buf_block_t* block, /*!< in: a buffer block where the page is created */ ulint comp) /*!< in: nonzero=compact page format */ { page_dir_slot_t* slot; mem_heap_t* heap; dtuple_t* tuple; dfield_t* field; byte* heap_top; rec_t* infimum_rec; rec_t* supremum_rec; page_t* page; dict_index_t* index; ulint* offsets; ut_ad(block); #if PAGE_BTR_IBUF_FREE_LIST + FLST_BASE_NODE_SIZE > PAGE_DATA # error "PAGE_BTR_IBUF_FREE_LIST + FLST_BASE_NODE_SIZE > PAGE_DATA" #endif #if PAGE_BTR_IBUF_FREE_LIST_NODE + FLST_NODE_SIZE > PAGE_DATA # error "PAGE_BTR_IBUF_FREE_LIST_NODE + FLST_NODE_SIZE > PAGE_DATA" #endif /* The infimum and supremum records use a dummy index. */ if (UNIV_LIKELY(comp)) { index = dict_ind_compact; } else { index = dict_ind_redundant; } /* 1. INCREMENT MODIFY CLOCK */ buf_block_modify_clock_inc(block); page = buf_block_get_frame(block); fil_page_set_type(page, FIL_PAGE_INDEX); heap = mem_heap_create(200); /* 3. CREATE THE INFIMUM AND SUPREMUM RECORDS */ /* Create first a data tuple for infimum record */ tuple = dtuple_create(heap, 1); dtuple_set_info_bits(tuple, REC_STATUS_INFIMUM); field = dtuple_get_nth_field(tuple, 0); dfield_set_data(field, "infimum", 8); dtype_set(dfield_get_type(field), DATA_VARCHAR, DATA_ENGLISH | DATA_NOT_NULL, 8); /* Set the corresponding physical record to its place in the page record heap */ heap_top = page + PAGE_DATA; infimum_rec = rec_convert_dtuple_to_rec(heap_top, index, tuple, 0); if (UNIV_LIKELY(comp)) { ut_a(infimum_rec == page + PAGE_NEW_INFIMUM); rec_set_n_owned_new(infimum_rec, NULL, 1); rec_set_heap_no_new(infimum_rec, 0); } else { ut_a(infimum_rec == page + PAGE_OLD_INFIMUM); rec_set_n_owned_old(infimum_rec, 1); rec_set_heap_no_old(infimum_rec, 0); } offsets = rec_get_offsets(infimum_rec, index, NULL, ULINT_UNDEFINED, &heap); heap_top = rec_get_end(infimum_rec, offsets); /* Create then a tuple for supremum */ tuple = dtuple_create(heap, 1); dtuple_set_info_bits(tuple, REC_STATUS_SUPREMUM); field = dtuple_get_nth_field(tuple, 0); dfield_set_data(field, "supremum", comp ? 8 : 9); dtype_set(dfield_get_type(field), DATA_VARCHAR, DATA_ENGLISH | DATA_NOT_NULL, comp ? 8 : 9); supremum_rec = rec_convert_dtuple_to_rec(heap_top, index, tuple, 0); if (UNIV_LIKELY(comp)) { ut_a(supremum_rec == page + PAGE_NEW_SUPREMUM); rec_set_n_owned_new(supremum_rec, NULL, 1); rec_set_heap_no_new(supremum_rec, 1); } else { ut_a(supremum_rec == page + PAGE_OLD_SUPREMUM); rec_set_n_owned_old(supremum_rec, 1); rec_set_heap_no_old(supremum_rec, 1); } offsets = rec_get_offsets(supremum_rec, index, offsets, ULINT_UNDEFINED, &heap); heap_top = rec_get_end(supremum_rec, offsets); ut_ad(heap_top == page + (comp ? PAGE_NEW_SUPREMUM_END : PAGE_OLD_SUPREMUM_END)); mem_heap_free(heap); /* 4. INITIALIZE THE PAGE */ page_header_set_field(page, NULL, PAGE_N_DIR_SLOTS, 2); page_header_set_ptr(page, NULL, PAGE_HEAP_TOP, heap_top); page_header_set_field(page, NULL, PAGE_N_HEAP, comp ? 0x8000 | PAGE_HEAP_NO_USER_LOW : PAGE_HEAP_NO_USER_LOW); page_header_set_ptr(page, NULL, PAGE_FREE, NULL); page_header_set_field(page, NULL, PAGE_GARBAGE, 0); page_header_set_ptr(page, NULL, PAGE_LAST_INSERT, NULL); page_header_set_field(page, NULL, PAGE_DIRECTION, PAGE_NO_DIRECTION); page_header_set_field(page, NULL, PAGE_N_DIRECTION, 0); page_header_set_field(page, NULL, PAGE_N_RECS, 0); page_set_max_trx_id(block, NULL, ut_dulint_zero, NULL); memset(heap_top, 0, UNIV_PAGE_SIZE - PAGE_EMPTY_DIR_START - page_offset(heap_top)); /* 5. SET POINTERS IN RECORDS AND DIR SLOTS */ /* Set the slots to point to infimum and supremum. */ slot = page_dir_get_nth_slot(page, 0); page_dir_slot_set_rec(slot, infimum_rec); slot = page_dir_get_nth_slot(page, 1); page_dir_slot_set_rec(slot, supremum_rec); /* Set the next pointers in infimum and supremum */ if (UNIV_LIKELY(comp)) { rec_set_next_offs_new(infimum_rec, PAGE_NEW_SUPREMUM); rec_set_next_offs_new(supremum_rec, 0); } else { rec_set_next_offs_old(infimum_rec, PAGE_OLD_SUPREMUM); rec_set_next_offs_old(supremum_rec, 0); } return(page); } /**********************************************************//** Create an uncompressed B-tree index page. @return pointer to the page */ UNIV_INTERN page_t* page_create( /*========*/ buf_block_t* block, /*!< in: a buffer block where the page is created */ mtr_t* mtr, /*!< in: mini-transaction handle */ ulint comp) /*!< in: nonzero=compact page format */ { page_create_write_log(buf_block_get_frame(block), mtr, comp); return(page_create_low(block, comp)); } /**********************************************************//** Create a compressed B-tree index page. @return pointer to the page */ UNIV_INTERN page_t* page_create_zip( /*============*/ buf_block_t* block, /*!< in/out: a buffer frame where the page is created */ dict_index_t* index, /*!< in: the index of the page */ ulint level, /*!< in: the B-tree level of the page */ mtr_t* mtr) /*!< in: mini-transaction handle */ { page_t* page; page_zip_des_t* page_zip = buf_block_get_page_zip(block); ut_ad(block); ut_ad(page_zip); ut_ad(index); ut_ad(dict_table_is_comp(index->table)); page = page_create_low(block, TRUE); mach_write_to_2(page + PAGE_HEADER + PAGE_LEVEL, level); if (UNIV_UNLIKELY(!page_zip_compress(page_zip, page, index, mtr))) { /* The compression of a newly created page should always succeed. */ ut_error; } return(page); } /*************************************************************//** Differs from page_copy_rec_list_end, because this function does not touch the lock table and max trx id on page or compress the page. */ UNIV_INTERN void page_copy_rec_list_end_no_locks( /*============================*/ buf_block_t* new_block, /*!< in: index page to copy to */ buf_block_t* block, /*!< in: index page of rec */ rec_t* rec, /*!< in: record on page */ dict_index_t* index, /*!< in: record descriptor */ mtr_t* mtr) /*!< in: mtr */ { page_t* new_page = buf_block_get_frame(new_block); page_cur_t cur1; rec_t* cur2; mem_heap_t* heap = NULL; ulint offsets_[REC_OFFS_NORMAL_SIZE]; ulint* offsets = offsets_; rec_offs_init(offsets_); page_cur_position(rec, block, &cur1); if (page_cur_is_before_first(&cur1)) { page_cur_move_to_next(&cur1); } ut_a((ibool)!!page_is_comp(new_page) == dict_table_is_comp(index->table)); ut_a(page_is_comp(new_page) == page_rec_is_comp(rec)); ut_a(mach_read_from_2(new_page + UNIV_PAGE_SIZE - 10) == (ulint) (page_is_comp(new_page) ? PAGE_NEW_INFIMUM : PAGE_OLD_INFIMUM)); cur2 = page_get_infimum_rec(buf_block_get_frame(new_block)); /* Copy records from the original page to the new page */ while (!page_cur_is_after_last(&cur1)) { rec_t* cur1_rec = page_cur_get_rec(&cur1); rec_t* ins_rec; offsets = rec_get_offsets(cur1_rec, index, offsets, ULINT_UNDEFINED, &heap); ins_rec = page_cur_insert_rec_low(cur2, index, cur1_rec, offsets, mtr); if (UNIV_UNLIKELY(!ins_rec)) { /* Track an assertion failure reported on the mailing list on June 18th, 2003 */ buf_page_print(new_page, 0); buf_page_print(page_align(rec), 0); ut_print_timestamp(ib_stream); ib_logger(ib_stream, "InnoDB: rec offset %lu, cur1 offset %lu," " cur2 offset %lu\n", (ulong) page_offset(rec), (ulong) page_offset(page_cur_get_rec(&cur1)), (ulong) page_offset(cur2)); ut_error; } page_cur_move_to_next(&cur1); cur2 = ins_rec; } if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } } #ifndef UNIV_HOTBACKUP /*************************************************************//** Copies records from page to new_page, from a given record onward, including that record. Infimum and supremum records are not copied. The records are copied to the start of the record list on new_page. @return pointer to the original successor of the infimum record on new_page, or NULL on zip overflow (new_block will be decompressed) */ UNIV_INTERN rec_t* page_copy_rec_list_end( /*===================*/ buf_block_t* new_block, /*!< in/out: index page to copy to */ buf_block_t* block, /*!< in: index page containing rec */ rec_t* rec, /*!< in: record on page */ dict_index_t* index, /*!< in: record descriptor */ mtr_t* mtr) /*!< in: mtr */ { page_t* new_page = buf_block_get_frame(new_block); page_zip_des_t* new_page_zip = buf_block_get_page_zip(new_block); page_t* page = page_align(rec); rec_t* ret = page_rec_get_next( page_get_infimum_rec(new_page)); ulint log_mode = 0; /* remove warning */ #ifdef UNIV_ZIP_DEBUG if (new_page_zip) { page_zip_des_t* page_zip = buf_block_get_page_zip(block); ut_a(page_zip); /* Strict page_zip_validate() may fail here. Furthermore, btr_compress() may set FIL_PAGE_PREV to FIL_NULL on new_page while leaving it intact on new_page_zip. So, we cannot validate new_page_zip. */ ut_a(page_zip_validate_low(page_zip, page, TRUE)); } #endif /* UNIV_ZIP_DEBUG */ ut_ad(buf_block_get_frame(block) == page); ut_ad(page_is_leaf(page) == page_is_leaf(new_page)); ut_ad(page_is_comp(page) == page_is_comp(new_page)); /* Here, "ret" may be pointing to a user record or the predefined supremum record. */ if (UNIV_LIKELY_NULL(new_page_zip)) { log_mode = mtr_set_log_mode(mtr, MTR_LOG_NONE); } if (page_dir_get_n_heap(new_page) == PAGE_HEAP_NO_USER_LOW) { page_copy_rec_list_end_to_created_page(new_page, rec, index, mtr); } else { page_copy_rec_list_end_no_locks(new_block, block, rec, index, mtr); } /* Update PAGE_MAX_TRX_ID on the uncompressed page. Modifications will be redo logged and copied to the compressed page in page_zip_compress() or page_zip_reorganize() below. */ if (dict_index_is_sec_or_ibuf(index) && page_is_leaf(page)) { page_update_max_trx_id(new_block, NULL, page_get_max_trx_id(page), mtr); } if (UNIV_LIKELY_NULL(new_page_zip)) { mtr_set_log_mode(mtr, log_mode); if (UNIV_UNLIKELY (!page_zip_compress(new_page_zip, new_page, index, mtr))) { /* Before trying to reorganize the page, store the number of preceding records on the page. */ ulint ret_pos = page_rec_get_n_recs_before(ret); /* Before copying, "ret" was the successor of the predefined infimum record. It must still have at least one predecessor (the predefined infimum record, or a freshly copied record that is smaller than "ret"). */ ut_a(ret_pos > 0); if (UNIV_UNLIKELY (!page_zip_reorganize(new_block, index, mtr))) { if (UNIV_UNLIKELY (!page_zip_decompress(new_page_zip, new_page, FALSE))) { ut_error; } ut_ad(page_validate(new_page, index)); return(NULL); } else { /* The page was reorganized: Seek to ret_pos. */ ret = new_page + PAGE_NEW_INFIMUM; do { ret = rec_get_next_ptr(ret, TRUE); } while (--ret_pos); } } } /* Update the lock table and possible hash index */ lock_move_rec_list_end(new_block, block, rec); btr_search_move_or_delete_hash_entries(new_block, block, index); return(ret); } /*************************************************************//** Copies records from page to new_page, up to the given record, NOT including that record. Infimum and supremum records are not copied. The records are copied to the end of the record list on new_page. @return pointer to the original predecessor of the supremum record on new_page, or NULL on zip overflow (new_block will be decompressed) */ UNIV_INTERN rec_t* page_copy_rec_list_start( /*=====================*/ buf_block_t* new_block, /*!< in/out: index page to copy to */ buf_block_t* block, /*!< in: index page containing rec */ rec_t* rec, /*!< in: record on page */ dict_index_t* index, /*!< in: record descriptor */ mtr_t* mtr) /*!< in: mtr */ { page_t* new_page = buf_block_get_frame(new_block); page_zip_des_t* new_page_zip = buf_block_get_page_zip(new_block); page_cur_t cur1; rec_t* cur2; ulint log_mode = 0 /* remove warning */; mem_heap_t* heap = NULL; rec_t* ret = page_rec_get_prev(page_get_supremum_rec(new_page)); ulint offsets_[REC_OFFS_NORMAL_SIZE]; ulint* offsets = offsets_; rec_offs_init(offsets_); /* Here, "ret" may be pointing to a user record or the predefined infimum record. */ if (page_rec_is_infimum(rec)) { return(ret); } if (UNIV_LIKELY_NULL(new_page_zip)) { log_mode = mtr_set_log_mode(mtr, MTR_LOG_NONE); } page_cur_set_before_first(block, &cur1); page_cur_move_to_next(&cur1); cur2 = ret; /* Copy records from the original page to the new page */ while (page_cur_get_rec(&cur1) != rec) { rec_t* cur1_rec = page_cur_get_rec(&cur1); offsets = rec_get_offsets(cur1_rec, index, offsets, ULINT_UNDEFINED, &heap); cur2 = page_cur_insert_rec_low(cur2, index, cur1_rec, offsets, mtr); ut_a(cur2); page_cur_move_to_next(&cur1); } if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } /* Update PAGE_MAX_TRX_ID on the uncompressed page. Modifications will be redo logged and copied to the compressed page in page_zip_compress() or page_zip_reorganize() below. */ if (dict_index_is_sec_or_ibuf(index) && page_is_leaf(page_align(rec))) { page_update_max_trx_id(new_block, NULL, page_get_max_trx_id(page_align(rec)), mtr); } if (UNIV_LIKELY_NULL(new_page_zip)) { mtr_set_log_mode(mtr, log_mode); if (UNIV_UNLIKELY (!page_zip_compress(new_page_zip, new_page, index, mtr))) { /* Before trying to reorganize the page, store the number of preceding records on the page. */ ulint ret_pos = page_rec_get_n_recs_before(ret); /* Before copying, "ret" was the predecessor of the predefined supremum record. If it was the predefined infimum record, then it would still be the infimum. Thus, the assertion ut_a(ret_pos > 0) would fail here. */ if (UNIV_UNLIKELY (!page_zip_reorganize(new_block, index, mtr))) { if (UNIV_UNLIKELY (!page_zip_decompress(new_page_zip, new_page, FALSE))) { ut_error; } ut_ad(page_validate(new_page, index)); return(NULL); } else { /* The page was reorganized: Seek to ret_pos. */ ret = new_page + PAGE_NEW_INFIMUM; do { ret = rec_get_next_ptr(ret, TRUE); } while (--ret_pos); } } } /* Update the lock table and possible hash index */ lock_move_rec_list_start(new_block, block, rec, ret); btr_search_move_or_delete_hash_entries(new_block, block, index); return(ret); } /**********************************************************//** Writes a log record of a record list end or start deletion. */ UNIV_INLINE void page_delete_rec_list_write_log( /*===========================*/ rec_t* rec, /*!< in: record on page */ dict_index_t* index, /*!< in: record descriptor */ byte type, /*!< in: operation type: MLOG_LIST_END_DELETE, ... */ mtr_t* mtr) /*!< in: mtr */ { byte* log_ptr; ut_ad(type == MLOG_LIST_END_DELETE || type == MLOG_LIST_START_DELETE || type == MLOG_COMP_LIST_END_DELETE || type == MLOG_COMP_LIST_START_DELETE); log_ptr = mlog_open_and_write_index(mtr, rec, index, type, 2); if (log_ptr) { /* Write the parameter as a 2-byte ulint */ mach_write_to_2(log_ptr, page_offset(rec)); mlog_close(mtr, log_ptr + 2); } } #else /* !UNIV_HOTBACKUP */ # define page_delete_rec_list_write_log(rec,index,type,mtr) ((void) 0) #endif /* !UNIV_HOTBACKUP */ /**********************************************************//** Parses a log record of a record list end or start deletion. @return end of log record or NULL */ UNIV_INTERN byte* page_parse_delete_rec_list( /*=======================*/ byte type, /*!< in: MLOG_LIST_END_DELETE, MLOG_LIST_START_DELETE, MLOG_COMP_LIST_END_DELETE or MLOG_COMP_LIST_START_DELETE */ byte* ptr, /*!< in: buffer */ byte* end_ptr,/*!< in: buffer end */ buf_block_t* block, /*!< in/out: buffer block or NULL */ dict_index_t* index, /*!< in: record descriptor */ mtr_t* mtr) /*!< in: mtr or NULL */ { page_t* page; ulint offset; ut_ad(type == MLOG_LIST_END_DELETE || type == MLOG_LIST_START_DELETE || type == MLOG_COMP_LIST_END_DELETE || type == MLOG_COMP_LIST_START_DELETE); /* Read the record offset as a 2-byte ulint */ if (end_ptr < ptr + 2) { return(NULL); } offset = mach_read_from_2(ptr); ptr += 2; if (!block) { return(ptr); } page = buf_block_get_frame(block); ut_ad(!!page_is_comp(page) == dict_table_is_comp(index->table)); if (type == MLOG_LIST_END_DELETE || type == MLOG_COMP_LIST_END_DELETE) { page_delete_rec_list_end(page + offset, block, index, ULINT_UNDEFINED, ULINT_UNDEFINED, mtr); } else { page_delete_rec_list_start(page + offset, block, index, mtr); } return(ptr); } /*************************************************************//** Deletes records from a page from a given record onward, including that record. The infimum and supremum records are not deleted. */ UNIV_INTERN void page_delete_rec_list_end( /*=====================*/ rec_t* rec, /*!< in: pointer to record on page */ buf_block_t* block, /*!< in: buffer block of the page */ dict_index_t* index, /*!< in: record descriptor */ ulint n_recs, /*!< in: number of records to delete, or ULINT_UNDEFINED if not known */ ulint size, /*!< in: the sum of the sizes of the records in the end of the chain to delete, or ULINT_UNDEFINED if not known */ mtr_t* mtr) /*!< in: mtr */ { page_dir_slot_t*slot; ulint slot_index; rec_t* last_rec; rec_t* prev_rec; ulint n_owned; page_zip_des_t* page_zip = buf_block_get_page_zip(block); page_t* page = page_align(rec); mem_heap_t* heap = NULL; ulint offsets_[REC_OFFS_NORMAL_SIZE]; ulint* offsets = offsets_; rec_offs_init(offsets_); ut_ad(size == ULINT_UNDEFINED || size < UNIV_PAGE_SIZE); ut_ad(!page_zip || page_rec_is_comp(rec)); #ifdef UNIV_ZIP_DEBUG ut_a(!page_zip || page_zip_validate(page_zip, page)); #endif /* UNIV_ZIP_DEBUG */ if (page_rec_is_infimum(rec)) { rec = page_rec_get_next(rec); } if (page_rec_is_supremum(rec)) { return; } /* Reset the last insert info in the page header and increment the modify clock for the frame */ page_header_set_ptr(page, page_zip, PAGE_LAST_INSERT, NULL); /* The page gets invalid for optimistic searches: increment the frame modify clock */ buf_block_modify_clock_inc(block); page_delete_rec_list_write_log(rec, index, page_is_comp(page) ? MLOG_COMP_LIST_END_DELETE : MLOG_LIST_END_DELETE, mtr); if (UNIV_LIKELY_NULL(page_zip)) { ulint log_mode; ut_a(page_is_comp(page)); /* Individual deletes are not logged */ log_mode = mtr_set_log_mode(mtr, MTR_LOG_NONE); do { page_cur_t cur; page_cur_position(rec, block, &cur); offsets = rec_get_offsets(rec, index, offsets, ULINT_UNDEFINED, &heap); rec = rec_get_next_ptr(rec, TRUE); #ifdef UNIV_ZIP_DEBUG ut_a(page_zip_validate(page_zip, page)); #endif /* UNIV_ZIP_DEBUG */ page_cur_delete_rec(&cur, index, offsets, mtr); } while (page_offset(rec) != PAGE_NEW_SUPREMUM); if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } /* Restore log mode */ mtr_set_log_mode(mtr, log_mode); return; } prev_rec = page_rec_get_prev(rec); last_rec = page_rec_get_prev(page_get_supremum_rec(page)); if ((size == ULINT_UNDEFINED) || (n_recs == ULINT_UNDEFINED)) { rec_t* rec2 = rec; /* Calculate the sum of sizes and the number of records */ size = 0; n_recs = 0; do { ulint s; offsets = rec_get_offsets(rec2, index, offsets, ULINT_UNDEFINED, &heap); s = rec_offs_size(offsets); ut_ad(rec2 - page + s - rec_offs_extra_size(offsets) < UNIV_PAGE_SIZE); ut_ad(size + s < UNIV_PAGE_SIZE); size += s; n_recs++; rec2 = page_rec_get_next(rec2); } while (!page_rec_is_supremum(rec2)); if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } } ut_ad(size < UNIV_PAGE_SIZE); /* Update the page directory; there is no need to balance the number of the records owned by the supremum record, as it is allowed to be less than PAGE_DIR_SLOT_MIN_N_OWNED */ if (page_is_comp(page)) { rec_t* rec2 = rec; ulint count = 0; while (rec_get_n_owned_new(rec2) == 0) { count++; rec2 = rec_get_next_ptr(rec2, TRUE); } ut_ad(rec_get_n_owned_new(rec2) > count); n_owned = rec_get_n_owned_new(rec2) - count; slot_index = page_dir_find_owner_slot(rec2); slot = page_dir_get_nth_slot(page, slot_index); } else { rec_t* rec2 = rec; ulint count = 0; while (rec_get_n_owned_old(rec2) == 0) { count++; rec2 = rec_get_next_ptr(rec2, FALSE); } ut_ad(rec_get_n_owned_old(rec2) > count); n_owned = rec_get_n_owned_old(rec2) - count; slot_index = page_dir_find_owner_slot(rec2); slot = page_dir_get_nth_slot(page, slot_index); } page_dir_slot_set_rec(slot, page_get_supremum_rec(page)); page_dir_slot_set_n_owned(slot, NULL, n_owned); page_dir_set_n_slots(page, NULL, slot_index + 1); /* Remove the record chain segment from the record chain */ page_rec_set_next(prev_rec, page_get_supremum_rec(page)); /* Catenate the deleted chain segment to the page free list */ page_rec_set_next(last_rec, page_header_get_ptr(page, PAGE_FREE)); page_header_set_ptr(page, NULL, PAGE_FREE, rec); page_header_set_field(page, NULL, PAGE_GARBAGE, size + page_header_get_field(page, PAGE_GARBAGE)); page_header_set_field(page, NULL, PAGE_N_RECS, (ulint)(page_get_n_recs(page) - n_recs)); } /*************************************************************//** Deletes records from page, up to the given record, NOT including that record. Infimum and supremum records are not deleted. */ UNIV_INTERN void page_delete_rec_list_start( /*=======================*/ rec_t* rec, /*!< in: record on page */ buf_block_t* block, /*!< in: buffer block of the page */ dict_index_t* index, /*!< in: record descriptor */ mtr_t* mtr) /*!< in: mtr */ { page_cur_t cur1; ulint log_mode; ulint offsets_[REC_OFFS_NORMAL_SIZE]; ulint* offsets = offsets_; mem_heap_t* heap = NULL; byte type; rec_offs_init(offsets_); ut_ad((ibool) !!page_rec_is_comp(rec) == dict_table_is_comp(index->table)); #ifdef UNIV_ZIP_DEBUG { page_zip_des_t* page_zip= buf_block_get_page_zip(block); page_t* page = buf_block_get_frame(block); /* page_zip_validate() would detect a min_rec_mark mismatch in btr_page_split_and_insert() between btr_attach_half_pages() and insert_page = ... when btr_page_get_split_rec_to_left() holds (direction == FSP_DOWN). */ ut_a(!page_zip || page_zip_validate_low(page_zip, page, TRUE)); } #endif /* UNIV_ZIP_DEBUG */ if (page_rec_is_infimum(rec)) { return; } if (page_rec_is_comp(rec)) { type = MLOG_COMP_LIST_START_DELETE; } else { type = MLOG_LIST_START_DELETE; } page_delete_rec_list_write_log(rec, index, type, mtr); page_cur_set_before_first(block, &cur1); page_cur_move_to_next(&cur1); /* Individual deletes are not logged */ log_mode = mtr_set_log_mode(mtr, MTR_LOG_NONE); while (page_cur_get_rec(&cur1) != rec) { offsets = rec_get_offsets(page_cur_get_rec(&cur1), index, offsets, ULINT_UNDEFINED, &heap); page_cur_delete_rec(&cur1, index, offsets, mtr); } if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } /* Restore log mode */ mtr_set_log_mode(mtr, log_mode); } #ifndef UNIV_HOTBACKUP /*************************************************************//** Moves record list end to another page. Moved records include split_rec. @return TRUE on success; FALSE on compression failure (new_block will be decompressed) */ UNIV_INTERN ibool page_move_rec_list_end( /*===================*/ buf_block_t* new_block, /*!< in/out: index page where to move */ buf_block_t* block, /*!< in: index page from where to move */ rec_t* split_rec, /*!< in: first record to move */ dict_index_t* index, /*!< in: record descriptor */ mtr_t* mtr) /*!< in: mtr */ { page_t* new_page = buf_block_get_frame(new_block); ulint old_data_size; ulint new_data_size; ulint old_n_recs; ulint new_n_recs; old_data_size = page_get_data_size(new_page); old_n_recs = page_get_n_recs(new_page); #ifdef UNIV_ZIP_DEBUG { page_zip_des_t* new_page_zip = buf_block_get_page_zip(new_block); page_zip_des_t* page_zip = buf_block_get_page_zip(block); ut_a(!new_page_zip == !page_zip); ut_a(!new_page_zip || page_zip_validate(new_page_zip, new_page)); ut_a(!page_zip || page_zip_validate(page_zip, page_align(split_rec))); } #endif /* UNIV_ZIP_DEBUG */ if (UNIV_UNLIKELY(!page_copy_rec_list_end(new_block, block, split_rec, index, mtr))) { return(FALSE); } new_data_size = page_get_data_size(new_page); new_n_recs = page_get_n_recs(new_page); ut_ad(new_data_size >= old_data_size); page_delete_rec_list_end(split_rec, block, index, new_n_recs - old_n_recs, new_data_size - old_data_size, mtr); return(TRUE); } /*************************************************************//** Moves record list start to another page. Moved records do not include split_rec. @return TRUE on success; FALSE on compression failure */ UNIV_INTERN ibool page_move_rec_list_start( /*=====================*/ buf_block_t* new_block, /*!< in/out: index page where to move */ buf_block_t* block, /*!< in/out: page containing split_rec */ rec_t* split_rec, /*!< in: first record not to move */ dict_index_t* index, /*!< in: record descriptor */ mtr_t* mtr) /*!< in: mtr */ { if (UNIV_UNLIKELY(!page_copy_rec_list_start(new_block, block, split_rec, index, mtr))) { return(FALSE); } page_delete_rec_list_start(split_rec, block, index, mtr); return(TRUE); } /***********************************************************************//** This is a low-level operation which is used in a database index creation to update the page number of a created B-tree to a data dictionary record. */ UNIV_INTERN void page_rec_write_index_page_no( /*=========================*/ rec_t* rec, /*!< in: record to update */ ulint i, /*!< in: index of the field to update */ ulint page_no,/*!< in: value to write */ mtr_t* mtr) /*!< in: mtr */ { byte* data; ulint len; data = rec_get_nth_field_old(rec, i, &len); ut_ad(len == 4); mlog_write_ulint(data, page_no, MLOG_4BYTES, mtr); } #endif /* !UNIV_HOTBACKUP */ /**************************************************************//** Used to delete n slots from the directory. This function updates also n_owned fields in the records, so that the first slot after the deleted ones inherits the records of the deleted slots. */ UNIV_INLINE void page_dir_delete_slot( /*=================*/ page_t* page, /*!< in/out: the index page */ page_zip_des_t* page_zip,/*!< in/out: compressed page, or NULL */ ulint slot_no)/*!< in: slot to be deleted */ { page_dir_slot_t* slot; ulint n_owned; ulint i; ulint n_slots; ut_ad(!page_zip || page_is_comp(page)); ut_ad(slot_no > 0); ut_ad(slot_no + 1 < page_dir_get_n_slots(page)); n_slots = page_dir_get_n_slots(page); /* 1. Reset the n_owned fields of the slots to be deleted */ slot = page_dir_get_nth_slot(page, slot_no); n_owned = page_dir_slot_get_n_owned(slot); page_dir_slot_set_n_owned(slot, page_zip, 0); /* 2. Update the n_owned value of the first non-deleted slot */ slot = page_dir_get_nth_slot(page, slot_no + 1); page_dir_slot_set_n_owned(slot, page_zip, n_owned + page_dir_slot_get_n_owned(slot)); /* 3. Destroy the slot by copying slots */ for (i = slot_no + 1; i < n_slots; i++) { rec_t* rec = (rec_t*) page_dir_slot_get_rec(page_dir_get_nth_slot(page, i)); page_dir_slot_set_rec(page_dir_get_nth_slot(page, i - 1), rec); } /* 4. Zero out the last slot, which will be removed */ mach_write_to_2(page_dir_get_nth_slot(page, n_slots - 1), 0); /* 5. Update the page header */ page_header_set_field(page, page_zip, PAGE_N_DIR_SLOTS, n_slots - 1); } /**************************************************************//** Used to add n slots to the directory. Does not set the record pointers in the added slots or update n_owned values: this is the responsibility of the caller. */ UNIV_INLINE void page_dir_add_slot( /*==============*/ page_t* page, /*!< in/out: the index page */ page_zip_des_t* page_zip,/*!< in/out: comprssed page, or NULL */ ulint start) /*!< in: the slot above which the new slots are added */ { page_dir_slot_t* slot; ulint n_slots; n_slots = page_dir_get_n_slots(page); ut_ad(start < n_slots - 1); /* Update the page header */ page_dir_set_n_slots(page, page_zip, n_slots + 1); /* Move slots up */ slot = page_dir_get_nth_slot(page, n_slots); memmove(slot, slot + PAGE_DIR_SLOT_SIZE, (n_slots - 1 - start) * PAGE_DIR_SLOT_SIZE); } /****************************************************************//** Splits a directory slot which owns too many records. */ UNIV_INTERN void page_dir_split_slot( /*================*/ page_t* page, /*!< in/out: index page */ page_zip_des_t* page_zip,/*!< in/out: compressed page whose uncompressed part will be written, or NULL */ ulint slot_no)/*!< in: the directory slot */ { rec_t* rec; page_dir_slot_t* new_slot; page_dir_slot_t* prev_slot; page_dir_slot_t* slot; ulint i; ulint n_owned; ut_ad(page); ut_ad(!page_zip || page_is_comp(page)); ut_ad(slot_no > 0); slot = page_dir_get_nth_slot(page, slot_no); n_owned = page_dir_slot_get_n_owned(slot); ut_ad(n_owned == PAGE_DIR_SLOT_MAX_N_OWNED + 1); /* 1. We loop to find a record approximately in the middle of the records owned by the slot. */ prev_slot = page_dir_get_nth_slot(page, slot_no - 1); rec = (rec_t*) page_dir_slot_get_rec(prev_slot); for (i = 0; i < n_owned / 2; i++) { rec = page_rec_get_next(rec); } ut_ad(n_owned / 2 >= PAGE_DIR_SLOT_MIN_N_OWNED); /* 2. We add one directory slot immediately below the slot to be split. */ page_dir_add_slot(page, page_zip, slot_no - 1); /* The added slot is now number slot_no, and the old slot is now number slot_no + 1 */ new_slot = page_dir_get_nth_slot(page, slot_no); slot = page_dir_get_nth_slot(page, slot_no + 1); /* 3. We store the appropriate values to the new slot. */ page_dir_slot_set_rec(new_slot, rec); page_dir_slot_set_n_owned(new_slot, page_zip, n_owned / 2); /* 4. Finally, we update the number of records field of the original slot */ page_dir_slot_set_n_owned(slot, page_zip, n_owned - (n_owned / 2)); } /*************************************************************//** Tries to balance the given directory slot with too few records with the upper neighbor, so that there are at least the minimum number of records owned by the slot; this may result in the merging of two slots. */ UNIV_INTERN void page_dir_balance_slot( /*==================*/ page_t* page, /*!< in/out: index page */ page_zip_des_t* page_zip,/*!< in/out: compressed page, or NULL */ ulint slot_no)/*!< in: the directory slot */ { page_dir_slot_t* slot; page_dir_slot_t* up_slot; ulint n_owned; ulint up_n_owned; rec_t* old_rec; rec_t* new_rec; ut_ad(page); ut_ad(!page_zip || page_is_comp(page)); ut_ad(slot_no > 0); slot = page_dir_get_nth_slot(page, slot_no); /* The last directory slot cannot be balanced with the upper neighbor, as there is none. */ if (UNIV_UNLIKELY(slot_no == page_dir_get_n_slots(page) - 1)) { return; } up_slot = page_dir_get_nth_slot(page, slot_no + 1); n_owned = page_dir_slot_get_n_owned(slot); up_n_owned = page_dir_slot_get_n_owned(up_slot); ut_ad(n_owned == PAGE_DIR_SLOT_MIN_N_OWNED - 1); /* If the upper slot has the minimum value of n_owned, we will merge the two slots, therefore we assert: */ ut_ad(2 * PAGE_DIR_SLOT_MIN_N_OWNED - 1 <= PAGE_DIR_SLOT_MAX_N_OWNED); if (up_n_owned > PAGE_DIR_SLOT_MIN_N_OWNED) { /* In this case we can just transfer one record owned by the upper slot to the property of the lower slot */ old_rec = (rec_t*) page_dir_slot_get_rec(slot); if (page_is_comp(page)) { new_rec = rec_get_next_ptr(old_rec, TRUE); rec_set_n_owned_new(old_rec, page_zip, 0); rec_set_n_owned_new(new_rec, page_zip, n_owned + 1); } else { new_rec = rec_get_next_ptr(old_rec, FALSE); rec_set_n_owned_old(old_rec, 0); rec_set_n_owned_old(new_rec, n_owned + 1); } page_dir_slot_set_rec(slot, new_rec); page_dir_slot_set_n_owned(up_slot, page_zip, up_n_owned -1); } else { /* In this case we may merge the two slots */ page_dir_delete_slot(page, page_zip, slot_no); } } #ifndef UNIV_HOTBACKUP /************************************************************//** Returns the middle record of the record list. If there are an even number of records in the list, returns the first record of the upper half-list. @return middle record */ UNIV_INTERN rec_t* page_get_middle_rec( /*================*/ page_t* page) /*!< in: page */ { page_dir_slot_t* slot; ulint middle; ulint i; ulint n_owned; ulint count; rec_t* rec; /* This many records we must leave behind */ middle = (page_get_n_recs(page) + PAGE_HEAP_NO_USER_LOW) / 2; count = 0; for (i = 0;; i++) { slot = page_dir_get_nth_slot(page, i); n_owned = page_dir_slot_get_n_owned(slot); if (count + n_owned > middle) { break; } else { count += n_owned; } } ut_ad(i > 0); slot = page_dir_get_nth_slot(page, i - 1); rec = (rec_t*) page_dir_slot_get_rec(slot); rec = page_rec_get_next(rec); /* There are now count records behind rec */ for (i = 0; i < middle - count; i++) { rec = page_rec_get_next(rec); } return(rec); } #endif /* !UNIV_HOTBACKUP */ /***************************************************************//** Returns the number of records before the given record in chain. The number includes infimum and supremum records. @return number of records */ UNIV_INTERN ulint page_rec_get_n_recs_before( /*=======================*/ const rec_t* rec) /*!< in: the physical record */ { const page_dir_slot_t* slot; const rec_t* slot_rec; const page_t* page; ulint i; lint n = 0; ut_ad(page_rec_check(rec)); page = page_align(rec); if (page_is_comp(page)) { while (rec_get_n_owned_new(rec) == 0) { rec = rec_get_next_ptr_const(rec, TRUE); n--; } for (i = 0; ; i++) { slot = page_dir_get_nth_slot(page, i); slot_rec = page_dir_slot_get_rec(slot); n += rec_get_n_owned_new(slot_rec); if (rec == slot_rec) { break; } } } else { while (rec_get_n_owned_old(rec) == 0) { rec = rec_get_next_ptr_const(rec, FALSE); n--; } for (i = 0; ; i++) { slot = page_dir_get_nth_slot(page, i); slot_rec = page_dir_slot_get_rec(slot); n += rec_get_n_owned_old(slot_rec); if (rec == slot_rec) { break; } } } n--; ut_ad(n >= 0); return((ulint) n); } #ifndef UNIV_HOTBACKUP /************************************************************//** Prints record contents including the data relevant only in the index page context. */ UNIV_INTERN void page_rec_print( /*===========*/ const rec_t* rec, /*!< in: physical record */ const ulint* offsets)/*!< in: record descriptor */ { ut_a(!page_rec_is_comp(rec) == !rec_offs_comp(offsets)); rec_print_new(ib_stream, rec, offsets); if (page_rec_is_comp(rec)) { ib_logger(ib_stream, " n_owned: %lu; heap_no: %lu; next rec: %lu\n", (ulong) rec_get_n_owned_new(rec), (ulong) rec_get_heap_no_new(rec), (ulong) rec_get_next_offs(rec, TRUE)); } else { ib_logger(ib_stream, " n_owned: %lu; heap_no: %lu; next rec: %lu\n", (ulong) rec_get_n_owned_old(rec), (ulong) rec_get_heap_no_old(rec), (ulong) rec_get_next_offs(rec, TRUE)); } page_rec_check(rec); rec_validate(rec, offsets); } /***************************************************************//** This is used to print the contents of the directory for debugging purposes. */ UNIV_INTERN void page_dir_print( /*===========*/ page_t* page, /*!< in: index page */ ulint pr_n) /*!< in: print n first and n last entries */ { ulint n; ulint i; page_dir_slot_t* slot; n = page_dir_get_n_slots(page); ib_logger(ib_stream, "--------------------------------\n" "PAGE DIRECTORY\n" "Page address %p\n" "Directory stack top at offs: %lu; number of slots: %lu\n", page, (ulong) page_offset(page_dir_get_nth_slot(page, n - 1)), (ulong) n); for (i = 0; i < n; i++) { slot = page_dir_get_nth_slot(page, i); if ((i == pr_n) && (i < n - pr_n)) { ib_logger(ib_stream, " ... \n"); } if ((i < pr_n) || (i >= n - pr_n)) { ib_logger(ib_stream, "Contents of slot: %lu: n_owned: %lu," " rec offs: %lu\n", (ulong) i, (ulong) page_dir_slot_get_n_owned(slot), (ulong) page_offset(page_dir_slot_get_rec(slot))); } } ib_logger(ib_stream, "Total of %lu records\n" "--------------------------------\n", (ulong) (PAGE_HEAP_NO_USER_LOW + page_get_n_recs(page))); } /***************************************************************//** This is used to print the contents of the page record list for debugging purposes. */ UNIV_INTERN void page_print_list( /*============*/ buf_block_t* block, /*!< in: index page */ dict_index_t* index, /*!< in: dictionary index of the page */ ulint pr_n) /*!< in: print n first and n last entries */ { page_t* page = block->frame; page_cur_t cur; ulint count; ulint n_recs; mem_heap_t* heap = NULL; ulint offsets_[REC_OFFS_NORMAL_SIZE]; ulint* offsets = offsets_; rec_offs_init(offsets_); ut_a((ibool)!!page_is_comp(page) == dict_table_is_comp(index->table)); ib_logger(ib_stream, "--------------------------------\n" "PAGE RECORD LIST\n" "Page address %p\n", page); n_recs = page_get_n_recs(page); page_cur_set_before_first(block, &cur); count = 0; for (;;) { offsets = rec_get_offsets(cur.rec, index, offsets, ULINT_UNDEFINED, &heap); page_rec_print(cur.rec, offsets); if (count == pr_n) { break; } if (page_cur_is_after_last(&cur)) { break; } page_cur_move_to_next(&cur); count++; } if (n_recs > 2 * pr_n) { ib_logger(ib_stream, " ... \n"); } while (!page_cur_is_after_last(&cur)) { page_cur_move_to_next(&cur); if (count + pr_n >= n_recs) { offsets = rec_get_offsets(cur.rec, index, offsets, ULINT_UNDEFINED, &heap); page_rec_print(cur.rec, offsets); } count++; } ib_logger(ib_stream, "Total of %lu records \n" "--------------------------------\n", (ulong) (count + 1)); if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } } /***************************************************************//** Prints the info in a page header. */ UNIV_INTERN void page_header_print( /*==============*/ const page_t* page) { ib_logger(ib_stream, "--------------------------------\n" "PAGE HEADER INFO\n" "Page address %p, n records %lu (%s)\n" "n dir slots %lu, heap top %lu\n" "Page n heap %lu, free %lu, garbage %lu\n" "Page last insert %lu, direction %lu, n direction %lu\n", page, (ulong) page_header_get_field(page, PAGE_N_RECS), page_is_comp(page) ? "compact format" : "original format", (ulong) page_header_get_field(page, PAGE_N_DIR_SLOTS), (ulong) page_header_get_field(page, PAGE_HEAP_TOP), (ulong) page_dir_get_n_heap(page), (ulong) page_header_get_field(page, PAGE_FREE), (ulong) page_header_get_field(page, PAGE_GARBAGE), (ulong) page_header_get_field(page, PAGE_LAST_INSERT), (ulong) page_header_get_field(page, PAGE_DIRECTION), (ulong) page_header_get_field(page, PAGE_N_DIRECTION)); } /***************************************************************//** This is used to print the contents of the page for debugging purposes. */ UNIV_INTERN void page_print( /*=======*/ buf_block_t* block, /*!< in: index page */ dict_index_t* index, /*!< in: dictionary index of the page */ ulint dn, /*!< in: print dn first and last entries in directory */ ulint rn) /*!< in: print rn first and last records in directory */ { page_t* page = block->frame; page_header_print(page); page_dir_print(page, dn); page_print_list(block, index, rn); } #endif /* !UNIV_HOTBACKUP */ /***************************************************************//** The following is used to validate a record on a page. This function differs from rec_validate as it can also check the n_owned field and the heap_no field. @return TRUE if ok */ UNIV_INTERN ibool page_rec_validate( /*==============*/ rec_t* rec, /*!< in: physical record */ const ulint* offsets)/*!< in: array returned by rec_get_offsets() */ { ulint n_owned; ulint heap_no; page_t* page; page = page_align(rec); ut_a(!page_is_comp(page) == !rec_offs_comp(offsets)); page_rec_check(rec); rec_validate(rec, offsets); if (page_rec_is_comp(rec)) { n_owned = rec_get_n_owned_new(rec); heap_no = rec_get_heap_no_new(rec); } else { n_owned = rec_get_n_owned_old(rec); heap_no = rec_get_heap_no_old(rec); } if (UNIV_UNLIKELY(!(n_owned <= PAGE_DIR_SLOT_MAX_N_OWNED))) { ib_logger(ib_stream, "InnoDB: Dir slot of rec %lu, n owned too big %lu\n", (ulong) page_offset(rec), (ulong) n_owned); return(FALSE); } if (UNIV_UNLIKELY(!(heap_no < page_dir_get_n_heap(page)))) { ib_logger(ib_stream, "InnoDB: Heap no of rec %lu too big %lu %lu\n", (ulong) page_offset(rec), (ulong) heap_no, (ulong) page_dir_get_n_heap(page)); return(FALSE); } return(TRUE); } #ifndef UNIV_HOTBACKUP /***************************************************************//** Checks that the first directory slot points to the infimum record and the last to the supremum. This function is intended to track if the bug fixed in 4.0.14 has caused corruption to users' databases. */ UNIV_INTERN void page_check_dir( /*===========*/ const page_t* page) /*!< in: index page */ { ulint n_slots; ulint infimum_offs; ulint supremum_offs; n_slots = page_dir_get_n_slots(page); infimum_offs = mach_read_from_2(page_dir_get_nth_slot(page, 0)); supremum_offs = mach_read_from_2(page_dir_get_nth_slot(page, n_slots - 1)); if (UNIV_UNLIKELY(!page_rec_is_infimum_low(infimum_offs))) { ib_logger(ib_stream, "InnoDB: Page directory corruption:" " infimum not pointed to\n"); buf_page_print(page, 0); } if (UNIV_UNLIKELY(!page_rec_is_supremum_low(supremum_offs))) { ib_logger(ib_stream, "InnoDB: Page directory corruption:" " supremum not pointed to\n"); buf_page_print(page, 0); } } #endif /* !UNIV_HOTBACKUP */ /***************************************************************//** This function checks the consistency of an index page when we do not know the index. This is also resilient so that this should never crash even if the page is total garbage. @return TRUE if ok */ UNIV_INTERN ibool page_simple_validate_old( /*=====================*/ page_t* page) /*!< in: old-style index page */ { page_dir_slot_t* slot; ulint slot_no; ulint n_slots; rec_t* rec; byte* rec_heap_top; ulint count; ulint own_count; ibool ret = FALSE; ut_a(!page_is_comp(page)); /* Check first that the record heap and the directory do not overlap. */ n_slots = page_dir_get_n_slots(page); if (UNIV_UNLIKELY(n_slots > UNIV_PAGE_SIZE / 4)) { ib_logger(ib_stream, "InnoDB: Nonsensical number %lu of page dir slots\n", (ulong) n_slots); goto func_exit; } rec_heap_top = page_header_get_ptr(page, PAGE_HEAP_TOP); if (UNIV_UNLIKELY(rec_heap_top > page_dir_get_nth_slot(page, n_slots - 1))) { ib_logger(ib_stream, "InnoDB: Record heap and dir overlap on a page," " heap top %lu, dir %lu\n", (ulong) page_header_get_field(page, PAGE_HEAP_TOP), (ulong) page_offset(page_dir_get_nth_slot(page, n_slots - 1))); goto func_exit; } /* Validate the record list in a loop checking also that it is consistent with the page record directory. */ count = 0; own_count = 1; slot_no = 0; slot = page_dir_get_nth_slot(page, slot_no); rec = page_get_infimum_rec(page); for (;;) { if (UNIV_UNLIKELY(rec > rec_heap_top)) { ib_logger(ib_stream, "InnoDB: Record %lu is above" " rec heap top %lu\n", (ulong)(rec - page), (ulong)(rec_heap_top - page)); goto func_exit; } if (UNIV_UNLIKELY(rec_get_n_owned_old(rec))) { /* This is a record pointed to by a dir slot */ if (UNIV_UNLIKELY(rec_get_n_owned_old(rec) != own_count)) { ib_logger(ib_stream, "InnoDB: Wrong owned count %lu, %lu," " rec %lu\n", (ulong) rec_get_n_owned_old(rec), (ulong) own_count, (ulong)(rec - page)); goto func_exit; } if (UNIV_UNLIKELY (page_dir_slot_get_rec(slot) != rec)) { ib_logger(ib_stream, "InnoDB: Dir slot does not point" " to right rec %lu\n", (ulong)(rec - page)); goto func_exit; } own_count = 0; if (!page_rec_is_supremum(rec)) { slot_no++; slot = page_dir_get_nth_slot(page, slot_no); } } if (page_rec_is_supremum(rec)) { break; } if (UNIV_UNLIKELY (rec_get_next_offs(rec, FALSE) < FIL_PAGE_DATA || rec_get_next_offs(rec, FALSE) >= UNIV_PAGE_SIZE)) { ib_logger(ib_stream, "InnoDB: Next record offset" " nonsensical %lu for rec %lu\n", (ulong) rec_get_next_offs(rec, FALSE), (ulong) (rec - page)); goto func_exit; } count++; if (UNIV_UNLIKELY(count > UNIV_PAGE_SIZE)) { ib_logger(ib_stream, "InnoDB: Page record list appears" " to be circular %lu\n", (ulong) count); goto func_exit; } rec = page_rec_get_next(rec); own_count++; } if (UNIV_UNLIKELY(rec_get_n_owned_old(rec) == 0)) { ib_logger(ib_stream, "InnoDB: n owned is zero in a supremum rec\n"); goto func_exit; } if (UNIV_UNLIKELY(slot_no != n_slots - 1)) { ib_logger(ib_stream, "InnoDB: n slots wrong %lu, %lu\n", (ulong) slot_no, (ulong) (n_slots - 1)); goto func_exit; } if (UNIV_UNLIKELY(page_header_get_field(page, PAGE_N_RECS) + PAGE_HEAP_NO_USER_LOW != count + 1)) { ib_logger(ib_stream, "InnoDB: n recs wrong %lu %lu\n", (ulong) page_header_get_field(page, PAGE_N_RECS) + PAGE_HEAP_NO_USER_LOW, (ulong) (count + 1)); goto func_exit; } /* Check then the free list */ rec = page_header_get_ptr(page, PAGE_FREE); while (rec != NULL) { if (UNIV_UNLIKELY(rec < page + FIL_PAGE_DATA || rec >= page + UNIV_PAGE_SIZE)) { ib_logger(ib_stream, "InnoDB: Free list record has" " a nonsensical offset %lu\n", (ulong) (rec - page)); goto func_exit; } if (UNIV_UNLIKELY(rec > rec_heap_top)) { ib_logger(ib_stream, "InnoDB: Free list record %lu" " is above rec heap top %lu\n", (ulong) (rec - page), (ulong) (rec_heap_top - page)); goto func_exit; } count++; if (UNIV_UNLIKELY(count > UNIV_PAGE_SIZE)) { ib_logger(ib_stream, "InnoDB: Page free list appears" " to be circular %lu\n", (ulong) count); goto func_exit; } rec = page_rec_get_next(rec); } if (UNIV_UNLIKELY(page_dir_get_n_heap(page) != count + 1)) { ib_logger(ib_stream, "InnoDB: N heap is wrong %lu, %lu\n", (ulong) page_dir_get_n_heap(page), (ulong) (count + 1)); goto func_exit; } ret = TRUE; func_exit: return(ret); } /***************************************************************//** This function checks the consistency of an index page when we do not know the index. This is also resilient so that this should never crash even if the page is total garbage. @return TRUE if ok */ UNIV_INTERN ibool page_simple_validate_new( /*=====================*/ page_t* page) /*!< in: new-style index page */ { page_dir_slot_t* slot; ulint slot_no; ulint n_slots; rec_t* rec; byte* rec_heap_top; ulint count; ulint own_count; ibool ret = FALSE; ut_a(page_is_comp(page)); /* Check first that the record heap and the directory do not overlap. */ n_slots = page_dir_get_n_slots(page); if (UNIV_UNLIKELY(n_slots > UNIV_PAGE_SIZE / 4)) { ib_logger(ib_stream, "InnoDB: Nonsensical number %lu" " of page dir slots\n", (ulong) n_slots); goto func_exit; } rec_heap_top = page_header_get_ptr(page, PAGE_HEAP_TOP); if (UNIV_UNLIKELY(rec_heap_top > page_dir_get_nth_slot(page, n_slots - 1))) { ib_logger(ib_stream, "InnoDB: Record heap and dir overlap on a page," " heap top %lu, dir %lu\n", (ulong) page_header_get_field(page, PAGE_HEAP_TOP), (ulong) page_offset(page_dir_get_nth_slot(page, n_slots - 1))); goto func_exit; } /* Validate the record list in a loop checking also that it is consistent with the page record directory. */ count = 0; own_count = 1; slot_no = 0; slot = page_dir_get_nth_slot(page, slot_no); rec = page_get_infimum_rec(page); for (;;) { if (UNIV_UNLIKELY(rec > rec_heap_top)) { ib_logger(ib_stream, "InnoDB: Record %lu is above rec" " heap top %lu\n", (ulong) page_offset(rec), (ulong) page_offset(rec_heap_top)); goto func_exit; } if (UNIV_UNLIKELY(rec_get_n_owned_new(rec))) { /* This is a record pointed to by a dir slot */ if (UNIV_UNLIKELY(rec_get_n_owned_new(rec) != own_count)) { ib_logger(ib_stream, "InnoDB: Wrong owned count %lu, %lu," " rec %lu\n", (ulong) rec_get_n_owned_new(rec), (ulong) own_count, (ulong) page_offset(rec)); goto func_exit; } if (UNIV_UNLIKELY (page_dir_slot_get_rec(slot) != rec)) { ib_logger(ib_stream, "InnoDB: Dir slot does not point" " to right rec %lu\n", (ulong) page_offset(rec)); goto func_exit; } own_count = 0; if (!page_rec_is_supremum(rec)) { slot_no++; slot = page_dir_get_nth_slot(page, slot_no); } } if (page_rec_is_supremum(rec)) { break; } if (UNIV_UNLIKELY (rec_get_next_offs(rec, TRUE) < FIL_PAGE_DATA || rec_get_next_offs(rec, TRUE) >= UNIV_PAGE_SIZE)) { ib_logger(ib_stream, "InnoDB: Next record offset nonsensical %lu" " for rec %lu\n", (ulong) rec_get_next_offs(rec, TRUE), (ulong) page_offset(rec)); goto func_exit; } count++; if (UNIV_UNLIKELY(count > UNIV_PAGE_SIZE)) { ib_logger(ib_stream, "InnoDB: Page record list appears" " to be circular %lu\n", (ulong) count); goto func_exit; } rec = page_rec_get_next(rec); own_count++; } if (UNIV_UNLIKELY(rec_get_n_owned_new(rec) == 0)) { ib_logger(ib_stream, "InnoDB: n owned is zero" " in a supremum rec\n"); goto func_exit; } if (UNIV_UNLIKELY(slot_no != n_slots - 1)) { ib_logger(ib_stream, "InnoDB: n slots wrong %lu, %lu\n", (ulong) slot_no, (ulong) (n_slots - 1)); goto func_exit; } if (UNIV_UNLIKELY(page_header_get_field(page, PAGE_N_RECS) + PAGE_HEAP_NO_USER_LOW != count + 1)) { ib_logger(ib_stream, "InnoDB: n recs wrong %lu %lu\n", (ulong) page_header_get_field(page, PAGE_N_RECS) + PAGE_HEAP_NO_USER_LOW, (ulong) (count + 1)); goto func_exit; } /* Check then the free list */ rec = page_header_get_ptr(page, PAGE_FREE); while (rec != NULL) { if (UNIV_UNLIKELY(rec < page + FIL_PAGE_DATA || rec >= page + UNIV_PAGE_SIZE)) { ib_logger(ib_stream, "InnoDB: Free list record has" " a nonsensical offset %lu\n", (ulong) page_offset(rec)); goto func_exit; } if (UNIV_UNLIKELY(rec > rec_heap_top)) { ib_logger(ib_stream, "InnoDB: Free list record %lu" " is above rec heap top %lu\n", (ulong) page_offset(rec), (ulong) page_offset(rec_heap_top)); goto func_exit; } count++; if (UNIV_UNLIKELY(count > UNIV_PAGE_SIZE)) { ib_logger(ib_stream, "InnoDB: Page free list appears" " to be circular %lu\n", (ulong) count); goto func_exit; } rec = page_rec_get_next(rec); } if (UNIV_UNLIKELY(page_dir_get_n_heap(page) != count + 1)) { ib_logger(ib_stream, "InnoDB: N heap is wrong %lu, %lu\n", (ulong) page_dir_get_n_heap(page), (ulong) (count + 1)); goto func_exit; } ret = TRUE; func_exit: return(ret); } /***************************************************************//** This function checks the consistency of an index page. @return TRUE if ok */ UNIV_INTERN ibool page_validate( /*==========*/ page_t* page, /*!< in: index page */ dict_index_t* index) /*!< in: data dictionary index containing the page record type definition */ { page_dir_slot_t*slot; mem_heap_t* heap; byte* buf; ulint count; ulint own_count; ulint rec_own_count; ulint slot_no; ulint data_size; rec_t* rec; rec_t* old_rec = NULL; ulint offs; ulint n_slots; ibool ret = FALSE; ulint i; ulint* offsets = NULL; ulint* old_offsets = NULL; if (UNIV_UNLIKELY((ibool) !!page_is_comp(page) != dict_table_is_comp(index->table))) { ib_logger("InnoDB: 'compact format' flag mismatch\n", ib_stream); goto func_exit2; } if (page_is_comp(page)) { if (UNIV_UNLIKELY(!page_simple_validate_new(page))) { goto func_exit2; } } else { if (UNIV_UNLIKELY(!page_simple_validate_old(page))) { goto func_exit2; } } heap = mem_heap_create(UNIV_PAGE_SIZE + 200); /* The following buffer is used to check that the records in the page record heap do not overlap */ buf = mem_heap_zalloc(heap, UNIV_PAGE_SIZE); /* Check first that the record heap and the directory do not overlap. */ n_slots = page_dir_get_n_slots(page); if (UNIV_UNLIKELY(!(page_header_get_ptr(page, PAGE_HEAP_TOP) <= page_dir_get_nth_slot(page, n_slots - 1)))) { ib_logger(ib_stream, "InnoDB: Record heap and dir overlap" " on space %lu page %lu index %s, %p, %p\n", (ulong) page_get_space_id(page), (ulong) page_get_page_no(page), index->name, page_header_get_ptr(page, PAGE_HEAP_TOP), page_dir_get_nth_slot(page, n_slots - 1)); goto func_exit; } /* Validate the record list in a loop checking also that it is consistent with the directory. */ count = 0; data_size = 0; own_count = 1; slot_no = 0; slot = page_dir_get_nth_slot(page, slot_no); rec = page_get_infimum_rec(page); for (;;) { offsets = rec_get_offsets(rec, index, offsets, ULINT_UNDEFINED, &heap); if (page_is_comp(page) && page_rec_is_user_rec(rec) && UNIV_UNLIKELY(rec_get_node_ptr_flag(rec) == page_is_leaf(page))) { ib_logger(ib_stream, "InnoDB: node_ptr flag mismatch\n"); goto func_exit; } if (UNIV_UNLIKELY(!page_rec_validate(rec, offsets))) { goto func_exit; } #ifndef UNIV_HOTBACKUP /* Check that the records are in the ascending order */ if (UNIV_LIKELY(count >= PAGE_HEAP_NO_USER_LOW) && !page_rec_is_supremum(rec)) { if (UNIV_UNLIKELY (1 != cmp_rec_rec(rec, old_rec, offsets, old_offsets, index))) { ib_logger(ib_stream, "InnoDB: Records in wrong order" " on space %lu page %lu index %s\n", (ulong) page_get_space_id(page), (ulong) page_get_page_no(page), index->name); ib_logger(ib_stream, "\nInnoDB: previous record "); rec_print_new(ib_stream, old_rec, old_offsets); ib_logger(ib_stream, "\nInnoDB: record "); rec_print_new(ib_stream, rec, offsets); ib_logger(ib_stream, "\n"); goto func_exit; } } #endif /* !UNIV_HOTBACKUP */ if (page_rec_is_user_rec(rec)) { data_size += rec_offs_size(offsets); } offs = page_offset(rec_get_start(rec, offsets)); i = rec_offs_size(offsets); if (UNIV_UNLIKELY(offs + i >= UNIV_PAGE_SIZE)) { ib_logger(ib_stream, "InnoDB: record offset out of bounds\n"); goto func_exit; } while (i--) { if (UNIV_UNLIKELY(buf[offs + i])) { /* No other record may overlap this */ ib_logger(ib_stream, "InnoDB: Record overlaps another\n"); goto func_exit; } buf[offs + i] = 1; } if (page_is_comp(page)) { rec_own_count = rec_get_n_owned_new(rec); } else { rec_own_count = rec_get_n_owned_old(rec); } if (UNIV_UNLIKELY(rec_own_count)) { /* This is a record pointed to by a dir slot */ if (UNIV_UNLIKELY(rec_own_count != own_count)) { ib_logger(ib_stream, "InnoDB: Wrong owned count %lu, %lu\n", (ulong) rec_own_count, (ulong) own_count); goto func_exit; } if (page_dir_slot_get_rec(slot) != rec) { ib_logger(ib_stream, "InnoDB: Dir slot does not" " point to right rec\n"); goto func_exit; } page_dir_slot_check(slot); own_count = 0; if (!page_rec_is_supremum(rec)) { slot_no++; slot = page_dir_get_nth_slot(page, slot_no); } } if (page_rec_is_supremum(rec)) { break; } count++; own_count++; old_rec = rec; rec = page_rec_get_next(rec); /* set old_offsets to offsets; recycle offsets */ { ulint* offs = old_offsets; old_offsets = offsets; offsets = offs; } } if (page_is_comp(page)) { if (UNIV_UNLIKELY(rec_get_n_owned_new(rec) == 0)) { goto n_owned_zero; } } else if (UNIV_UNLIKELY(rec_get_n_owned_old(rec) == 0)) { n_owned_zero: ib_logger(ib_stream, "InnoDB: n owned is zero\n"); goto func_exit; } if (UNIV_UNLIKELY(slot_no != n_slots - 1)) { ib_logger(ib_stream, "InnoDB: n slots wrong %lu %lu\n", (ulong) slot_no, (ulong) (n_slots - 1)); goto func_exit; } if (UNIV_UNLIKELY(page_header_get_field(page, PAGE_N_RECS) + PAGE_HEAP_NO_USER_LOW != count + 1)) { ib_logger(ib_stream, "InnoDB: n recs wrong %lu %lu\n", (ulong) page_header_get_field(page, PAGE_N_RECS) + PAGE_HEAP_NO_USER_LOW, (ulong) (count + 1)); goto func_exit; } if (UNIV_UNLIKELY(data_size != page_get_data_size(page))) { ib_logger(ib_stream, "InnoDB: Summed data size %lu, returned by func %lu\n", (ulong) data_size, (ulong) page_get_data_size(page)); goto func_exit; } /* Check then the free list */ rec = page_header_get_ptr(page, PAGE_FREE); while (rec != NULL) { offsets = rec_get_offsets(rec, index, offsets, ULINT_UNDEFINED, &heap); if (UNIV_UNLIKELY(!page_rec_validate(rec, offsets))) { goto func_exit; } count++; offs = page_offset(rec_get_start(rec, offsets)); i = rec_offs_size(offsets); if (UNIV_UNLIKELY(offs + i >= UNIV_PAGE_SIZE)) { ib_logger(ib_stream, "InnoDB: record offset out of bounds\n"); goto func_exit; } while (i--) { if (UNIV_UNLIKELY(buf[offs + i])) { ib_logger(ib_stream, "InnoDB: Record overlaps another" " in free list\n"); goto func_exit; } buf[offs + i] = 1; } rec = page_rec_get_next(rec); } if (UNIV_UNLIKELY(page_dir_get_n_heap(page) != count + 1)) { ib_logger(ib_stream, "InnoDB: N heap is wrong %lu %lu\n", (ulong) page_dir_get_n_heap(page), (ulong) count + 1); goto func_exit; } ret = TRUE; func_exit: mem_heap_free(heap); if (UNIV_UNLIKELY(ret == FALSE)) { func_exit2: ib_logger(ib_stream, "InnoDB: Apparent corruption" " in space %lu page %lu index %s\n", (ulong) page_get_space_id(page), (ulong) page_get_page_no(page), index->name); buf_page_print(page, 0); } return(ret); } #ifndef UNIV_HOTBACKUP /***************************************************************//** Looks in the page record list for a record with the given heap number. @return record, NULL if not found */ UNIV_INTERN const rec_t* page_find_rec_with_heap_no( /*=======================*/ const page_t* page, /*!< in: index page */ ulint heap_no)/*!< in: heap number */ { const rec_t* rec; if (page_is_comp(page)) { rec = page + PAGE_NEW_INFIMUM; for(;;) { ulint rec_heap_no = rec_get_heap_no_new(rec); if (rec_heap_no == heap_no) { return(rec); } else if (rec_heap_no == PAGE_HEAP_NO_SUPREMUM) { return(NULL); } rec = page + rec_get_next_offs(rec, TRUE); } } else { rec = page + PAGE_OLD_INFIMUM; for (;;) { ulint rec_heap_no = rec_get_heap_no_old(rec); if (rec_heap_no == heap_no) { return(rec); } else if (rec_heap_no == PAGE_HEAP_NO_SUPREMUM) { return(NULL); } rec = page + rec_get_next_offs(rec, FALSE); } } } #endif /* !UNIV_HOTBACKUP */ haildb-2.3.2/log/0000755000175000017500000000000011513177437014423 5ustar00pcrewspcrews00000000000000haildb-2.3.2/log/log0recv.c0000644000175000017500000030457511513177357016327 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1997, 2010, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file log/log0recv.c Recovery Created 9/20/1997 Heikki Tuuri *******************************************************/ #include "log0recv.h" #ifdef UNIV_NONINL #include "log0recv.ic" #endif #include "mem0mem.h" #include "buf0buf.h" #include "buf0flu.h" #include "mtr0mtr.h" #include "mtr0log.h" #include "page0cur.h" #include "page0zip.h" #include "btr0btr.h" #include "btr0cur.h" #include "ibuf0ibuf.h" #include "trx0undo.h" #include "trx0rec.h" #include "fil0fil.h" #ifndef UNIV_HOTBACKUP # include "buf0rea.h" # include "srv0srv.h" # include "srv0start.h" # include "trx0roll.h" # include "ddl0ddl.h" # include "sync0sync.h" #else /* !UNIV_HOTBACKUP */ #include "btr0btr.h" /** This is set to FALSE if the backup was originally taken with the ibbackup --include regexp option: then we do not want to create tables in directories which were not included */ UNIV_INTERN ibool recv_replay_file_ops = TRUE; #endif /* !UNIV_HOTBACKUP */ /** Log records are stored in the hash table in chunks at most of this size; this must be less than UNIV_PAGE_SIZE as it is stored in the buffer pool */ #define RECV_DATA_BLOCK_SIZE (MEM_MAX_ALLOC_IN_BUF - sizeof(recv_data_t)) /** Read-ahead area in applying log records to file pages */ #define RECV_READ_AHEAD_AREA 32 /** The recovery system */ UNIV_INTERN recv_sys_t* recv_sys = NULL; /** TRUE when applying redo log records during crash recovery; FALSE otherwise. Note that this is FALSE while a background thread is rolling back incomplete transactions. */ UNIV_INTERN ibool recv_recovery_on; #ifdef UNIV_LOG_ARCHIVE /** TRUE when applying redo log records from an archived log file */ UNIV_INTERN ibool recv_recovery_from_backup_on; #endif /* UNIV_LOG_ARCHIVE */ #ifndef UNIV_HOTBACKUP /** TRUE when recv_init_crash_recovery() has been called. */ UNIV_INTERN ibool recv_needed_recovery; # ifdef UNIV_DEBUG /** TRUE if writing to the redo log (mtr_commit) is forbidden. Protected by log_sys->mutex. */ UNIV_INTERN ibool recv_no_log_write = FALSE; # endif /* UNIV_DEBUG */ /** TRUE if buf_page_is_corrupted() should check if the log sequence number (FIL_PAGE_LSN) is in the future. Initially FALSE, and set by recv_recovery_from_checkpoint_start_func(). */ UNIV_INTERN ibool recv_lsn_checks_on; /* User callback function that is called before InnoDB attempts to rollback incomplete transaction after crash recovery. */ UNIV_INTERN ib_cb_t recv_pre_rollback_hook = NULL; /* There are two conditions under which we scan the logs, the first is normal startup and the second is when we do a recovery from an archive. This flag is set if we are doing a scan from the last checkpoint during startup. If we find log entries that were written after the last checkpoint we know that the server was not cleanly shutdown. We must then initialize the crash recovery environment before attempting to store these entries in the log hash table. */ static ibool recv_log_scan_is_startup_type; /** If the following is TRUE, the buffer pool file pages must be invalidated after recovery and no ibuf operations are allowed; this becomes TRUE if the log record hash table becomes too full, and log records must be merged to file pages already before the recovery is finished: in this case no ibuf operations are allowed, as they could modify the pages read in the buffer pool before the pages have been recovered to the up-to-date state. TRUE means that recovery is running and no operations on the log files are allowed yet: the variable name is misleading. */ UNIV_INTERN ibool recv_no_ibuf_operations; /** TRUE when the redo log is being backed up */ # define recv_is_making_a_backup FALSE /** TRUE when recovering from a backed up redo log file */ # define recv_is_from_backup FALSE #else /* !UNIV_HOTBACKUP */ # define recv_needed_recovery FALSE /** TRUE when the redo log is being backed up */ UNIV_INTERN ibool recv_is_making_a_backup = FALSE; /** TRUE when recovering from a backed up redo log file */ UNIV_INTERN ibool recv_is_from_backup = FALSE; # define buf_pool_get_curr_size() (5 * 1024 * 1024) #endif /* !UNIV_HOTBACKUP */ /** The following counter is used to decide when to print info on log scan */ static ulint recv_scan_print_counter; /** The type of the previous parsed redo log record */ static ulint recv_previous_parsed_rec_type; /** The offset of the previous parsed redo log record */ static ulint recv_previous_parsed_rec_offset; /** The 'multi' flag of the previous parsed redo log record */ static ulint recv_previous_parsed_rec_is_multi; /** Maximum page number encountered in the redo log */ UNIV_INTERN ulint recv_max_parsed_page_no; /** This many frames must be left free in the buffer pool when we scan the log and store the scanned log records in the buffer pool: we will use these free frames to read in pages when we start applying the log records to the database. This is the default value. If the actual size of the buffer pool is larger than 10 MB we'll set this value to 512. */ UNIV_INTERN ulint recv_n_pool_free_frames; /** The maximum lsn we see for a page during the recovery process. If this is bigger than the lsn we are able to scan up to, that is an indication that the recovery failed and the database may be corrupt. */ UNIV_INTERN ib_uint64_t recv_max_page_lsn; /* prototypes */ #ifndef UNIV_HOTBACKUP /*******************************************************//** Initialize crash recovery environment. Can be called iff recv_needed_recovery == FALSE. */ static void recv_start_crash_recovery( /*======================*/ ib_recovery_t recovery); /*!< in: recovery flag */ #endif /* !UNIV_HOTBACKUP */ /********************************************************//** Reset the state of the recovery system variables. */ UNIV_INTERN void recv_sys_var_init(void) /*===================*/ { recv_sys = NULL; recv_lsn_checks_on = FALSE; recv_n_pool_free_frames = 256; recv_recovery_on = FALSE; #ifdef UNIV_HOTBACKUP recv_recovery_from_backup_on = FALSE; recv_is_from_backup = FALSE; #endif recv_needed_recovery = FALSE; recv_lsn_checks_on = FALSE; recv_log_scan_is_startup_type = FALSE; recv_no_ibuf_operations = FALSE; recv_scan_print_counter = 0; recv_previous_parsed_rec_type = 999999; recv_previous_parsed_rec_offset = 0; recv_previous_parsed_rec_is_multi = 0; recv_max_parsed_page_no = 0; recv_n_pool_free_frames = 256; recv_max_page_lsn = 0; } /************************************************************ Creates the recovery system. */ UNIV_INTERN void recv_sys_create(void) /*=================*/ { if (recv_sys != NULL) { return; } recv_sys = mem_alloc(sizeof(*recv_sys)); memset(recv_sys, 0x0, sizeof(*recv_sys)); mutex_create(&recv_sys->mutex, SYNC_RECV); recv_sys->heap = NULL; recv_sys->addr_hash = NULL; } /********************************************************//** Release recovery system mutexes. */ UNIV_INTERN void recv_sys_close(void) /*===============*/ { if (recv_sys != NULL) { mutex_free(&recv_sys->mutex); memset(&recv_sys->mutex, 0x0, sizeof(recv_sys->mutex)); } } /************************************************************ Frees the recovery system memory. */ UNIV_INTERN void recv_sys_mem_free(void) /*===================*/ { if (recv_sys != NULL) { if (recv_sys->addr_hash != NULL) { hash_table_free(recv_sys->addr_hash); } if (recv_sys->heap != NULL) { mem_heap_free(recv_sys->heap); } if (recv_sys->buf != NULL) { ut_free(recv_sys->buf); } if (recv_sys->last_block_buf_start != NULL) { mem_free(recv_sys->last_block_buf_start); } mem_free(recv_sys); recv_sys = NULL; } } /************************************************************ Inits the recovery system for a recovery operation. */ UNIV_INTERN void recv_sys_init( /*==========*/ ulint available_memory) /*!< in: available memory in bytes */ { if (recv_sys->heap != NULL) { return; } /* Initialize red-black tree for fast insertions into the flush_list during recovery process. As this initialization is done while holding the buffer pool mutex we perform it before acquiring recv_sys->mutex. */ #ifndef UNIV_HOTBACKUP buf_flush_init_flush_rbt(); mutex_enter(&(recv_sys->mutex)); recv_sys->heap = mem_heap_create_in_buffer(256); #else /* !UNIV_HOTBACKUP */ recv_sys->heap = mem_heap_create(256); recv_is_from_backup = TRUE; #endif /* !UNIV_HOTBACKUP */ /* Set appropriate value of recv_n_pool_free_frames. */ if (buf_pool_get_curr_size() >= (10 * 1024 * 1024)) { /* Buffer pool of size greater than 10 MB. */ recv_n_pool_free_frames = 512; } recv_sys->buf = ut_malloc(RECV_PARSING_BUF_SIZE); recv_sys->len = 0; recv_sys->recovered_offset = 0; recv_sys->addr_hash = hash_create(available_memory / 64); recv_sys->n_addrs = 0; recv_sys->apply_log_recs = FALSE; recv_sys->apply_batch_on = FALSE; recv_sys->last_block_buf_start = mem_alloc(2 * OS_FILE_LOG_BLOCK_SIZE); recv_sys->last_block = ut_align(recv_sys->last_block_buf_start, OS_FILE_LOG_BLOCK_SIZE); recv_sys->found_corrupt_log = FALSE; recv_max_page_lsn = 0; mutex_exit(&(recv_sys->mutex)); } /********************************************************//** Empties the hash table when it has been fully processed. */ static void recv_sys_empty_hash(void) /*=====================*/ { ut_ad(mutex_own(&(recv_sys->mutex))); if (recv_sys->n_addrs != 0) { ib_logger(ib_stream, "InnoDB: Error: %lu pages with log records" " were left unprocessed!\n" "InnoDB: Maximum page number with" " log records on it %lu\n", (ulong) recv_sys->n_addrs, (ulong) recv_max_parsed_page_no); ut_error; } hash_table_free(recv_sys->addr_hash); mem_heap_empty(recv_sys->heap); recv_sys->addr_hash = hash_create(buf_pool_get_curr_size() / 256); } #ifndef UNIV_HOTBACKUP # ifndef UNIV_LOG_DEBUG /********************************************************//** Frees the recovery system. */ static void recv_sys_debug_free(void) /*=====================*/ { mutex_enter(&(recv_sys->mutex)); hash_table_free(recv_sys->addr_hash); mem_heap_free(recv_sys->heap); ut_free(recv_sys->buf); mem_free(recv_sys->last_block_buf_start); recv_sys->buf = NULL; recv_sys->heap = NULL; recv_sys->addr_hash = NULL; recv_sys->last_block_buf_start = NULL; mutex_exit(&(recv_sys->mutex)); /* Free up the flush_rbt. */ buf_flush_free_flush_rbt(); } # endif /* UNIV_LOG_DEBUG */ /********************************************************//** Truncates possible corrupted or extra records from a log group. */ static void recv_truncate_group( /*================*/ log_group_t* group, /*!< in: log group */ ib_uint64_t recovered_lsn, /*!< in: recovery succeeded up to this lsn */ ib_uint64_t limit_lsn, /*!< in: this was the limit for recovery */ ib_uint64_t checkpoint_lsn, /*!< in: recovery was started from this checkpoint */ ib_uint64_t archived_lsn) /*!< in: the log has been archived up to this lsn */ { ib_uint64_t start_lsn; ib_uint64_t end_lsn; ib_uint64_t finish_lsn1; ib_uint64_t finish_lsn2; ib_uint64_t finish_lsn; ulint len; ulint i; if (archived_lsn == IB_UINT64_T_MAX) { /* Checkpoint was taken in the NOARCHIVELOG mode */ archived_lsn = checkpoint_lsn; } finish_lsn1 = ut_uint64_align_down(archived_lsn, OS_FILE_LOG_BLOCK_SIZE) + log_group_get_capacity(group); finish_lsn2 = ut_uint64_align_up(recovered_lsn, OS_FILE_LOG_BLOCK_SIZE) + recv_sys->last_log_buf_size; if (limit_lsn != IB_UINT64_T_MAX) { /* We do not know how far we should erase log records: erase as much as possible */ finish_lsn = finish_lsn1; } else { /* It is enough to erase the length of the log buffer */ finish_lsn = finish_lsn1 < finish_lsn2 ? finish_lsn1 : finish_lsn2; } ut_a(RECV_SCAN_SIZE <= log_sys->buf_size); /* Write the log buffer full of zeros */ for (i = 0; i < RECV_SCAN_SIZE; i++) { *(log_sys->buf + i) = '\0'; } start_lsn = ut_uint64_align_down(recovered_lsn, OS_FILE_LOG_BLOCK_SIZE); if (start_lsn != recovered_lsn) { /* Copy the last incomplete log block to the log buffer and edit its data length: */ ut_memcpy(log_sys->buf, recv_sys->last_block, OS_FILE_LOG_BLOCK_SIZE); log_block_set_data_len(log_sys->buf, (ulint) (recovered_lsn - start_lsn)); } if (start_lsn >= finish_lsn) { return; } for (;;) { end_lsn = start_lsn + RECV_SCAN_SIZE; if (end_lsn > finish_lsn) { end_lsn = finish_lsn; } len = (ulint) (end_lsn - start_lsn); log_group_write_buf(group, log_sys->buf, len, start_lsn, 0); if (end_lsn >= finish_lsn) { return; } /* Write the log buffer full of zeros */ for (i = 0; i < RECV_SCAN_SIZE; i++) { *(log_sys->buf + i) = '\0'; } start_lsn = end_lsn; } } /********************************************************//** Copies the log segment between group->recovered_lsn and recovered_lsn from the most up-to-date log group to group, so that it contains the latest log data. */ static void recv_copy_group( /*============*/ log_group_t* up_to_date_group, /*!< in: the most up-to-date log group */ log_group_t* group, /*!< in: copy to this log group */ ib_uint64_t recovered_lsn) /*!< in: recovery succeeded up to this lsn */ { ib_uint64_t start_lsn; ib_uint64_t end_lsn; ulint len; if (group->scanned_lsn >= recovered_lsn) { return; } ut_a(RECV_SCAN_SIZE <= log_sys->buf_size); start_lsn = ut_uint64_align_down(group->scanned_lsn, OS_FILE_LOG_BLOCK_SIZE); for (;;) { end_lsn = start_lsn + RECV_SCAN_SIZE; if (end_lsn > recovered_lsn) { end_lsn = ut_uint64_align_up(recovered_lsn, OS_FILE_LOG_BLOCK_SIZE); } log_group_read_log_seg(LOG_RECOVER, log_sys->buf, up_to_date_group, start_lsn, end_lsn); len = (ulint) (end_lsn - start_lsn); log_group_write_buf(group, log_sys->buf, len, start_lsn, 0); if (end_lsn >= recovered_lsn) { return; } start_lsn = end_lsn; } } /********************************************************//** Copies a log segment from the most up-to-date log group to the other log groups, so that they all contain the latest log data. Also writes the info about the latest checkpoint to the groups, and inits the fields in the group memory structs to up-to-date values. */ static void recv_synchronize_groups( /*====================*/ log_group_t* up_to_date_group) /*!< in: the most up-to-date log group */ { log_group_t* group; ib_uint64_t start_lsn; ib_uint64_t end_lsn; ib_uint64_t recovered_lsn; ib_uint64_t limit_lsn; recovered_lsn = recv_sys->recovered_lsn; limit_lsn = recv_sys->limit_lsn; /* Read the last recovered log block to the recovery system buffer: the block is always incomplete */ start_lsn = ut_uint64_align_down(recovered_lsn, OS_FILE_LOG_BLOCK_SIZE); end_lsn = ut_uint64_align_up(recovered_lsn, OS_FILE_LOG_BLOCK_SIZE); ut_a(start_lsn != end_lsn); log_group_read_log_seg(LOG_RECOVER, recv_sys->last_block, up_to_date_group, start_lsn, end_lsn); group = UT_LIST_GET_FIRST(log_sys->log_groups); while (group) { if (group != up_to_date_group) { /* Copy log data if needed */ recv_copy_group(group, up_to_date_group, recovered_lsn); } /* Update the fields in the group struct to correspond to recovered_lsn */ log_group_set_fields(group, recovered_lsn); group = UT_LIST_GET_NEXT(log_groups, group); } /* Copy the checkpoint info to the groups; remember that we have incremented checkpoint_no by one, and the info will not be written over the max checkpoint info, thus making the preservation of max checkpoint info on disk certain */ log_groups_write_checkpoint_info(); mutex_exit(&(log_sys->mutex)); /* Wait for the checkpoint write to complete */ rw_lock_s_lock(&(log_sys->checkpoint_lock)); rw_lock_s_unlock(&(log_sys->checkpoint_lock)); mutex_enter(&(log_sys->mutex)); } #endif /* !UNIV_HOTBACKUP */ /***********************************************************************//** Checks the consistency of the checkpoint info @return TRUE if ok */ static ibool recv_check_cp_is_consistent( /*========================*/ const byte* buf) /*!< in: buffer containing checkpoint info */ { ulint fold; fold = ut_fold_binary(buf, LOG_CHECKPOINT_CHECKSUM_1); if ((fold & 0xFFFFFFFFUL) != mach_read_from_4( buf + LOG_CHECKPOINT_CHECKSUM_1)) { return(FALSE); } fold = ut_fold_binary(buf + LOG_CHECKPOINT_LSN, LOG_CHECKPOINT_CHECKSUM_2 - LOG_CHECKPOINT_LSN); if ((fold & 0xFFFFFFFFUL) != mach_read_from_4( buf + LOG_CHECKPOINT_CHECKSUM_2)) { return(FALSE); } return(TRUE); } #ifndef UNIV_HOTBACKUP /********************************************************//** Looks for the maximum consistent checkpoint from the log groups. @return error code or DB_SUCCESS */ static ulint recv_find_max_checkpoint( /*=====================*/ log_group_t** max_group, /*!< out: max group */ ulint* max_field) /*!< out: LOG_CHECKPOINT_1 or LOG_CHECKPOINT_2 */ { log_group_t* group; ib_uint64_t max_no; ib_uint64_t checkpoint_no; ulint field; byte* buf; group = UT_LIST_GET_FIRST(log_sys->log_groups); max_no = 0; *max_group = NULL; *max_field = 0; buf = log_sys->checkpoint_buf; while (group) { group->state = LOG_GROUP_CORRUPTED; for (field = LOG_CHECKPOINT_1; field <= LOG_CHECKPOINT_2; field += LOG_CHECKPOINT_2 - LOG_CHECKPOINT_1) { log_group_read_checkpoint_info(group, field); if (!recv_check_cp_is_consistent(buf)) { #ifdef UNIV_DEBUG if (log_debug_writes) { ib_logger(ib_stream, "InnoDB: Checkpoint in group" " %lu at %lu invalid, %lu\n", (ulong) group->id, (ulong) field, (ulong) mach_read_from_4( buf + LOG_CHECKPOINT_CHECKSUM_1)); } #endif /* UNIV_DEBUG */ goto not_consistent; } group->state = LOG_GROUP_OK; group->lsn = mach_read_ull( buf + LOG_CHECKPOINT_LSN); group->lsn_offset = mach_read_from_4( buf + LOG_CHECKPOINT_OFFSET); checkpoint_no = mach_read_ull( buf + LOG_CHECKPOINT_NO); #ifdef UNIV_DEBUG if (log_debug_writes) { ib_logger(ib_stream, "InnoDB: Checkpoint number %lu" " found in group %lu\n", (ulong) checkpoint_no, (ulong) group->id); } #endif /* UNIV_DEBUG */ if (checkpoint_no >= max_no) { *max_group = group; *max_field = field; max_no = checkpoint_no; } not_consistent: ; } group = UT_LIST_GET_NEXT(log_groups, group); } if (*max_group == NULL) { ib_logger(ib_stream, "InnoDB: No valid checkpoint found.\n" "InnoDB: If this error appears when you are" " creating an InnoDB database,\n" "InnoDB: the problem may be that during" " an earlier attempt you managed\n" "InnoDB: to create the InnoDB data files," " but log file creation failed.\n" "InnoDB: If that is the case, please refer to\n" "InnoDB: the InnoDB website for details\n"); return(DB_ERROR); } return(DB_SUCCESS); } #else /* !UNIV_HOTBACKUP */ /*******************************************************************//** Reads the checkpoint info needed in hot backup. @return TRUE if success */ UNIV_INTERN ibool recv_read_cp_info_for_backup( /*=========================*/ const byte* hdr, /*!< in: buffer containing the log group header */ ib_uint64_t* lsn, /*!< out: checkpoint lsn */ ulint* offset, /*!< out: checkpoint offset in the log group */ ulint* fsp_limit,/*!< out: fsp limit of space 0, 1000000000 if the database is running with < version 3.23.50 of InnoDB */ ib_uint64_t* cp_no, /*!< out: checkpoint number */ ib_uint64_t* first_header_lsn) /*!< out: lsn of of the start of the first log file */ { ulint max_cp = 0; ib_uint64_t max_cp_no = 0; const byte* cp_buf; cp_buf = hdr + LOG_CHECKPOINT_1; if (recv_check_cp_is_consistent(cp_buf)) { max_cp_no = mach_read_ull(cp_buf + LOG_CHECKPOINT_NO); max_cp = LOG_CHECKPOINT_1; } cp_buf = hdr + LOG_CHECKPOINT_2; if (recv_check_cp_is_consistent(cp_buf)) { if (mach_read_ull(cp_buf + LOG_CHECKPOINT_NO) > max_cp_no) { max_cp = LOG_CHECKPOINT_2; } } if (max_cp == 0) { return(FALSE); } cp_buf = hdr + max_cp; *lsn = mach_read_ull(cp_buf + LOG_CHECKPOINT_LSN); *offset = mach_read_from_4(cp_buf + LOG_CHECKPOINT_OFFSET); /* If the user is running a pre-3.23.50 version of InnoDB, its checkpoint data does not contain the fsp limit info */ if (mach_read_from_4(cp_buf + LOG_CHECKPOINT_FSP_MAGIC_N) == LOG_CHECKPOINT_FSP_MAGIC_N_VAL) { *fsp_limit = mach_read_from_4( cp_buf + LOG_CHECKPOINT_FSP_FREE_LIMIT); if (*fsp_limit == 0) { *fsp_limit = 1000000000; } } else { *fsp_limit = 1000000000; } /* ib_logger(ib_stream, "fsp limit %lu MB\n", *fsp_limit); */ *cp_no = mach_read_ull(cp_buf + LOG_CHECKPOINT_NO); *first_header_lsn = mach_read_ull(hdr + LOG_FILE_START_LSN); return(TRUE); } #endif /* !UNIV_HOTBACKUP */ /******************************************************//** Checks the 4-byte checksum to the trailer checksum field of a log block. We also accept a log block in the old format before InnoDB-3.23.52 where the checksum field contains the log block number. @return TRUE if ok, or if the log block may be in the format of InnoDB version predating 3.23.52 */ static ibool log_block_checksum_is_ok_or_old_format( /*===================================*/ const byte* block) /*!< in: pointer to a log block */ { #ifdef UNIV_LOG_DEBUG return(TRUE); #endif /* UNIV_LOG_DEBUG */ if (log_block_calc_checksum(block) == log_block_get_checksum(block)) { return(TRUE); } if (log_block_get_hdr_no(block) == log_block_get_checksum(block)) { /* We assume the log block is in the format of InnoDB version < 3.23.52 and the block is ok */ #if 0 ib_logger(ib_stream, "InnoDB: Scanned old format < InnoDB-3.23.52" " log block number %lu\n", log_block_get_hdr_no(block)); #endif return(TRUE); } return(FALSE); } #ifdef UNIV_HOTBACKUP /*******************************************************************//** Scans the log segment and n_bytes_scanned is set to the length of valid log scanned. */ UNIV_INTERN void recv_scan_log_seg_for_backup( /*=========================*/ byte* buf, /*!< in: buffer containing log data */ ulint buf_len, /*!< in: data length in that buffer */ ib_uint64_t* scanned_lsn, /*!< in/out: lsn of buffer start, we return scanned lsn */ ulint* scanned_checkpoint_no, /*!< in/out: 4 lowest bytes of the highest scanned checkpoint number so far */ ulint* n_bytes_scanned)/*!< out: how much we were able to scan, smaller than buf_len if log data ended here */ { ulint data_len; byte* log_block; ulint no; *n_bytes_scanned = 0; for (log_block = buf; log_block < buf + buf_len; log_block += OS_FILE_LOG_BLOCK_SIZE) { no = log_block_get_hdr_no(log_block); #if 0 ib_logger(ib_stream, "Log block header no %lu\n", no); #endif if (no != log_block_convert_lsn_to_no(*scanned_lsn) || !log_block_checksum_is_ok_or_old_format(log_block)) { #if 0 ib_logger(ib_stream, "Log block n:o %lu, scanned lsn n:o %lu\n", no, log_block_convert_lsn_to_no(*scanned_lsn)); #endif /* Garbage or an incompletely written log block */ log_block += OS_FILE_LOG_BLOCK_SIZE; #if 0 ib_logger(ib_stream, "Next log block n:o %lu\n", log_block_get_hdr_no(log_block)); #endif break; } if (*scanned_checkpoint_no > 0 && log_block_get_checkpoint_no(log_block) < *scanned_checkpoint_no && *scanned_checkpoint_no - log_block_get_checkpoint_no(log_block) > 0x80000000UL) { /* Garbage from a log buffer flush which was made before the most recent database recovery */ #if 0 ib_logger(ib_stream, "Scanned cp n:o %lu, block cp n:o %lu\n", *scanned_checkpoint_no, log_block_get_checkpoint_no(log_block)); #endif break; } data_len = log_block_get_data_len(log_block); *scanned_checkpoint_no = log_block_get_checkpoint_no(log_block); *scanned_lsn += data_len; *n_bytes_scanned += data_len; if (data_len < OS_FILE_LOG_BLOCK_SIZE) { /* Log data ends here */ #if 0 ib_logger(ib_stream, "Log block data len %lu\n", data_len); #endif break; } } } #endif /* UNIV_HOTBACKUP */ /*******************************************************************//** Tries to parse a single log record body and also applies it to a page if specified. File ops are parsed, but not applied in this function. @return log record end, NULL if not a complete record */ static byte* recv_parse_or_apply_log_rec_body( /*=============================*/ byte type, /*!< in: type */ byte* ptr, /*!< in: pointer to a buffer */ byte* end_ptr,/*!< in: pointer to the buffer end */ buf_block_t* block, /*!< in/out: buffer block or NULL; if not NULL, then the log record is applied to the page, and the log record should be complete then */ mtr_t* mtr) /*!< in: mtr or NULL; should be non-NULL if and only if block is non-NULL */ { dict_index_t* index = NULL; page_t* page; page_zip_des_t* page_zip; #ifdef UNIV_DEBUG ulint page_type; #endif /* UNIV_DEBUG */ ut_ad(!block == !mtr); if (block) { page = block->frame; page_zip = buf_block_get_page_zip(block); ut_d(page_type = fil_page_get_type(page)); } else { page = NULL; page_zip = NULL; ut_d(page_type = FIL_PAGE_TYPE_ALLOCATED); } switch (type) { #ifdef UNIV_LOG_LSN_DEBUG case MLOG_LSN: /* The LSN is checked in recv_parse_log_rec(). */ break; #endif /* UNIV_LOG_LSN_DEBUG */ case MLOG_1BYTE: case MLOG_2BYTES: case MLOG_4BYTES: case MLOG_8BYTES: #ifdef UNIV_DEBUG if (page && page_type == FIL_PAGE_TYPE_ALLOCATED && end_ptr >= ptr + 2) { /* It is OK to set FIL_PAGE_TYPE and certain list node fields on an empty page. Any other write is not OK. */ /* NOTE: There may be bogus assertion failures for dict_hdr_create(), trx_rseg_header_create(), trx_sys_create_doublewrite_buf(), and trx_sysf_create(). These are only called during database creation. */ ulint offs = mach_read_from_2(ptr); switch (type) { default: ut_error; case MLOG_2BYTES: /* Note that this can fail when the redo log been written with something older than InnoDB Plugin 1.0.4. */ ut_ad(offs == FIL_PAGE_TYPE || offs == IBUF_TREE_SEG_HEADER + IBUF_HEADER + FSEG_HDR_OFFSET || offs == PAGE_BTR_IBUF_FREE_LIST + PAGE_HEADER + FIL_ADDR_BYTE || offs == PAGE_BTR_IBUF_FREE_LIST + PAGE_HEADER + FIL_ADDR_BYTE + FIL_ADDR_SIZE || offs == PAGE_BTR_SEG_LEAF + PAGE_HEADER + FSEG_HDR_OFFSET || offs == PAGE_BTR_SEG_TOP + PAGE_HEADER + FSEG_HDR_OFFSET || offs == PAGE_BTR_IBUF_FREE_LIST_NODE + PAGE_HEADER + FIL_ADDR_BYTE + 0 /*FLST_PREV*/ || offs == PAGE_BTR_IBUF_FREE_LIST_NODE + PAGE_HEADER + FIL_ADDR_BYTE + FIL_ADDR_SIZE /*FLST_NEXT*/); break; case MLOG_4BYTES: /* Note that this can fail when the redo log been written with something older than InnoDB Plugin 1.0.4. */ ut_ad(0 || offs == IBUF_TREE_SEG_HEADER + IBUF_HEADER + FSEG_HDR_SPACE || offs == IBUF_TREE_SEG_HEADER + IBUF_HEADER + FSEG_HDR_PAGE_NO || offs == PAGE_BTR_IBUF_FREE_LIST + PAGE_HEADER/* flst_init */ || offs == PAGE_BTR_IBUF_FREE_LIST + PAGE_HEADER + FIL_ADDR_PAGE || offs == PAGE_BTR_IBUF_FREE_LIST + PAGE_HEADER + FIL_ADDR_PAGE + FIL_ADDR_SIZE || offs == PAGE_BTR_SEG_LEAF + PAGE_HEADER + FSEG_HDR_PAGE_NO || offs == PAGE_BTR_SEG_LEAF + PAGE_HEADER + FSEG_HDR_SPACE || offs == PAGE_BTR_SEG_TOP + PAGE_HEADER + FSEG_HDR_PAGE_NO || offs == PAGE_BTR_SEG_TOP + PAGE_HEADER + FSEG_HDR_SPACE || offs == PAGE_BTR_IBUF_FREE_LIST_NODE + PAGE_HEADER + FIL_ADDR_PAGE + 0 /*FLST_PREV*/ || offs == PAGE_BTR_IBUF_FREE_LIST_NODE + PAGE_HEADER + FIL_ADDR_PAGE + FIL_ADDR_SIZE /*FLST_NEXT*/); break; } } #endif /* UNIV_DEBUG */ ptr = mlog_parse_nbytes(type, ptr, end_ptr, page, page_zip); break; case MLOG_REC_INSERT: case MLOG_COMP_REC_INSERT: ut_ad(!page || page_type == FIL_PAGE_INDEX); if (NULL != (ptr = mlog_parse_index( ptr, end_ptr, type == MLOG_COMP_REC_INSERT, &index))) { ut_a(!page || (ibool)!!page_is_comp(page) == dict_table_is_comp(index->table)); ptr = page_cur_parse_insert_rec(FALSE, ptr, end_ptr, block, index, mtr); } break; case MLOG_REC_CLUST_DELETE_MARK: case MLOG_COMP_REC_CLUST_DELETE_MARK: ut_ad(!page || page_type == FIL_PAGE_INDEX); if (NULL != (ptr = mlog_parse_index( ptr, end_ptr, type == MLOG_COMP_REC_CLUST_DELETE_MARK, &index))) { ut_a(!page || (ibool)!!page_is_comp(page) == dict_table_is_comp(index->table)); ptr = btr_cur_parse_del_mark_set_clust_rec( ptr, end_ptr, page, page_zip, index); } break; case MLOG_COMP_REC_SEC_DELETE_MARK: ut_ad(!page || page_type == FIL_PAGE_INDEX); /* This log record type is obsolete, but we process it for backward compatibility with v5.0.3 and v5.0.4. */ ut_a(!page || page_is_comp(page)); ut_a(!page_zip); ptr = mlog_parse_index(ptr, end_ptr, TRUE, &index); if (!ptr) { break; } /* Fall through */ case MLOG_REC_SEC_DELETE_MARK: ut_ad(!page || page_type == FIL_PAGE_INDEX); ptr = btr_cur_parse_del_mark_set_sec_rec(ptr, end_ptr, page, page_zip); break; case MLOG_REC_UPDATE_IN_PLACE: case MLOG_COMP_REC_UPDATE_IN_PLACE: ut_ad(!page || page_type == FIL_PAGE_INDEX); if (NULL != (ptr = mlog_parse_index( ptr, end_ptr, type == MLOG_COMP_REC_UPDATE_IN_PLACE, &index))) { ut_a(!page || (ibool)!!page_is_comp(page) == dict_table_is_comp(index->table)); ptr = btr_cur_parse_update_in_place(ptr, end_ptr, page, page_zip, index); } break; case MLOG_LIST_END_DELETE: case MLOG_COMP_LIST_END_DELETE: case MLOG_LIST_START_DELETE: case MLOG_COMP_LIST_START_DELETE: ut_ad(!page || page_type == FIL_PAGE_INDEX); if (NULL != (ptr = mlog_parse_index( ptr, end_ptr, type == MLOG_COMP_LIST_END_DELETE || type == MLOG_COMP_LIST_START_DELETE, &index))) { ut_a(!page || (ibool)!!page_is_comp(page) == dict_table_is_comp(index->table)); ptr = page_parse_delete_rec_list(type, ptr, end_ptr, block, index, mtr); } break; case MLOG_LIST_END_COPY_CREATED: case MLOG_COMP_LIST_END_COPY_CREATED: ut_ad(!page || page_type == FIL_PAGE_INDEX); if (NULL != (ptr = mlog_parse_index( ptr, end_ptr, type == MLOG_COMP_LIST_END_COPY_CREATED, &index))) { ut_a(!page || (ibool)!!page_is_comp(page) == dict_table_is_comp(index->table)); ptr = page_parse_copy_rec_list_to_created_page( ptr, end_ptr, block, index, mtr); } break; case MLOG_PAGE_REORGANIZE: case MLOG_COMP_PAGE_REORGANIZE: ut_ad(!page || page_type == FIL_PAGE_INDEX); if (NULL != (ptr = mlog_parse_index( ptr, end_ptr, type == MLOG_COMP_PAGE_REORGANIZE, &index))) { ut_a(!page || (ibool)!!page_is_comp(page) == dict_table_is_comp(index->table)); ptr = btr_parse_page_reorganize(ptr, end_ptr, index, block, mtr); } break; case MLOG_PAGE_CREATE: case MLOG_COMP_PAGE_CREATE: /* Allow anything in page_type when creating a page. */ ut_a(!page_zip); ptr = page_parse_create(ptr, end_ptr, type == MLOG_COMP_PAGE_CREATE, block, mtr); break; case MLOG_UNDO_INSERT: ut_ad(!page || page_type == FIL_PAGE_UNDO_LOG); ptr = trx_undo_parse_add_undo_rec(ptr, end_ptr, page); break; case MLOG_UNDO_ERASE_END: ut_ad(!page || page_type == FIL_PAGE_UNDO_LOG); ptr = trx_undo_parse_erase_page_end(ptr, end_ptr, page, mtr); break; case MLOG_UNDO_INIT: /* Allow anything in page_type when creating a page. */ ptr = trx_undo_parse_page_init(ptr, end_ptr, page, mtr); break; case MLOG_UNDO_HDR_DISCARD: ut_ad(!page || page_type == FIL_PAGE_UNDO_LOG); ptr = trx_undo_parse_discard_latest(ptr, end_ptr, page, mtr); break; case MLOG_UNDO_HDR_CREATE: case MLOG_UNDO_HDR_REUSE: ut_ad(!page || page_type == FIL_PAGE_UNDO_LOG); ptr = trx_undo_parse_page_header(type, ptr, end_ptr, page, mtr); break; case MLOG_REC_MIN_MARK: case MLOG_COMP_REC_MIN_MARK: ut_ad(!page || page_type == FIL_PAGE_INDEX); /* On a compressed page, MLOG_COMP_REC_MIN_MARK will be followed by MLOG_COMP_REC_DELETE or MLOG_ZIP_WRITE_HEADER(FIL_PAGE_PREV, FIL_NULL) in the same mini-transaction. */ ut_a(type == MLOG_COMP_REC_MIN_MARK || !page_zip); ptr = btr_parse_set_min_rec_mark( ptr, end_ptr, type == MLOG_COMP_REC_MIN_MARK, page, mtr); break; case MLOG_REC_DELETE: case MLOG_COMP_REC_DELETE: ut_ad(!page || page_type == FIL_PAGE_INDEX); if (NULL != (ptr = mlog_parse_index( ptr, end_ptr, type == MLOG_COMP_REC_DELETE, &index))) { ut_a(!page || (ibool)!!page_is_comp(page) == dict_table_is_comp(index->table)); ptr = page_cur_parse_delete_rec(ptr, end_ptr, block, index, mtr); } break; case MLOG_IBUF_BITMAP_INIT: /* Allow anything in page_type when creating a page. */ ptr = ibuf_parse_bitmap_init(ptr, end_ptr, block, mtr); break; case MLOG_INIT_FILE_PAGE: /* Allow anything in page_type when creating a page. */ ptr = fsp_parse_init_file_page(ptr, end_ptr, block); break; case MLOG_WRITE_STRING: ut_ad(!page || page_type != FIL_PAGE_TYPE_ALLOCATED); ptr = mlog_parse_string(ptr, end_ptr, page, page_zip); break; case MLOG_FILE_CREATE: case MLOG_FILE_RENAME: case MLOG_FILE_DELETE: case MLOG_FILE_CREATE2: ptr = fil_op_log_parse_or_replay(ptr, end_ptr, type, 0, 0); break; case MLOG_ZIP_WRITE_NODE_PTR: ut_ad(!page || page_type == FIL_PAGE_INDEX); ptr = page_zip_parse_write_node_ptr(ptr, end_ptr, page, page_zip); break; case MLOG_ZIP_WRITE_BLOB_PTR: ut_ad(!page || page_type == FIL_PAGE_INDEX); ptr = page_zip_parse_write_blob_ptr(ptr, end_ptr, page, page_zip); break; case MLOG_ZIP_WRITE_HEADER: ut_ad(!page || page_type == FIL_PAGE_INDEX); ptr = page_zip_parse_write_header(ptr, end_ptr, page, page_zip); break; case MLOG_ZIP_PAGE_COMPRESS: /* Allow anything in page_type when creating a page. */ ptr = page_zip_parse_compress(ptr, end_ptr, page, page_zip); break; default: ptr = NULL; recv_sys->found_corrupt_log = TRUE; } if (index) { dict_table_t* table = index->table; dict_mem_index_free(index); dict_mem_table_free(table); } return(ptr); } /*********************************************************************//** Calculates the fold value of a page file address: used in inserting or searching for a log record in the hash table. @return folded value */ UNIV_INLINE ulint recv_fold( /*======*/ ulint space, /*!< in: space */ ulint page_no)/*!< in: page number */ { return(ut_fold_ulint_pair(space, page_no)); } /*********************************************************************//** Calculates the hash value of a page file address: used in inserting or searching for a log record in the hash table. @return folded value */ UNIV_INLINE ulint recv_hash( /*======*/ ulint space, /*!< in: space */ ulint page_no)/*!< in: page number */ { return(hash_calc_hash(recv_fold(space, page_no), recv_sys->addr_hash)); } /*********************************************************************//** Gets the hashed file address struct for a page. @return file address struct, NULL if not found from the hash table */ static recv_addr_t* recv_get_fil_addr_struct( /*=====================*/ ulint space, /*!< in: space id */ ulint page_no)/*!< in: page number */ { recv_addr_t* recv_addr; recv_addr = HASH_GET_FIRST(recv_sys->addr_hash, recv_hash(space, page_no)); while (recv_addr) { if ((recv_addr->space == space) && (recv_addr->page_no == page_no)) { break; } recv_addr = HASH_GET_NEXT(addr_hash, recv_addr); } return(recv_addr); } /*******************************************************************//** Adds a new log record to the hash table of log records. */ static void recv_add_to_hash_table( /*===================*/ byte type, /*!< in: log record type */ ulint space, /*!< in: space id */ ulint page_no, /*!< in: page number */ byte* body, /*!< in: log record body */ byte* rec_end, /*!< in: log record end */ ib_uint64_t start_lsn, /*!< in: start lsn of the mtr */ ib_uint64_t end_lsn) /*!< in: end lsn of the mtr */ { recv_t* recv; ulint len; recv_data_t* recv_data; recv_data_t** prev_field; recv_addr_t* recv_addr; if (fil_tablespace_deleted_or_being_deleted_in_mem(space, -1)) { /* The tablespace does not exist any more: do not store the log record */ return; } len = rec_end - body; recv = mem_heap_alloc(recv_sys->heap, sizeof(recv_t)); recv->type = type; recv->len = rec_end - body; recv->start_lsn = start_lsn; recv->end_lsn = end_lsn; recv_addr = recv_get_fil_addr_struct(space, page_no); if (recv_addr == NULL) { recv_addr = mem_heap_alloc(recv_sys->heap, sizeof(recv_addr_t)); recv_addr->space = space; recv_addr->page_no = page_no; recv_addr->state = RECV_NOT_PROCESSED; UT_LIST_INIT(recv_addr->rec_list); HASH_INSERT(recv_addr_t, addr_hash, recv_sys->addr_hash, recv_fold(space, page_no), recv_addr); recv_sys->n_addrs++; #if 0 ib_logger(ib_stream, "Inserting log rec for space %lu, page %lu\n", space, page_no); #endif } UT_LIST_ADD_LAST(rec_list, recv_addr->rec_list, recv); prev_field = &(recv->data); /* Store the log record body in chunks of less than UNIV_PAGE_SIZE: recv_sys->heap grows into the buffer pool, and bigger chunks could not be allocated */ while (rec_end > body) { len = rec_end - body; if (len > RECV_DATA_BLOCK_SIZE) { len = RECV_DATA_BLOCK_SIZE; } recv_data = mem_heap_alloc(recv_sys->heap, sizeof(recv_data_t) + len); *prev_field = recv_data; memcpy(recv_data + 1, body, len); prev_field = &(recv_data->next); body += len; } *prev_field = NULL; } /*********************************************************************//** Copies the log record body from recv to buf. */ static void recv_data_copy_to_buf( /*==================*/ byte* buf, /*!< in: buffer of length at least recv->len */ recv_t* recv) /*!< in: log record */ { recv_data_t* recv_data; ulint part_len; ulint len; len = recv->len; recv_data = recv->data; while (len > 0) { if (len > RECV_DATA_BLOCK_SIZE) { part_len = RECV_DATA_BLOCK_SIZE; } else { part_len = len; } ut_memcpy(buf, ((byte*)recv_data) + sizeof(recv_data_t), part_len); buf += part_len; len -= part_len; recv_data = recv_data->next; } } /************************************************************************//** Applies the hashed log records to the page, if the page lsn is less than the lsn of a log record. This can be called when a buffer page has just been read in, or also for a page already in the buffer pool. */ UNIV_INTERN void recv_recover_page_func( /*===================*/ #ifndef UNIV_HOTBACKUP ibool just_read_in, /*!< in: TRUE if the i/o handler calls this for a freshly read page */ #endif /* !UNIV_HOTBACKUP */ buf_block_t* block) /*!< in/out: buffer block */ { page_t* page; page_zip_des_t* page_zip; recv_addr_t* recv_addr; recv_t* recv; byte* buf; ib_uint64_t start_lsn; ib_uint64_t end_lsn; ib_uint64_t page_lsn; ib_uint64_t page_newest_lsn; ibool modification_to_page; #ifndef UNIV_HOTBACKUP ibool success; #endif /* !UNIV_HOTBACKUP */ mtr_t mtr; mutex_enter(&(recv_sys->mutex)); if (recv_sys->apply_log_recs == FALSE) { /* Log records should not be applied now */ mutex_exit(&(recv_sys->mutex)); return; } recv_addr = recv_get_fil_addr_struct(buf_block_get_space(block), buf_block_get_page_no(block)); if ((recv_addr == NULL) || (recv_addr->state == RECV_BEING_PROCESSED) || (recv_addr->state == RECV_PROCESSED)) { mutex_exit(&(recv_sys->mutex)); return; } #if 0 ib_logger(ib_stream, "Recovering space %lu, page %lu\n", buf_block_get_space(block), buf_block_get_page_no(block)); #endif recv_addr->state = RECV_BEING_PROCESSED; mutex_exit(&(recv_sys->mutex)); mtr_start(&mtr); mtr_set_log_mode(&mtr, MTR_LOG_NONE); page = block->frame; page_zip = buf_block_get_page_zip(block); #ifndef UNIV_HOTBACKUP if (just_read_in) { /* Move the ownership of the x-latch on the page to this OS thread, so that we can acquire a second x-latch on it. This is needed for the operations to the page to pass the debug checks. */ rw_lock_x_lock_move_ownership(&block->lock); } success = buf_page_get_known_nowait(RW_X_LATCH, block, BUF_KEEP_OLD, __FILE__, __LINE__, &mtr); ut_a(success); buf_block_dbg_add_level(block, SYNC_NO_ORDER_CHECK); #endif /* !UNIV_HOTBACKUP */ /* Read the newest modification lsn from the page */ page_lsn = mach_read_ull(page + FIL_PAGE_LSN); #ifndef UNIV_HOTBACKUP /* It may be that the page has been modified in the buffer pool: read the newest modification lsn there */ page_newest_lsn = buf_page_get_newest_modification(&block->page); if (page_newest_lsn) { page_lsn = page_newest_lsn; } #else /* !UNIV_HOTBACKUP */ /* In recovery from a backup we do not really use the buffer pool */ page_newest_lsn = 0; #endif /* !UNIV_HOTBACKUP */ modification_to_page = FALSE; start_lsn = end_lsn = 0; recv = UT_LIST_GET_FIRST(recv_addr->rec_list); while (recv) { end_lsn = recv->end_lsn; if (recv->len > RECV_DATA_BLOCK_SIZE) { /* We have to copy the record body to a separate buffer */ buf = mem_alloc(recv->len); recv_data_copy_to_buf(buf, recv); } else { buf = ((byte*)(recv->data)) + sizeof(recv_data_t); } if (recv->type == MLOG_INIT_FILE_PAGE) { page_lsn = page_newest_lsn; memset(FIL_PAGE_LSN + page, 0, 8); memset(UNIV_PAGE_SIZE - FIL_PAGE_END_LSN_OLD_CHKSUM + page, 0, 8); if (page_zip) { memset(FIL_PAGE_LSN + page_zip->data, 0, 8); } } if (recv->start_lsn >= page_lsn) { ib_uint64_t end_lsn; if (!modification_to_page) { modification_to_page = TRUE; start_lsn = recv->start_lsn; } #ifdef UNIV_DEBUG if (log_debug_writes) { ib_logger(ib_stream, "InnoDB: Applying log rec" " type %lu len %lu" " to space %lu page no %lu\n", (ulong) recv->type, (ulong) recv->len, (ulong) recv_addr->space, (ulong) recv_addr->page_no); } #endif /* UNIV_DEBUG */ recv_parse_or_apply_log_rec_body(recv->type, buf, buf + recv->len, block, &mtr); end_lsn = recv->start_lsn + recv->len; mach_write_ull(FIL_PAGE_LSN + page, end_lsn); mach_write_ull(UNIV_PAGE_SIZE - FIL_PAGE_END_LSN_OLD_CHKSUM + page, end_lsn); if (page_zip) { mach_write_ull(FIL_PAGE_LSN + page_zip->data, end_lsn); } } if (recv->len > RECV_DATA_BLOCK_SIZE) { mem_free(buf); } recv = UT_LIST_GET_NEXT(rec_list, recv); } #ifdef UNIV_ZIP_DEBUG if (fil_page_get_type(page) == FIL_PAGE_INDEX) { page_zip_des_t* page_zip = buf_block_get_page_zip(block); if (page_zip) { ut_a(page_zip_validate_low(page_zip, page, FALSE)); } } #endif /* UNIV_ZIP_DEBUG */ mutex_enter(&(recv_sys->mutex)); if (recv_max_page_lsn < page_lsn) { recv_max_page_lsn = page_lsn; } recv_addr->state = RECV_PROCESSED; ut_a(recv_sys->n_addrs); recv_sys->n_addrs--; mutex_exit(&(recv_sys->mutex)); #ifndef UNIV_HOTBACKUP if (modification_to_page) { ut_a(block); buf_flush_recv_note_modification(block, start_lsn, end_lsn); } #endif /* !UNIV_HOTBACKUP */ /* Make sure that committing mtr does not change the modification lsn values of page */ mtr.modifications = FALSE; mtr_commit(&mtr); } #ifndef UNIV_HOTBACKUP /*******************************************************************//** Reads in pages which have hashed log records, from an area around a given page number. @return number of pages found */ static ulint recv_read_in_area( /*==============*/ ulint space, /*!< in: space */ ulint zip_size,/*!< in: compressed page size in bytes, or 0 */ ulint page_no)/*!< in: page number */ { recv_addr_t* recv_addr; ulint page_nos[RECV_READ_AHEAD_AREA]; ulint low_limit; ulint n; low_limit = page_no - (page_no % RECV_READ_AHEAD_AREA); n = 0; for (page_no = low_limit; page_no < low_limit + RECV_READ_AHEAD_AREA; page_no++) { recv_addr = recv_get_fil_addr_struct(space, page_no); if (recv_addr && !buf_page_peek(space, page_no)) { mutex_enter(&(recv_sys->mutex)); if (recv_addr->state == RECV_NOT_PROCESSED) { recv_addr->state = RECV_BEING_READ; page_nos[n] = page_no; n++; } mutex_exit(&(recv_sys->mutex)); } } buf_read_recv_pages(FALSE, space, zip_size, page_nos, n); /* ib_logger(ib_stream, "Recv pages at %lu n %lu\n", page_nos[0], n); */ return(n); } /*******************************************************************//** Empties the hash table of stored log records, applying them to appropriate pages. */ UNIV_INTERN void recv_apply_hashed_log_recs( /*=======================*/ ibool allow_ibuf) /*!< in: if TRUE, also ibuf operations are allowed during the application; if FALSE, no ibuf operations are allowed, and after the application all file pages are flushed to disk and invalidated in buffer pool: this alternative means that no new log records can be generated during the application; the caller must in this case own the log mutex */ { recv_addr_t* recv_addr; ulint i; ulint n_pages; ibool has_printed = FALSE; mtr_t mtr; loop: mutex_enter(&(recv_sys->mutex)); if (recv_sys->apply_batch_on) { mutex_exit(&(recv_sys->mutex)); os_thread_sleep(500000); goto loop; } ut_ad(!allow_ibuf == mutex_own(&log_sys->mutex)); if (!allow_ibuf) { recv_no_ibuf_operations = TRUE; } recv_sys->apply_log_recs = TRUE; recv_sys->apply_batch_on = TRUE; for (i = 0; i < hash_get_n_cells(recv_sys->addr_hash); i++) { recv_addr = HASH_GET_FIRST(recv_sys->addr_hash, i); while (recv_addr) { ulint space = recv_addr->space; ulint zip_size = fil_space_get_zip_size(space); ulint page_no = recv_addr->page_no; if (recv_addr->state == RECV_NOT_PROCESSED) { if (!has_printed) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Starting an apply " "batch of log records to the " "database...\n" "InnoDB: Progress in percents: "); has_printed = TRUE; } mutex_exit(&(recv_sys->mutex)); if (buf_page_peek(space, page_no)) { buf_block_t* block; mtr_start(&mtr); block = buf_page_get( space, zip_size, page_no, RW_X_LATCH, &mtr); buf_block_dbg_add_level( block, SYNC_NO_ORDER_CHECK); recv_recover_page(FALSE, block); mtr_commit(&mtr); } else { recv_read_in_area(space, zip_size, page_no); } mutex_enter(&(recv_sys->mutex)); } recv_addr = HASH_GET_NEXT(addr_hash, recv_addr); } if (has_printed && (i * 100) / hash_get_n_cells(recv_sys->addr_hash) != ((i + 1) * 100) / hash_get_n_cells(recv_sys->addr_hash)) { ib_logger(ib_stream, "%lu ", (ulong) ((i * 100) / hash_get_n_cells(recv_sys->addr_hash))); } } /* Wait until all the pages have been processed */ while (recv_sys->n_addrs != 0) { mutex_exit(&(recv_sys->mutex)); os_thread_sleep(500000); mutex_enter(&(recv_sys->mutex)); } if (has_printed) { ib_logger(ib_stream, "\n"); } if (!allow_ibuf) { /* Flush all the file pages to disk and invalidate them in the buffer pool */ ut_d(recv_no_log_write = TRUE); mutex_exit(&(recv_sys->mutex)); mutex_exit(&(log_sys->mutex)); n_pages = buf_flush_batch(BUF_FLUSH_LIST, ULINT_MAX, IB_UINT64_T_MAX); ut_a(n_pages != ULINT_UNDEFINED); buf_flush_wait_batch_end(BUF_FLUSH_LIST); buf_pool_invalidate(); mutex_enter(&(log_sys->mutex)); mutex_enter(&(recv_sys->mutex)); ut_d(recv_no_log_write = FALSE); recv_no_ibuf_operations = FALSE; } recv_sys->apply_log_recs = FALSE; recv_sys->apply_batch_on = FALSE; recv_sys_empty_hash(); if (has_printed) { ib_logger(ib_stream, "InnoDB: Apply batch completed\n"); } mutex_exit(&(recv_sys->mutex)); } #else /* !UNIV_HOTBACKUP */ /*******************************************************************//** Applies log records in the hash table to a backup. */ UNIV_INTERN void recv_apply_log_recs_for_backup(void) /*================================*/ { recv_addr_t* recv_addr; ulint n_hash_cells; buf_block_t* block; ulint actual_size; ibool success; ulint error; ulint i; recv_sys->apply_log_recs = TRUE; recv_sys->apply_batch_on = TRUE; block = back_block1; ib_logger(ib_stream, "InnoDB: Starting an apply batch of log records " "to the database...\n" "InnoDB: Progress in percents: "); n_hash_cells = hash_get_n_cells(recv_sys->addr_hash); for (i = 0; i < n_hash_cells; i++) { /* The address hash table is externally chained */ recv_addr = hash_get_nth_cell(recv_sys->addr_hash, i)->node; while (recv_addr != NULL) { ulint zip_size = fil_space_get_zip_size(recv_addr->space); if (zip_size == ULINT_UNDEFINED) { #if 0 ib_logger(ib_stream, "InnoDB: Warning: cannot apply" " log record to" " tablespace %lu page %lu,\n" "InnoDB: because tablespace with" " that id does not exist.\n", recv_addr->space, recv_addr->page_no); #endif recv_addr->state = RECV_PROCESSED; ut_a(recv_sys->n_addrs); recv_sys->n_addrs--; goto skip_this_recv_addr; } /* We simulate a page read made by the buffer pool, to make sure the recovery apparatus works ok. We must init the block. */ buf_page_init_for_backup_restore( recv_addr->space, recv_addr->page_no, zip_size, block); /* Extend the tablespace's last file if the page_no does not fall inside its bounds; we assume the last file is auto-extending, and ibbackup copied the file when it still was smaller */ success = fil_extend_space_to_desired_size( &actual_size, recv_addr->space, recv_addr->page_no + 1); if (!success) { srv_panic(DB_ERROR, "InnoDB: Fatal error: cannot extend" " tablespace %lu to hold %lu pages\n", recv_addr->space, recv_addr->page_no); } /* Read the page from the tablespace file using the fil0fil.c routines */ if (zip_size) { error = fil_io(OS_FILE_READ, TRUE, recv_addr->space, zip_size, recv_addr->page_no, 0, zip_size, block->page.zip.data, NULL); if (error == DB_SUCCESS && !buf_zip_decompress(block, TRUE)) { srv_panic(DB_ERROR, "decompressing page"); } } else { error = fil_io(OS_FILE_READ, TRUE, recv_addr->space, 0, recv_addr->page_no, 0, UNIV_PAGE_SIZE, block->frame, NULL); } if (error != DB_SUCCESS) { srv_panic(DB_ERROR, "InnoDB: Fatal error: cannot read " "from tablespace %lu page " "number %lu\n", (ulong) recv_addr->space, (ulong) recv_addr->page_no); } /* Apply the log records to this page */ recv_recover_page(FALSE, block); /* Write the page back to the tablespace file using the fil0fil.c routines */ buf_flush_init_for_writing( block->frame, buf_block_get_page_zip(block), mach_read_ull(block->frame + FIL_PAGE_LSN)); if (zip_size) { error = fil_io(OS_FILE_WRITE, TRUE, recv_addr->space, zip_size, recv_addr->page_no, 0, zip_size, block->page.zip.data, NULL); } else { error = fil_io(OS_FILE_WRITE, TRUE, recv_addr->space, 0, recv_addr->page_no, 0, UNIV_PAGE_SIZE, block->frame, NULL); } skip_this_recv_addr: recv_addr = HASH_GET_NEXT(addr_hash, recv_addr); } if ((100 * i) / n_hash_cells != (100 * (i + 1)) / n_hash_cells) { ib_logger(ib_stream, "%lu ", (ulong) ((100 * i) / n_hash_cells)); } } recv_sys_empty_hash(); } #endif /* !UNIV_HOTBACKUP */ /*******************************************************************//** Tries to parse a single log record and returns its length. @return length of the record, or 0 if the record was not complete */ static ulint recv_parse_log_rec( /*===============*/ byte* ptr, /*!< in: pointer to a buffer */ byte* end_ptr,/*!< in: pointer to the buffer end */ byte* type, /*!< out: type */ ulint* space, /*!< out: space id */ ulint* page_no,/*!< out: page number */ byte** body) /*!< out: log record body start */ { byte* new_ptr; *body = NULL; if (ptr == end_ptr) { return(0); } if (*ptr == MLOG_MULTI_REC_END) { *type = *ptr; return(1); } if (*ptr == MLOG_DUMMY_RECORD) { *type = *ptr; *space = ULINT_UNDEFINED - 1; /* For debugging */ return(1); } new_ptr = mlog_parse_initial_log_record(ptr, end_ptr, type, space, page_no); *body = new_ptr; if (UNIV_UNLIKELY(!new_ptr)) { return(0); } #ifdef UNIV_LOG_LSN_DEBUG if (*type == MLOG_LSN) { ib_uint64_t lsn = (ib_uint64_t) *space << 32 | *page_no; # ifdef UNIV_LOG_DEBUG ut_a(lsn == log_sys->old_lsn); # else /* UNIV_LOG_DEBUG */ ut_a(lsn == recv_sys->recovered_lsn); # endif /* UNIV_LOG_DEBUG */ } #endif /* UNIV_LOG_LSN_DEBUG */ new_ptr = recv_parse_or_apply_log_rec_body(*type, new_ptr, end_ptr, NULL, NULL); if (UNIV_UNLIKELY(new_ptr == NULL)) { return(0); } if (*page_no > recv_max_parsed_page_no) { recv_max_parsed_page_no = *page_no; } return(new_ptr - ptr); } /*******************************************************//** Calculates the new value for lsn when more data is added to the log. */ static ib_uint64_t recv_calc_lsn_on_data_add( /*======================*/ ib_uint64_t lsn, /*!< in: old lsn */ ib_uint64_t len) /*!< in: this many bytes of data is added, log block headers not included */ { ulint frag_len; ulint lsn_len; frag_len = (((ulint) lsn) % OS_FILE_LOG_BLOCK_SIZE) - LOG_BLOCK_HDR_SIZE; ut_ad(frag_len < OS_FILE_LOG_BLOCK_SIZE - LOG_BLOCK_HDR_SIZE - LOG_BLOCK_TRL_SIZE); lsn_len = (ulint) len; lsn_len += (lsn_len + frag_len) / (OS_FILE_LOG_BLOCK_SIZE - LOG_BLOCK_HDR_SIZE - LOG_BLOCK_TRL_SIZE) * (LOG_BLOCK_HDR_SIZE + LOG_BLOCK_TRL_SIZE); return(lsn + lsn_len); } #ifdef UNIV_LOG_DEBUG /*******************************************************//** Checks that the parser recognizes incomplete initial segments of a log record as incomplete. */ static void recv_check_incomplete_log_recs( /*===========================*/ byte* ptr, /*!< in: pointer to a complete log record */ ulint len) /*!< in: length of the log record */ { ulint i; byte type; ulint space; ulint page_no; byte* body; for (i = 0; i < len; i++) { ut_a(0 == recv_parse_log_rec(ptr, ptr + i, &type, &space, &page_no, &body)); } } #endif /* UNIV_LOG_DEBUG */ /*******************************************************//** Prints diagnostic info of corrupt log. */ static void recv_report_corrupt_log( /*====================*/ byte* ptr, /*!< in: pointer to corrupt log record */ byte type, /*!< in: type of the record */ ulint space, /*!< in: space id, this may also be garbage */ ulint page_no)/*!< in: page number, this may also be garbage */ { ib_logger(ib_stream, "InnoDB: ############### CORRUPT LOG RECORD FOUND\n" "InnoDB: Log record type %lu, space id %lu, page number %lu\n" "InnoDB: Log parsing proceeded successfully up to %llu\n" "InnoDB: Previous log record type %lu, is multi %lu\n" "InnoDB: Recv offset %lu, prev %lu\n", (ulong) type, (ulong) space, (ulong) page_no, recv_sys->recovered_lsn, (ulong) recv_previous_parsed_rec_type, (ulong) recv_previous_parsed_rec_is_multi, (ulong) (ptr - recv_sys->buf), (ulong) recv_previous_parsed_rec_offset); if ((ulint)(ptr - recv_sys->buf + 100) > recv_previous_parsed_rec_offset && (ulint)(ptr - recv_sys->buf + 100 - recv_previous_parsed_rec_offset) < 200000) { ib_logger(ib_stream, "InnoDB: Hex dump of corrupt log starting" " 100 bytes before the start\n" "InnoDB: of the previous log rec,\n" "InnoDB: and ending 100 bytes after the start" " of the corrupt rec:\n"); ut_print_buf(ib_stream, recv_sys->buf + recv_previous_parsed_rec_offset - 100, ptr - recv_sys->buf + 200 - recv_previous_parsed_rec_offset); ib_logger(ib_stream, "\n"); } #ifndef UNIV_HOTBACKUP if (!srv_force_recovery) { ib_logger(ib_stream, "InnoDB: Set innodb_force_recovery" " to ignore this error.\n"); ut_error; } #endif /* !UNIV_HOTBACKUP */ ib_logger(ib_stream, "InnoDB: WARNING: the log file may have been corrupt and it\n" "InnoDB: is possible that the log scan did not proceed\n" "InnoDB: far enough in recovery! Please run CHECK TABLE\n" "InnoDB: on your InnoDB tables to check that they are ok!\n" "InnoDB: If the engine crashes after this recovery, check\n" "InnoDB: the InnoDB website for details\n" "InnoDB: about forcing recovery.\n"); } /*******************************************************//** Parses log records from a buffer and stores them to a hash table to wait merging to file pages. @return currently always returns FALSE */ static ibool recv_parse_log_recs( /*================*/ ibool store_to_hash) /*!< in: TRUE if the records should be stored to the hash table; this is set to FALSE if just debug checking is needed */ { byte* ptr; byte* end_ptr; ulint single_rec; ulint len; ulint total_len; ib_uint64_t new_recovered_lsn; ib_uint64_t old_lsn; byte type; ulint space; ulint page_no; byte* body; ulint n_recs; ut_ad(mutex_own(&(log_sys->mutex))); ut_ad(recv_sys->parse_start_lsn != 0); loop: ptr = recv_sys->buf + recv_sys->recovered_offset; end_ptr = recv_sys->buf + recv_sys->len; if (ptr == end_ptr) { return(FALSE); } single_rec = (ulint)*ptr & MLOG_SINGLE_REC_FLAG; if (single_rec || *ptr == MLOG_DUMMY_RECORD) { /* The mtr only modified a single page, or this is a file op */ old_lsn = recv_sys->recovered_lsn; /* Try to parse a log record, fetching its type, space id, page no, and a pointer to the body of the log record */ len = recv_parse_log_rec(ptr, end_ptr, &type, &space, &page_no, &body); if (len == 0 || recv_sys->found_corrupt_log) { if (recv_sys->found_corrupt_log) { recv_report_corrupt_log(ptr, type, space, page_no); } return(FALSE); } new_recovered_lsn = recv_calc_lsn_on_data_add(old_lsn, len); if (new_recovered_lsn > recv_sys->scanned_lsn) { /* The log record filled a log block, and we require that also the next log block should have been scanned in */ return(FALSE); } recv_previous_parsed_rec_type = (ulint)type; recv_previous_parsed_rec_offset = recv_sys->recovered_offset; recv_previous_parsed_rec_is_multi = 0; recv_sys->recovered_offset += len; recv_sys->recovered_lsn = new_recovered_lsn; #ifdef UNIV_DEBUG if (log_debug_writes) { ib_logger(ib_stream, "InnoDB: Parsed a single log rec " "type %lu len %lu space %lu page no %lu\n", (ulong) type, (ulong) len, (ulong) space, (ulong) page_no); } #endif /* UNIV_DEBUG */ if (type == MLOG_DUMMY_RECORD) { /* Do nothing */ } else if (!store_to_hash) { /* In debug checking, update a replicate page according to the log record, and check that it becomes identical with the original page */ #ifdef UNIV_LOG_DEBUG recv_check_incomplete_log_recs(ptr, len); #endif/* UNIV_LOG_DEBUG */ } else if (type == MLOG_FILE_CREATE || type == MLOG_FILE_CREATE2 || type == MLOG_FILE_RENAME || type == MLOG_FILE_DELETE) { ut_a(space); /* In normal crash recovery we do not try to replay file operations */ #ifdef UNIV_LOG_LSN_DEBUG } else if (type == MLOG_LSN) { /* Do not add these records to the hash table. The page number and space id fields are misused for something else. */ #endif /* UNIV_LOG_LSN_DEBUG */ } else { recv_add_to_hash_table(type, space, page_no, body, ptr + len, old_lsn, recv_sys->recovered_lsn); } } else { /* Check that all the records associated with the single mtr are included within the buffer */ total_len = 0; n_recs = 0; for (;;) { len = recv_parse_log_rec(ptr, end_ptr, &type, &space, &page_no, &body); if (len == 0 || recv_sys->found_corrupt_log) { if (recv_sys->found_corrupt_log) { recv_report_corrupt_log( ptr, type, space, page_no); } return(FALSE); } recv_previous_parsed_rec_type = (ulint)type; recv_previous_parsed_rec_offset = recv_sys->recovered_offset + total_len; recv_previous_parsed_rec_is_multi = 1; #ifdef UNIV_LOG_DEBUG if ((!store_to_hash) && (type != MLOG_MULTI_REC_END)) { recv_check_incomplete_log_recs(ptr, len); } #endif /* UNIV_LOG_DEBUG */ #ifdef UNIV_DEBUG if (log_debug_writes) { ib_logger(ib_stream, "InnoDB: Parsed a multi log rec " "type %lu len %lu " "space %lu page no %lu\n", (ulong) type, (ulong) len, (ulong) space, (ulong) page_no); } #endif /* UNIV_DEBUG */ total_len += len; n_recs++; ptr += len; if (type == MLOG_MULTI_REC_END) { /* Found the end mark for the records */ break; } } new_recovered_lsn = recv_calc_lsn_on_data_add( recv_sys->recovered_lsn, total_len); if (new_recovered_lsn > recv_sys->scanned_lsn) { /* The log record filled a log block, and we require that also the next log block should have been scanned in */ return(FALSE); } /* Add all the records to the hash table */ ptr = recv_sys->buf + recv_sys->recovered_offset; for (;;) { old_lsn = recv_sys->recovered_lsn; len = recv_parse_log_rec(ptr, end_ptr, &type, &space, &page_no, &body); if (recv_sys->found_corrupt_log) { recv_report_corrupt_log(ptr, type, space, page_no); } ut_a(len != 0); ut_a(0 == ((ulint)*ptr & MLOG_SINGLE_REC_FLAG)); recv_sys->recovered_offset += len; recv_sys->recovered_lsn = recv_calc_lsn_on_data_add(old_lsn, len); if (type == MLOG_MULTI_REC_END) { /* Found the end mark for the records */ break; } if (store_to_hash #ifdef UNIV_LOG_LSN_DEBUG && type != MLOG_LSN #endif /* UNIV_LOG_LSN_DEBUG */ ) { recv_add_to_hash_table(type, space, page_no, body, ptr + len, old_lsn, new_recovered_lsn); } ptr += len; } } goto loop; } /*******************************************************//** Adds data from a new log block to the parsing buffer of recv_sys if recv_sys->parse_start_lsn is non-zero. @return TRUE if more data added */ static ibool recv_sys_add_to_parsing_buf( /*========================*/ const byte* log_block, /*!< in: log block */ ib_uint64_t scanned_lsn) /*!< in: lsn of how far we were able to find data in this log block */ { ulint more_len; ulint data_len; ulint start_offset; ulint end_offset; ut_ad(scanned_lsn >= recv_sys->scanned_lsn); if (!recv_sys->parse_start_lsn) { /* Cannot start parsing yet because no start point for it found */ return(FALSE); } data_len = log_block_get_data_len(log_block); if (recv_sys->parse_start_lsn >= scanned_lsn) { return(FALSE); } else if (recv_sys->scanned_lsn >= scanned_lsn) { return(FALSE); } else if (recv_sys->parse_start_lsn > recv_sys->scanned_lsn) { more_len = (ulint) (scanned_lsn - recv_sys->parse_start_lsn); } else { more_len = (ulint) (scanned_lsn - recv_sys->scanned_lsn); } if (more_len == 0) { return(FALSE); } ut_ad(data_len >= more_len); start_offset = data_len - more_len; if (start_offset < LOG_BLOCK_HDR_SIZE) { start_offset = LOG_BLOCK_HDR_SIZE; } end_offset = data_len; if (end_offset > OS_FILE_LOG_BLOCK_SIZE - LOG_BLOCK_TRL_SIZE) { end_offset = OS_FILE_LOG_BLOCK_SIZE - LOG_BLOCK_TRL_SIZE; } ut_ad(start_offset <= end_offset); if (start_offset < end_offset) { ut_memcpy(recv_sys->buf + recv_sys->len, log_block + start_offset, end_offset - start_offset); recv_sys->len += end_offset - start_offset; ut_a(recv_sys->len <= RECV_PARSING_BUF_SIZE); } return(TRUE); } /*******************************************************//** Moves the parsing buffer data left to the buffer start. */ static void recv_sys_justify_left_parsing_buf(void) /*===================================*/ { ut_memmove(recv_sys->buf, recv_sys->buf + recv_sys->recovered_offset, recv_sys->len - recv_sys->recovered_offset); recv_sys->len -= recv_sys->recovered_offset; recv_sys->recovered_offset = 0; } /*******************************************************//** Scans log from a buffer and stores new log data to the parsing buffer. Parses and hashes the log records if new data found. Unless UNIV_HOTBACKUP is defined, this function will apply log records automatically when the hash table becomes full. @return TRUE if limit_lsn has been reached, or not able to scan any more in this log group */ UNIV_INTERN ibool recv_scan_log_recs( /*===============*/ ib_recovery_t recovery, /*!< in: recovery flag */ ulint available_memory,/*!< in: we let the hash table of recs to grow to this size, at the maximum */ ibool store_to_hash, /*!< in: TRUE if the records should be stored to the hash table; this is set to FALSE if just debug checking is needed */ const byte* buf, /*!< in: buffer containing a log segment or garbage */ ulint len, /*!< in: buffer length */ ib_uint64_t start_lsn, /*!< in: buffer start lsn */ ib_uint64_t* contiguous_lsn, /*!< in/out: it is known that all log groups contain contiguous log data up to this lsn */ ib_uint64_t* group_scanned_lsn)/*!< out: scanning succeeded up to this lsn */ { const byte* log_block; ulint no; ib_uint64_t scanned_lsn; ibool finished; ulint data_len; ibool more_data; ut_ad(start_lsn % OS_FILE_LOG_BLOCK_SIZE == 0); ut_ad(len % OS_FILE_LOG_BLOCK_SIZE == 0); ut_ad(len >= OS_FILE_LOG_BLOCK_SIZE); ut_a(store_to_hash <= TRUE); finished = FALSE; log_block = buf; scanned_lsn = start_lsn; more_data = FALSE; do { no = log_block_get_hdr_no(log_block); /* ib_logger(ib_stream, "Log block header no %lu\n", no); ib_logger(ib_stream, "Scanned lsn no %lu\n", log_block_convert_lsn_to_no(scanned_lsn)); */ if (no != log_block_convert_lsn_to_no(scanned_lsn) || !log_block_checksum_is_ok_or_old_format(log_block)) { if (no == log_block_convert_lsn_to_no(scanned_lsn) && !log_block_checksum_is_ok_or_old_format( log_block)) { ib_logger(ib_stream, "InnoDB: Log block no %lu at " "lsn %llu has\n" "InnoDB: ok header, but checksum " "field contains %lu, " "should be %lu\n", (ulong) no, scanned_lsn, (ulong) log_block_get_checksum( log_block), (ulong) log_block_calc_checksum( log_block)); } /* Garbage or an incompletely written log block */ finished = TRUE; break; } if (log_block_get_flush_bit(log_block)) { /* This block was a start of a log flush operation: we know that the previous flush operation must have been completed for all log groups before this block can have been flushed to any of the groups. Therefore, we know that log data is contiguous up to scanned_lsn in all non-corrupt log groups. */ if (scanned_lsn > *contiguous_lsn) { *contiguous_lsn = scanned_lsn; } } data_len = log_block_get_data_len(log_block); if ((store_to_hash || (data_len == OS_FILE_LOG_BLOCK_SIZE)) && scanned_lsn + data_len > recv_sys->scanned_lsn && (recv_sys->scanned_checkpoint_no > 0) && (log_block_get_checkpoint_no(log_block) < recv_sys->scanned_checkpoint_no) && (recv_sys->scanned_checkpoint_no - log_block_get_checkpoint_no(log_block) > 0x80000000UL)) { /* Garbage from a log buffer flush which was made before the most recent database recovery */ finished = TRUE; #ifdef UNIV_LOG_DEBUG /* This is not really an error, but currently we stop here in the debug version: */ ut_error; #endif break; } if (!recv_sys->parse_start_lsn && (log_block_get_first_rec_group(log_block) > 0)) { /* We found a point from which to start the parsing of log records */ recv_sys->parse_start_lsn = scanned_lsn + log_block_get_first_rec_group(log_block); recv_sys->scanned_lsn = recv_sys->parse_start_lsn; recv_sys->recovered_lsn = recv_sys->parse_start_lsn; } scanned_lsn += data_len; if (scanned_lsn > recv_sys->scanned_lsn) { /* We have found more entries. If this scan is of startup type, we must initiate crash recovery environment before parsing these log records. */ #ifndef UNIV_HOTBACKUP if (recv_log_scan_is_startup_type && !recv_needed_recovery) { ib_logger(ib_stream, "InnoDB: Log scan progressed" " past the checkpoint lsn %llu\n", recv_sys->scanned_lsn); recv_start_crash_recovery(recovery); } #endif /* !UNIV_HOTBACKUP */ /* We were able to find more log data: add it to the parsing buffer if parse_start_lsn is already non-zero */ if (recv_sys->len + 4 * OS_FILE_LOG_BLOCK_SIZE >= RECV_PARSING_BUF_SIZE) { ib_logger(ib_stream, "InnoDB: Error: log parsing" " buffer overflow." " Recovery may have failed!\n"); recv_sys->found_corrupt_log = TRUE; #ifndef UNIV_HOTBACKUP if (!srv_force_recovery) { ib_logger(ib_stream, "InnoDB: Set" " innodb_force_recovery" " to ignore this error.\n"); ut_error; } #endif /* !UNIV_HOTBACKUP */ } else if (!recv_sys->found_corrupt_log) { more_data = recv_sys_add_to_parsing_buf( log_block, scanned_lsn); } recv_sys->scanned_lsn = scanned_lsn; recv_sys->scanned_checkpoint_no = log_block_get_checkpoint_no(log_block); } if (data_len < OS_FILE_LOG_BLOCK_SIZE) { /* Log data for this group ends here */ finished = TRUE; break; } else { log_block += OS_FILE_LOG_BLOCK_SIZE; } } while (log_block < buf + len && !finished); *group_scanned_lsn = scanned_lsn; if (recv_needed_recovery || (recv_is_from_backup && !recv_is_making_a_backup)) { recv_scan_print_counter++; if (finished || (recv_scan_print_counter % 80 == 0)) { ib_logger(ib_stream, "InnoDB: Doing recovery: scanned up to" " log sequence number %llu\n", *group_scanned_lsn); } } if (more_data && !recv_sys->found_corrupt_log) { /* Try to parse more log records */ recv_parse_log_recs(store_to_hash); #ifndef UNIV_HOTBACKUP if (store_to_hash && mem_heap_get_size(recv_sys->heap) > available_memory) { /* Hash table of log records has grown too big: empty it; FALSE means no ibuf operations allowed, as we cannot add new records to the log yet: they would be produced by ibuf operations */ recv_apply_hashed_log_recs(FALSE); } #endif /* !UNIV_HOTBACKUP */ if (recv_sys->recovered_offset > RECV_PARSING_BUF_SIZE / 4) { /* Move parsing buffer data to the buffer start */ recv_sys_justify_left_parsing_buf(); } } return(finished); } #ifndef UNIV_HOTBACKUP /*******************************************************//** Scans log from a buffer and stores new log data to the parsing buffer. Parses and hashes the log records if new data found. */ static void recv_group_scan_log_recs( /*=====================*/ ib_recovery_t recovery, /*!< in: recovery flag */ log_group_t* group, /*!< in: log group */ ib_uint64_t* contiguous_lsn, /*!< in/out: it is known that all log groups contain contiguous log data up to this lsn */ ib_uint64_t* group_scanned_lsn)/*!< out: scanning succeeded up to this lsn */ { ibool finished; ib_uint64_t start_lsn; ib_uint64_t end_lsn; finished = FALSE; start_lsn = *contiguous_lsn; while (!finished) { end_lsn = start_lsn + RECV_SCAN_SIZE; log_group_read_log_seg(LOG_RECOVER, log_sys->buf, group, start_lsn, end_lsn); finished = recv_scan_log_recs( recovery, (buf_pool->curr_size - recv_n_pool_free_frames) * UNIV_PAGE_SIZE, TRUE, log_sys->buf, RECV_SCAN_SIZE, start_lsn, contiguous_lsn, group_scanned_lsn); start_lsn = end_lsn; } #ifdef UNIV_DEBUG if (log_debug_writes) { ib_logger(ib_stream, "InnoDB: Scanned group %lu up to" " log sequence number %llu\n", (ulong) group->id, *group_scanned_lsn); } #endif /* UNIV_DEBUG */ } /*******************************************************//** Initialize crash recovery environment. Can be called iff recv_needed_recovery == FALSE. */ static void recv_start_crash_recovery( /*======================*/ ib_recovery_t recovery) /*!< in: recovery flag */ { ut_a(!recv_needed_recovery); recv_needed_recovery = TRUE; ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Database was not shut down normally!\n" "InnoDB: Starting crash recovery.\n"); ib_logger(ib_stream, "InnoDB: Reading tablespace information" " from the .ibd files...\n"); fil_load_single_table_tablespaces(recovery); /* If we are using the doublewrite method, we will check if there are half-written pages in data files, and restore them from the doublewrite buffer if possible */ if (recovery < IB_RECOVERY_NO_LOG_REDO) { ib_logger(ib_stream, "InnoDB: Restoring possible half-written data " "pages from the doublewrite\n" "InnoDB: buffer...\n"); trx_sys_doublewrite_init_or_restore_pages(TRUE); } } /********************************************************//** Recover form ibbackup log file. */ static void recv_recover_from_ibbackup( /*=======================*/ log_group_t* max_cp_group) /*!< in/out: log group that contains the maximum consistent checkpoint */ { byte log_hdr_buf[LOG_FILE_HDR_SIZE]; /* Read the first log file header to print a note if this is a recovery from a restored InnoDB Hot Backup */ fil_io(OS_FILE_READ | OS_FILE_LOG, TRUE, max_cp_group->space_id, 0, 0, 0, LOG_FILE_HDR_SIZE, log_hdr_buf, max_cp_group); if (0 == ut_memcmp(log_hdr_buf + LOG_FILE_WAS_CREATED_BY_HOT_BACKUP, (byte*)"ibbackup", (sizeof "ibbackup") - 1)) { /* This log file was created by ibbackup --restore: print a note to the user about it */ ib_logger(ib_stream, "InnoDB: The log file was created by" " ibbackup --apply-log at\n" "InnoDB: %s\n", log_hdr_buf + LOG_FILE_WAS_CREATED_BY_HOT_BACKUP); ib_logger(ib_stream, "InnoDB: NOTE: the following crash recovery" " is part of a normal restore.\n"); /* Wipe over the label now */ memset(log_hdr_buf + LOG_FILE_WAS_CREATED_BY_HOT_BACKUP, ' ', 4); /* Write to the log file to wipe over the label */ fil_io(OS_FILE_WRITE | OS_FILE_LOG, TRUE, max_cp_group->space_id, 0, 0, 0, OS_FILE_LOG_BLOCK_SIZE, log_hdr_buf, max_cp_group); } } /************************************************************ Compare the checkpoint lsn with the lsn recorded in the data files and start crash recovery if required. */ static void recv_init_crash_recovery( /*=====================*/ ib_recovery_t recovery, /*!< in: recovery flag */ ib_uint64_t checkpoint_lsn, /*!< in: checkpoint lsn */ ib_uint64_t min_flushed_lsn,/*!< in: min flushed lsn from data files */ ib_uint64_t max_flushed_lsn)/*!< in: max flushed lsn from data files */ { /* NOTE: we always do a 'recovery' at startup, but only if there is something wrong we will print a message to the user about recovery: */ if (checkpoint_lsn != max_flushed_lsn || checkpoint_lsn != min_flushed_lsn) { if (checkpoint_lsn < max_flushed_lsn) { ib_logger(ib_stream, "InnoDB: #########################" "#################################\n" "InnoDB: " "WARNING!\n" "InnoDB: The log sequence number" " in ibdata files is higher\n" "InnoDB: than the log sequence number" " in the ib_logfiles! Are you sure\n" "InnoDB: you are using the right" " ib_logfiles to start up" " the database?\n" "InnoDB: Log sequence number in" " ib_logfiles is %llu, log\n" "InnoDB: sequence numbers stamped" " to ibdata file headers are between\n" "InnoDB: %llu and %llu.\n" "InnoDB: #########################" "#################################\n", checkpoint_lsn, min_flushed_lsn, max_flushed_lsn); } if (!recv_needed_recovery) { ib_logger(ib_stream, "InnoDB: The log sequence number" " in ibdata files does not match\n" "InnoDB: the log sequence number" " in the ib_logfiles!\n"); recv_start_crash_recovery(recovery); } } if (!recv_needed_recovery) { /* Init the doublewrite buffer memory structure */ trx_sys_doublewrite_init_or_restore_pages(FALSE); } } /************************************************************ Recovers from a checkpoint. When this function returns, the database is able to start processing of new user transactions, but the function recv_recovery_from_checkpoint_finish should be called later to complete the recovery and free the resources used in it. @return error code or DB_SUCCESS */ UNIV_INTERN ulint recv_recovery_from_checkpoint_start_func( /*=====================================*/ ib_recovery_t recovery, /*!< in: recovery flag */ #ifdef UNIV_LOG_ARCHIVE ulint type, /*!< in: LOG_CHECKPOINT or LOG_ARCHIVE */ ib_uint64_t limit_lsn, /*!< in: recover up to this lsn if possible */ #endif /* UNIV_LOG_ARCHIVE */ ib_uint64_t min_flushed_lsn,/*!< in: min flushed lsn from data files */ ib_uint64_t max_flushed_lsn)/*!< in: max flushed lsn from data files */ { log_group_t* group; log_group_t* max_cp_group; log_group_t* up_to_date_group; ulint max_cp_field; ib_uint64_t checkpoint_lsn; ib_uint64_t checkpoint_no; ib_uint64_t old_scanned_lsn; ib_uint64_t group_scanned_lsn; ib_uint64_t contiguous_lsn; ib_uint64_t archived_lsn; byte* buf; ulint err; #ifdef UNIV_LOG_ARCHIVE ut_ad(type != LOG_CHECKPOINT || limit_lsn == IB_UINT64_T_MAX); /** TRUE when recovering from a checkpoint */ # define TYPE_CHECKPOINT (type == LOG_CHECKPOINT) /** Recover up to this log sequence number */ # define LIMIT_LSN limit_lsn #else /* UNIV_LOG_ARCHIVE */ /** TRUE when recovering from a checkpoint */ # define TYPE_CHECKPOINT 1 /** Recover up to this log sequence number */ # define LIMIT_LSN IB_UINT64_T_MAX #endif /* UNIV_LOG_ARCHIVE */ if (TYPE_CHECKPOINT) { recv_sys_create(); recv_sys_init(buf_pool_get_curr_size()); } if (recovery >= IB_RECOVERY_NO_LOG_REDO) { ib_logger(ib_stream, "InnoDB: The user has set " "IB_RECOVERY_NO_LOG_REDO on\n"); ib_logger(ib_stream, "InnoDB: Skipping log redo\n"); return(DB_SUCCESS); } recv_recovery_on = TRUE; recv_sys->limit_lsn = LIMIT_LSN; mutex_enter(&(log_sys->mutex)); /* Look for the latest checkpoint from any of the log groups */ err = recv_find_max_checkpoint(&max_cp_group, &max_cp_field); if (err != DB_SUCCESS) { mutex_exit(&(log_sys->mutex)); return(err); } log_group_read_checkpoint_info(max_cp_group, max_cp_field); buf = log_sys->checkpoint_buf; checkpoint_lsn = mach_read_ull(buf + LOG_CHECKPOINT_LSN); checkpoint_no = mach_read_ull(buf + LOG_CHECKPOINT_NO); archived_lsn = mach_read_ull(buf + LOG_CHECKPOINT_ARCHIVED_LSN); recv_recover_from_ibbackup(max_cp_group); #ifdef UNIV_LOG_ARCHIVE group = UT_LIST_GET_FIRST(log_sys->log_groups); while (group) { log_checkpoint_get_nth_group_info(buf, group->id, &(group->archived_file_no), &(group->archived_offset)); group = UT_LIST_GET_NEXT(log_groups, group); } #endif /* UNIV_LOG_ARCHIVE */ if (TYPE_CHECKPOINT) { /* Start reading the log groups from the checkpoint lsn up. The variable contiguous_lsn contains an lsn up to which the log is known to be contiguously written to all log groups. */ recv_sys->parse_start_lsn = checkpoint_lsn; recv_sys->scanned_lsn = checkpoint_lsn; recv_sys->scanned_checkpoint_no = 0; recv_sys->recovered_lsn = checkpoint_lsn; srv_start_lsn = checkpoint_lsn; } contiguous_lsn = ut_uint64_align_down(recv_sys->scanned_lsn, OS_FILE_LOG_BLOCK_SIZE); if (TYPE_CHECKPOINT) { up_to_date_group = max_cp_group; #ifdef UNIV_LOG_ARCHIVE } else { ulint capacity; /* Try to recover the remaining part from logs: first from the logs of the archived group */ group = recv_sys->archive_group; capacity = log_group_get_capacity(group); if (recv_sys->scanned_lsn > checkpoint_lsn + capacity || checkpoint_lsn > recv_sys->scanned_lsn + capacity) { mutex_exit(&(log_sys->mutex)); /* The group does not contain enough log: probably an archived log file was missing or corrupt */ return(DB_ERROR); } recv_group_scan_log_recs(recovery, group, &contiguous_lsn, &group_scanned_lsn); if (recv_sys->scanned_lsn < checkpoint_lsn) { mutex_exit(&(log_sys->mutex)); /* The group did not contain enough log: an archived log file was missing or invalid, or the log group was corrupt */ return(DB_ERROR); } group->scanned_lsn = group_scanned_lsn; up_to_date_group = group; #endif /* UNIV_LOG_ARCHIVE */ } ut_ad(RECV_SCAN_SIZE <= log_sys->buf_size); group = UT_LIST_GET_FIRST(log_sys->log_groups); #ifdef UNIV_LOG_ARCHIVE if ((type == LOG_ARCHIVE) && (group == recv_sys->archive_group)) { group = UT_LIST_GET_NEXT(log_groups, group); } #endif /* UNIV_LOG_ARCHIVE */ /* Set the flag to publish that we are doing startup scan. */ recv_log_scan_is_startup_type = TYPE_CHECKPOINT; while (group) { old_scanned_lsn = recv_sys->scanned_lsn; recv_group_scan_log_recs(recovery, group, &contiguous_lsn, &group_scanned_lsn); group->scanned_lsn = group_scanned_lsn; if (old_scanned_lsn < group_scanned_lsn) { /* We found a more up-to-date group */ up_to_date_group = group; } #ifdef UNIV_LOG_ARCHIVE if ((type == LOG_ARCHIVE) && (group == recv_sys->archive_group)) { group = UT_LIST_GET_NEXT(log_groups, group); } #endif /* UNIV_LOG_ARCHIVE */ group = UT_LIST_GET_NEXT(log_groups, group); } /* Done with startup scan. Clear the flag. */ recv_log_scan_is_startup_type = FALSE; if (TYPE_CHECKPOINT) { recv_init_crash_recovery( recovery, checkpoint_lsn, min_flushed_lsn, max_flushed_lsn); } /* We currently have only one log group */ if (group_scanned_lsn < checkpoint_lsn) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: ERROR: We were only able to scan the log" " up to\n" "InnoDB: %llu, but a checkpoint was at %llu.\n" "InnoDB: It is possible that" " the database is now corrupt!\n", group_scanned_lsn, checkpoint_lsn); } if (group_scanned_lsn < recv_max_page_lsn) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: ERROR: We were only able to scan the log" " up to %llu\n" "InnoDB: but a database page a had an lsn %llu." " It is possible that the\n" "InnoDB: database is now corrupt!\n", group_scanned_lsn, recv_max_page_lsn); } if (recv_sys->recovered_lsn < checkpoint_lsn) { mutex_exit(&(log_sys->mutex)); if (recv_sys->recovered_lsn >= LIMIT_LSN) { return(DB_SUCCESS); } ut_error; return(DB_ERROR); } /* Synchronize the uncorrupted log groups to the most up-to-date log group; we also copy checkpoint info to groups */ log_sys->next_checkpoint_lsn = checkpoint_lsn; log_sys->next_checkpoint_no = checkpoint_no + 1; #ifdef UNIV_LOG_ARCHIVE log_sys->archived_lsn = archived_lsn; #endif /* UNIV_LOG_ARCHIVE */ recv_synchronize_groups(up_to_date_group); if (!recv_needed_recovery) { ut_a(checkpoint_lsn == recv_sys->recovered_lsn); } else { srv_start_lsn = recv_sys->recovered_lsn; } log_sys->lsn = recv_sys->recovered_lsn; ut_memcpy(log_sys->buf, recv_sys->last_block, OS_FILE_LOG_BLOCK_SIZE); log_sys->buf_free = (ulint) log_sys->lsn % OS_FILE_LOG_BLOCK_SIZE; log_sys->buf_next_to_write = log_sys->buf_free; log_sys->written_to_some_lsn = log_sys->lsn; log_sys->written_to_all_lsn = log_sys->lsn; log_sys->last_checkpoint_lsn = checkpoint_lsn; log_sys->next_checkpoint_no = checkpoint_no + 1; #ifdef UNIV_LOG_ARCHIVE if (archived_lsn == IB_UINT64_T_MAX) { log_sys->archiving_state = LOG_ARCH_OFF; } #endif /* UNIV_LOG_ARCHIVE */ mutex_enter(&(recv_sys->mutex)); recv_sys->apply_log_recs = TRUE; mutex_exit(&(recv_sys->mutex)); mutex_exit(&(log_sys->mutex)); recv_lsn_checks_on = TRUE; /* The database is now ready to start almost normal processing of user transactions: transaction rollbacks and the application of the log records in the hash table can be run in background. */ return(DB_SUCCESS); #undef TYPE_CHECKPOINT #undef LIMIT_LSN } /********************************************************//** Completes recovery from a checkpoint. */ UNIV_INTERN void recv_recovery_from_checkpoint_finish( /*=================================*/ ib_recovery_t recovery) /*!< in: recovery flag */ { /* Apply the hashed log records to the respective file pages */ if (recovery < IB_RECOVERY_NO_LOG_REDO) { recv_apply_hashed_log_recs(TRUE); } #ifdef UNIV_DEBUG if (log_debug_writes) { ib_logger(ib_stream, "InnoDB: Log records applied to the database\n"); } #endif /* UNIV_DEBUG */ if (recv_sys->found_corrupt_log) { ib_logger(ib_stream, "InnoDB: WARNING: the log file may have been" " corrupt and it\n" "InnoDB: is possible that the log scan or parsing" " did not proceed\n" "InnoDB: far enough in recovery. Please run" " CHECK TABLE\n" "InnoDB: on your InnoDB tables to check that" " they are ok!\n" "InnoDB: It may be safest to recover your" " InnoDB database from\n" "InnoDB: a backup!\n"); } /* Free the resources of the recovery system */ recv_recovery_on = FALSE; #ifndef UNIV_LOG_DEBUG recv_sys_debug_free(); #endif /* Roll back any recovered data dictionary transactions, so that the data dictionary tables will be free of any locks. The data dictionary latch should guarantee that there is at most one data dictionary transaction active at a time. */ trx_rollback_or_clean_recovered(FALSE); } /********************************************************//** Initiates the rollback of active transactions. */ UNIV_INTERN void recv_recovery_rollback_active(void) /*===============================*/ { int i; /* This is required to set the compare context before recovery, also it's a one-shot. We can safely set it to NULL after calling it. */ if (recv_pre_rollback_hook != NULL) { recv_pre_rollback_hook(); recv_pre_rollback_hook = NULL; } #ifdef UNIV_SYNC_DEBUG /* Wait for a while so that created threads have time to suspend themselves before we switch the latching order checks on */ os_thread_sleep(1000000); /* Switch latching order checks on in sync0sync.c */ sync_order_checks_on = TRUE; #endif ddl_drop_all_temp_indexes(srv_force_recovery); ddl_drop_all_temp_tables(srv_force_recovery); if (srv_force_recovery < IB_RECOVERY_NO_TRX_UNDO) { /* Rollback the uncommitted transactions which have no user session */ os_thread_create(trx_rollback_or_clean_all_recovered, (void *)&i, NULL); } } /******************************************************//** Resets the logs. The contents of log files will be lost! */ UNIV_INTERN void recv_reset_logs( /*============*/ ib_uint64_t lsn, /*!< in: reset to this lsn rounded up to be divisible by OS_FILE_LOG_BLOCK_SIZE, after which we add LOG_BLOCK_HDR_SIZE */ #ifdef UNIV_LOG_ARCHIVE ulint arch_log_no, /*!< in: next archived log file number */ #endif /* UNIV_LOG_ARCHIVE */ ibool new_logs_created)/*!< in: TRUE if resetting logs is done at the log creation; FALSE if it is done after archive recovery */ { log_group_t* group; ut_ad(mutex_own(&(log_sys->mutex))); log_sys->lsn = ut_uint64_align_up(lsn, OS_FILE_LOG_BLOCK_SIZE); group = UT_LIST_GET_FIRST(log_sys->log_groups); while (group) { group->lsn = log_sys->lsn; group->lsn_offset = LOG_FILE_HDR_SIZE; #ifdef UNIV_LOG_ARCHIVE group->archived_file_no = arch_log_no; group->archived_offset = 0; #endif /* UNIV_LOG_ARCHIVE */ if (!new_logs_created) { recv_truncate_group(group, group->lsn, group->lsn, group->lsn, group->lsn); } group = UT_LIST_GET_NEXT(log_groups, group); } log_sys->buf_next_to_write = 0; log_sys->written_to_some_lsn = log_sys->lsn; log_sys->written_to_all_lsn = log_sys->lsn; log_sys->next_checkpoint_no = 0; log_sys->last_checkpoint_lsn = 0; #ifdef UNIV_LOG_ARCHIVE log_sys->archived_lsn = log_sys->lsn; #endif /* UNIV_LOG_ARCHIVE */ log_block_init(log_sys->buf, log_sys->lsn); log_block_set_first_rec_group(log_sys->buf, LOG_BLOCK_HDR_SIZE); log_sys->buf_free = LOG_BLOCK_HDR_SIZE; log_sys->lsn += LOG_BLOCK_HDR_SIZE; mutex_exit(&(log_sys->mutex)); /* Reset the checkpoint fields in logs */ log_make_checkpoint_at(IB_UINT64_T_MAX, TRUE); log_make_checkpoint_at(IB_UINT64_T_MAX, TRUE); mutex_enter(&(log_sys->mutex)); } #endif /* !UNIV_HOTBACKUP */ #ifdef UNIV_HOTBACKUP /******************************************************//** Creates new log files after a backup has been restored. */ UNIV_INTERN void recv_reset_log_files_for_backup( /*============================*/ const char* log_dir, /*!< in: log file directory path */ ulint n_log_files, /*!< in: number of log files */ ulint log_file_size, /*!< in: log file size */ ib_uint64_t lsn) /*!< in: new start lsn, must be divisible by OS_FILE_LOG_BLOCK_SIZE */ { os_file_t log_file; ibool success; byte* buf; ulint i; ulint log_dir_len; char name[5000]; static const char ib_logfile_basename[] = "ib_logfile"; log_dir_len = strlen(log_dir); /* full path name of ib_logfile consists of log dir path + basename + number. This must fit in the name buffer. */ ut_a(log_dir_len + strlen(ib_logfile_basename) + 11 < sizeof(name)); buf = ut_malloc(LOG_FILE_HDR_SIZE + OS_FILE_LOG_BLOCK_SIZE); memset(buf, '\0', LOG_FILE_HDR_SIZE + OS_FILE_LOG_BLOCK_SIZE); for (i = 0; i < n_log_files; i++) { ut_snprintf(name, sizeof(name), "%s%s%lu", log_dir, ib_logfile_basename, (ulong)i); log_file = os_file_create_simple(name, OS_FILE_CREATE, OS_FILE_READ_WRITE, &success); if (!success) { srv_panic(DB_ERROR, "InnoDB: Cannot create %s. Check that" " the file does not exist yet.\n", name); } ib_logger(ib_stream, "Setting log file size to %lu %lu\n", (ulong) ut_get_high32(log_file_size), (ulong) log_file_size & 0xFFFFFFFFUL); success = os_file_set_size(name, log_file, log_file_size & 0xFFFFFFFFUL, ut_get_high32(log_file_size)); if (!success) { srv_panic(DB_ERROR, "InnoDB: Cannot set %s size to %lu %lu\n", name, (ulong) ut_get_high32(log_file_size), (ulong) (log_file_size & 0xFFFFFFFFUL)); } os_file_flush(log_file); os_file_close(log_file); } /* We pretend there is a checkpoint at lsn + LOG_BLOCK_HDR_SIZE */ log_reset_first_header_and_checkpoint(buf, lsn); log_block_init_in_old_format(buf + LOG_FILE_HDR_SIZE, lsn); log_block_set_first_rec_group(buf + LOG_FILE_HDR_SIZE, LOG_BLOCK_HDR_SIZE); ut_snprintf(name, sizeof(name)-1, "%s%s%lu", log_dir, ib_logfile_basename, (ulong)0); log_file = os_file_create_simple(name, OS_FILE_OPEN, OS_FILE_READ_WRITE, &success); if (!success) { srv_panic(DB_ERROR, "InnoDB: Cannot open %s.\n", name); } os_file_write(name, log_file, buf, 0, 0, LOG_FILE_HDR_SIZE + OS_FILE_LOG_BLOCK_SIZE); os_file_flush(log_file); os_file_close(log_file); ut_free(buf); } #endif /* UNIV_HOTBACKUP */ #ifdef UNIV_LOG_ARCHIVE /******************************************************//** Reads from the archive of a log group and performs recovery. @return TRUE if no more complete consistent archive files */ static ibool log_group_recover_from_archive_file( /*================================*/ ib_recovery_t recovery, /*!< in: recovery flag */ log_group_t* group) /*!< in: log group */ { os_file_t file_handle; ib_uint64_t start_lsn; ib_uint64_t file_end_lsn; ib_uint64_t dummy_lsn; ib_uint64_t scanned_lsn; ulint len; ibool ret; byte* buf; ulint read_offset; ulint file_size; ulint file_size_high; int input_char; char name[10000]; ut_a(0); try_open_again: buf = log_sys->buf; /* Add the file to the archive file space; open the file */ log_archived_file_name_gen(name, group->id, group->archived_file_no); file_handle = os_file_create(name, OS_FILE_OPEN, OS_FILE_LOG, OS_FILE_AIO, &ret); if (ret == FALSE) { ask_again: ib_logger(ib_stream, "InnoDB: Do you want to copy additional" " archived log files\n" "InnoDB: to the directory\n"); ib_logger(ib_stream, "InnoDB: or were these all the files needed" " in recovery?\n"); ib_logger(ib_stream, "InnoDB: (Y == copy more files; N == this is all)?"); input_char = getchar(); if (input_char == (int) 'N') { return(TRUE); } else if (input_char == (int) 'Y') { goto try_open_again; } else { goto ask_again; } } ret = os_file_get_size(file_handle, &file_size, &file_size_high); ut_a(ret); ut_a(file_size_high == 0); ib_logger(ib_stream, "InnoDB: Opened archived log file %s\n", name); ret = os_file_close(file_handle); if (file_size < LOG_FILE_HDR_SIZE) { ib_logger(ib_stream, "InnoDB: Archive file header incomplete %s\n", name); return(TRUE); } ut_a(ret); /* Add the archive file as a node to the space */ fil_node_create(name, 1 + file_size / UNIV_PAGE_SIZE, group->archive_space_id, FALSE); #if RECV_SCAN_SIZE < LOG_FILE_HDR_SIZE # error "RECV_SCAN_SIZE < LOG_FILE_HDR_SIZE" #endif /* Read the archive file header */ fil_io(OS_FILE_READ | OS_FILE_LOG, TRUE, group->archive_space_id, 0, // FIXME: ARCHIVE: Zip size 0, 0, LOG_FILE_HDR_SIZE, buf, NULL); /* Check if the archive file header is consistent */ if (mach_read_from_4(buf + LOG_GROUP_ID) != group->id || mach_read_from_4(buf + LOG_FILE_NO) != group->archived_file_no) { ib_logger(ib_stream, "InnoDB: Archive file header inconsistent %s\n", name); return(TRUE); } if (!mach_read_from_4(buf + LOG_FILE_ARCH_COMPLETED)) { ib_logger(ib_stream, "InnoDB: Archive file not completely written %s\n", name); return(TRUE); } start_lsn = mach_read_ull(buf + LOG_FILE_START_LSN); file_end_lsn = mach_read_ull(buf + LOG_FILE_END_LSN); if (!recv_sys->scanned_lsn) { if (recv_sys->parse_start_lsn < start_lsn) { ib_logger(ib_stream, "InnoDB: Archive log file %s" " starts from too big a lsn\n", name); return(TRUE); } recv_sys->scanned_lsn = start_lsn; } if (recv_sys->scanned_lsn != start_lsn) { ib_logger(ib_stream, "InnoDB: Archive log file %s starts from" " a wrong lsn\n", name); return(TRUE); } read_offset = LOG_FILE_HDR_SIZE; for (;;) { len = RECV_SCAN_SIZE; if (read_offset + len > file_size) { len = ut_calc_align_down(file_size - read_offset, OS_FILE_LOG_BLOCK_SIZE); } if (len == 0) { break; } #ifdef UNIV_DEBUG if (log_debug_writes) { ib_logger(ib_stream, "InnoDB: Archive read starting at" " lsn %llu, len %lu from file %s\n", start_lsn, (ulong) len, name); } #endif /* UNIV_DEBUG */ fil_io(OS_FILE_READ | OS_FILE_LOG, TRUE, group->archive_space_id, 0, // FIXME: ARCHIVE: Zip size read_offset / UNIV_PAGE_SIZE, read_offset % UNIV_PAGE_SIZE, len, buf, NULL); ret = recv_scan_log_recs( recovery, // FIXME: ARCHIVE: buf_pool_t::n_frames (buf_pool->curr_size - recv_n_pool_free_frames) * UNIV_PAGE_SIZE, TRUE, buf, len, start_lsn, &dummy_lsn, &scanned_lsn); if (scanned_lsn == file_end_lsn) { return(FALSE); } if (ret) { ib_logger(ib_stream, "InnoDB: Archive log file %s" " does not scan right\n", name); return(TRUE); } read_offset += len; start_lsn += len; ut_ad(start_lsn == scanned_lsn); } return(FALSE); } /********************************************************//** Recovers from archived log files, and also from log files, if they exist. @return error code or DB_SUCCESS */ UNIV_INTERN ulint recv_recovery_from_archive_start( /*=============================*/ ib_uint64_t min_flushed_lsn,/*!< in: min flushed lsn field from the data files */ ib_uint64_t limit_lsn, /*!< in: recover up to this lsn if possible */ ulint first_log_no) /*!< in: number of the first archived log file to use in the recovery; the file will be searched from INNOBASE_LOG_ARCH_DIR specified in server config file */ { log_group_t* group; ulint group_id; ulint trunc_len; ibool ret; ulint err; ut_a(0); recv_sys_create(); recv_sys_init(buf_pool_get_curr_size()); recv_recovery_on = TRUE; recv_recovery_from_backup_on = TRUE; recv_sys->limit_lsn = limit_lsn; group_id = 0; group = UT_LIST_GET_FIRST(log_sys->log_groups); while (group) { if (group->id == group_id) { break; } group = UT_LIST_GET_NEXT(log_groups, group); } if (!group) { ib_logger(ib_stream, "InnoDB: There is no log group defined with id %lu!\n", (ulong) group_id); return(DB_ERROR); } group->archived_file_no = first_log_no; recv_sys->parse_start_lsn = min_flushed_lsn; recv_sys->scanned_lsn = 0; recv_sys->scanned_checkpoint_no = 0; recv_sys->recovered_lsn = recv_sys->parse_start_lsn; recv_sys->archive_group = group; ret = FALSE; mutex_enter(&(log_sys->mutex)); while (!ret) { ret = log_group_recover_from_archive_file(group); /* Close and truncate a possible processed archive file from the file space */ trunc_len = UNIV_PAGE_SIZE * fil_space_get_size(group->archive_space_id); if (trunc_len > 0) { fil_space_truncate_start(group->archive_space_id, trunc_len); } group->archived_file_no++; } if (recv_sys->recovered_lsn < limit_lsn) { if (!recv_sys->scanned_lsn) { recv_sys->scanned_lsn = recv_sys->parse_start_lsn; } mutex_exit(&(log_sys->mutex)); err = recv_recovery_from_checkpoint_start(LOG_ARCHIVE, limit_lsn, IB_UINT64_T_MAX, IB_UINT64_T_MAX); if (err != DB_SUCCESS) { return(err); } mutex_enter(&(log_sys->mutex)); } if (limit_lsn != IB_UINT64_T_MAX) { recv_apply_hashed_log_recs(FALSE); recv_reset_logs(recv_sys->recovered_lsn, 0, FALSE); } mutex_exit(&(log_sys->mutex)); return(DB_SUCCESS); } /********************************************************//** Completes recovery from archive. */ UNIV_INTERN void recv_recovery_from_archive_finish(void) /*===================================*/ { recv_recovery_from_checkpoint_finish(); recv_recovery_from_backup_on = FALSE; } #endif /* UNIV_LOG_ARCHIVE */ haildb-2.3.2/log/log0log.c0000644000175000017500000025260611513177357016146 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2010, Innobase Oy. All Rights Reserved. Copyright (c) 2009, Google Inc. Portions of this file contain modifications contributed and copyrighted by Google, Inc. Those modifications are gratefully acknowledged and are described briefly in the InnoDB documentation. The contributions by Google are incorporated with their permission, and subject to the conditions contained in the file COPYING.Google. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file log/log0log.c Database log Created 12/9/1995 Heikki Tuuri *******************************************************/ #include "log0log.h" #ifdef UNIV_NONINL #include "log0log.ic" #endif #ifndef UNIV_HOTBACKUP #include "mem0mem.h" #include "buf0buf.h" #include "buf0flu.h" #include "srv0srv.h" #include "log0recv.h" #include "fil0fil.h" #include "dict0boot.h" #include "srv0srv.h" #include "srv0start.h" #include "trx0sys.h" #include "trx0trx.h" /* General philosophy of InnoDB redo-logs: 1) Every change to a contents of a data page must be done through mtr, which in mtr_commit() writes log records to the InnoDB redo log. 2) Normally these changes are performed using a mlog_write_ulint() or similar function. 3) In some page level operations only a code number of a c-function and its parameters are written to the log to reduce the size of the log. 3a) You should not add parameters to these kind of functions (e.g. trx_undo_header_create(), trx_undo_insert_header_reuse()) 3b) You should not add such functionality which either change working when compared with the old or are dependent on data outside of the page. These kind of functions should implement self-contained page transformation and it should be unchanged if you don't have very essential reasons to change log semantics or format. */ /* Current free limit of space 0; protected by the log sys mutex; 0 means uninitialized */ UNIV_INTERN ulint log_fsp_current_free_limit = 0; /* Global log system variable */ UNIV_INTERN log_t* log_sys = NULL; #ifdef UNIV_DEBUG UNIV_INTERN ibool log_do_write = TRUE; #endif /* UNIV_DEBUG */ /* These control how often we print warnings if the last checkpoint is too old */ UNIV_INTERN ibool log_has_printed_chkp_warning = FALSE; UNIV_INTERN time_t log_last_warning_time; #ifdef UNIV_LOG_ARCHIVE /* Pointer to this variable is used as the i/o-message when we do i/o to an archive */ UNIV_INTERN byte log_archive_io; #endif /* UNIV_LOG_ARCHIVE */ /* A margin for free space in the log buffer before a log entry is catenated */ #define LOG_BUF_WRITE_MARGIN (4 * OS_FILE_LOG_BLOCK_SIZE) /* Margins for free space in the log buffer after a log entry is catenated */ #define LOG_BUF_FLUSH_RATIO 2 #define LOG_BUF_FLUSH_MARGIN (LOG_BUF_WRITE_MARGIN + 4 * UNIV_PAGE_SIZE) /* Margin for the free space in the smallest log group, before a new query step which modifies the database, is started */ #define LOG_CHECKPOINT_FREE_PER_THREAD (4 * UNIV_PAGE_SIZE) #define LOG_CHECKPOINT_EXTRA_FREE (8 * UNIV_PAGE_SIZE) /* This parameter controls asynchronous making of a new checkpoint; the value should be bigger than LOG_POOL_PREFLUSH_RATIO_SYNC */ #define LOG_POOL_CHECKPOINT_RATIO_ASYNC 32 /* This parameter controls synchronous preflushing of modified buffer pages */ #define LOG_POOL_PREFLUSH_RATIO_SYNC 16 /* The same ratio for asynchronous preflushing; this value should be less than the previous */ #define LOG_POOL_PREFLUSH_RATIO_ASYNC 8 /* Extra margin, in addition to one log file, used in archiving */ #define LOG_ARCHIVE_EXTRA_MARGIN (4 * UNIV_PAGE_SIZE) /* This parameter controls asynchronous writing to the archive */ #define LOG_ARCHIVE_RATIO_ASYNC 16 /* Codes used in unlocking flush latches */ #define LOG_UNLOCK_NONE_FLUSHED_LOCK 1 #define LOG_UNLOCK_FLUSH_LOCK 2 /* States of an archiving operation */ #define LOG_ARCHIVE_READ 1 #define LOG_ARCHIVE_WRITE 2 /******************************************************//** Completes a checkpoint write i/o to a log file. */ static void log_io_complete_checkpoint(void); /*============================*/ #ifdef UNIV_LOG_ARCHIVE /******************************************************//** Completes an archiving i/o. */ static void log_io_complete_archive(void); /*=========================*/ #endif /* UNIV_LOG_ARCHIVE */ /****************************************************************//** Reset the variables. */ UNIV_INTERN void log_var_init(void) /*==============*/ { log_fsp_current_free_limit = 0; log_sys = NULL; #ifdef UNIV_DEBUG log_do_write = TRUE; log_debug_writes = FALSE; #endif /* UNIV_DEBUG */ log_has_printed_chkp_warning = FALSE; log_last_warning_time = 0; #ifdef UNIV_LOG_ARCHIVE log_archive_io = 0; #endif /* UNIV_LOG_ARCHIVE */ } /******************************************************************** Sets the global variable log_fsp_current_free_limit. Also makes a checkpoint, so that we know that the limit has been written to a log checkpoint field on disk. */ UNIV_INTERN void log_fsp_current_free_limit_set_and_checkpoint( /*==========================================*/ ulint limit) /*!< in: limit to set */ { ibool success; log_acquire(); log_fsp_current_free_limit = limit; log_release(); /* Try to make a synchronous checkpoint */ success = FALSE; while (!success) { success = log_checkpoint(TRUE, TRUE); } } /****************************************************************//** Returns the oldest modified block lsn in the pool, or log_sys->lsn if none exists. @return LSN of oldest modification */ static ib_uint64_t log_buf_pool_get_oldest_modification(void) /*======================================*/ { ib_uint64_t lsn; ut_ad(mutex_own(&(log_sys->mutex))); lsn = buf_pool_get_oldest_modification(); if (!lsn) { lsn = log_sys->lsn; } return(lsn); } /************************************************************//** Opens the log for log_write_low. The log must be closed with log_close and released with log_release. @return start lsn of the log record */ UNIV_INTERN ib_uint64_t log_reserve_and_open( /*=================*/ ulint len) /*!< in: length of data to be catenated */ { log_t* log = log_sys; ulint len_upper_limit; #ifdef UNIV_LOG_ARCHIVE ulint archived_lsn_age; ulint dummy; #endif /* UNIV_LOG_ARCHIVE */ #ifdef UNIV_DEBUG ulint count = 0; #endif /* UNIV_DEBUG */ ut_a(len < log->buf_size / 2); loop: log_acquire(); ut_ad(!recv_no_log_write); /* Calculate an upper limit for the space the string may take in the log buffer */ len_upper_limit = LOG_BUF_WRITE_MARGIN + (5 * len) / 4; if (log->buf_free + len_upper_limit > log->buf_size) { log_release(); /* Not enough free space, do a syncronous flush of the log buffer */ log_buffer_flush_to_disk(); srv_log_waits++; ut_ad(++count < 50); goto loop; } #ifdef UNIV_LOG_ARCHIVE if (log->archiving_state != LOG_ARCH_OFF) { archived_lsn_age = log->lsn - log->archived_lsn; if (archived_lsn_age + len_upper_limit > log->max_archived_lsn_age) { /* Not enough free archived space in log groups: do a synchronous archive write batch: */ log_release(); ut_ad(len_upper_limit <= log->max_archived_lsn_age); log_archive_do(TRUE, &dummy); ut_ad(++count < 50); goto loop; } } #endif /* UNIV_LOG_ARCHIVE */ #ifdef UNIV_LOG_DEBUG log->old_buf_free = log->buf_free; log->old_lsn = log->lsn; #endif return(log->lsn); } /************************************************************//** Writes to the log the string given. It is assumed that the caller holds the log mutex. */ UNIV_INTERN void log_write_low( /*==========*/ byte* str, /*!< in: string */ ulint str_len) /*!< in: string length */ { log_t* log = log_sys; ulint len; ulint data_len; byte* log_block; ut_ad(mutex_own(&(log->mutex))); part_loop: ut_ad(!recv_no_log_write); /* Calculate a part length */ data_len = (log->buf_free % OS_FILE_LOG_BLOCK_SIZE) + str_len; if (data_len <= OS_FILE_LOG_BLOCK_SIZE - LOG_BLOCK_TRL_SIZE) { /* The string fits within the current log block */ len = str_len; } else { data_len = OS_FILE_LOG_BLOCK_SIZE - LOG_BLOCK_TRL_SIZE; len = OS_FILE_LOG_BLOCK_SIZE - (log->buf_free % OS_FILE_LOG_BLOCK_SIZE) - LOG_BLOCK_TRL_SIZE; } ut_memcpy(log->buf + log->buf_free, str, len); str_len -= len; str = str + len; log_block = ut_align_down(log->buf + log->buf_free, OS_FILE_LOG_BLOCK_SIZE); log_block_set_data_len(log_block, data_len); if (data_len == OS_FILE_LOG_BLOCK_SIZE - LOG_BLOCK_TRL_SIZE) { /* This block became full */ log_block_set_data_len(log_block, OS_FILE_LOG_BLOCK_SIZE); log_block_set_checkpoint_no(log_block, log_sys->next_checkpoint_no); len += LOG_BLOCK_HDR_SIZE + LOG_BLOCK_TRL_SIZE; log->lsn += len; /* Initialize the next block header */ log_block_init(log_block + OS_FILE_LOG_BLOCK_SIZE, log->lsn); } else { log->lsn += len; } log->buf_free += len; ut_ad(log->buf_free <= log->buf_size); if (str_len > 0) { goto part_loop; } srv_log_write_requests++; } /************************************************************//** Closes the log. @return lsn */ UNIV_INTERN ib_uint64_t log_close( /*======*/ ib_recovery_t recovery) /*!< in: recovery flag */ { byte* log_block; ulint first_rec_group; ib_uint64_t oldest_lsn; ib_uint64_t lsn; log_t* log = log_sys; ib_uint64_t checkpoint_age; ut_ad(mutex_own(&(log->mutex))); ut_ad(!recv_no_log_write); lsn = log->lsn; log_block = ut_align_down(log->buf + log->buf_free, OS_FILE_LOG_BLOCK_SIZE); first_rec_group = log_block_get_first_rec_group(log_block); if (first_rec_group == 0) { /* We initialized a new log block which was not written full by the current mtr: the next mtr log record group will start within this block at the offset data_len */ log_block_set_first_rec_group( log_block, log_block_get_data_len(log_block)); } if (log->buf_free > log->max_buf_free) { log->check_flush_or_checkpoint = TRUE; } checkpoint_age = lsn - log->last_checkpoint_lsn; if (checkpoint_age >= log->log_group_capacity) { /* TODO: split btr_store_big_rec_extern_fields() into small steps so that we can release all latches in the middle, and call log_free_check() to ensure we never write over log written after the latest checkpoint. In principle, we should split all big_rec operations, but other operations are smaller. */ if (!log_has_printed_chkp_warning || difftime(time(NULL), log_last_warning_time) > 15) { log_has_printed_chkp_warning = TRUE; log_last_warning_time = time(NULL); ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: ERROR: the age of the last" " checkpoint is %lu,\n" "InnoDB: which exceeds the log group" " capacity %lu.\n" "InnoDB: If you are using big" " BLOB or TEXT rows, you must set the\n" "InnoDB: combined size of log files" " at least 10 times bigger than the\n" "InnoDB: largest such row.\n", (ulong) checkpoint_age, (ulong) log->log_group_capacity); } } if (checkpoint_age <= log->max_modified_age_async) { goto function_exit; } oldest_lsn = buf_pool_get_oldest_modification(); if (!oldest_lsn || lsn - oldest_lsn > log->max_modified_age_async || checkpoint_age > log->max_checkpoint_age_async) { log->check_flush_or_checkpoint = TRUE; } function_exit: #ifdef UNIV_LOG_DEBUG log_check_log_recs(recovery, log->buf + log->old_buf_free, log->buf_free - log->old_buf_free, log->old_lsn); #endif return(lsn); } #ifdef UNIV_LOG_ARCHIVE /******************************************************//** Pads the current log block full with dummy log records. Used in producing consistent archived log files. */ static void log_pad_current_log_block( /*======================*/ ib_recovery_t recovery) /*!< in: recovery flag */ { byte b = MLOG_DUMMY_RECORD; ulint pad_length; ulint i; ib_uint64_t lsn; /* We retrieve lsn only because otherwise gcc crashed on HP-UX */ lsn = log_reserve_and_open(OS_FILE_LOG_BLOCK_SIZE); pad_length = OS_FILE_LOG_BLOCK_SIZE - (log_sys->buf_free % OS_FILE_LOG_BLOCK_SIZE) - LOG_BLOCK_TRL_SIZE; for (i = 0; i < pad_length; i++) { log_write_low(&b, 1); } lsn = log_sys->lsn; log_close(recovery); log_release(); ut_a(lsn % OS_FILE_LOG_BLOCK_SIZE == LOG_BLOCK_HDR_SIZE); } #endif /* UNIV_LOG_ARCHIVE */ /******************************************************//** Calculates the data capacity of a log group, when the log file headers are not included. @return capacity in bytes */ UNIV_INTERN ulint log_group_get_capacity( /*===================*/ const log_group_t* group) /*!< in: log group */ { ut_ad(mutex_own(&(log_sys->mutex))); return((group->file_size - LOG_FILE_HDR_SIZE) * group->n_files); } /******************************************************//** Calculates the offset within a log group, when the log file headers are not included. @return size offset (<= offset) */ UNIV_INLINE ulint log_group_calc_size_offset( /*=======================*/ ulint offset, /*!< in: real offset within the log group */ const log_group_t* group) /*!< in: log group */ { ut_ad(mutex_own(&(log_sys->mutex))); return(offset - LOG_FILE_HDR_SIZE * (1 + offset / group->file_size)); } /******************************************************//** Calculates the offset within a log group, when the log file headers are included. @return real offset (>= offset) */ UNIV_INLINE ulint log_group_calc_real_offset( /*=======================*/ ulint offset, /*!< in: size offset within the log group */ const log_group_t* group) /*!< in: log group */ { ut_ad(mutex_own(&(log_sys->mutex))); return(offset + LOG_FILE_HDR_SIZE * (1 + offset / (group->file_size - LOG_FILE_HDR_SIZE))); } /******************************************************//** Calculates the offset of an lsn within a log group. @return offset within the log group */ static ulint log_group_calc_lsn_offset( /*======================*/ ib_uint64_t lsn, /*!< in: lsn, must be within 4 GB of group->lsn */ const log_group_t* group) /*!< in: log group */ { ib_uint64_t gr_lsn; ib_int64_t gr_lsn_size_offset; ib_int64_t difference; ib_int64_t group_size; ib_int64_t offset; ut_ad(mutex_own(&(log_sys->mutex))); /* If total log file size is > 2 GB we can easily get overflows with 32-bit integers. Use 64-bit integers instead. */ gr_lsn = group->lsn; gr_lsn_size_offset = (ib_int64_t) log_group_calc_size_offset(group->lsn_offset, group); group_size = (ib_int64_t) log_group_get_capacity(group); if (lsn >= gr_lsn) { difference = (ib_int64_t) (lsn - gr_lsn); } else { difference = (ib_int64_t) (gr_lsn - lsn); difference = difference % group_size; difference = group_size - difference; } offset = (gr_lsn_size_offset + difference) % group_size; ut_a(offset < (((ib_int64_t) 1) << 32)); /* offset must be < 4 GB */ /* ib_logger(ib_stream, "Offset is %lu gr_lsn_offset is %lu difference is %lu\n", (ulint)offset,(ulint)gr_lsn_size_offset, (ulint)difference); */ return(log_group_calc_real_offset((ulint)offset, group)); } #endif /* !UNIV_HOTBACKUP */ #ifdef UNIV_DEBUG UNIV_INTERN ibool log_debug_writes = FALSE; #endif /* UNIV_DEBUG */ /*******************************************************************//** Calculates where in log files we find a specified lsn. @return log file number */ UNIV_INTERN ulint log_calc_where_lsn_is( /*==================*/ ib_int64_t* log_file_offset, /*!< out: offset in that file (including the header) */ ib_uint64_t first_header_lsn, /*!< in: first log file start lsn */ ib_uint64_t lsn, /*!< in: lsn whose position to determine */ ulint n_log_files, /*!< in: total number of log files */ ib_int64_t log_file_size) /*!< in: log file size (including the header) */ { ib_int64_t capacity = log_file_size - LOG_FILE_HDR_SIZE; ulint file_no; ib_int64_t add_this_many; if (lsn < first_header_lsn) { add_this_many = 1 + (first_header_lsn - lsn) / (capacity * (ib_int64_t)n_log_files); lsn += add_this_many * capacity * (ib_int64_t)n_log_files; } ut_a(lsn >= first_header_lsn); file_no = ((ulint)((lsn - first_header_lsn) / capacity)) % n_log_files; *log_file_offset = (lsn - first_header_lsn) % capacity; *log_file_offset = *log_file_offset + LOG_FILE_HDR_SIZE; return(file_no); } #ifndef UNIV_HOTBACKUP /********************************************************//** Sets the field values in group to correspond to a given lsn. For this function to work, the values must already be correctly initialized to correspond to some lsn, for instance, a checkpoint lsn. */ UNIV_INTERN void log_group_set_fields( /*=================*/ log_group_t* group, /*!< in/out: group */ ib_uint64_t lsn) /*!< in: lsn for which the values should be set */ { group->lsn_offset = log_group_calc_lsn_offset(lsn, group); group->lsn = lsn; } /*****************************************************************//** Calculates the recommended highest values for lsn - last_checkpoint_lsn, lsn - buf_get_oldest_modification(), and lsn - max_archive_lsn_age. @return error value FALSE if the smallest log group is too small to accommodate the number of OS threads in the database server */ static ibool log_calc_max_ages(void) /*===================*/ { log_group_t* group; ulint margin; ulint free; ibool success = TRUE; ulint smallest_capacity; ulint archive_margin; ulint smallest_archive_margin; log_acquire(); group = UT_LIST_GET_FIRST(log_sys->log_groups); ut_ad(group); smallest_capacity = ULINT_MAX; smallest_archive_margin = ULINT_MAX; while (group) { if (log_group_get_capacity(group) < smallest_capacity) { smallest_capacity = log_group_get_capacity(group); } archive_margin = log_group_get_capacity(group) - (group->file_size - LOG_FILE_HDR_SIZE) - LOG_ARCHIVE_EXTRA_MARGIN; if (archive_margin < smallest_archive_margin) { smallest_archive_margin = archive_margin; } group = UT_LIST_GET_NEXT(log_groups, group); } /* Add extra safety */ smallest_capacity = smallest_capacity - smallest_capacity / 10; /* For each OS thread we must reserve so much free space in the smallest log group that it can accommodate the log entries produced by single query steps: running out of free log space is a serious system error which requires rebooting the database. */ free = LOG_CHECKPOINT_FREE_PER_THREAD * 10 + LOG_CHECKPOINT_EXTRA_FREE; if (free >= smallest_capacity / 2) { success = FALSE; goto failure; } else { margin = smallest_capacity - free; } margin = ut_min(margin, log_sys->adm_checkpoint_interval); margin = margin - margin / 10; /* Add still some extra safety */ log_sys->log_group_capacity = smallest_capacity; log_sys->max_modified_age_async = margin - margin / LOG_POOL_PREFLUSH_RATIO_ASYNC; log_sys->max_modified_age_sync = margin - margin / LOG_POOL_PREFLUSH_RATIO_SYNC; log_sys->max_checkpoint_age_async = margin - margin / LOG_POOL_CHECKPOINT_RATIO_ASYNC; log_sys->max_checkpoint_age = margin; #ifdef UNIV_LOG_ARCHIVE log_sys->max_archived_lsn_age = smallest_archive_margin; log_sys->max_archived_lsn_age_async = smallest_archive_margin - smallest_archive_margin / LOG_ARCHIVE_RATIO_ASYNC; #endif /* UNIV_LOG_ARCHIVE */ failure: log_release(); if (!success) { srv_panic(DB_ERROR, "InnoDB: Error: ib_logfiles are too small" " for thread_concurrency %lu.\n" "InnoDB: The combined size of ib_logfiles" " should be bigger than\n" "InnoDB: 200 kB.\n" "InnoDB: To get the server to start up, set" " thread_concurrency variable\n" "InnoDB: to a lower value, for example, to 8." " After an ERROR-FREE shutdown\n" "InnoDB: of the server you can adjust the size of" " ib_logfiles, as explained on\n" "InnoDB: the InnoDB website." "InnoDB: Cannot continue operation." " Forcing shutdown.\n"); } return(success); } /******************************************************//** Initializes the log. */ UNIV_INTERN void innobase_log_init(void) /*===================*/ { log_sys = mem_alloc(sizeof(log_t)); mutex_create(&log_sys->mutex, SYNC_LOG); log_acquire(); /* Start the lsn from one log block from zero: this way every log record has a start lsn != zero, a fact which we will use */ log_sys->lsn = LOG_START_LSN; ut_a(LOG_BUFFER_SIZE >= 16 * OS_FILE_LOG_BLOCK_SIZE); ut_a(LOG_BUFFER_SIZE >= 4 * UNIV_PAGE_SIZE); log_sys->buf_ptr = mem_alloc(LOG_BUFFER_SIZE + OS_FILE_LOG_BLOCK_SIZE); log_sys->buf = ut_align(log_sys->buf_ptr, OS_FILE_LOG_BLOCK_SIZE); log_sys->buf_size = LOG_BUFFER_SIZE; memset(log_sys->buf, '\0', LOG_BUFFER_SIZE); log_sys->max_buf_free = log_sys->buf_size / LOG_BUF_FLUSH_RATIO - LOG_BUF_FLUSH_MARGIN; log_sys->check_flush_or_checkpoint = TRUE; UT_LIST_INIT(log_sys->log_groups); log_sys->n_log_ios = 0; log_sys->n_log_ios_old = log_sys->n_log_ios; log_sys->last_printout_time = time(NULL); /*----------------------------*/ log_sys->buf_next_to_write = 0; log_sys->write_lsn = 0; log_sys->current_flush_lsn = 0; log_sys->flushed_to_disk_lsn = 0; log_sys->written_to_some_lsn = log_sys->lsn; log_sys->written_to_all_lsn = log_sys->lsn; log_sys->n_pending_writes = 0; log_sys->no_flush_event = os_event_create(NULL); os_event_set(log_sys->no_flush_event); log_sys->one_flushed_event = os_event_create(NULL); os_event_set(log_sys->one_flushed_event); /*----------------------------*/ log_sys->adm_checkpoint_interval = ULINT_MAX; log_sys->next_checkpoint_no = 0; log_sys->last_checkpoint_lsn = log_sys->lsn; log_sys->n_pending_checkpoint_writes = 0; rw_lock_create(&log_sys->checkpoint_lock, SYNC_NO_ORDER_CHECK); log_sys->checkpoint_buf_ptr = mem_alloc(2 * OS_FILE_LOG_BLOCK_SIZE); log_sys->checkpoint_buf = ut_align( log_sys->checkpoint_buf_ptr, OS_FILE_LOG_BLOCK_SIZE); memset(log_sys->checkpoint_buf, '\0', OS_FILE_LOG_BLOCK_SIZE); /*----------------------------*/ #ifdef UNIV_LOG_ARCHIVE /* By default log archiving is always off */ log_sys->archiving_state = LOG_ARCH_OFF; log_sys->archived_lsn = log_sys->lsn; log_sys->next_archived_lsn = 0; log_sys->n_pending_archive_ios = 0; rw_lock_create(&log_sys->archive_lock, SYNC_NO_ORDER_CHECK); log_sys->archive_buf = NULL; /* ut_align( ut_malloc(LOG_ARCHIVE_BUF_SIZE + OS_FILE_LOG_BLOCK_SIZE), OS_FILE_LOG_BLOCK_SIZE); */ log_sys->archive_buf_size = 0; /* memset(log_sys->archive_buf, '\0', LOG_ARCHIVE_BUF_SIZE); */ log_sys->archiving_on = os_event_create(NULL); #endif /* UNIV_LOG_ARCHIVE */ /*----------------------------*/ log_block_init(log_sys->buf, log_sys->lsn); log_block_set_first_rec_group(log_sys->buf, LOG_BLOCK_HDR_SIZE); log_sys->buf_free = LOG_BLOCK_HDR_SIZE; log_sys->lsn = LOG_START_LSN + LOG_BLOCK_HDR_SIZE; log_release(); #ifdef UNIV_LOG_DEBUG recv_sys_create(); recv_sys_init(buf_pool_get_curr_size()); recv_sys->parse_start_lsn = log_sys->lsn; recv_sys->scanned_lsn = log_sys->lsn; recv_sys->scanned_checkpoint_no = 0; recv_sys->recovered_lsn = log_sys->lsn; recv_sys->limit_lsn = IB_UINT64_T_MAX; #endif } /******************************************************************//** Inits a log group to the log system. */ UNIV_INTERN void log_group_init( /*===========*/ ulint id, /*!< in: group id */ ulint n_files, /*!< in: number of log files */ ulint file_size, /*!< in: log file size in bytes */ ulint space_id, /*!< in: space id of the file space which contains the log files of this group */ ulint archive_space_id __attribute__((unused))) /*!< in: space id of the file space which contains some archived log files for this group; currently, only for the first log group this is used */ { ulint i; log_group_t* group; group = mem_alloc(sizeof(log_group_t)); group->id = id; group->n_files = n_files; group->file_size = file_size; group->space_id = space_id; group->state = LOG_GROUP_OK; group->lsn = LOG_START_LSN; group->lsn_offset = LOG_FILE_HDR_SIZE; group->n_pending_writes = 0; group->file_header_bufs_ptr = mem_alloc(sizeof(byte*) * n_files); group->file_header_bufs = mem_alloc(sizeof(byte*) * n_files); #ifdef UNIV_LOG_ARCHIVE group->archive_file_header_bufs_ptr = mem_alloc( sizeof(byte*) * n_files); group->archive_file_header_bufs = mem_alloc(sizeof(byte*) * n_files); #endif /* UNIV_LOG_ARCHIVE */ for (i = 0; i < n_files; i++) { group->file_header_bufs_ptr[i] = mem_alloc( LOG_FILE_HDR_SIZE + OS_FILE_LOG_BLOCK_SIZE); group->file_header_bufs[i] = ut_align( group->file_header_bufs_ptr[i], OS_FILE_LOG_BLOCK_SIZE); memset(*(group->file_header_bufs + i), '\0', LOG_FILE_HDR_SIZE); #ifdef UNIV_LOG_ARCHIVE group->archive_file_header_bufs_ptr[i] = mem_alloc( LOG_FILE_HDR_SIZE + OS_FILE_LOG_BLOCK_SIZE); group->archive_file_header_bufs[i] = ut_align( group->archive_file_header_bufs_ptr[i], OS_FILE_LOG_BLOCK_SIZE); memset(*(group->archive_file_header_bufs + i), '\0', LOG_FILE_HDR_SIZE); #endif /* UNIV_LOG_ARCHIVE */ } #ifdef UNIV_LOG_ARCHIVE group->archive_space_id = archive_space_id; group->archived_file_no = 0; group->archived_offset = 0; #endif /* UNIV_LOG_ARCHIVE */ group->checkpoint_buf_ptr = mem_alloc(2 * OS_FILE_LOG_BLOCK_SIZE); group->checkpoint_buf = ut_align( group->checkpoint_buf_ptr, OS_FILE_LOG_BLOCK_SIZE); memset(group->checkpoint_buf, '\0', OS_FILE_LOG_BLOCK_SIZE); UT_LIST_ADD_LAST(log_groups, log_sys->log_groups, group); ut_a(log_calc_max_ages()); } /******************************************************************//** Does the unlockings needed in flush i/o completion. */ UNIV_INLINE void log_flush_do_unlocks( /*=================*/ ulint code) /*!< in: any ORed combination of LOG_UNLOCK_FLUSH_LOCK and LOG_UNLOCK_NONE_FLUSHED_LOCK */ { ut_ad(mutex_own(&(log_sys->mutex))); /* NOTE that we must own the log mutex when doing the setting of the events: this is because transactions will wait for these events to be set, and at that moment the log flush they were waiting for must have ended. If the log mutex were not reserved here, the i/o-thread calling this function might be preempted for a while, and when it resumed execution, it might be that a new flush had been started, and this function would erroneously signal the NEW flush as completed. Thus, the changes in the state of these events are performed atomically in conjunction with the changes in the state of log_sys->n_pending_writes etc. */ if (code & LOG_UNLOCK_NONE_FLUSHED_LOCK) { os_event_set(log_sys->one_flushed_event); } if (code & LOG_UNLOCK_FLUSH_LOCK) { os_event_set(log_sys->no_flush_event); } } /******************************************************************//** Checks if a flush is completed for a log group and does the completion routine if yes. @return LOG_UNLOCK_NONE_FLUSHED_LOCK or 0 */ UNIV_INLINE ulint log_group_check_flush_completion( /*=============================*/ log_group_t* group) /*!< in: log group */ { ut_ad(mutex_own(&(log_sys->mutex))); if (!log_sys->one_flushed && group->n_pending_writes == 0) { #ifdef UNIV_DEBUG if (log_debug_writes) { ib_logger(ib_stream, "Log flushed first to group %lu\n", (ulong) group->id); } #endif /* UNIV_DEBUG */ log_sys->written_to_some_lsn = log_sys->write_lsn; log_sys->one_flushed = TRUE; return(LOG_UNLOCK_NONE_FLUSHED_LOCK); } #ifdef UNIV_DEBUG if (log_debug_writes && (group->n_pending_writes == 0)) { ib_logger(ib_stream, "Log flushed to group %lu\n", (ulong) group->id); } #endif /* UNIV_DEBUG */ return(0); } /******************************************************//** Checks if a flush is completed and does the completion routine if yes. @return LOG_UNLOCK_FLUSH_LOCK or 0 */ static ulint log_sys_check_flush_completion(void) /*================================*/ { ulint move_start; ulint move_end; ut_ad(mutex_own(&(log_sys->mutex))); if (log_sys->n_pending_writes == 0) { log_sys->written_to_all_lsn = log_sys->write_lsn; log_sys->buf_next_to_write = log_sys->write_end_offset; if (log_sys->write_end_offset > log_sys->max_buf_free / 2) { /* Move the log buffer content to the start of the buffer */ move_start = ut_calc_align_down( log_sys->write_end_offset, OS_FILE_LOG_BLOCK_SIZE); move_end = ut_calc_align(log_sys->buf_free, OS_FILE_LOG_BLOCK_SIZE); ut_memmove(log_sys->buf, log_sys->buf + move_start, move_end - move_start); log_sys->buf_free -= move_start; log_sys->buf_next_to_write -= move_start; } return(LOG_UNLOCK_FLUSH_LOCK); } return(0); } /******************************************************//** Completes an i/o to a log file. */ UNIV_INTERN void log_io_complete( /*============*/ log_group_t* group) /*!< in: log group or a dummy pointer */ { ulint unlock; #ifdef UNIV_LOG_ARCHIVE if ((byte*)group == &log_archive_io) { /* It was an archive write */ log_io_complete_archive(); return; } #endif /* UNIV_LOG_ARCHIVE */ if ((ulint)group & 0x1UL) { /* It was a checkpoint write */ group = (log_group_t*)((ulint)group - 1); if (srv_unix_file_flush_method != SRV_UNIX_O_DSYNC && srv_unix_file_flush_method != SRV_UNIX_NOSYNC) { fil_flush(group->space_id); } #ifdef UNIV_DEBUG if (log_debug_writes) { ib_logger(ib_stream, "Checkpoint info written to group %lu\n", group->id); } #endif /* UNIV_DEBUG */ log_io_complete_checkpoint(); return; } ut_error; /*!< We currently use synchronous writing of the logs and cannot end up here! */ if (srv_unix_file_flush_method != SRV_UNIX_O_DSYNC && srv_unix_file_flush_method != SRV_UNIX_NOSYNC && srv_flush_log_at_trx_commit != 2) { fil_flush(group->space_id); } log_acquire(); ut_ad(!recv_no_log_write); ut_a(group->n_pending_writes > 0); ut_a(log_sys->n_pending_writes > 0); group->n_pending_writes--; log_sys->n_pending_writes--; unlock = log_group_check_flush_completion(group); unlock = unlock | log_sys_check_flush_completion(); log_flush_do_unlocks(unlock); log_release(); } /******************************************************//** Writes a log file header to a log file space. */ static void log_group_file_header_flush( /*========================*/ log_group_t* group, /*!< in: log group */ ulint nth_file, /*!< in: header to the nth file in the log file space */ ib_uint64_t start_lsn) /*!< in: log file data starts at this lsn */ { byte* buf; ulint dest_offset; ut_ad(mutex_own(&(log_sys->mutex))); ut_ad(!recv_no_log_write); ut_a(nth_file < group->n_files); buf = group->file_header_bufs[nth_file]; mach_write_to_4(buf + LOG_GROUP_ID, group->id); mach_write_ull(buf + LOG_FILE_START_LSN, start_lsn); /* Wipe over possible label of ibbackup --restore */ memcpy(buf + LOG_FILE_WAS_CREATED_BY_HOT_BACKUP, " ", 4); dest_offset = nth_file * group->file_size; #ifdef UNIV_DEBUG if (log_debug_writes) { ib_logger(ib_stream, "Writing log file header to group %lu file %lu\n", (ulong) group->id, (ulong) nth_file); } #endif /* UNIV_DEBUG */ if (log_do_write) { log_sys->n_log_ios++; srv_os_log_pending_writes++; fil_io(OS_FILE_WRITE | OS_FILE_LOG, TRUE, group->space_id, 0, // FIXME: ARCHIVE: Zip size ? dest_offset / UNIV_PAGE_SIZE, dest_offset % UNIV_PAGE_SIZE, OS_FILE_LOG_BLOCK_SIZE, buf, group); srv_os_log_pending_writes--; } } /******************************************************//** Stores a 4-byte checksum to the trailer checksum field of a log block before writing it to a log file. This checksum is used in recovery to check the consistency of a log block. */ static void log_block_store_checksum( /*=====================*/ byte* block) /*!< in/out: pointer to a log block */ { log_block_set_checksum(block, log_block_calc_checksum(block)); } /******************************************************//** Writes a buffer to a log file group. */ UNIV_INTERN void log_group_write_buf( /*================*/ log_group_t* group, /*!< in: log group */ byte* buf, /*!< in: buffer */ ulint len, /*!< in: buffer len; must be divisible by OS_FILE_LOG_BLOCK_SIZE */ ib_uint64_t start_lsn, /*!< in: start lsn of the buffer; must be divisible by OS_FILE_LOG_BLOCK_SIZE */ ulint new_data_offset)/*!< in: start offset of new data in buf: this parameter is used to decide if we have to write a new log file header */ { ulint write_len; ibool write_header; ulint next_offset; ulint i; ut_ad(mutex_own(&(log_sys->mutex))); ut_ad(!recv_no_log_write); ut_a(len % OS_FILE_LOG_BLOCK_SIZE == 0); ut_a(((ulint) start_lsn) % OS_FILE_LOG_BLOCK_SIZE == 0); if (new_data_offset == 0) { write_header = TRUE; } else { write_header = FALSE; } loop: if (len == 0) { return; } next_offset = log_group_calc_lsn_offset(start_lsn, group); if ((next_offset % group->file_size == LOG_FILE_HDR_SIZE) && write_header) { /* We start to write a new log file instance in the group */ log_group_file_header_flush(group, next_offset / group->file_size, start_lsn); srv_os_log_written+= OS_FILE_LOG_BLOCK_SIZE; srv_log_writes++; } if ((next_offset % group->file_size) + len > group->file_size) { write_len = group->file_size - (next_offset % group->file_size); } else { write_len = len; } #ifdef UNIV_DEBUG if (log_debug_writes) { ib_logger(ib_stream, "Writing log file segment to group %lu" " offset %lu len %lu\n" "start lsn %llu\n" "First block n:o %lu last block n:o %lu\n", (ulong) group->id, (ulong) next_offset, (ulong) write_len, start_lsn, (ulong) log_block_get_hdr_no(buf), (ulong) log_block_get_hdr_no( buf + write_len - OS_FILE_LOG_BLOCK_SIZE)); ut_a(log_block_get_hdr_no(buf) == log_block_convert_lsn_to_no(start_lsn)); for (i = 0; i < write_len / OS_FILE_LOG_BLOCK_SIZE; i++) { ut_a(log_block_get_hdr_no(buf) + i == log_block_get_hdr_no( buf + i * OS_FILE_LOG_BLOCK_SIZE)); } } #endif /* UNIV_DEBUG */ /* Calculate the checksums for each log block and write them to the trailer fields of the log blocks */ for (i = 0; i < write_len / OS_FILE_LOG_BLOCK_SIZE; i++) { log_block_store_checksum(buf + i * OS_FILE_LOG_BLOCK_SIZE); } if (log_do_write) { log_sys->n_log_ios++; srv_os_log_pending_writes++; fil_io(OS_FILE_WRITE | OS_FILE_LOG, TRUE, group->space_id, 0, next_offset / UNIV_PAGE_SIZE, next_offset % UNIV_PAGE_SIZE, write_len, buf, group); srv_os_log_pending_writes--; srv_os_log_written+= write_len; srv_log_writes++; } if (write_len < len) { start_lsn += write_len; len -= write_len; buf += write_len; write_header = TRUE; goto loop; } } /******************************************************//** This function is called, e.g., when a transaction wants to commit. It checks that the log has been written to the log file up to the last log entry written by the transaction. If there is a flush running, it waits and checks if the flush flushed enough. If not, starts a new flush. */ UNIV_INTERN void log_write_up_to( /*============*/ ib_uint64_t lsn, /*!< in: log sequence number up to which the log should be written, IB_UINT64_T_MAX if not specified */ ulint wait, /*!< in: LOG_NO_WAIT, LOG_WAIT_ONE_GROUP, or LOG_WAIT_ALL_GROUPS */ ibool flush_to_disk) /*!< in: TRUE if we want the written log also to be flushed to disk */ { log_group_t* group; ulint start_offset; ulint end_offset; ulint area_start; ulint area_end; #ifdef UNIV_DEBUG ulint loop_count = 0; #endif /* UNIV_DEBUG */ ulint unlock; if (recv_no_ibuf_operations) { /* Recovery is running and no operations on the log files are allowed yet (the variable name .._no_ibuf_.. is misleading) */ return; } loop: #ifdef UNIV_DEBUG loop_count++; ut_ad(loop_count < 5); # if 0 if (loop_count > 2) { ib_logger(ib_stream, "Log loop count %lu\n", loop_count); } # endif #endif log_acquire(); ut_ad(!recv_no_log_write); if (flush_to_disk && log_sys->flushed_to_disk_lsn >= lsn) { log_release(); return; } if (!flush_to_disk && (log_sys->written_to_all_lsn >= lsn || (log_sys->written_to_some_lsn >= lsn && wait != LOG_WAIT_ALL_GROUPS))) { log_release(); return; } if (log_sys->n_pending_writes > 0) { /* A write (+ possibly flush to disk) is running */ if (flush_to_disk && log_sys->current_flush_lsn >= lsn) { /* The write + flush will write enough: wait for it to complete */ goto do_waits; } if (!flush_to_disk && log_sys->write_lsn >= lsn) { /* The write will write enough: wait for it to complete */ goto do_waits; } log_release(); /* Wait for the write to complete and try to start a new write */ os_event_wait(log_sys->no_flush_event); goto loop; } if (!flush_to_disk && log_sys->buf_free == log_sys->buf_next_to_write) { /* Nothing to write and no flush to disk requested */ log_release(); return; } #ifdef UNIV_DEBUG if (log_debug_writes) { ib_logger(ib_stream, "Writing log from %llu up to lsn %llu\n", log_sys->written_to_all_lsn, log_sys->lsn); } #endif /* UNIV_DEBUG */ log_sys->n_pending_writes++; group = UT_LIST_GET_FIRST(log_sys->log_groups); group->n_pending_writes++; /*!< We assume here that we have only one log group! */ os_event_reset(log_sys->no_flush_event); os_event_reset(log_sys->one_flushed_event); start_offset = log_sys->buf_next_to_write; end_offset = log_sys->buf_free; area_start = ut_calc_align_down(start_offset, OS_FILE_LOG_BLOCK_SIZE); area_end = ut_calc_align(end_offset, OS_FILE_LOG_BLOCK_SIZE); ut_ad(area_end - area_start > 0); log_sys->write_lsn = log_sys->lsn; if (flush_to_disk) { log_sys->current_flush_lsn = log_sys->lsn; } log_sys->one_flushed = FALSE; log_block_set_flush_bit(log_sys->buf + area_start, TRUE); log_block_set_checkpoint_no( log_sys->buf + area_end - OS_FILE_LOG_BLOCK_SIZE, log_sys->next_checkpoint_no); /* Copy the last, incompletely written, log block a log block length up, so that when the flush operation writes from the log buffer, the segment to write will not be changed by writers to the log */ ut_memcpy(log_sys->buf + area_end, log_sys->buf + area_end - OS_FILE_LOG_BLOCK_SIZE, OS_FILE_LOG_BLOCK_SIZE); log_sys->buf_free += OS_FILE_LOG_BLOCK_SIZE; log_sys->write_end_offset = log_sys->buf_free; group = UT_LIST_GET_FIRST(log_sys->log_groups); /* Do the write to the log files */ while (group) { log_group_write_buf( group, log_sys->buf + area_start, area_end - area_start, ut_uint64_align_down(log_sys->written_to_all_lsn, OS_FILE_LOG_BLOCK_SIZE), start_offset - area_start); log_group_set_fields(group, log_sys->write_lsn); group = UT_LIST_GET_NEXT(log_groups, group); } log_release(); if (srv_unix_file_flush_method == SRV_UNIX_O_DSYNC) { /* O_DSYNC means the OS did not buffer the log file at all: so we have also flushed to disk what we have written */ log_sys->flushed_to_disk_lsn = log_sys->write_lsn; } else if (flush_to_disk) { group = UT_LIST_GET_FIRST(log_sys->log_groups); fil_flush(group->space_id); log_sys->flushed_to_disk_lsn = log_sys->write_lsn; } log_acquire(); group = UT_LIST_GET_FIRST(log_sys->log_groups); ut_a(group->n_pending_writes == 1); ut_a(log_sys->n_pending_writes == 1); group->n_pending_writes--; log_sys->n_pending_writes--; unlock = log_group_check_flush_completion(group); unlock = unlock | log_sys_check_flush_completion(); log_flush_do_unlocks(unlock); log_release(); return; do_waits: log_release(); switch (wait) { case LOG_WAIT_ONE_GROUP: os_event_wait(log_sys->one_flushed_event); break; case LOG_WAIT_ALL_GROUPS: os_event_wait(log_sys->no_flush_event); break; #ifdef UNIV_DEBUG case LOG_NO_WAIT: break; default: ut_error; #endif /* UNIV_DEBUG */ } } /****************************************************************//** Does a syncronous flush of the log buffer to disk. */ UNIV_INTERN void log_buffer_flush_to_disk(void) /*==========================*/ { ib_uint64_t lsn; log_acquire(); lsn = log_sys->lsn; log_release(); log_write_up_to(lsn, LOG_WAIT_ALL_GROUPS, TRUE); } /****************************************************************//** This functions writes the log buffer to the log file and if 'flush' is set it forces a flush of the log file as well. This is meant to be called from background master thread only as it does not wait for the write (+ possible flush) to finish. */ UNIV_INTERN void log_buffer_sync_in_background( /*==========================*/ ibool flush) /*!< in: flush the logs to disk */ { ib_uint64_t lsn; mutex_enter(&(log_sys->mutex)); lsn = log_sys->lsn; mutex_exit(&(log_sys->mutex)); log_write_up_to(lsn, LOG_NO_WAIT, flush); } /******************************************************************** Tries to establish a big enough margin of free space in the log buffer, such that a new log entry can be catenated without an immediate need for a flush. */ static void log_flush_margin(void) /*==================*/ { log_t* log = log_sys; ib_uint64_t lsn = 0; log_acquire(); if (log->buf_free > log->max_buf_free) { if (log->n_pending_writes > 0) { /* A flush is running: hope that it will provide enough free space */ } else { lsn = log->lsn; } } log_release(); if (lsn) { log_write_up_to(lsn, LOG_NO_WAIT, FALSE); } } /****************************************************************//** Advances the smallest lsn for which there are unflushed dirty blocks in the buffer pool. NOTE: this function may only be called if the calling thread owns no synchronization objects! @return FALSE if there was a flush batch of the same type running, which means that we could not start this flush batch */ UNIV_INTERN ibool log_preflush_pool_modified_pages( /*=============================*/ ib_uint64_t new_oldest, /*!< in: try to advance oldest_modified_lsn at least to this lsn */ ibool sync) /*!< in: TRUE if synchronous operation is desired */ { ulint n_pages; if (recv_recovery_on) { /* If the recovery is running, we must first apply all log records to their respective file pages to get the right modify lsn values to these pages: otherwise, there might be pages on disk which are not yet recovered to the current lsn, and even after calling this function, we could not know how up-to-date the disk version of the database is, and we could not make a new checkpoint on the basis of the info on the buffer pool only. */ recv_apply_hashed_log_recs(TRUE); } n_pages = buf_flush_batch(BUF_FLUSH_LIST, ULINT_MAX, new_oldest); if (sync) { buf_flush_wait_batch_end(BUF_FLUSH_LIST); } if (n_pages == ULINT_UNDEFINED) { return(FALSE); } return(TRUE); } /******************************************************//** Completes a checkpoint. */ static void log_complete_checkpoint(void) /*=========================*/ { ut_ad(mutex_own(&(log_sys->mutex))); ut_ad(log_sys->n_pending_checkpoint_writes == 0); log_sys->next_checkpoint_no++; log_sys->last_checkpoint_lsn = log_sys->next_checkpoint_lsn; rw_lock_x_unlock_gen(&(log_sys->checkpoint_lock), LOG_CHECKPOINT); } /******************************************************//** Completes an asynchronous checkpoint info write i/o to a log file. */ static void log_io_complete_checkpoint(void) /*============================*/ { log_acquire(); ut_ad(log_sys->n_pending_checkpoint_writes > 0); log_sys->n_pending_checkpoint_writes--; if (log_sys->n_pending_checkpoint_writes == 0) { log_complete_checkpoint(); } log_release(); } /*******************************************************************//** Writes info to a checkpoint about a log group. */ static void log_checkpoint_set_nth_group_info( /*==============================*/ byte* buf, /*!< in: buffer for checkpoint info */ ulint n, /*!< in: nth slot */ ulint file_no,/*!< in: archived file number */ ulint offset) /*!< in: archived file offset */ { ut_ad(n < LOG_MAX_N_GROUPS); mach_write_to_4(buf + LOG_CHECKPOINT_GROUP_ARRAY + 8 * n + LOG_CHECKPOINT_ARCHIVED_FILE_NO, file_no); mach_write_to_4(buf + LOG_CHECKPOINT_GROUP_ARRAY + 8 * n + LOG_CHECKPOINT_ARCHIVED_OFFSET, offset); } /*******************************************************************//** Gets info from a checkpoint about a log group. */ UNIV_INTERN void log_checkpoint_get_nth_group_info( /*==============================*/ const byte* buf, /*!< in: buffer containing checkpoint info */ ulint n, /*!< in: nth slot */ ulint* file_no,/*!< out: archived file number */ ulint* offset) /*!< out: archived file offset */ { ut_ad(n < LOG_MAX_N_GROUPS); *file_no = mach_read_from_4(buf + LOG_CHECKPOINT_GROUP_ARRAY + 8 * n + LOG_CHECKPOINT_ARCHIVED_FILE_NO); *offset = mach_read_from_4(buf + LOG_CHECKPOINT_GROUP_ARRAY + 8 * n + LOG_CHECKPOINT_ARCHIVED_OFFSET); } /******************************************************//** Writes the checkpoint info to a log group header. */ static void log_group_checkpoint( /*=================*/ log_group_t* group) /*!< in: log group */ { log_group_t* group2; #ifdef UNIV_LOG_ARCHIVE ib_uint64_t archived_lsn; ib_uint64_t next_archived_lsn; #endif /* UNIV_LOG_ARCHIVE */ ulint write_offset; ulint fold; byte* buf; ulint i; ut_ad(mutex_own(&(log_sys->mutex))); #if LOG_CHECKPOINT_SIZE > OS_FILE_LOG_BLOCK_SIZE # error "LOG_CHECKPOINT_SIZE > OS_FILE_LOG_BLOCK_SIZE" #endif buf = group->checkpoint_buf; mach_write_ull(buf + LOG_CHECKPOINT_NO, log_sys->next_checkpoint_no); mach_write_ull(buf + LOG_CHECKPOINT_LSN, log_sys->next_checkpoint_lsn); mach_write_to_4(buf + LOG_CHECKPOINT_OFFSET, log_group_calc_lsn_offset( log_sys->next_checkpoint_lsn, group)); mach_write_to_4(buf + LOG_CHECKPOINT_LOG_BUF_SIZE, log_sys->buf_size); #ifdef UNIV_LOG_ARCHIVE if (log_sys->archiving_state == LOG_ARCH_OFF) { archived_lsn = IB_UINT64_T_MAX; } else { archived_lsn = log_sys->archived_lsn; if (archived_lsn != log_sys->next_archived_lsn) { next_archived_lsn = log_sys->next_archived_lsn; /* For debugging only */ } } mach_write_ull(buf + LOG_CHECKPOINT_ARCHIVED_LSN, archived_lsn); #else /* UNIV_LOG_ARCHIVE */ mach_write_ull(buf + LOG_CHECKPOINT_ARCHIVED_LSN, IB_UINT64_T_MAX); #endif /* UNIV_LOG_ARCHIVE */ for (i = 0; i < LOG_MAX_N_GROUPS; i++) { log_checkpoint_set_nth_group_info(buf, i, 0, 0); } group2 = UT_LIST_GET_FIRST(log_sys->log_groups); while (group2) { log_checkpoint_set_nth_group_info(buf, group2->id, #ifdef UNIV_LOG_ARCHIVE group2->archived_file_no, group2->archived_offset #else /* UNIV_LOG_ARCHIVE */ 0, 0 #endif /* UNIV_LOG_ARCHIVE */ ); group2 = UT_LIST_GET_NEXT(log_groups, group2); } fold = ut_fold_binary(buf, LOG_CHECKPOINT_CHECKSUM_1); mach_write_to_4(buf + LOG_CHECKPOINT_CHECKSUM_1, fold); fold = ut_fold_binary(buf + LOG_CHECKPOINT_LSN, LOG_CHECKPOINT_CHECKSUM_2 - LOG_CHECKPOINT_LSN); mach_write_to_4(buf + LOG_CHECKPOINT_CHECKSUM_2, fold); /* Starting from InnoDB-3.23.50, we also write info on allocated size in the tablespace */ mach_write_to_4(buf + LOG_CHECKPOINT_FSP_FREE_LIMIT, log_fsp_current_free_limit); mach_write_to_4(buf + LOG_CHECKPOINT_FSP_MAGIC_N, LOG_CHECKPOINT_FSP_MAGIC_N_VAL); /* We alternate the physical place of the checkpoint info in the first log file */ if ((log_sys->next_checkpoint_no & 1) == 0) { write_offset = LOG_CHECKPOINT_1; } else { write_offset = LOG_CHECKPOINT_2; } if (log_do_write) { if (log_sys->n_pending_checkpoint_writes == 0) { rw_lock_x_lock_gen(&(log_sys->checkpoint_lock), LOG_CHECKPOINT); } log_sys->n_pending_checkpoint_writes++; log_sys->n_log_ios++; /* We send as the last parameter the group machine address added with 1, as we want to distinguish between a normal log file write and a checkpoint field write */ fil_io(OS_FILE_WRITE | OS_FILE_LOG, FALSE, group->space_id, 0, write_offset / UNIV_PAGE_SIZE, write_offset % UNIV_PAGE_SIZE, OS_FILE_LOG_BLOCK_SIZE, buf, ((byte*)group + 1)); ut_ad(((ulint)group & 0x1UL) == 0); } } #endif /* !UNIV_HOTBACKUP */ #ifdef UNIV_HOTBACKUP /******************************************************//** Writes info to a buffer of a log group when log files are created in backup restoration. */ UNIV_INTERN void log_reset_first_header_and_checkpoint( /*==================================*/ byte* hdr_buf,/*!< in: buffer which will be written to the start of the first log file */ ib_uint64_t start) /*!< in: lsn of the start of the first log file; we pretend that there is a checkpoint at start + LOG_BLOCK_HDR_SIZE */ { ulint fold; byte* buf; ib_uint64_t lsn; mach_write_to_4(hdr_buf + LOG_GROUP_ID, 0); mach_write_ull(hdr_buf + LOG_FILE_START_LSN, start); lsn = start + LOG_BLOCK_HDR_SIZE; /* Write the label of ibbackup --restore */ strcpy((char*) hdr_buf + LOG_FILE_WAS_CREATED_BY_HOT_BACKUP, "ibbackup "); ut_sprintf_timestamp((char*) hdr_buf + (LOG_FILE_WAS_CREATED_BY_HOT_BACKUP + (sizeof "ibbackup ") - 1)); buf = hdr_buf + LOG_CHECKPOINT_1; mach_write_ull(buf + LOG_CHECKPOINT_NO, 0); mach_write_ull(buf + LOG_CHECKPOINT_LSN, lsn); mach_write_to_4(buf + LOG_CHECKPOINT_OFFSET, LOG_FILE_HDR_SIZE + LOG_BLOCK_HDR_SIZE); mach_write_to_4(buf + LOG_CHECKPOINT_LOG_BUF_SIZE, 2 * 1024 * 1024); mach_write_ull(buf + LOG_CHECKPOINT_ARCHIVED_LSN, IB_UINT64_T_MAX); fold = ut_fold_binary(buf, LOG_CHECKPOINT_CHECKSUM_1); mach_write_to_4(buf + LOG_CHECKPOINT_CHECKSUM_1, fold); fold = ut_fold_binary(buf + LOG_CHECKPOINT_LSN, LOG_CHECKPOINT_CHECKSUM_2 - LOG_CHECKPOINT_LSN); mach_write_to_4(buf + LOG_CHECKPOINT_CHECKSUM_2, fold); /* Starting from InnoDB-3.23.50, we should also write info on allocated size in the tablespace, but unfortunately we do not know it here */ } #endif /* UNIV_HOTBACKUP */ #ifndef UNIV_HOTBACKUP /******************************************************//** Reads a checkpoint info from a log group header to log_sys->checkpoint_buf. */ UNIV_INTERN void log_group_read_checkpoint_info( /*===========================*/ log_group_t* group, /*!< in: log group */ ulint field) /*!< in: LOG_CHECKPOINT_1 or LOG_CHECKPOINT_2 */ { ut_ad(mutex_own(&(log_sys->mutex))); log_sys->n_log_ios++; fil_io(OS_FILE_READ | OS_FILE_LOG, TRUE, group->space_id, 0, field / UNIV_PAGE_SIZE, field % UNIV_PAGE_SIZE, OS_FILE_LOG_BLOCK_SIZE, log_sys->checkpoint_buf, NULL); } /******************************************************//** Writes checkpoint info to groups. */ UNIV_INTERN void log_groups_write_checkpoint_info(void) /*==================================*/ { log_group_t* group; ut_ad(mutex_own(&(log_sys->mutex))); group = UT_LIST_GET_FIRST(log_sys->log_groups); while (group) { log_group_checkpoint(group); group = UT_LIST_GET_NEXT(log_groups, group); } } /******************************************************//** Makes a checkpoint. Note that this function does not flush dirty blocks from the buffer pool: it only checks what is lsn of the oldest modification in the pool, and writes information about the lsn in log files. Use log_make_checkpoint_at to flush also the pool. @return TRUE if success, FALSE if a checkpoint write was already running */ UNIV_INTERN ibool log_checkpoint( /*===========*/ ibool sync, /*!< in: TRUE if synchronous operation is desired */ ibool write_always) /*!< in: the function normally checks if the the new checkpoint would have a greater lsn than the previous one: if not, then no physical write is done; by setting this parameter TRUE, a physical write will always be made to log files */ { ib_uint64_t oldest_lsn; if (recv_recovery_is_on()) { recv_apply_hashed_log_recs(TRUE); } if (srv_unix_file_flush_method != SRV_UNIX_NOSYNC) { fil_flush_file_spaces(FIL_TABLESPACE); } log_acquire(); ut_ad(!recv_no_log_write); oldest_lsn = log_buf_pool_get_oldest_modification(); log_release(); /* Because log also contains headers and dummy log records, if the buffer pool contains no dirty buffers, oldest_lsn gets the value log_sys->lsn from the previous function, and we must make sure that the log is flushed up to that lsn. If there are dirty buffers in the buffer pool, then our write-ahead-logging algorithm ensures that the log has been flushed up to oldest_lsn. */ log_write_up_to(oldest_lsn, LOG_WAIT_ALL_GROUPS, TRUE); log_acquire(); if (!write_always && log_sys->last_checkpoint_lsn >= oldest_lsn) { log_release(); return(TRUE); } ut_ad(log_sys->flushed_to_disk_lsn >= oldest_lsn); if (log_sys->n_pending_checkpoint_writes > 0) { /* A checkpoint write is running */ log_release(); if (sync) { /* Wait for the checkpoint write to complete */ rw_lock_s_lock(&(log_sys->checkpoint_lock)); rw_lock_s_unlock(&(log_sys->checkpoint_lock)); } return(FALSE); } log_sys->next_checkpoint_lsn = oldest_lsn; #ifdef UNIV_DEBUG if (log_debug_writes) { ib_logger(ib_stream, "Making checkpoint no %lu at lsn %llu\n", (ulong) log_sys->next_checkpoint_no, oldest_lsn); } #endif /* UNIV_DEBUG */ log_groups_write_checkpoint_info(); log_release(); if (sync) { /* Wait for the checkpoint write to complete */ rw_lock_s_lock(&(log_sys->checkpoint_lock)); rw_lock_s_unlock(&(log_sys->checkpoint_lock)); } return(TRUE); } /****************************************************************//** Makes a checkpoint at a given lsn or later. */ UNIV_INTERN void log_make_checkpoint_at( /*===================*/ ib_uint64_t lsn, /*!< in: make a checkpoint at this or a later lsn, if IB_UINT64_T_MAX, makes a checkpoint at the latest lsn */ ibool write_always) /*!< in: the function normally checks if the new checkpoint would have a greater lsn than the previous one: if not, then no physical write is done; by setting this parameter TRUE, a physical write will always be made to log files */ { /* Preflush pages synchronously */ while (!log_preflush_pool_modified_pages(lsn, TRUE)); while (!log_checkpoint(TRUE, write_always)); } /****************************************************************//** Tries to establish a big enough margin of free space in the log groups, such that a new log entry can be catenated without an immediate need for a checkpoint. NOTE: this function may only be called if the calling thread owns no synchronization objects! */ static void log_checkpoint_margin(void) /*=======================*/ { log_t* log = log_sys; ib_uint64_t age; ib_uint64_t checkpoint_age; ib_uint64_t advance; ib_uint64_t oldest_lsn; ibool sync; ibool checkpoint_sync; ibool do_checkpoint; ibool success; loop: sync = FALSE; checkpoint_sync = FALSE; do_checkpoint = FALSE; log_acquire(); ut_ad(!recv_no_log_write); if (log->check_flush_or_checkpoint == FALSE) { log_release(); return; } oldest_lsn = log_buf_pool_get_oldest_modification(); age = log->lsn - oldest_lsn; if (age > log->max_modified_age_sync) { /* A flush is urgent: we have to do a synchronous preflush */ sync = TRUE; advance = 2 * (age - log->max_modified_age_sync); } else if (age > log->max_modified_age_async) { /* A flush is not urgent: we do an asynchronous preflush */ advance = age - log->max_modified_age_async; } else { advance = 0; } checkpoint_age = log->lsn - log->last_checkpoint_lsn; if (checkpoint_age > log->max_checkpoint_age) { /* A checkpoint is urgent: we do it synchronously */ checkpoint_sync = TRUE; do_checkpoint = TRUE; } else if (checkpoint_age > log->max_checkpoint_age_async) { /* A checkpoint is not urgent: do it asynchronously */ do_checkpoint = TRUE; log->check_flush_or_checkpoint = FALSE; } else { log->check_flush_or_checkpoint = FALSE; } log_release(); if (advance) { ib_uint64_t new_oldest = oldest_lsn + advance; success = log_preflush_pool_modified_pages(new_oldest, sync); /* If the flush succeeded, this thread has done its part and can proceed. If it did not succeed, there was another thread doing a flush at the same time. If sync was FALSE, the flush was not urgent, and we let this thread proceed. Otherwise, we let it start from the beginning again. */ if (sync && !success) { log_acquire(); log->check_flush_or_checkpoint = TRUE; log_release(); goto loop; } } if (do_checkpoint) { log_checkpoint(checkpoint_sync, FALSE); if (checkpoint_sync) { goto loop; } } } /******************************************************//** Reads a specified log segment to a buffer. */ UNIV_INTERN void log_group_read_log_seg( /*===================*/ ulint type, /*!< in: LOG_ARCHIVE or LOG_RECOVER */ byte* buf, /*!< in: buffer where to read */ log_group_t* group, /*!< in: log group */ ib_uint64_t start_lsn, /*!< in: read area start */ ib_uint64_t end_lsn) /*!< in: read area end */ { ulint len; ulint source_offset; ibool sync; ut_ad(mutex_own(&(log_sys->mutex))); sync = (type == LOG_RECOVER); loop: source_offset = log_group_calc_lsn_offset(start_lsn, group); len = (ulint) (end_lsn - start_lsn); ut_ad(len != 0); if ((source_offset % group->file_size) + len > group->file_size) { len = group->file_size - (source_offset % group->file_size); } #ifdef UNIV_LOG_ARCHIVE if (type == LOG_ARCHIVE) { log_sys->n_pending_archive_ios++; } #endif /* UNIV_LOG_ARCHIVE */ log_sys->n_log_ios++; fil_io(OS_FILE_READ | OS_FILE_LOG, sync, group->space_id, 0, source_offset / UNIV_PAGE_SIZE, source_offset % UNIV_PAGE_SIZE, len, buf, NULL); start_lsn += len; buf += len; if (start_lsn != end_lsn) { goto loop; } } #ifdef UNIV_LOG_ARCHIVE /******************************************************//** Generates an archived log file name. */ UNIV_INTERN void log_archived_file_name_gen( /*=======================*/ char* buf, /*!< in: buffer where to write */ ulint id __attribute__((unused)), /*!< in: group id; currently we only archive the first group */ ulint file_no)/*!< in: file number */ { sprintf(buf, "%sib_arch_log_%010lu", srv_arch_dir, (ulong) file_no); } /******************************************************//** Writes a log file header to a log file space. */ static void log_group_archive_file_header_write( /*================================*/ log_group_t* group, /*!< in: log group */ ulint nth_file, /*!< in: header to the nth file in the archive log file space */ ulint file_no, /*!< in: archived file number */ ib_uint64_t start_lsn) /*!< in: log file data starts at this lsn */ { byte* buf; ulint dest_offset; ut_ad(mutex_own(&(log_sys->mutex))); ut_a(nth_file < group->n_files); buf = group->archive_file_header_bufs[nth_file]; mach_write_to_4(buf + LOG_GROUP_ID, group->id); mach_write_ull(buf + LOG_FILE_START_LSN, start_lsn); mach_write_to_4(buf + LOG_FILE_NO, file_no); mach_write_to_4(buf + LOG_FILE_ARCH_COMPLETED, FALSE); dest_offset = nth_file * group->file_size; log_sys->n_log_ios++; fil_io(OS_FILE_WRITE | OS_FILE_LOG, TRUE, group->archive_space_id, 0, /* FIXME: ARCHIVE Zip size */ dest_offset / UNIV_PAGE_SIZE, dest_offset % UNIV_PAGE_SIZE, 2 * OS_FILE_LOG_BLOCK_SIZE, buf, &log_archive_io); } /******************************************************//** Writes a log file header to a completed archived log file. */ static void log_group_archive_completed_header_write( /*=====================================*/ log_group_t* group, /*!< in: log group */ ulint nth_file, /*!< in: header to the nth file in the archive log file space */ ib_uint64_t end_lsn) /*!< in: end lsn of the file */ { byte* buf; ulint dest_offset; ut_ad(mutex_own(&(log_sys->mutex))); ut_a(nth_file < group->n_files); buf = group->archive_file_header_bufs[nth_file]; mach_write_to_4(buf + LOG_FILE_ARCH_COMPLETED, TRUE); mach_write_ull(buf + LOG_FILE_END_LSN, end_lsn); dest_offset = nth_file * group->file_size + LOG_FILE_ARCH_COMPLETED; log_sys->n_log_ios++; fil_io(OS_FILE_WRITE | OS_FILE_LOG, TRUE, group->archive_space_id, 0, /* FIXME: ARCHIVE Zip size */ dest_offset / UNIV_PAGE_SIZE, dest_offset % UNIV_PAGE_SIZE, OS_FILE_LOG_BLOCK_SIZE, buf + LOG_FILE_ARCH_COMPLETED, &log_archive_io); } /******************************************************//** Does the archive writes for a single log group. */ static void log_group_archive( /*==============*/ log_group_t* group) /*!< in: log group */ { os_file_t file_handle; ib_uint64_t start_lsn; ib_uint64_t end_lsn; char name[1024]; byte* buf; ulint len; ibool ret; ulint next_offset; ulint n_files; ulint open_mode; ut_ad(mutex_own(&(log_sys->mutex))); start_lsn = log_sys->archived_lsn; ut_a(start_lsn % OS_FILE_LOG_BLOCK_SIZE == 0); end_lsn = log_sys->next_archived_lsn; ut_a(end_lsn % OS_FILE_LOG_BLOCK_SIZE == 0); buf = log_sys->archive_buf; n_files = 0; next_offset = group->archived_offset; loop: if ((next_offset % group->file_size == 0) || (fil_space_get_size(group->archive_space_id) == 0)) { /* Add the file to the archive file space; create or open the file */ if (next_offset % group->file_size == 0) { open_mode = OS_FILE_CREATE; } else { open_mode = OS_FILE_OPEN; } log_archived_file_name_gen(name, group->id, group->archived_file_no + n_files); file_handle = os_file_create(name, open_mode, OS_FILE_AIO, OS_DATA_FILE, &ret); if (!ret && (open_mode == OS_FILE_CREATE)) { file_handle = os_file_create( name, OS_FILE_OPEN, OS_FILE_AIO, OS_DATA_FILE, &ret); } if (!ret) { srv_panic(DB_ERROR, "InnoDB: Cannot create or open" " archive log file %s.\n" "InnoDB: Cannot continue operation.\n" "InnoDB: Check that the log archive" " directory exists,\n" "InnoDB: you have access rights to it, and\n" "InnoDB: there is space available.\n", name); } #ifdef UNIV_DEBUG if (log_debug_writes) { ib_logger(ib_stream, "Created archive file %s\n", name); } #endif /* UNIV_DEBUG */ ret = os_file_close(file_handle); ut_a(ret); /* Add the archive file as a node to the space */ fil_node_create(name, group->file_size / UNIV_PAGE_SIZE, group->archive_space_id, FALSE); if (next_offset % group->file_size == 0) { log_group_archive_file_header_write( group, n_files, group->archived_file_no + n_files, start_lsn); next_offset += LOG_FILE_HDR_SIZE; } } len = end_lsn - start_lsn; if (group->file_size < (next_offset % group->file_size) + len) { len = group->file_size - (next_offset % group->file_size); } #ifdef UNIV_DEBUG if (log_debug_writes) { ib_logger(ib_stream, "Archiving starting at lsn %llu, len %lu" " to group %lu\n", start_lsn, (ulong) len, (ulong) group->id); } #endif /* UNIV_DEBUG */ log_sys->n_pending_archive_ios++; log_sys->n_log_ios++; fil_io(OS_FILE_WRITE | OS_FILE_LOG, FALSE, group->archive_space_id, 0, // FIXME: ARCHIVE: Zip size next_offset / UNIV_PAGE_SIZE, next_offset % UNIV_PAGE_SIZE, ut_calc_align(len, OS_FILE_LOG_BLOCK_SIZE), buf, &log_archive_io); start_lsn += len; next_offset += len; buf += len; if (next_offset % group->file_size == 0) { n_files++; } if (end_lsn != start_lsn) { goto loop; } group->next_archived_file_no = group->archived_file_no + n_files; group->next_archived_offset = next_offset % group->file_size; ut_a(group->next_archived_offset % OS_FILE_LOG_BLOCK_SIZE == 0); } /*****************************************************//** (Writes to the archive of each log group.) Currently, only the first group is archived. */ static void log_archive_groups(void) /*====================*/ { log_group_t* group; ut_ad(mutex_own(&(log_sys->mutex))); group = UT_LIST_GET_FIRST(log_sys->log_groups); log_group_archive(group); } /*****************************************************//** Completes the archiving write phase for (each log group), currently, the first log group. */ static void log_archive_write_complete_groups(void) /*===================================*/ { log_group_t* group; ulint end_offset; ulint trunc_files; ulint n_files; ib_uint64_t start_lsn; ib_uint64_t end_lsn; ulint i; ut_ad(mutex_own(&(log_sys->mutex))); group = UT_LIST_GET_FIRST(log_sys->log_groups); group->archived_file_no = group->next_archived_file_no; group->archived_offset = group->next_archived_offset; /* Truncate from the archive file space all but the last file, or if it has been written full, all files */ n_files = (UNIV_PAGE_SIZE * fil_space_get_size(group->archive_space_id)) / group->file_size; ut_ad(n_files > 0); end_offset = group->archived_offset; if (end_offset % group->file_size == 0) { trunc_files = n_files; } else { trunc_files = n_files - 1; } #ifdef UNIV_DEBUG if (log_debug_writes && trunc_files) { ib_logger(ib_stream, "Complete file(s) archived to group %lu\n", (ulong) group->id); } #endif /* UNIV_DEBUG */ /* Calculate the archive file space start lsn */ start_lsn = log_sys->next_archived_lsn - (end_offset - LOG_FILE_HDR_SIZE + trunc_files * (group->file_size - LOG_FILE_HDR_SIZE)); end_lsn = start_lsn; for (i = 0; i < trunc_files; i++) { end_lsn += group->file_size - LOG_FILE_HDR_SIZE; /* Write a notice to the headers of archived log files that the file write has been completed */ log_group_archive_completed_header_write(group, i, end_lsn); } fil_space_truncate_start(group->archive_space_id, trunc_files * group->file_size); #ifdef UNIV_DEBUG if (log_debug_writes) { ib_logger("Archiving writes completed\n", ib_stream); } #endif /* UNIV_DEBUG */ } /******************************************************//** Completes an archiving i/o. */ static void log_archive_check_completion_low(void) /*==================================*/ { ut_ad(mutex_own(&(log_sys->mutex))); if (log_sys->n_pending_archive_ios == 0 && log_sys->archiving_phase == LOG_ARCHIVE_READ) { #ifdef UNIV_DEBUG if (log_debug_writes) { ib_logger("Archiving read completed\n", ib_stream); } #endif /* UNIV_DEBUG */ /* Archive buffer has now been read in: start archive writes */ log_sys->archiving_phase = LOG_ARCHIVE_WRITE; log_archive_groups(); } if (log_sys->n_pending_archive_ios == 0 && log_sys->archiving_phase == LOG_ARCHIVE_WRITE) { log_archive_write_complete_groups(); log_sys->archived_lsn = log_sys->next_archived_lsn; rw_lock_x_unlock_gen(&(log_sys->archive_lock), LOG_ARCHIVE); } } /******************************************************//** Completes an archiving i/o. */ static void log_io_complete_archive(void) /*=========================*/ { log_group_t* group; log_acquire(); group = UT_LIST_GET_FIRST(log_sys->log_groups); log_release(); fil_flush(group->archive_space_id); log_acquire(); ut_ad(log_sys->n_pending_archive_ios > 0); log_sys->n_pending_archive_ios--; log_archive_check_completion_low(); log_release(); } /********************************************************************//** Starts an archiving operation. @return TRUE if succeed, FALSE if an archiving operation was already running */ UNIV_INTERN ibool log_archive_do( /*===========*/ ibool sync, /*!< in: TRUE if synchronous operation is desired */ ulint* n_bytes)/*!< out: archive log buffer size, 0 if nothing to archive */ { ibool calc_new_limit; ib_uint64_t start_lsn; ib_uint64_t limit_lsn; calc_new_limit = TRUE; loop: log_acquire(); switch (log_sys->archiving_state) { case LOG_ARCH_OFF: arch_none: log_release(); *n_bytes = 0; return(TRUE); case LOG_ARCH_STOPPED: case LOG_ARCH_STOPPING2: log_release(); os_event_wait(log_sys->archiving_on); goto loop; } start_lsn = log_sys->archived_lsn; if (calc_new_limit) { ut_a(log_sys->archive_buf_size % OS_FILE_LOG_BLOCK_SIZE == 0); limit_lsn = start_lsn + log_sys->archive_buf_size; *n_bytes = log_sys->archive_buf_size; if (limit_lsn >= log_sys->lsn) { limit_lsn = ut_uint64_align_down( log_sys->lsn, OS_FILE_LOG_BLOCK_SIZE); } } if (log_sys->archived_lsn >= limit_lsn) { goto arch_none; } if (log_sys->written_to_all_lsn < limit_lsn) { log_release(); log_write_up_to(limit_lsn, LOG_WAIT_ALL_GROUPS, TRUE); calc_new_limit = FALSE; goto loop; } if (log_sys->n_pending_archive_ios > 0) { /* An archiving operation is running */ log_release(); if (sync) { rw_lock_s_lock(&(log_sys->archive_lock)); rw_lock_s_unlock(&(log_sys->archive_lock)); } *n_bytes = log_sys->archive_buf_size; return(FALSE); } rw_lock_x_lock_gen(&(log_sys->archive_lock), LOG_ARCHIVE); log_sys->archiving_phase = LOG_ARCHIVE_READ; log_sys->next_archived_lsn = limit_lsn; #ifdef UNIV_DEBUG if (log_debug_writes) { ib_logger(ib_stream, "Archiving from lsn %llu to lsn %llu\n", log_sys->archived_lsn, limit_lsn); } #endif /* UNIV_DEBUG */ /* Read the log segment to the archive buffer */ log_group_read_log_seg(LOG_ARCHIVE, log_sys->archive_buf, UT_LIST_GET_FIRST(log_sys->log_groups), start_lsn, limit_lsn); log_release(); if (sync) { rw_lock_s_lock(&(log_sys->archive_lock)); rw_lock_s_unlock(&(log_sys->archive_lock)); } *n_bytes = log_sys->archive_buf_size; return(TRUE); } /****************************************************************//** Writes the log contents to the archive at least up to the lsn when this function was called. */ static void log_archive_all( /*============*/ ib_recovery_t recovery) /*!< in: recovery flag */ { ib_uint64_t present_lsn; ulint dummy; log_acquire(); if (log_sys->archiving_state == LOG_ARCH_OFF) { log_release(); return; } present_lsn = log_sys->lsn; log_release(); log_pad_current_log_block(recovery); for (;;) { log_acquire(); if (present_lsn <= log_sys->archived_lsn) { log_release(); return; } log_release(); log_archive_do(TRUE, &dummy); } } /*****************************************************//** Closes the possible open archive log file (for each group) the first group, and if it was open, increments the group file count by 2, if desired. */ static void log_archive_close_groups( /*=====================*/ ibool increment_file_count) /*!< in: TRUE if we want to increment the file count */ { log_group_t* group; ulint trunc_len; ut_ad(mutex_own(&(log_sys->mutex))); if (log_sys->archiving_state == LOG_ARCH_OFF) { return; } group = UT_LIST_GET_FIRST(log_sys->log_groups); trunc_len = UNIV_PAGE_SIZE * fil_space_get_size(group->archive_space_id); if (trunc_len > 0) { ut_a(trunc_len == group->file_size); /* Write a notice to the headers of archived log files that the file write has been completed */ log_group_archive_completed_header_write( group, 0, log_sys->archived_lsn); fil_space_truncate_start(group->archive_space_id, trunc_len); if (increment_file_count) { group->archived_offset = 0; group->archived_file_no += 2; } #ifdef UNIV_DEBUG if (log_debug_writes) { ib_logger(ib_stream, "Incrementing arch file no to %lu" " in log group %lu\n", (ulong) group->archived_file_no + 2, (ulong) group->id); } #endif /* UNIV_DEBUG */ } } /****************************************************************//** Writes the log contents to the archive up to the lsn when this function was called, and stops the archiving. When archiving is started again, the archived log file numbers start from 2 higher, so that the archiving will not write again to the archived log files which exist when this function returns. @return DB_SUCCESS or DB_ERROR */ UNIV_INTERN ulint log_archive_stop(void) /*==================*/ { ibool success; log_acquire(); if (log_sys->archiving_state != LOG_ARCH_ON) { log_release(); return(DB_ERROR); } log_sys->archiving_state = LOG_ARCH_STOPPING; log_release(); log_archive_all(); log_acquire(); log_sys->archiving_state = LOG_ARCH_STOPPING2; os_event_reset(log_sys->archiving_on); log_release(); /* Wait for a possible archiving operation to end */ rw_lock_s_lock(&(log_sys->archive_lock)); rw_lock_s_unlock(&(log_sys->archive_lock)); log_acquire(); /* Close all archived log files, incrementing the file count by 2, if appropriate */ log_archive_close_groups(TRUE); log_release(); /* Make a checkpoint, so that if recovery is needed, the file numbers of new archived log files will start from the right value */ success = FALSE; while (!success) { success = log_checkpoint(TRUE, TRUE); } log_acquire(); log_sys->archiving_state = LOG_ARCH_STOPPED; log_release(); return(DB_SUCCESS); } /****************************************************************//** Starts again archiving which has been stopped. @return DB_SUCCESS or DB_ERROR */ UNIV_INTERN ulint log_archive_start(void) /*===================*/ { mutex_enter(&(log_sys->mutex)); if (log_sys->archiving_state != LOG_ARCH_STOPPED) { mutex_exit(&(log_sys->mutex)); return(DB_ERROR); } log_sys->archiving_state = LOG_ARCH_ON; os_event_set(log_sys->archiving_on); mutex_exit(&(log_sys->mutex)); return(DB_SUCCESS); } /****************************************************************//** Stop archiving the log so that a gap may occur in the archived log files. @return DB_SUCCESS or DB_ERROR */ UNIV_INTERN ulint log_archive_noarchivelog(void) /*==========================*/ { loop: log_acquire(); if (log_sys->archiving_state == LOG_ARCH_STOPPED || log_sys->archiving_state == LOG_ARCH_OFF) { log_sys->archiving_state = LOG_ARCH_OFF; os_event_set(log_sys->archiving_on); log_release(); return(DB_SUCCESS); } log_release(); log_archive_stop(); os_thread_sleep(500000); goto loop; } /****************************************************************//** Start archiving the log so that a gap may occur in the archived log files. @return DB_SUCCESS or DB_ERROR */ UNIV_INTERN ulint log_archive_archivelog(void) /*========================*/ { log_acquire(); if (log_sys->archiving_state == LOG_ARCH_OFF) { log_sys->archiving_state = LOG_ARCH_ON; log_sys->archived_lsn = ut_uint64_align_down(log_sys->lsn, OS_FILE_LOG_BLOCK_SIZE); log_release(); return(DB_SUCCESS); } log_release(); return(DB_ERROR); } /****************************************************************//** Tries to establish a big enough margin of free space in the log groups, such that a new log entry can be catenated without an immediate need for archiving. */ static void log_archive_margin(void) /*====================*/ { log_t* log = log_sys; ulint age; ibool sync; ulint dummy; loop: log_acquire(); if (log->archiving_state == LOG_ARCH_OFF) { log_release(); return; } age = log->lsn - log->archived_lsn; if (age > log->max_archived_lsn_age) { /* An archiving is urgent: we have to do synchronous i/o */ sync = TRUE; } else if (age > log->max_archived_lsn_age_async) { /* An archiving is not urgent: we do asynchronous i/o */ sync = FALSE; } else { /* No archiving required yet */ log_release(); return; } log_release(); log_archive_do(sync, &dummy); if (sync == TRUE) { /* Check again that enough was written to the archive */ goto loop; } } #endif /* UNIV_LOG_ARCHIVE */ /********************************************************************//** Checks that there is enough free space in the log to start a new query step. Flushes the log buffer or makes a new checkpoint if necessary. NOTE: this function may only be called if the calling thread owns no synchronization objects! */ UNIV_INTERN void log_check_margins(void) /*===================*/ { loop: log_flush_margin(); log_checkpoint_margin(); #ifdef UNIV_LOG_ARCHIVE log_archive_margin(); #endif /* UNIV_LOG_ARCHIVE */ log_acquire(); ut_ad(!recv_no_log_write); if (log_sys->check_flush_or_checkpoint) { log_release(); goto loop; } log_release(); } /****************************************************************//** Makes a checkpoint at the latest lsn and writes it to first page of each data file in the database, so that we know that the file spaces contain all modifications up to that lsn. This can only be called at database shutdown. This function also writes all log in log files to the log archive. */ UNIV_INTERN void logs_empty_and_mark_files_at_shutdown( /*==================================*/ ib_recovery_t recovery, /*!< in: recovery flag */ ib_shutdown_t shutdown) /*!< in: shutdown flag */ { ib_uint64_t lsn; ulint arch_log_no; /* If we have to abort during the startup phase then it's possible that the log sub-system hasn't as yet been initialized. We simply attempt to close all open files and return. */ if (log_sys == NULL || UT_LIST_GET_LEN(log_sys->log_groups) == 0) { fil_close_all_files(); return; } if (srv_print_verbose_log) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Starting shutdown...\n"); } /* Wait until the master thread and all other operations are idle: our algorithm only works if the server is idle at shutdown */ srv_shutdown_state = SRV_SHUTDOWN_CLEANUP; loop: os_thread_sleep(100000); mutex_enter(&kernel_mutex); /* We need the monitor threads to stop before we proceed with a normal shutdown. In case of very fast shutdown, however, we can proceed without waiting for monitor threads. */ if (shutdown != IB_SHUTDOWN_NO_BUFPOOL_FLUSH && (srv_error_monitor_active || srv_lock_timeout_active || srv_monitor_active)) { mutex_exit(&kernel_mutex); goto loop; } /* Check that there are no longer transactions. We need this wait even for the 'very fast' shutdown, because the InnoDB layer may have committed or prepared transactions and we don't want to lose them. */ if (trx_n_transactions > 0 || (trx_sys != NULL && UT_LIST_GET_LEN(trx_sys->trx_list) > 0)) { mutex_exit(&kernel_mutex); goto loop; } if (shutdown == IB_SHUTDOWN_NO_BUFPOOL_FLUSH) { /* In this fastest shutdown we do not flush the buffer pool: it is essentially a 'crash' of the InnoDB server. Make sure that the log is all flushed to disk, so that we can recover all committed transactions in a crash recovery. We must not write the lsn stamps to the data files, since at a startup InnoDB deduces from the stamps if the previous shutdown was clean. */ log_buffer_flush_to_disk(); mutex_exit(&kernel_mutex); return; /* We SKIP ALL THE REST !! */ } /* Check that the master thread is suspended */ if (srv_n_threads_active[SRV_MASTER] != 0) { mutex_exit(&kernel_mutex); goto loop; } mutex_exit(&kernel_mutex); log_acquire(); if (log_sys->n_pending_checkpoint_writes #ifdef UNIV_LOG_ARCHIVE || log_sys->n_pending_archive_ios #endif /* UNIV_LOG_ARCHIVE */ || log_sys->n_pending_writes) { log_release(); goto loop; } log_release(); if (!buf_pool_check_no_pending_io()) { goto loop; } #ifdef UNIV_LOG_ARCHIVE log_archive_all(recovery); #endif /* UNIV_LOG_ARCHIVE */ log_make_checkpoint_at(IB_UINT64_T_MAX, TRUE); log_acquire(); lsn = log_sys->lsn; if (lsn != log_sys->last_checkpoint_lsn #ifdef UNIV_LOG_ARCHIVE || (srv_log_archive_on && lsn != log_sys->archived_lsn + LOG_BLOCK_HDR_SIZE) #endif /* UNIV_LOG_ARCHIVE */ ) { log_release(); goto loop; } arch_log_no = 0; #ifdef UNIV_LOG_ARCHIVE // FIXME: ARCHIVE: Statement has no effect //UT_LIST_GET_FIRST(log_sys->log_groups)->archived_file_no; if (0 == UT_LIST_GET_FIRST(log_sys->log_groups)->archived_offset) { arch_log_no--; } log_archive_close_groups(TRUE); #endif /* UNIV_LOG_ARCHIVE */ log_release(); mutex_enter(&kernel_mutex); /* Check that the master thread has stayed suspended */ if (srv_n_threads_active[SRV_MASTER] != 0) { ib_logger(ib_stream, "InnoDB: Warning: the master thread woke up" " during shutdown\n"); mutex_exit(&kernel_mutex); goto loop; } mutex_exit(&kernel_mutex); fil_flush_file_spaces(FIL_TABLESPACE); fil_flush_file_spaces(FIL_LOG); /* The call fil_write_flushed_lsn_to_data_files() will pass the buffer pool: therefore it is essential that the buffer pool has been completely flushed to disk! (We do not call fil_write... if the 'very fast' shutdown is enabled.) */ if (!buf_all_freed()) { goto loop; } srv_shutdown_state = SRV_SHUTDOWN_LAST_PHASE; /* Make some checks that the server really is quiet */ ut_a(srv_n_threads_active[SRV_MASTER] == 0); ut_a(buf_all_freed()); ut_a(lsn == log_sys->lsn); if (lsn < srv_start_lsn) { ib_logger(ib_stream, "InnoDB: Error: log sequence number" " at shutdown %llu\n" "InnoDB: is lower than at startup %llu!\n", lsn, srv_start_lsn); } srv_shutdown_lsn = lsn; fil_write_flushed_lsn_to_data_files(lsn, arch_log_no); fil_flush_file_spaces(FIL_TABLESPACE); fil_close_all_files(); /* Make some checks that the server really is quiet */ ut_a(srv_n_threads_active[SRV_MASTER] == 0); ut_a(buf_all_freed()); ut_a(lsn == log_sys->lsn); } #ifdef UNIV_LOG_DEBUG /******************************************************//** Checks by parsing that the catenated log segment for a single mtr is consistent. */ UNIV_INTERN ibool log_check_log_recs( /*===============*/ const byte* buf, /*!< in: pointer to the start of the log segment in the log_sys->buf log buffer */ ulint len, /*!< in: segment length in bytes */ ib_uint64_t buf_start_lsn) /*!< in: buffer start lsn */ { ib_uint64_t contiguous_lsn; ib_uint64_t scanned_lsn; const byte* start; const byte* end; byte* buf1; byte* scan_buf; ut_ad(mutex_own(&(log_sys->mutex))); if (len == 0) { return(TRUE); } start = ut_align_down(buf, OS_FILE_LOG_BLOCK_SIZE); end = ut_align(buf + len, OS_FILE_LOG_BLOCK_SIZE); buf1 = mem_alloc((end - start) + OS_FILE_LOG_BLOCK_SIZE); scan_buf = ut_align(buf1, OS_FILE_LOG_BLOCK_SIZE); ut_memcpy(scan_buf, start, end - start); recv_scan_log_recs(recovery, (buf_pool->curr_size - recv_n_pool_free_frames) * UNIV_PAGE_SIZE, FALSE, scan_buf, end - start, ut_uint64_align_down(buf_start_lsn, OS_FILE_LOG_BLOCK_SIZE), &contiguous_lsn, &scanned_lsn); ut_a(scanned_lsn == buf_start_lsn + len); ut_a(recv_sys->recovered_lsn == scanned_lsn); mem_free(buf1); return(TRUE); } #endif /* UNIV_LOG_DEBUG */ /******************************************************//** Peeks the current lsn. @return TRUE if success, FALSE if could not get the log system mutex */ UNIV_INTERN ibool log_peek_lsn( /*=========*/ ib_uint64_t* lsn) /*!< out: if returns TRUE, current lsn is here */ { if (0 == mutex_enter_nowait(&(log_sys->mutex))) { *lsn = log_sys->lsn; log_release(); return(TRUE); } return(FALSE); } /******************************************************//** Prints info of the log. */ UNIV_INTERN void log_print( /*======*/ ib_stream_t ib_stream) /*!< in: file where to print */ { double time_elapsed; time_t current_time; log_acquire(); ib_logger(ib_stream, "Log sequence number %llu\n" "Log flushed up to %llu\n" "Last checkpoint at %llu\n", log_sys->lsn, log_sys->flushed_to_disk_lsn, log_sys->last_checkpoint_lsn); current_time = time(NULL); time_elapsed = 0.001 + difftime(current_time, log_sys->last_printout_time); ib_logger(ib_stream, "%lu pending log writes, %lu pending chkp writes\n" "%lu log i/o's done, %.2f log i/o's/second\n", (ulong) log_sys->n_pending_writes, (ulong) log_sys->n_pending_checkpoint_writes, (ulong) log_sys->n_log_ios, ((log_sys->n_log_ios - log_sys->n_log_ios_old) / time_elapsed)); log_sys->n_log_ios_old = log_sys->n_log_ios; log_sys->last_printout_time = current_time; log_release(); } /**********************************************************************//** Refreshes the statistics used to print per-second averages. */ UNIV_INTERN void log_refresh_stats(void) /*===================*/ { log_sys->n_log_ios_old = log_sys->n_log_ios; log_sys->last_printout_time = time(NULL); } /********************************************************************** Closes a log group. */ static void log_group_close( /*===========*/ log_group_t* group) /*!< in,own: log group to close */ { ulint i; for (i = 0; i < group->n_files; ++i) { mem_free(group->file_header_bufs_ptr[i]); #ifdef UNIV_LOG_ARCHIVE mem_free(group->archive_file_header_bufs_ptr[i]); #endif } mem_free(group->file_header_bufs); mem_free(group->file_header_bufs_ptr); #ifdef UNIV_LOG_ARCHIVE mem_free(group->archive_file_header_bufs); mem_free(group->archive_file_header_bufs_ptr); #endif /* UNIV_LOG_ARCHIVE */ mem_free(group->checkpoint_buf_ptr); mem_free(group); } /********************************************************** Shutdown log system but doesn't release all the memory. */ UNIV_INTERN void log_shutdown(void) /*==============*/ { log_group_t* group; /* This can happen if we have to abort during startup. */ if (log_sys == NULL || UT_LIST_GET_LEN(log_sys->log_groups) == 0) { return; } group = UT_LIST_GET_FIRST(log_sys->log_groups); while (UT_LIST_GET_LEN(log_sys->log_groups) > 0) { log_group_t* prev_group = group; group = UT_LIST_GET_NEXT(log_groups, group); UT_LIST_REMOVE(log_groups, log_sys->log_groups, prev_group); log_group_close(prev_group); } mem_free(log_sys->buf_ptr); log_sys->buf_ptr = NULL; mem_free(log_sys->checkpoint_buf_ptr); log_sys->checkpoint_buf_ptr = NULL; os_event_free(log_sys->no_flush_event); os_event_free(log_sys->one_flushed_event); rw_lock_free(&log_sys->checkpoint_lock); #ifdef UNIV_LOG_ARCHIVE rw_lock_free(&log_sys->archive_lock); // FIXME: ARCHIVE, changed to NULL os_event_create(NULL); #endif /* UNIV_LOG_ARCHIVE */ #ifdef UNIV_LOG_DEBUG recv_sys_debug_free(); #endif recv_sys_close(); } /********************************************************** Free the log system data structures. */ UNIV_INTERN void log_mem_free(void) /*==============*/ { if (log_sys != NULL) { recv_sys_mem_free(); mem_free(log_sys); log_sys = NULL; } } #endif /* !UNIV_HOTBACKUP */ haildb-2.3.2/include/0000755000175000017500000000000011513177437015265 5ustar00pcrewspcrews00000000000000haildb-2.3.2/include/os0sync.h0000644000175000017500000003636411513177357017051 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2010, Innobase Oy. All Rights Reserved. Copyright (c) 2008, Google Inc. Portions of this file contain modifications contributed and copyrighted by Google, Inc. Those modifications are gratefully acknowledged and are described briefly in the InnoDB documentation. The contributions by Google are incorporated with their permission, and subject to the conditions contained in the file COPYING.Google. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/os0sync.h The interface to the operating system synchronization primitives. Created 9/6/1995 Heikki Tuuri *******************************************************/ #ifndef os0sync_h #define os0sync_h #include "univ.i" #include "ut0lst.h" #ifdef HAVE_PTHREAD_H #include #endif /* HAVE_PTHREAD_H */ #ifdef __WIN__ #include /** Native mutex */ #define os_fast_mutex_t CRITICAL_SECTION /** Native event */ typedef HANDLE os_native_event_t; /** Operating system event */ typedef struct os_event_struct os_event_struct_t; /** Operating system event handle */ typedef os_event_struct_t* os_event_t; /** An asynchronous signal sent between threads */ struct os_event_struct { os_native_event_t handle; /*!< Windows event */ UT_LIST_NODE_T(os_event_struct_t) os_event_list; /*!< list of all created events */ }; #else /** Native mutex */ typedef pthread_mutex_t os_fast_mutex_t; /** Operating system event */ typedef struct os_event_struct os_event_struct_t; /** Operating system event handle */ typedef os_event_struct_t* os_event_t; /** An asynchronous signal sent between threads */ struct os_event_struct { os_fast_mutex_t os_mutex; /*!< this mutex protects the next fields */ ibool is_set; /*!< this is TRUE when the event is in the signaled state, i.e., a thread does not stop if it tries to wait for this event */ ib_int64_t signal_count; /*!< this is incremented each time the event becomes signaled */ pthread_cond_t cond_var; /*!< condition variable is used in waiting for the event */ UT_LIST_NODE_T(os_event_struct_t) os_event_list; /*!< list of all created events */ }; #endif /** Operating system mutex */ typedef struct os_mutex_struct os_mutex_str_t; /** Operating system mutex handle */ typedef os_mutex_str_t* os_mutex_t; /** Denotes an infinite delay for os_event_wait_time() */ #define OS_SYNC_INFINITE_TIME ((ulint)(-1)) /** Return value of os_event_wait_time() when the time is exceeded */ #define OS_SYNC_TIME_EXCEEDED 1 /** Mutex protecting counts and the event and OS 'slow' mutex lists */ extern os_mutex_t os_sync_mutex; /** This is incremented by 1 in os_thread_create and decremented by 1 in os_thread_exit */ extern ulint os_thread_count; extern ulint os_event_count; extern ulint os_mutex_count; extern ulint os_fast_mutex_count; /*********************************************************//** Initializes global event and OS 'slow' mutex lists. */ UNIV_INTERN void os_sync_init(void); /*==============*/ /*********************************************************//** Frees created events and OS 'slow' mutexes. */ UNIV_INTERN void os_sync_free(void); /*==============*/ /*********************************************************//** Creates an event semaphore, i.e., a semaphore which may just have two states: signaled and nonsignaled. The created event is manual reset: it must be reset explicitly by calling sync_os_reset_event. @return the event handle */ UNIV_INTERN os_event_t os_event_create( /*============*/ const char* name); /*!< in: the name of the event, if NULL the event is created without a name */ /**********************************************************//** Sets an event semaphore to the signaled state: lets waiting threads proceed. */ UNIV_INTERN void os_event_set( /*=========*/ os_event_t event); /*!< in: event to set */ /**********************************************************//** Resets an event semaphore to the nonsignaled state. Waiting threads will stop to wait for the event. The return value should be passed to os_even_wait_low() if it is desired that this thread should not wait in case of an intervening call to os_event_set() between this os_event_reset() and the os_event_wait_low() call. See comments for os_event_wait_low(). */ UNIV_INTERN ib_int64_t os_event_reset( /*===========*/ os_event_t event); /*!< in: event to reset */ /**********************************************************//** Frees an event object. */ UNIV_INTERN void os_event_free( /*==========*/ os_event_t event); /*!< in: event to free */ /**********************************************************//** Waits for an event object until it is in the signaled state. If srv_shutdown_state == SRV_SHUTDOWN_EXIT_THREADS this also exits the waiting thread when the event becomes signaled (or immediately if the event is already in the signaled state). Typically, if the event has been signalled after the os_event_reset() we'll return immediately because event->is_set == TRUE. There are, however, situations (e.g.: sync_array code) where we may lose this information. For example: thread A calls os_event_reset() thread B calls os_event_set() [event->is_set == TRUE] thread C calls os_event_reset() [event->is_set == FALSE] thread A calls os_event_wait() [infinite wait!] thread C calls os_event_wait() [infinite wait!] Where such a scenario is possible, to avoid infinite wait, the value returned by os_event_reset() should be passed in as reset_sig_count. */ UNIV_INTERN void os_event_wait_low( /*==============*/ os_event_t event, /*!< in: event to wait */ ib_int64_t reset_sig_count);/*!< in: zero or the value returned by previous call of os_event_reset(). */ #ifdef __WIN__ /**********************************************************//** Waits for any event in an OS native event array. Returns if even a single one is signaled or becomes signaled. @return index of the event which was signaled */ UNIV_INTERN ulint os_event_wait_multiple( /*===================*/ ulint n, /*!< in: number of events in the array */ os_native_event_t* native_event_array); /*!< in: pointer to an array of event handles */ #endif #define os_event_wait(event) os_event_wait_low(event, 0) /************************************************************//** Waits for an event object until it is in the signaled state or a timeout is exceeded. In Unix the timeout is always infinite. @return 0 if success, OS_SYNC_TIME_EXCEEDED if timeout was exceeded */ UNIV_INTERN ulint os_event_wait_time( /*===============*/ os_event_t event, /*!< in: event to wait */ ulint time); /*!< in: timeout in microseconds, or OS_SYNC_INFINITE_TIME */ /*********************************************************//** Creates an operating system mutex semaphore. Because these are slow, the mutex semaphore of InnoDB itself (mutex_t) should be used where possible. @return the mutex handle */ UNIV_INTERN os_mutex_t os_mutex_create( /*============*/ const char* name); /*!< in: the name of the mutex, if NULL the mutex is created without a name */ /**********************************************************//** Acquires ownership of a mutex semaphore. */ UNIV_INTERN void os_mutex_enter( /*===========*/ os_mutex_t mutex); /*!< in: mutex to acquire */ /**********************************************************//** Releases ownership of a mutex. */ UNIV_INTERN void os_mutex_exit( /*==========*/ os_mutex_t mutex); /*!< in: mutex to release */ /**********************************************************//** Frees an mutex object. */ UNIV_INTERN void os_mutex_free( /*==========*/ os_mutex_t mutex); /*!< in: mutex to free */ /**********************************************************//** Acquires ownership of a fast mutex. Currently in Windows this is the same as os_fast_mutex_lock! @return 0 if success, != 0 if was reserved by another thread */ UNIV_INLINE ulint os_fast_mutex_trylock( /*==================*/ os_fast_mutex_t* fast_mutex); /*!< in: mutex to acquire */ /**********************************************************//** Releases ownership of a fast mutex. */ UNIV_INTERN void os_fast_mutex_unlock( /*=================*/ os_fast_mutex_t* fast_mutex); /*!< in: mutex to release */ /*********************************************************//** Initializes an operating system fast mutex semaphore. */ UNIV_INTERN void os_fast_mutex_init( /*===============*/ os_fast_mutex_t* fast_mutex); /*!< in: fast mutex */ /**********************************************************//** Acquires ownership of a fast mutex. */ UNIV_INTERN void os_fast_mutex_lock( /*===============*/ os_fast_mutex_t* fast_mutex); /*!< in: mutex to acquire */ /**********************************************************//** Frees an mutex object. */ UNIV_INTERN void os_fast_mutex_free( /*===============*/ os_fast_mutex_t* fast_mutex); /*!< in: mutex to free */ /************************************************************* Reset the variables. */ UNIV_INTERN void os_sync_var_init(void); /*==================*/ /**********************************************************//** Atomic compare-and-swap and increment for InnoDB. */ #if defined(HAVE_IB_GCC_ATOMIC_BUILTINS) \ && defined(IB_ATOMIC_MODE_GCC_ATOMIC_BUILTINS) #define HAVE_ATOMIC_BUILTINS /**********************************************************//** Returns true if swapped, ptr is pointer to target, old_val is value to compare to, new_val is the value to swap in. */ # define os_compare_and_swap(ptr, old_val, new_val) \ __sync_bool_compare_and_swap(ptr, old_val, new_val) # define os_compare_and_swap_ulint(ptr, old_val, new_val) \ os_compare_and_swap(ptr, old_val, new_val) # define os_compare_and_swap_lint(ptr, old_val, new_val) \ os_compare_and_swap(ptr, old_val, new_val) # ifdef HAVE_IB_ATOMIC_PTHREAD_T_GCC # define os_compare_and_swap_thread_id(ptr, old_val, new_val) \ os_compare_and_swap(ptr, old_val, new_val) # define INNODB_RW_LOCKS_USE_ATOMICS # define IB_ATOMICS_STARTUP_MSG \ "Mutexes and rw_locks use GCC atomic builtins" # else /* HAVE_IB_ATOMIC_PTHREAD_T_GCC */ # define IB_ATOMICS_STARTUP_MSG \ "Mutexes use GCC atomic builtins, rw_locks do not" # endif /* HAVE_IB_ATOMIC_PTHREAD_T_GCC */ /**********************************************************//** Returns the resulting value, ptr is pointer to target, amount is the amount of increment. */ # define os_atomic_increment(ptr, amount) \ __sync_add_and_fetch(ptr, amount) # define os_atomic_increment_lint(ptr, amount) \ os_atomic_increment(ptr, amount) # define os_atomic_increment_ulint(ptr, amount) \ os_atomic_increment(ptr, amount) /**********************************************************//** Returns the old value of *ptr, atomically sets *ptr to new_val */ # define os_atomic_test_and_set_byte(ptr, new_val) \ __sync_lock_test_and_set(ptr, new_val) #elif defined(HAVE_IB_SOLARIS_ATOMICS) \ && defined(IB_ATOMIC_MODE_SOLARIS_ATOMICS) #define HAVE_ATOMIC_BUILTINS /* If not compiling with GCC or GCC doesn't support the atomic intrinsics and running on Solaris >= 10 use Solaris atomics */ #include /**********************************************************//** Returns true if swapped, ptr is pointer to target, old_val is value to compare to, new_val is the value to swap in. */ # define os_compare_and_swap_ulint(ptr, old_val, new_val) \ (atomic_cas_ulong(ptr, old_val, new_val) == old_val) # define os_compare_and_swap_lint(ptr, old_val, new_val) \ ((lint)atomic_cas_ulong((ulong_t*) ptr, old_val, new_val) == old_val) # ifdef HAVE_IB_ATOMIC_PTHREAD_T_SOLARIS # if SIZEOF_PTHREAD_T == 4 # define os_compare_and_swap_thread_id(ptr, old_val, new_val) \ ((pthread_t)atomic_cas_32(ptr, old_val, new_val) == old_val) # elif SIZEOF_PTHREAD_T == 8 # define os_compare_and_swap_thread_id(ptr, old_val, new_val) \ ((pthread_t)atomic_cas_64(ptr, old_val, new_val) == old_val) # else # error "SIZEOF_PTHREAD_T != 4 or 8" # endif /* SIZEOF_PTHREAD_T CHECK */ # define INNODB_RW_LOCKS_USE_ATOMICS # define IB_ATOMICS_STARTUP_MSG \ "Mutexes and rw_locks use Solaris atomic functions" # else /* HAVE_IB_ATOMIC_PTHREAD_T_SOLARIS */ # define IB_ATOMICS_STARTUP_MSG \ "Mutexes use Solaris atomic functions, rw_locks do not" # endif /* HAVE_IB_ATOMIC_PTHREAD_T_SOLARIS */ /**********************************************************//** Returns the resulting value, ptr is pointer to target, amount is the amount of increment. */ # define os_atomic_increment_lint(ptr, amount) \ atomic_add_long_nv((ulong_t*) ptr, amount) # define os_atomic_increment_ulint(ptr, amount) \ atomic_add_long_nv(ptr, amount) /**********************************************************//** Returns the old value of *ptr, atomically sets *ptr to new_val */ # define os_atomic_test_and_set_byte(ptr, new_val) \ atomic_swap_uchar(ptr, new_val) #elif defined(HAVE_WINDOWS_ATOMICS) #define HAVE_ATOMIC_BUILTINS /* On Windows, use Windows atomics / interlocked */ # ifdef _WIN64 # define win_cmp_and_xchg InterlockedCompareExchange64 # define win_xchg_and_add InterlockedExchangeAdd64 # else /* _WIN64 */ # define win_cmp_and_xchg InterlockedCompareExchange # define win_xchg_and_add InterlockedExchangeAdd # endif /**********************************************************//** Returns true if swapped, ptr is pointer to target, old_val is value to compare to, new_val is the value to swap in. */ # define os_compare_and_swap_ulint(ptr, old_val, new_val) \ (win_cmp_and_xchg(ptr, new_val, old_val) == old_val) # define os_compare_and_swap_lint(ptr, old_val, new_val) \ (win_cmp_and_xchg(ptr, new_val, old_val) == old_val) /* windows thread objects can always be passed to windows atomic functions */ # define os_compare_and_swap_thread_id(ptr, old_val, new_val) \ (InterlockedCompareExchange(ptr, new_val, old_val) == old_val) # define INNODB_RW_LOCKS_USE_ATOMICS # define IB_ATOMICS_STARTUP_MSG \ "Mutexes and rw_locks use Windows interlocked functions" /**********************************************************//** Returns the resulting value, ptr is pointer to target, amount is the amount of increment. */ # define os_atomic_increment_lint(ptr, amount) \ (win_xchg_and_add(ptr, amount) + amount) # define os_atomic_increment_ulint(ptr, amount) \ ((ulint) (win_xchg_and_add(ptr, amount) + amount)) /**********************************************************//** Returns the old value of *ptr, atomically sets *ptr to new_val. InterlockedExchange() operates on LONG, and the LONG will be clobbered */ # define os_atomic_test_and_set_byte(ptr, new_val) \ ((byte) InterlockedExchange(ptr, new_val)) #else # define IB_ATOMICS_STARTUP_MSG \ "Mutexes and rw_locks use InnoDB's own implementation" #endif #ifndef UNIV_NONINL #include "os0sync.ic" #endif #endif haildb-2.3.2/include/mtr0mtr.ic0000644000175000017500000001414511513177357017215 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2010, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/mtr0mtr.ic Mini-transaction buffer Created 11/26/1995 Heikki Tuuri *******************************************************/ #ifndef UNIV_HOTBACKUP # include "sync0sync.h" # include "sync0rw.h" #endif /* !UNIV_HOTBACKUP */ #include "mach0data.h" /***************************************************************//** Starts a mini-transaction and creates a mini-transaction handle and a buffer in the memory buffer given by the caller. @return mtr buffer which also acts as the mtr handle */ UNIV_INLINE mtr_t* mtr_start( /*======*/ mtr_t* mtr) /*!< in: memory buffer for the mtr buffer */ { dyn_array_create(&(mtr->memo)); dyn_array_create(&(mtr->log)); mtr->log_mode = MTR_LOG_ALL; mtr->modifications = FALSE; mtr->n_log_recs = 0; ut_d(mtr->state = MTR_ACTIVE); ut_d(mtr->magic_n = MTR_MAGIC_N); return(mtr); } /***************************************************//** Pushes an object to an mtr memo stack. */ UNIV_INLINE void mtr_memo_push( /*==========*/ mtr_t* mtr, /*!< in: mtr */ void* object, /*!< in: object */ ulint type) /*!< in: object type: MTR_MEMO_S_LOCK, ... */ { dyn_array_t* memo; mtr_memo_slot_t* slot; ut_ad(object); ut_ad(type >= MTR_MEMO_PAGE_S_FIX); ut_ad(type <= MTR_MEMO_X_LOCK); ut_ad(mtr); ut_ad(mtr->magic_n == MTR_MAGIC_N); ut_ad(mtr->state == MTR_ACTIVE); memo = &(mtr->memo); slot = (mtr_memo_slot_t*) dyn_array_push(memo, sizeof *slot); slot->object = object; slot->type = type; } /**********************************************************//** Sets and returns a savepoint in mtr. @return savepoint */ UNIV_INLINE ulint mtr_set_savepoint( /*==============*/ mtr_t* mtr) /*!< in: mtr */ { dyn_array_t* memo; ut_ad(mtr); ut_ad(mtr->magic_n == MTR_MAGIC_N); ut_ad(mtr->state == MTR_ACTIVE); memo = &(mtr->memo); return(dyn_array_get_data_size(memo)); } #ifndef UNIV_HOTBACKUP /**********************************************************//** Releases the (index tree) s-latch stored in an mtr memo after a savepoint. */ UNIV_INLINE void mtr_release_s_latch_at_savepoint( /*=============================*/ mtr_t* mtr, /*!< in: mtr */ ulint savepoint, /*!< in: savepoint */ rw_lock_t* lock) /*!< in: latch to release */ { mtr_memo_slot_t* slot; dyn_array_t* memo; ut_ad(mtr); ut_ad(mtr->magic_n == MTR_MAGIC_N); ut_ad(mtr->state == MTR_ACTIVE); memo = &(mtr->memo); ut_ad(dyn_array_get_data_size(memo) > savepoint); slot = (mtr_memo_slot_t*) dyn_array_get_element(memo, savepoint); ut_ad(slot->object == lock); ut_ad(slot->type == MTR_MEMO_S_LOCK); rw_lock_s_unlock(lock); slot->object = NULL; } # ifdef UNIV_DEBUG /**********************************************************//** Checks if memo contains the given item. @return TRUE if contains */ UNIV_INLINE ibool mtr_memo_contains( /*==============*/ mtr_t* mtr, /*!< in: mtr */ const void* object, /*!< in: object to search */ ulint type) /*!< in: type of object */ { mtr_memo_slot_t* slot; dyn_array_t* memo; ulint offset; ut_ad(mtr); ut_ad(mtr->magic_n == MTR_MAGIC_N); ut_ad(mtr->state == MTR_ACTIVE || mtr->state == MTR_COMMITTING); memo = &(mtr->memo); offset = dyn_array_get_data_size(memo); while (offset > 0) { offset -= sizeof(mtr_memo_slot_t); slot = dyn_array_get_element(memo, offset); if ((object == slot->object) && (type == slot->type)) { return(TRUE); } } return(FALSE); } # endif /* UNIV_DEBUG */ #endif /* !UNIV_HOTBACKUP */ /***************************************************************//** Gets the logging mode of a mini-transaction. @return logging mode: MTR_LOG_NONE, ... */ UNIV_INLINE ulint mtr_get_log_mode( /*=============*/ mtr_t* mtr) /*!< in: mtr */ { ut_ad(mtr); ut_ad(mtr->log_mode >= MTR_LOG_ALL); ut_ad(mtr->log_mode <= MTR_LOG_SHORT_INSERTS); return(mtr->log_mode); } /***************************************************************//** Changes the logging mode of a mini-transaction. @return old mode */ UNIV_INLINE ulint mtr_set_log_mode( /*=============*/ mtr_t* mtr, /*!< in: mtr */ ulint mode) /*!< in: logging mode: MTR_LOG_NONE, ... */ { ulint old_mode; ut_ad(mtr); ut_ad(mode >= MTR_LOG_ALL); ut_ad(mode <= MTR_LOG_SHORT_INSERTS); old_mode = mtr->log_mode; if ((mode == MTR_LOG_SHORT_INSERTS) && (old_mode == MTR_LOG_NONE)) { /* Do nothing */ } else { mtr->log_mode = mode; } ut_ad(old_mode >= MTR_LOG_ALL); ut_ad(old_mode <= MTR_LOG_SHORT_INSERTS); return(old_mode); } #ifndef UNIV_HOTBACKUP /*********************************************************************//** Locks a lock in s-mode. */ UNIV_INLINE void mtr_s_lock_func( /*============*/ rw_lock_t* lock, /*!< in: rw-lock */ const char* file, /*!< in: file name */ ulint line, /*!< in: line number */ mtr_t* mtr) /*!< in: mtr */ { ut_ad(mtr); ut_ad(lock); rw_lock_s_lock_func(lock, 0, file, line); mtr_memo_push(mtr, lock, MTR_MEMO_S_LOCK); } /*********************************************************************//** Locks a lock in x-mode. */ UNIV_INLINE void mtr_x_lock_func( /*============*/ rw_lock_t* lock, /*!< in: rw-lock */ const char* file, /*!< in: file name */ ulint line, /*!< in: line number */ mtr_t* mtr) /*!< in: mtr */ { ut_ad(mtr); ut_ad(lock); rw_lock_x_lock_func(lock, 0, file, line); mtr_memo_push(mtr, lock, MTR_MEMO_X_LOCK); } #endif /* !UNIV_HOTBACKUP */ haildb-2.3.2/include/trx0xa.h0000644000175000017500000000465411513177357016676 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2010, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /* * Start of xa.h header * * Define a symbol to prevent multiple inclusions of this header file */ #ifndef XA_H #define XA_H /* * Transaction branch identification: XID and NULLXID: */ #ifndef XIDDATASIZE /** Sizes of transaction identifier */ #define XIDDATASIZE 128 /*!< maximum size of a transaction identifier, in bytes */ #define MAXGTRIDSIZE 64 /*!< maximum size in bytes of gtrid */ #define MAXBQUALSIZE 64 /*!< maximum size in bytes of bqual */ /** X/Open XA distributed transaction identifier */ struct xid_t { long formatID; /*!< format identifier; -1 means that the XID is null */ long gtrid_length; /*!< value from 1 through 64 */ long bqual_length; /*!< value from 1 through 64 */ char data[XIDDATASIZE]; /*!< distributed transaction identifier */ }; /** X/Open XA distributed transaction identifier */ typedef struct xid_t XID; #endif /** X/Open XA distributed transaction status codes */ /* @{ */ #define XA_OK 0 /*!< normal execution */ #define XAER_ASYNC -2 /*!< asynchronous operation already outstanding */ #define XAER_RMERR -3 /*!< a resource manager error occurred in the transaction branch */ #define XAER_NOTA -4 /*!< the XID is not valid */ #define XAER_INVAL -5 /*!< invalid arguments were given */ #define XAER_PROTO -6 /*!< routine invoked in an improper context */ #define XAER_RMFAIL -7 /*!< resource manager unavailable */ #define XAER_DUPID -8 /*!< the XID already exists */ #define XAER_OUTSIDE -9 /*!< resource manager doing work outside transaction */ /* @} */ #endif /* ifndef XA_H */ /* * End of xa.h header */ haildb-2.3.2/include/rem0cmp.h0000644000175000017500000002003311513177357017000 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1994, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /*******************************************************************//** @file include/rem0cmp.h Comparison services for records Created 7/1/1994 Heikki Tuuri ************************************************************************/ #ifndef rem0cmp_h #define rem0cmp_h #include "univ.i" #include "data0data.h" #include "data0type.h" #include "dict0dict.h" #include "rem0rec.h" /*************************************************************//** Returns TRUE if two columns are equal for comparison purposes. @return TRUE if the columns are considered equal in comparisons */ UNIV_INTERN ibool cmp_cols_are_equal( /*===============*/ const dict_col_t* col1, /*!< in: column 1 */ const dict_col_t* col2, /*!< in: column 2 */ ibool check_charsets); /*!< in: whether to check charsets */ /*************************************************************//** This function is used to compare two data fields for which we know the data type. @return 1, 0, -1, if data1 is greater, equal, less than data2, respectively */ UNIV_INLINE int cmp_data_data( /*==========*/ void* cmp_ctx,/*!< in: client compare context */ ulint mtype, /*!< in: main type */ ulint prtype, /*!< in: precise type */ const byte* data1, /*!< in: data field (== a pointer to a memory buffer) */ ulint len1, /*!< in: data field length or UNIV_SQL_NULL */ const byte* data2, /*!< in: data field (== a pointer to a memory buffer) */ ulint len2); /*!< in: data field length or UNIV_SQL_NULL */ /*************************************************************//** This function is used to compare two data fields for which we know the data type. @return 1, 0, -1, if data1 is greater, equal, less than data2, respectively */ UNIV_INTERN int cmp_data_data_slow( /*===============*/ void* cmp_ctx,/*!< in: client compare context */ ulint mtype, /*!< in: main type */ ulint prtype, /*!< in: precise type */ const byte* data1, /*!< in: data field (== a pointer to a memory buffer) */ ulint len1, /*!< in: data field length or UNIV_SQL_NULL */ const byte* data2, /*!< in: data field (== a pointer to a memory buffer) */ ulint len2); /*!< in: data field length or UNIV_SQL_NULL */ /*************************************************************//** This function is used to compare two dfields where at least the first has its data type field set. @return 1, 0, -1, if dfield1 is greater, equal, less than dfield2, respectively */ UNIV_INLINE int cmp_dfield_dfield( /*==============*/ void* cmp_ctx,/*!< in: client compare context */ const dfield_t* dfield1,/*!< in: data field; must have type field set */ const dfield_t* dfield2);/*!< in: data field */ /*************************************************************//** This function is used to compare a data tuple to a physical record. Only dtuple->n_fields_cmp first fields are taken into account for the data tuple! If we denote by n = n_fields_cmp, then rec must have either m >= n fields, or it must differ from dtuple in some of the m fields rec has. If rec has an externally stored field we do not compare it but return with value 0 if such a comparison should be made. @return 1, 0, -1, if dtuple is greater, equal, less than rec, respectively, when only the common first fields are compared, or until the first externally stored field in rec */ UNIV_INTERN int cmp_dtuple_rec_with_match( /*======================*/ void* cmp_ctx,/*!< in: client compare context */ const dtuple_t* dtuple, /*!< in: data tuple */ const rec_t* rec, /*!< in: physical record which differs from dtuple in some of the common fields, or which has an equal number or more fields than dtuple */ const ulint* offsets,/*!< in: array returned by rec_get_offsets() */ ulint* matched_fields, /*!< in/out: number of already completely matched fields; when function returns, contains the value for current comparison */ ulint* matched_bytes); /*!< in/out: number of already matched bytes within the first field not completely matched; when function returns, contains the value for current comparison */ /**************************************************************//** Compares a data tuple to a physical record. @see cmp_dtuple_rec_with_match @return 1, 0, -1, if dtuple is greater, equal, less than rec, respectively */ UNIV_INTERN int cmp_dtuple_rec( /*===========*/ void* cmp_ctx,/*!< in: client compare context */ const dtuple_t* dtuple, /*!< in: data tuple */ const rec_t* rec, /*!< in: physical record */ const ulint* offsets);/*!< in: array returned by rec_get_offsets() */ /**************************************************************//** Checks if a dtuple is a prefix of a record. The last field in dtuple is allowed to be a prefix of the corresponding field in the record. @return TRUE if prefix */ UNIV_INTERN ibool cmp_dtuple_is_prefix_of_rec( /*========================*/ void* cmp_ctx,/*!< in: client compare context */ const dtuple_t* dtuple, /*!< in: data tuple */ const rec_t* rec, /*!< in: physical record */ const ulint* offsets);/*!< in: array returned by rec_get_offsets() */ /*************************************************************//** Compare two physical records that contain the same number of columns, none of which are stored externally. @return 1, 0, -1 if rec1 is greater, equal, less, respectively, than rec2 */ UNIV_INTERN int cmp_rec_rec_simple( /*===============*/ const rec_t* rec1, /*!< in: physical record */ const rec_t* rec2, /*!< in: physical record */ const ulint* offsets1,/*!< in: rec_get_offsets(rec1, ...) */ const ulint* offsets2,/*!< in: rec_get_offsets(rec2, ...) */ const dict_index_t* index); /*!< in: data dictionary index */ /*************************************************************//** This function is used to compare two physical records. Only the common first fields are compared, and if an externally stored field is encountered, then 0 is returned. @return 1, 0, -1 if rec1 is greater, equal, less, respectively */ UNIV_INTERN int cmp_rec_rec_with_match( /*===================*/ const rec_t* rec1, /*!< in: physical record */ const rec_t* rec2, /*!< in: physical record */ const ulint* offsets1,/*!< in: rec_get_offsets(rec1, index) */ const ulint* offsets2,/*!< in: rec_get_offsets(rec2, index) */ dict_index_t* index, /*!< in: data dictionary index */ ulint* matched_fields, /*!< in/out: number of already completely matched fields; when the function returns, contains the value the for current comparison */ ulint* matched_bytes);/*!< in/out: number of already matched bytes within the first field not completely matched; when the function returns, contains the value for the current comparison */ /*************************************************************//** This function is used to compare two physical records. Only the common first fields are compared. @return 1, 0 , -1 if rec1 is greater, equal, less, respectively, than rec2; only the common first fields are compared */ UNIV_INLINE int cmp_rec_rec( /*========*/ const rec_t* rec1, /*!< in: physical record */ const rec_t* rec2, /*!< in: physical record */ const ulint* offsets1,/*!< in: rec_get_offsets(rec1, index) */ const ulint* offsets2,/*!< in: rec_get_offsets(rec2, index) */ dict_index_t* index); /*!< in: data dictionary index */ #ifndef UNIV_NONINL #include "rem0cmp.ic" #endif #endif haildb-2.3.2/include/log0log.ic0000644000175000017500000002747411513177357017164 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2010, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/log0log.ic Database log Created 12/9/1995 Heikki Tuuri *******************************************************/ #include "os0file.h" #include "mach0data.h" #include "mtr0mtr.h" #include "srv0srv.h" /*************************************************************************//** Acquire the log mutex. */ UNIV_INLINE void log_acquire(void) /*=============*/ { ut_ad(!mutex_own(&log_sys->mutex)); mutex_enter(&log_sys->mutex); } /*************************************************************************//** Releases the log mutex. */ UNIV_INLINE void log_release(void) /*=============*/ { ut_ad(mutex_own(&log_sys->mutex)); mutex_exit(&log_sys->mutex); } #ifdef UNIV_LOG_DEBUG /******************************************************//** Checks by parsing that the catenated log segment for a single mtr is consistent. */ UNIV_INTERN ibool log_check_log_recs( /*===============*/ const byte* buf, /*!< in: pointer to the start of the log segment in the log_sys->buf log buffer */ ulint len, /*!< in: segment length in bytes */ ib_uint64_t buf_start_lsn); /*!< in: buffer start lsn */ #endif /* UNIV_LOG_DEBUG */ /************************************************************//** Gets a log block flush bit. @return TRUE if this block was the first to be written in a log flush */ UNIV_INLINE ibool log_block_get_flush_bit( /*====================*/ const byte* log_block) /*!< in: log block */ { if (LOG_BLOCK_FLUSH_BIT_MASK & mach_read_from_4(log_block + LOG_BLOCK_HDR_NO)) { return(TRUE); } return(FALSE); } /************************************************************//** Sets the log block flush bit. */ UNIV_INLINE void log_block_set_flush_bit( /*====================*/ byte* log_block, /*!< in/out: log block */ ibool val) /*!< in: value to set */ { ulint field; field = mach_read_from_4(log_block + LOG_BLOCK_HDR_NO); if (val) { field = field | LOG_BLOCK_FLUSH_BIT_MASK; } else { field = field & ~LOG_BLOCK_FLUSH_BIT_MASK; } mach_write_to_4(log_block + LOG_BLOCK_HDR_NO, field); } /************************************************************//** Gets a log block number stored in the header. @return log block number stored in the block header */ UNIV_INLINE ulint log_block_get_hdr_no( /*=================*/ const byte* log_block) /*!< in: log block */ { return(~LOG_BLOCK_FLUSH_BIT_MASK & mach_read_from_4(log_block + LOG_BLOCK_HDR_NO)); } /************************************************************//** Sets the log block number stored in the header; NOTE that this must be set before the flush bit! */ UNIV_INLINE void log_block_set_hdr_no( /*=================*/ byte* log_block, /*!< in/out: log block */ ulint n) /*!< in: log block number: must be > 0 and < LOG_BLOCK_FLUSH_BIT_MASK */ { ut_ad(n > 0); ut_ad(n < LOG_BLOCK_FLUSH_BIT_MASK); mach_write_to_4(log_block + LOG_BLOCK_HDR_NO, n); } /************************************************************//** Gets a log block data length. @return log block data length measured as a byte offset from the block start */ UNIV_INLINE ulint log_block_get_data_len( /*===================*/ const byte* log_block) /*!< in: log block */ { return(mach_read_from_2(log_block + LOG_BLOCK_HDR_DATA_LEN)); } /************************************************************//** Sets the log block data length. */ UNIV_INLINE void log_block_set_data_len( /*===================*/ byte* log_block, /*!< in/out: log block */ ulint len) /*!< in: data length */ { mach_write_to_2(log_block + LOG_BLOCK_HDR_DATA_LEN, len); } /************************************************************//** Gets a log block first mtr log record group offset. @return first mtr log record group byte offset from the block start, 0 if none */ UNIV_INLINE ulint log_block_get_first_rec_group( /*==========================*/ const byte* log_block) /*!< in: log block */ { return(mach_read_from_2(log_block + LOG_BLOCK_FIRST_REC_GROUP)); } /************************************************************//** Sets the log block first mtr log record group offset. */ UNIV_INLINE void log_block_set_first_rec_group( /*==========================*/ byte* log_block, /*!< in/out: log block */ ulint offset) /*!< in: offset, 0 if none */ { mach_write_to_2(log_block + LOG_BLOCK_FIRST_REC_GROUP, offset); } /************************************************************//** Gets a log block checkpoint number field (4 lowest bytes). @return checkpoint no (4 lowest bytes) */ UNIV_INLINE ulint log_block_get_checkpoint_no( /*========================*/ const byte* log_block) /*!< in: log block */ { return(mach_read_from_4(log_block + LOG_BLOCK_CHECKPOINT_NO)); } /************************************************************//** Sets a log block checkpoint number field (4 lowest bytes). */ UNIV_INLINE void log_block_set_checkpoint_no( /*========================*/ byte* log_block, /*!< in/out: log block */ ib_uint64_t no) /*!< in: checkpoint no */ { mach_write_to_4(log_block + LOG_BLOCK_CHECKPOINT_NO, (ulint) no); } /************************************************************//** Converts a lsn to a log block number. @return log block number, it is > 0 and <= 1G */ UNIV_INLINE ulint log_block_convert_lsn_to_no( /*========================*/ ib_uint64_t lsn) /*!< in: lsn of a byte within the block */ { return(((ulint) (lsn / OS_FILE_LOG_BLOCK_SIZE) & 0x3FFFFFFFUL) + 1); } /************************************************************//** Calculates the checksum for a log block. @return checksum */ UNIV_INLINE ulint log_block_calc_checksum( /*====================*/ const byte* block) /*!< in: log block */ { ulint sum; ulint sh; ulint i; sum = 1; sh = 0; for (i = 0; i < OS_FILE_LOG_BLOCK_SIZE - LOG_BLOCK_TRL_SIZE; i++) { ulint b = (ulint) block[i]; sum &= 0x7FFFFFFFUL; sum += b; sum += b << sh; sh++; if (sh > 24) { sh = 0; } } return(sum); } /************************************************************//** Gets a log block checksum field value. @return checksum */ UNIV_INLINE ulint log_block_get_checksum( /*===================*/ const byte* log_block) /*!< in: log block */ { return(mach_read_from_4(log_block + OS_FILE_LOG_BLOCK_SIZE - LOG_BLOCK_CHECKSUM)); } /************************************************************//** Sets a log block checksum field value. */ UNIV_INLINE void log_block_set_checksum( /*===================*/ byte* log_block, /*!< in/out: log block */ ulint checksum) /*!< in: checksum */ { mach_write_to_4(log_block + OS_FILE_LOG_BLOCK_SIZE - LOG_BLOCK_CHECKSUM, checksum); } /************************************************************//** Initializes a log block in the log buffer. */ UNIV_INLINE void log_block_init( /*===========*/ byte* log_block, /*!< in: pointer to the log buffer */ ib_uint64_t lsn) /*!< in: lsn within the log block */ { ulint no; ut_ad(mutex_own(&(log_sys->mutex))); no = log_block_convert_lsn_to_no(lsn); log_block_set_hdr_no(log_block, no); log_block_set_data_len(log_block, LOG_BLOCK_HDR_SIZE); log_block_set_first_rec_group(log_block, 0); } /************************************************************//** Initializes a log block in the log buffer in the old format, where there was no checksum yet. */ UNIV_INLINE void log_block_init_in_old_format( /*=========================*/ byte* log_block, /*!< in: pointer to the log buffer */ ib_uint64_t lsn) /*!< in: lsn within the log block */ { ulint no; ut_ad(mutex_own(&(log_sys->mutex))); no = log_block_convert_lsn_to_no(lsn); log_block_set_hdr_no(log_block, no); mach_write_to_4(log_block + OS_FILE_LOG_BLOCK_SIZE - LOG_BLOCK_CHECKSUM, no); log_block_set_data_len(log_block, LOG_BLOCK_HDR_SIZE); log_block_set_first_rec_group(log_block, 0); } #ifndef UNIV_HOTBACKUP /************************************************************//** Writes to the log the string given. The log must be released with log_release(). @return end lsn of the log record, zero if did not succeed */ UNIV_INLINE ib_uint64_t log_reserve_and_write_fast( /*=======================*/ const void* str, /*!< in: string */ ulint len, /*!< in: string length */ ib_uint64_t* start_lsn)/*!< out: start lsn of the log record */ { ulint data_len; #ifdef UNIV_LOG_LSN_DEBUG /* length of the LSN pseudo-record */ ulint lsn_len; #endif /* UNIV_LOG_LSN_DEBUG */ #ifdef UNIV_LOG_LSN_DEBUG lsn_len = 1 + mach_get_compressed_size(log_sys->lsn >> 32) + mach_get_compressed_size(log_sys->lsn & 0xFFFFFFFFUL); #endif /* UNIV_LOG_LSN_DEBUG */ data_len = len #ifdef UNIV_LOG_LSN_DEBUG + lsn_len #endif /* UNIV_LOG_LSN_DEBUG */ + log_sys->buf_free % OS_FILE_LOG_BLOCK_SIZE; if (data_len >= OS_FILE_LOG_BLOCK_SIZE - LOG_BLOCK_TRL_SIZE) { /* The string does not fit within the current log block or the log block would become full */ return(0); } *start_lsn = log_sys->lsn; #ifdef UNIV_LOG_LSN_DEBUG { /* Write the LSN pseudo-record. */ byte* b = &log_sys->buf[log_sys->buf_free]; *b++ = MLOG_LSN | (MLOG_SINGLE_REC_FLAG & *(const byte*) str); /* Write the LSN in two parts, as a pseudo page number and space id. */ b += mach_write_compressed(b, log_sys->lsn >> 32); b += mach_write_compressed(b, log_sys->lsn & 0xFFFFFFFFUL); ut_a(b - lsn_len == &log_sys->buf[log_sys->buf_free]); memcpy(b, str, len); len += lsn_len; } #else /* UNIV_LOG_LSN_DEBUG */ memcpy(log_sys->buf + log_sys->buf_free, str, len); #endif /* UNIV_LOG_LSN_DEBUG */ log_block_set_data_len((byte*) ut_align_down(log_sys->buf + log_sys->buf_free, OS_FILE_LOG_BLOCK_SIZE), data_len); #ifdef UNIV_LOG_DEBUG log_sys->old_buf_free = log_sys->buf_free; log_sys->old_lsn = log_sys->lsn; #endif log_sys->buf_free += len; ut_ad(log_sys->buf_free <= log_sys->buf_size); log_sys->lsn += len; #ifdef UNIV_LOG_DEBUG log_check_log_recs(log_sys->buf + log_sys->old_buf_free, log_sys->buf_free - log_sys->old_buf_free, log_sys->old_lsn); #endif return(log_sys->lsn); } /************************************************************//** Gets the current lsn. @return current lsn */ UNIV_INLINE ib_uint64_t log_get_lsn(void) /*=============*/ { ib_uint64_t lsn; log_acquire(); lsn = log_sys->lsn; log_release(); return(lsn); } /**************************************************************** Gets the log group capacity. It is OK to read the value without holding log_sys->mutex because it is constant. @return log group capacity */ UNIV_INLINE ulint log_get_capacity(void) /*==================*/ { return(log_sys->log_group_capacity); } /***********************************************************************//** Checks if there is need for a log buffer flush or a new checkpoint, and does this if yes. Any database operation should call this when it has modified more than about 4 pages. NOTE that this function may only be called when the OS thread owns no synchronization objects except the dictionary mutex. */ UNIV_INLINE void log_free_check(void) /*================*/ { /* ut_ad(sync_thread_levels_empty()); */ if (log_sys->check_flush_or_checkpoint) { log_check_margins(); } } #endif /* !UNIV_HOTBACKUP */ haildb-2.3.2/include/btr0types.h0000644000175000017500000000347011513177357017377 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /********************************************************************//** @file include/btr0types.h The index tree general types Created 2/17/1996 Heikki Tuuri *************************************************************************/ #ifndef btr0types_h #define btr0types_h #include "univ.i" #include "rem0types.h" #include "page0types.h" /** Persistent cursor */ typedef struct btr_pcur_struct btr_pcur_t; /** B-tree cursor */ typedef struct btr_cur_struct btr_cur_t; /** B-tree search information for the adaptive hash index */ typedef struct btr_search_struct btr_search_t; /** The size of a reference to data stored on a different page. The reference is stored at the end of the prefix of the field in the index record. */ #define BTR_EXTERN_FIELD_REF_SIZE 20 /** A BLOB field reference full of zero, for use in assertions and tests. Initially, BLOB field references are set to zero, in dtuple_convert_big_rec(). */ extern const byte field_ref_zero[BTR_EXTERN_FIELD_REF_SIZE]; #endif haildb-2.3.2/include/ut0rnd.ic0000644000175000017500000001275411513177357017030 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1994, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************************//** @file include/ut0rnd.ic Random numbers and hashing Created 5/30/1994 Heikki Tuuri *******************************************************************/ #define UT_HASH_RANDOM_MASK 1463735687 #define UT_HASH_RANDOM_MASK2 1653893711 #define UT_RND1 151117737 #define UT_RND2 119785373 #define UT_RND3 85689495 #define UT_RND4 76595339 #define UT_SUM_RND2 98781234 #define UT_SUM_RND3 126792457 #define UT_SUM_RND4 63498502 #define UT_XOR_RND1 187678878 #define UT_XOR_RND2 143537923 /** Seed value of ut_rnd_gen_ulint() */ extern ulint ut_rnd_ulint_counter; /********************************************************//** This is used to set the random number seed. */ UNIV_INLINE void ut_rnd_set_seed( /*============*/ ulint seed) /*!< in: seed */ { ut_rnd_ulint_counter = seed; } /********************************************************//** The following function generates a series of 'random' ulint integers. @return the next 'random' number */ UNIV_INLINE ulint ut_rnd_gen_next_ulint( /*==================*/ ulint rnd) /*!< in: the previous random number value */ { ulint n_bits; n_bits = 8 * sizeof(ulint); rnd = UT_RND2 * rnd + UT_SUM_RND3; rnd = UT_XOR_RND1 ^ rnd; rnd = (rnd << 20) + (rnd >> (n_bits - 20)); rnd = UT_RND3 * rnd + UT_SUM_RND4; rnd = UT_XOR_RND2 ^ rnd; rnd = (rnd << 20) + (rnd >> (n_bits - 20)); rnd = UT_RND1 * rnd + UT_SUM_RND2; return(rnd); } /********************************************************//** The following function generates 'random' ulint integers which enumerate the value space of ulint integers in a pseudo random fashion. Note that the same integer is repeated always after 2 to power 32 calls to the generator (if ulint is 32-bit). @return the 'random' number */ UNIV_INLINE ulint ut_rnd_gen_ulint(void) /*==================*/ { ulint rnd; ulint n_bits; n_bits = 8 * sizeof(ulint); ut_rnd_ulint_counter = UT_RND1 * ut_rnd_ulint_counter + UT_RND2; rnd = ut_rnd_gen_next_ulint(ut_rnd_ulint_counter); return(rnd); } /********************************************************//** Generates a random integer from a given interval. @return the 'random' number */ UNIV_INLINE ulint ut_rnd_interval( /*============*/ ulint low, /*!< in: low limit; can generate also this value */ ulint high) /*!< in: high limit; can generate also this value */ { ulint rnd; ut_ad(high >= low); if (low == high) { return(low); } rnd = ut_rnd_gen_ulint(); return(low + (rnd % (high - low + 1))); } /*********************************************************//** Generates a random iboolean value. @return the random value */ UNIV_INLINE ibool ut_rnd_gen_ibool(void) /*=================*/ { ulint x; x = ut_rnd_gen_ulint(); if (((x >> 20) + (x >> 15)) & 1) { return(TRUE); } return(FALSE); } /*******************************************************//** The following function generates a hash value for a ulint integer to a hash table of size table_size, which should be a prime or some random number for the hash table to work reliably. @return hash value */ UNIV_INLINE ulint ut_hash_ulint( /*==========*/ ulint key, /*!< in: value to be hashed */ ulint table_size) /*!< in: hash table size */ { ut_ad(table_size); key = key ^ UT_HASH_RANDOM_MASK2; return(key % table_size); } /*************************************************************//** Folds a pair of ulints. @return folded value */ UNIV_INLINE ulint ut_fold_ulint_pair( /*===============*/ ulint n1, /*!< in: ulint */ ulint n2) /*!< in: ulint */ { return(((((n1 ^ n2 ^ UT_HASH_RANDOM_MASK2) << 8) + n1) ^ UT_HASH_RANDOM_MASK) + n2); } /*************************************************************//** Folds a dulint. @return folded value */ UNIV_INLINE ulint ut_fold_dulint( /*===========*/ dulint d) /*!< in: dulint */ { return(ut_fold_ulint_pair(ut_dulint_get_low(d), ut_dulint_get_high(d))); } /*************************************************************//** Folds a character string ending in the null character. @return folded value */ UNIV_INLINE ulint ut_fold_string( /*===========*/ const char* str) /*!< in: null-terminated string */ { ulint fold = 0; ut_ad(str); while (*str != '\0') { fold = ut_fold_ulint_pair(fold, (ulint)(*str)); str++; } return(fold); } /*************************************************************//** Folds a binary string. @return folded value */ UNIV_INLINE ulint ut_fold_binary( /*===========*/ const byte* str, /*!< in: string of bytes */ ulint len) /*!< in: length */ { const byte* str_end = str + len; ulint fold = 0; ut_ad(str || !len); while (str < str_end) { fold = ut_fold_ulint_pair(fold, (ulint)(*str)); str++; } return(fold); } haildb-2.3.2/include/fsp0fsp.ic0000644000175000017500000000311311513177357017162 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/fsp0fsp.ic File space management Created 12/18/1995 Heikki Tuuri *******************************************************/ /***********************************************************************//** Checks if a page address is an extent descriptor page address. @return TRUE if a descriptor page */ UNIV_INLINE ibool fsp_descr_page( /*===========*/ ulint zip_size,/*!< in: compressed page size in bytes; 0 for uncompressed pages */ ulint page_no)/*!< in: page number */ { ut_ad(ut_is_2pow(zip_size)); if (!zip_size) { return(UNIV_UNLIKELY((page_no & (UNIV_PAGE_SIZE - 1)) == FSP_XDES_OFFSET)); } return(UNIV_UNLIKELY((page_no & (zip_size - 1)) == FSP_XDES_OFFSET)); } haildb-2.3.2/include/buf0lru.h0000644000175000017500000002614211513177357017023 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/buf0lru.h The database buffer pool LRU replacement algorithm Created 11/5/1995 Heikki Tuuri *******************************************************/ #ifndef buf0lru_h #define buf0lru_h #include "univ.i" #include "ut0byte.h" #include "buf0types.h" /** The return type of buf_LRU_free_block() */ enum buf_lru_free_block_status { /** freed */ BUF_LRU_FREED = 0, /** not freed because the caller asked to remove the uncompressed frame but the control block cannot be relocated */ BUF_LRU_CANNOT_RELOCATE, /** not freed because of some other reason */ BUF_LRU_NOT_FREED }; /******************************************************************//** Tries to remove LRU flushed blocks from the end of the LRU list and put them to the free list. This is beneficial for the efficiency of the insert buffer operation, as flushed pages from non-unique non-clustered indexes are here taken out of the buffer pool, and their inserts redirected to the insert buffer. Otherwise, the flushed blocks could get modified again before read operations need new buffer blocks, and the i/o work done in flushing would be wasted. */ UNIV_INTERN void buf_LRU_try_free_flushed_blocks(void); /*==================================*/ /******************************************************************//** Returns TRUE if less than 25 % of the buffer pool is available. This can be used in heuristics to prevent huge transactions eating up the whole buffer pool for their locks. @return TRUE if less than 25 % of buffer pool left */ UNIV_INTERN ibool buf_LRU_buf_pool_running_out(void); /*==============================*/ /*####################################################################### These are low-level functions #########################################################################*/ /** Minimum LRU list length for which the LRU_old pointer is defined */ #define BUF_LRU_OLD_MIN_LEN 512 /* 8 megabytes of 16k pages */ /** Maximum LRU list search length in buf_flush_LRU_recommendation() */ #define BUF_LRU_FREE_SEARCH_LEN (5 + 2 * BUF_READ_AHEAD_AREA) /******************************************************************//** Invalidates all pages belonging to a given tablespace when we are deleting the data file(s) of that tablespace. A PROBLEM: if readahead is being started, what guarantees that it will not try to read in pages after this operation has completed? */ UNIV_INTERN void buf_LRU_invalidate_tablespace( /*==========================*/ ulint id); /*!< in: space id */ /********************************************************************//** Insert a compressed block into buf_pool->zip_clean in the LRU order. */ UNIV_INTERN void buf_LRU_insert_zip_clean( /*=====================*/ buf_page_t* bpage); /*!< in: pointer to the block in question */ /******************************************************************//** Try to free a block. If bpage is a descriptor of a compressed-only page, the descriptor object will be freed as well. NOTE: If this function returns BUF_LRU_FREED, it will not temporarily release buf_pool_mutex. Furthermore, the page frame will no longer be accessible via bpage. The caller must hold buf_pool_mutex and buf_page_get_mutex(bpage) and release these two mutexes after the call. No other buf_page_get_mutex() may be held when calling this function. @return BUF_LRU_FREED if freed, BUF_LRU_CANNOT_RELOCATE or BUF_LRU_NOT_FREED otherwise. */ UNIV_INTERN enum buf_lru_free_block_status buf_LRU_free_block( /*===============*/ buf_page_t* bpage, /*!< in: block to be freed */ ibool zip, /*!< in: TRUE if should remove also the compressed page of an uncompressed page */ ibool* buf_pool_mutex_released); /*!< in: pointer to a variable that will be assigned TRUE if buf_pool_mutex was temporarily released, or NULL */ /******************************************************************//** Try to free a replaceable block. @return TRUE if found and freed */ UNIV_INTERN ibool buf_LRU_search_and_free_block( /*==========================*/ ulint n_iterations); /*!< in: how many times this has been called repeatedly without result: a high value means that we should search farther; if n_iterations < 10, then we search n_iterations / 10 * buf_pool->curr_size pages from the end of the LRU list; if n_iterations < 5, then we will also search n_iterations / 5 of the unzip_LRU list. */ /******************************************************************//** Returns a free block from the buf_pool. The block is taken off the free list. If it is empty, returns NULL. @return a free control block, or NULL if the buf_block->free list is empty */ UNIV_INTERN buf_block_t* buf_LRU_get_free_only(void); /*=======================*/ /******************************************************************//** Returns a free block from the buf_pool. The block is taken off the free list. If it is empty, blocks are moved from the end of the LRU list to the free list. @return the free control block, in state BUF_BLOCK_READY_FOR_USE */ UNIV_INTERN buf_block_t* buf_LRU_get_free_block( /*===================*/ ulint zip_size); /*!< in: compressed page size in bytes, or 0 if uncompressed tablespace */ /******************************************************************//** Puts a block back to the free list. */ UNIV_INTERN void buf_LRU_block_free_non_file_page( /*=============================*/ buf_block_t* block); /*!< in: block, must not contain a file page */ /******************************************************************//** Adds a block to the LRU list. */ UNIV_INTERN void buf_LRU_add_block( /*==============*/ buf_page_t* bpage, /*!< in: control block */ ibool old); /*!< in: TRUE if should be put to the old blocks in the LRU list, else put to the start; if the LRU list is very short, added to the start regardless of this parameter */ /******************************************************************//** Adds a block to the LRU list of decompressed zip pages. */ UNIV_INTERN void buf_unzip_LRU_add_block( /*====================*/ buf_block_t* block, /*!< in: control block */ ibool old); /*!< in: TRUE if should be put to the end of the list, else put to the start */ /******************************************************************//** Moves a block to the start of the LRU list. */ UNIV_INTERN void buf_LRU_make_block_young( /*=====================*/ buf_page_t* bpage); /*!< in: control block */ /******************************************************************//** Moves a block to the end of the LRU list. */ UNIV_INTERN void buf_LRU_make_block_old( /*===================*/ buf_page_t* bpage); /*!< in: control block */ /**********************************************************************//** Updates buf_LRU_old_ratio. @return updated old_pct */ UNIV_INTERN ulint buf_LRU_old_ratio_update( /*=====================*/ ulint old_pct,/*!< in: Reserve this percentage of the buffer pool for "old" blocks. */ ibool adjust);/*!< in: TRUE=adjust the LRU list; FALSE=just assign buf_LRU_old_ratio during the initialization of InnoDB */ /********************************************************************//** Update the historical stats that we are collecting for LRU eviction policy at the end of each interval. */ UNIV_INTERN void buf_LRU_stat_update(void); /*=====================*/ /********************************************************************** Reset buffer LRU variables. */ UNIV_INTERN void buf_LRU_var_init(void); /*==================*/ #if defined UNIV_DEBUG || defined UNIV_BUF_DEBUG /**********************************************************************//** Validates the LRU list. @return TRUE */ UNIV_INTERN ibool buf_LRU_validate(void); /*==================*/ #endif /* UNIV_DEBUG || UNIV_BUF_DEBUG */ #if defined UNIV_DEBUG_PRINT || defined UNIV_DEBUG || defined UNIV_BUF_DEBUG /**********************************************************************//** Prints the LRU list. */ UNIV_INTERN void buf_LRU_print(void); /*===============*/ #endif /* UNIV_DEBUG_PRINT || UNIV_DEBUG || UNIV_BUF_DEBUG */ /** @name Heuristics for detecting index scan @{ */ /** Reserve this much/BUF_LRU_OLD_RATIO_DIV of the buffer pool for "old" blocks. Protected by buf_pool_mutex. */ extern ulint buf_LRU_old_ratio; /** The denominator of buf_LRU_old_ratio. */ #define BUF_LRU_OLD_RATIO_DIV 1024 /** Maximum value of buf_LRU_old_ratio. @see buf_LRU_old_adjust_len @see buf_LRU_old_ratio_update */ #define BUF_LRU_OLD_RATIO_MAX BUF_LRU_OLD_RATIO_DIV /** Minimum value of buf_LRU_old_ratio. @see buf_LRU_old_adjust_len @see buf_LRU_old_ratio_update The minimum must exceed (BUF_LRU_OLD_TOLERANCE + 5) * BUF_LRU_OLD_RATIO_DIV / BUF_LRU_OLD_MIN_LEN. */ #define BUF_LRU_OLD_RATIO_MIN 51 #if BUF_LRU_OLD_RATIO_MIN >= BUF_LRU_OLD_RATIO_MAX # error "BUF_LRU_OLD_RATIO_MIN >= BUF_LRU_OLD_RATIO_MAX" #endif #if BUF_LRU_OLD_RATIO_MAX > BUF_LRU_OLD_RATIO_DIV # error "BUF_LRU_OLD_RATIO_MAX > BUF_LRU_OLD_RATIO_DIV" #endif /** Move blocks to "new" LRU list only if the first access was at least this many milliseconds ago. Not protected by any mutex or latch. */ extern ulint buf_LRU_old_threshold_ms; /* @} */ /** @brief Statistics for selecting the LRU list for eviction. These statistics are not 'of' LRU but 'for' LRU. We keep count of I/O and page_zip_decompress() operations. Based on the statistics we decide if we want to evict from buf_pool->unzip_LRU or buf_pool->LRU. */ struct buf_LRU_stat_struct { ulint io; /**< Counter of buffer pool I/O operations. */ ulint unzip; /**< Counter of page_zip_decompress operations. */ }; /** Statistics for selecting the LRU list for eviction. */ typedef struct buf_LRU_stat_struct buf_LRU_stat_t; /** Current operation counters. Not protected by any mutex. Cleared by buf_LRU_stat_update(). */ extern buf_LRU_stat_t buf_LRU_stat_cur; /** Running sum of past values of buf_LRU_stat_cur. Updated by buf_LRU_stat_update(). Protected by buf_pool_mutex. */ extern buf_LRU_stat_t buf_LRU_stat_sum; /********************************************************************//** Increments the I/O counter in buf_LRU_stat_cur. */ #define buf_LRU_stat_inc_io() buf_LRU_stat_cur.io++ /********************************************************************//** Increments the page_zip_decompress() counter in buf_LRU_stat_cur. */ #define buf_LRU_stat_inc_unzip() buf_LRU_stat_cur.unzip++ #ifndef UNIV_NONINL #include "buf0lru.ic" #endif #endif haildb-2.3.2/include/dict0mem.h0000644000175000017500000004220211513177357017141 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/dict0mem.h Data dictionary memory object creation Created 1/8/1996 Heikki Tuuri *******************************************************/ #ifndef dict0mem_h #define dict0mem_h #include "univ.i" #include "dict0types.h" #include "data0type.h" #include "mem0mem.h" #include "rem0types.h" #include "btr0types.h" #ifndef UNIV_HOTBACKUP # include "lock0types.h" # include "que0types.h" # include "sync0rw.h" #endif /* !UNIV_HOTBACKUP */ #include "ut0mem.h" #include "ut0lst.h" #include "ut0rnd.h" #include "ut0byte.h" #include "hash0hash.h" #include "trx0types.h" /** Type flags of an index: OR'ing of the flags is allowed to define a combination of types */ /* @{ */ #define DICT_CLUSTERED 1 /*!< clustered index */ #define DICT_UNIQUE 2 /*!< unique index */ #define DICT_UNIVERSAL 4 /*!< index which can contain records from any other index */ #define DICT_IBUF 8 /*!< insert buffer tree */ /* @} */ /** Types for a table object */ #define DICT_TABLE_ORDINARY 1 /*!< ordinary table */ #if 0 /* not implemented */ #define DICT_TABLE_CLUSTER_MEMBER 2 #define DICT_TABLE_CLUSTER 3 /* this means that the table is really a cluster definition */ #endif /** Table flags. All unused bits must be 0. */ /* @{ */ #define DICT_TF_COMPACT 1 /* Compact page format. This must be set for new file formats (later than DICT_TF_FORMAT_51). */ /** Compressed page size (0=uncompressed, up to 15 compressed sizes) */ /* @{ */ #define DICT_TF_ZSSIZE_SHIFT 1 #define DICT_TF_ZSSIZE_MASK (15 << DICT_TF_ZSSIZE_SHIFT) #define DICT_TF_ZSSIZE_MAX (UNIV_PAGE_SIZE_SHIFT - PAGE_ZIP_MIN_SIZE_SHIFT + 1) /* @} */ /** File format */ /* @{ */ #define DICT_TF_FORMAT_SHIFT 5 /* file format */ #define DICT_TF_FORMAT_MASK \ ((~(~0 << (DICT_TF_BITS - DICT_TF_FORMAT_SHIFT))) << DICT_TF_FORMAT_SHIFT) #define DICT_TF_FORMAT_51 0 /*!< InnoDB/MySQL up to 5.1 */ #define DICT_TF_FORMAT_ZIP 1 /*!< InnoDB plugin for 5.1: compressed tables, new BLOB treatment */ /** Maximum supported file format */ #define DICT_TF_FORMAT_MAX DICT_TF_FORMAT_ZIP /* @} */ #define DICT_TF_BITS 6 /*!< number of flag bits */ #if (1 << (DICT_TF_BITS - DICT_TF_FORMAT_SHIFT)) <= DICT_TF_FORMAT_MAX # error "DICT_TF_BITS is insufficient for DICT_TF_FORMAT_MAX" #endif /* @} */ /** @brief Additional table flags. These flags will be stored in SYS_TABLES.MIX_LEN. All unused flags will be written as 0. The column may contain garbage for tables created with old versions of InnoDB that only implemented ROW_FORMAT=REDUNDANT. */ /* @{ */ #define DICT_TF2_SHIFT DICT_TF_BITS /*!< Shift value for table->flags. */ #define DICT_TF2_TEMPORARY 1 /*!< TRUE for tables from CREATE TEMPORARY TABLE. */ #define DICT_TF2_BITS (DICT_TF2_SHIFT + 1) /*!< Total number of bits in table->flags. */ /* @} */ /**********************************************************************//** Creates a table memory object. @return own: table object */ UNIV_INTERN dict_table_t* dict_mem_table_create( /*==================*/ const char* name, /*!< in: table name */ ulint space, /*!< in: space where the clustered index of the table is placed; this parameter is ignored if the table is made a member of a cluster */ ulint n_cols, /*!< in: number of columns */ ulint flags); /*!< in: table flags */ /****************************************************************//** Free a table memory object. */ UNIV_INTERN void dict_mem_table_free( /*================*/ dict_table_t* table); /*!< in: table */ /**********************************************************************//** Adds a column definition to a table. */ UNIV_INTERN void dict_mem_table_add_col( /*===================*/ dict_table_t* table, /*!< in: table */ mem_heap_t* heap, /*!< in: temporary memory heap, or NULL */ const char* name, /*!< in: column name, or NULL */ ulint mtype, /*!< in: main datatype */ ulint prtype, /*!< in: precise type */ ulint len); /*!< in: precision */ /**********************************************************************//** Creates an index memory object. @return own: index object */ UNIV_INTERN dict_index_t* dict_mem_index_create( /*==================*/ const char* table_name, /*!< in: table name */ const char* index_name, /*!< in: index name */ ulint space, /*!< in: space where the index tree is placed, ignored if the index is of the clustered type */ ulint type, /*!< in: DICT_UNIQUE, DICT_CLUSTERED, ... ORed */ ulint n_fields); /*!< in: number of fields */ /**********************************************************************//** Adds a field definition to an index. NOTE: does not take a copy of the column name if the field is a column. The memory occupied by the column name may be released only after publishing the index. */ UNIV_INTERN void dict_mem_index_add_field( /*=====================*/ dict_index_t* index, /*!< in: index */ const char* name, /*!< in: column name */ ulint prefix_len); /*!< in: 0 or the column prefix length in a column prefix index like INDEX (textcol(25)) */ /**********************************************************************//** Frees an index memory object. */ UNIV_INTERN void dict_mem_index_free( /*================*/ dict_index_t* index); /*!< in: index */ /**********************************************************************//** Creates and initializes a foreign constraint memory object. @return own: foreign constraint struct */ UNIV_INTERN dict_foreign_t* dict_mem_foreign_create(void); /*=========================*/ /** Data structure for a column in a table */ struct dict_col_struct{ /* @{ */ DTYPE_FIELDS /* @} */ unsigned ind:10; /*!< table column position (starting from 0) */ unsigned ord_part:1; /*!< nonzero if this column appears in the ordering fields of an index */ }; /** @brief DICT_MAX_INDEX_COL_LEN is measured in bytes and is the maximum indexed column length (or indexed prefix length). It is set to 3*256, so that one can create a column prefix index on 256 characters of a TEXT or VARCHAR column also in the UTF-8 charset. In that charset, a character may take at most 3 bytes. This constant MUST NOT BE CHANGED, or the compatibility of InnoDB data files would be at risk! */ #define DICT_MAX_INDEX_COL_LEN REC_MAX_INDEX_COL_LEN /** Data structure for a field in an index */ struct dict_field_struct{ dict_col_t* col; /*!< pointer to the table column */ const char* name; /*!< name of the column */ unsigned prefix_len:10; /*!< 0 or the length of the column prefix in bytes e.g., for INDEX (textcol(25)); must be smaller than DICT_MAX_INDEX_COL_LEN; NOTE that in the UTF-8 charset, MySQL sets this to 3 * the prefix len in UTF-8 chars */ unsigned fixed_len:10; /*!< 0 or the fixed length of the column if smaller than DICT_MAX_INDEX_COL_LEN */ }; /** Data structure for an index. Most fields will be initialized to 0, NULL or FALSE in dict_mem_index_create(). */ struct dict_index_struct{ dulint id; /*!< id of the index */ mem_heap_t* heap; /*!< memory heap */ const char* name; /*!< index name */ const char* table_name;/*!< table name */ dict_table_t* table; /*!< back pointer to table */ #ifndef UNIV_HOTBACKUP unsigned space:32; /*!< space where the index tree is placed */ unsigned page:32;/*!< index tree root page number */ #endif /* !UNIV_HOTBACKUP */ unsigned type:4; /*!< index type (DICT_CLUSTERED, DICT_UNIQUE, DICT_UNIVERSAL, DICT_IBUF) */ unsigned trx_id_offset:10;/*!< position of the trx id column in a clustered index record, if the fields before it are known to be of a fixed size, 0 otherwise */ unsigned n_user_defined_cols:10; /*!< number of columns the user defined to be in the index: in the internal representation we add more columns */ unsigned n_uniq:10;/*!< number of fields from the beginning which are enough to determine an index entry uniquely */ unsigned n_def:10;/*!< number of fields defined so far */ unsigned n_fields:10;/*!< number of fields in the index */ unsigned n_nullable:10;/*!< number of nullable fields */ unsigned cached:1;/*!< TRUE if the index object is in the dictionary cache */ unsigned to_be_dropped:1; /*!< TRUE if this index is marked to be dropped in ha_innobase::prepare_drop_index(), otherwise FALSE */ dict_field_t* fields; /*!< array of field descriptions */ #ifndef UNIV_HOTBACKUP UT_LIST_NODE_T(dict_index_t) indexes;/*!< list of indexes of the table */ btr_search_t* search_info; /*!< info used in optimistic searches */ /*----------------------*/ /** Statistics for query optimization */ /* @{ */ ib_int64_t* stat_n_diff_key_vals; /*!< approximate number of different key values for this index, for each n-column prefix where n <= dict_get_n_unique(index); we periodically calculate new estimates */ ulint stat_index_size; /*!< approximate index size in database pages */ ulint stat_n_leaf_pages; /*!< approximate number of leaf pages in the index tree */ rw_lock_t lock; /* read-write lock protecting the upper levels of the index tree */ void* cmp_ctx;/* Client compare context. For use defined column types and BLOBs the client is responsible for comparing the column values. This field is the argument for the callback compare function. */ ib_uint64_t trx_id; /* id of the transaction that created this index, or 0 if the index existed when InnoDB was started up */ /* @} */ #endif /* !UNIV_HOTBACKUP */ #ifdef UNIV_DEBUG ulint magic_n;/*!< magic number */ /** Value of dict_index_struct::magic_n */ # define DICT_INDEX_MAGIC_N 76789786 #endif }; /** Data structure for a foreign key constraint; an example: FOREIGN KEY (A, B) REFERENCES TABLE2 (C, D). Most fields will be initialized to 0, NULL or FALSE in dict_mem_foreign_create(). */ struct dict_foreign_struct{ mem_heap_t* heap; /*!< this object is allocated from this memory heap */ char* id; /*!< id of the constraint as a null-terminated string */ unsigned n_fields:10; /*!< number of indexes' first fields for which the foreign key constraint is defined: we allow the indexes to contain more fields than mentioned in the constraint, as long as the first fields are as mentioned */ unsigned type:6; /*!< 0 or DICT_FOREIGN_ON_DELETE_CASCADE or DICT_FOREIGN_ON_DELETE_SET_NULL */ char* foreign_table_name;/*!< foreign table name */ dict_table_t* foreign_table; /*!< table where the foreign key is */ const char** foreign_col_names;/*!< names of the columns in the foreign key */ char* referenced_table_name;/*!< referenced table name */ dict_table_t* referenced_table;/*!< table where the referenced key is */ const char** referenced_col_names;/*!< names of the referenced columns in the referenced table */ dict_index_t* foreign_index; /*!< foreign index; we require that both tables contain explicitly defined indexes for the constraint: InnoDB does not generate new indexes implicitly */ dict_index_t* referenced_index;/*!< referenced index */ UT_LIST_NODE_T(dict_foreign_t) foreign_list; /*!< list node for foreign keys of the table */ UT_LIST_NODE_T(dict_foreign_t) referenced_list;/*!< list node for referenced keys of the table */ }; /** The flags for ON_UPDATE and ON_DELETE can be ORed; the default is that a foreign key constraint is enforced, therefore RESTRICT just means no flag */ /* @{ */ #define DICT_FOREIGN_ON_DELETE_CASCADE 1 /*!< ON DELETE CASCADE */ #define DICT_FOREIGN_ON_DELETE_SET_NULL 2 /*!< ON UPDATE SET NULL */ #define DICT_FOREIGN_ON_UPDATE_CASCADE 4 /*!< ON DELETE CASCADE */ #define DICT_FOREIGN_ON_UPDATE_SET_NULL 8 /*!< ON UPDATE SET NULL */ #define DICT_FOREIGN_ON_DELETE_NO_ACTION 16 /*!< ON DELETE NO ACTION */ #define DICT_FOREIGN_ON_UPDATE_NO_ACTION 32 /*!< ON UPDATE NO ACTION */ /* @} */ /** Data structure for a database table. Most fields will be initialized to 0, NULL or FALSE in dict_mem_table_create(). */ struct dict_table_struct{ dulint id; /*!< id of the table */ mem_heap_t* heap; /*!< memory heap */ const char* name; /*!< table name */ const char* dir_path_of_temp_table;/*!< NULL or the directory path where a TEMPORARY table that was explicitly created by a user should be placed if innodb_file_per_table is defined; in Unix this is usually /tmp/..., in Windows temp\... */ unsigned space:32; /*!< space where the clustered index of the table is placed */ unsigned flags:DICT_TF2_BITS;/*!< DICT_TF_COMPACT, ... */ unsigned ibd_file_missing:1; /*!< TRUE if this is in a single-table tablespace and the .ibd file is missing; then we must return in ha_innodb.cc an error if the user tries to query such an orphaned table */ unsigned tablespace_discarded:1; /*!< this flag is set TRUE when the user calls DISCARD TABLESPACE on this table, and reset to FALSE in IMPORT TABLESPACE */ unsigned cached:1;/*!< TRUE if the table object has been added to the dictionary cache */ unsigned n_def:10;/*!< number of columns defined so far */ unsigned n_cols:10;/*!< number of columns */ dict_col_t* cols; /*!< array of column descriptions */ const char* col_names; /*!< Column names packed in a character string "name1\0name2\0...nameN\0". Until the string contains n_cols, it will be allocated from a temporary heap. The final string will be allocated from table->heap. */ #ifndef UNIV_HOTBACKUP hash_node_t name_hash; /*!< hash chain node */ hash_node_t id_hash; /*!< hash chain node */ UT_LIST_BASE_NODE_T(dict_index_t) indexes; /*!< list of indexes of the table */ UT_LIST_BASE_NODE_T(dict_foreign_t) foreign_list;/*!< list of foreign key constraints in the table; these refer to columns in other tables */ UT_LIST_BASE_NODE_T(dict_foreign_t) referenced_list;/*!< list of foreign key constraints which refer to this table */ UT_LIST_NODE_T(dict_table_t) table_LRU; /*!< node of the LRU list of tables */ ulint n_handles_opened; /*!< count of how many handles the user has opened to this table; dropping of the table is NOT allowed until this count gets to zero */ ulint n_foreign_key_checks_running; /*!< count of how many foreign key check operations are currently being performed on the table: we cannot drop the table while there are foreign key checks running on it! */ UT_LIST_BASE_NODE_T(lock_t) locks; /*!< list of locks on the table */ #ifdef UNIV_DEBUG /*----------------------*/ ibool does_not_fit_in_memory; /*!< this field is used to specify in simulations tables which are so big that disk should be accessed: disk access is simulated by putting the thread to sleep for a while; NOTE that this flag is not stored to the data dictionary on disk, and the database will forget about value TRUE if it has to reload the table definition from disk */ #endif /* UNIV_DEBUG */ /*----------------------*/ unsigned big_rows:1; /*!< flag: TRUE if the maximum length of a single row exceeds BIG_ROW_SIZE; initialized in dict_table_add_to_cache() */ /** Statistics for query optimization */ /* @{ */ unsigned stat_initialized:1; /*!< TRUE if statistics have been calculated the first time after database startup or table creation */ ib_int64_t stat_n_rows; /*!< approximate number of rows in the table; we periodically calculate new estimates */ ulint stat_clustered_index_size; /*!< approximate clustered index size in database pages */ ulint stat_sum_of_other_index_sizes; /*!< other indexes in database pages */ ulint stat_modified_counter; /*!< when a row is inserted, updated, or deleted, we add 1 to this number; we calculate new estimates for the stat_... values for the table and the indexes at an interval of 2 GB or when about 1 / 16 of table has been modified; also when an estimate operation is called for; the counter is reset to zero at statistics calculation; this counter is not protected by any latch, because this is only used for heuristics */ /* @} */ /*----------------------*/ #endif /* !UNIV_HOTBACKUP */ #ifdef UNIV_DEBUG ulint magic_n;/*!< magic number */ /** Value of dict_table_struct::magic_n */ # define DICT_TABLE_MAGIC_N 76333786 #endif /* UNIV_DEBUG */ }; #ifndef UNIV_NONINL #include "dict0mem.ic" #endif #endif haildb-2.3.2/include/dict0crea.h0000644000175000017500000001657411513177357017312 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/dict0crea.h Database object creation Created 1/8/1996 Heikki Tuuri *******************************************************/ #ifndef dict0crea_h #define dict0crea_h #include "univ.i" #include "dict0types.h" #include "dict0dict.h" #include "que0types.h" #include "row0types.h" #include "mtr0mtr.h" /*********************************************************************//** Creates a table create graph. @return own: table create node */ UNIV_INTERN tab_node_t* tab_create_graph_create( /*====================*/ dict_table_t* table, /*!< in: table to create, built as a memory data structure */ mem_heap_t* heap, /*!< in: heap where created */ ibool commit);/*!< in: if TRUE commit transaction */ /*********************************************************************//** Creates an index create graph. @return own: index create node */ UNIV_INTERN ind_node_t* ind_create_graph_create( /*====================*/ dict_index_t* index, /*!< in: index to create, built as a memory data structure */ mem_heap_t* heap, /*!< in: heap where created */ ibool commit);/*!< in: TRUE if transaction should be commit */ /***********************************************************//** Creates a table. This is a high-level function used in SQL execution graphs. @return query thread to run next or NULL */ UNIV_INTERN que_thr_t* dict_create_table_step( /*===================*/ que_thr_t* thr); /*!< in: query thread */ /***********************************************************//** Creates an index. This is a high-level function used in SQL execution graphs. @return query thread to run next or NULL */ UNIV_INTERN que_thr_t* dict_create_index_step( /*===================*/ que_thr_t* thr); /*!< in: query thread */ /*******************************************************************//** Truncates the index tree associated with a row in SYS_INDEXES table. @return new root page number, or FIL_NULL on failure */ UNIV_INTERN ulint dict_truncate_index_tree( /*=====================*/ dict_table_t* table, /*!< in: the table the index belongs to */ ulint space, /*!< in: 0=truncate, nonzero=create the index tree in the given tablespace */ btr_pcur_t* pcur, /*!< in/out: persistent cursor pointing to record in the clustered index of SYS_INDEXES table. The cursor may be repositioned in this call. */ mtr_t* mtr); /*!< in: mtr having the latch on the record page. The mtr may be committed and restarted in this call. */ /*******************************************************************//** Drops the index tree associated with a row in SYS_INDEXES table. */ UNIV_INTERN void dict_drop_index_tree( /*=================*/ rec_t* rec, /*!< in/out: record in the clustered index of SYS_INDEXES table */ mtr_t* mtr); /*!< in: mtr having the latch on the record page */ /****************************************************************//** Creates the foreign key constraints system tables inside InnoDB at database creation or database start if they are not found or are not of the right form. @return DB_SUCCESS or error code */ UNIV_INTERN ulint dict_create_or_check_foreign_constraint_tables(void); /*================================================*/ /********************************************************************//** Adds foreign key definitions to data dictionary tables in the database. We look at table->foreign_list, and also generate names to constraints that were not named by the user. A generated constraint has a name of the format databasename/tablename_ibfk_NUMBER, where the numbers start from 1, and are given locally for this table, that is, the number is not global, as in the old format constraints < 4.0.18 it used to be. @return error code or DB_SUCCESS */ UNIV_INTERN ulint dict_create_add_foreigns_to_dictionary( /*===================================*/ ulint start_id,/*!< in: if we are actually doing ALTER TABLE ADD CONSTRAINT, we want to generate constraint numbers which are bigger than in the table so far; we number the constraints from start_id + 1 up; start_id should be set to 0 if we are creating a new table, or if the table so far has no constraints for which the name was generated here */ dict_table_t* table, /*!< in: table */ trx_t* trx); /*!< in: transaction */ /* Table create node structure */ struct tab_node_struct{ que_common_t common; /*!< node type: QUE_NODE_TABLE_CREATE */ dict_table_t* table; /*!< table to create, built as a memory data structure with dict_mem_... functions */ ins_node_t* tab_def; /* child node which does the insert of the table definition; the row to be inserted is built by the parent node */ ins_node_t* col_def; /* child node which does the inserts of the column definitions; the row to be inserted is built by the parent node */ commit_node_t* commit_node; /* child node which performs a commit after a successful table creation */ /*----------------------*/ /* Local storage for this graph node */ ulint state; /*!< node execution state */ ulint col_no; /*!< next column definition to insert */ mem_heap_t* heap; /*!< memory heap used as auxiliary storage */ }; /* Table create node states */ #define TABLE_BUILD_TABLE_DEF 1 #define TABLE_BUILD_COL_DEF 2 #define TABLE_COMMIT_WORK 3 #define TABLE_ADD_TO_CACHE 4 #define TABLE_COMPLETED 5 /* Index create node struct */ struct ind_node_struct{ que_common_t common; /*!< node type: QUE_NODE_INDEX_CREATE */ dict_index_t* index; /*!< index to create, built as a memory data structure with dict_mem_... functions */ ins_node_t* ind_def; /* child node which does the insert of the index definition; the row to be inserted is built by the parent node */ ins_node_t* field_def; /* child node which does the inserts of the field definitions; the row to be inserted is built by the parent node */ commit_node_t* commit_node; /* child node which performs a commit after a successful index creation */ /*----------------------*/ /* Local storage for this graph node */ ulint state; /*!< node execution state */ ulint page_no;/* root page number of the index */ dict_table_t* table; /*!< table which owns the index */ dtuple_t* ind_row;/* index definition row built */ ulint field_no;/* next field definition to insert */ mem_heap_t* heap; /*!< memory heap used as auxiliary storage */ }; /* Index create node states */ #define INDEX_BUILD_INDEX_DEF 1 #define INDEX_BUILD_FIELD_DEF 2 #define INDEX_CREATE_INDEX_TREE 3 #define INDEX_COMMIT_WORK 4 #define INDEX_ADD_TO_CACHE 5 #ifndef UNIV_NONINL #include "dict0crea.ic" #endif #endif haildb-2.3.2/include/trx0sys.h0000644000175000017500000004775211513177357017112 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2010, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/trx0sys.h Transaction system Created 3/26/1996 Heikki Tuuri *******************************************************/ #ifndef trx0sys_h #define trx0sys_h #include "univ.i" #include "trx0types.h" #include "fsp0types.h" #include "fil0fil.h" #include "buf0buf.h" #ifndef UNIV_HOTBACKUP #include "mtr0mtr.h" #include "ut0byte.h" #include "mem0mem.h" #include "sync0sync.h" #include "ut0lst.h" #include "read0types.h" #include "page0types.h" /** The transaction system */ extern trx_sys_t* trx_sys; /** Doublewrite system */ extern trx_doublewrite_t* trx_doublewrite; /** The following is set to TRUE when we are upgrading from pre-4.1 format data files to the multiple tablespaces format data files */ extern ibool trx_doublewrite_must_reset_space_ids; /** Set to TRUE when the doublewrite buffer is being created */ extern ibool trx_doublewrite_buf_is_being_created; /** The following is TRUE when we are using the database in the post-4.1 format, i.e., we have successfully upgraded, or have created a new database installation */ extern ibool trx_sys_multiple_tablespace_format; /****************************************************************//** Creates the doublewrite buffer to a new InnoDB installation. The header of the doublewrite buffer is placed on the trx system header page. */ UNIV_INTERN enum db_err trx_sys_create_doublewrite_buf(void); /*================================*/ /****************************************************************//** At a database startup initializes the doublewrite buffer memory structure if we already have a doublewrite buffer created in the data files. If we are upgrading to an InnoDB version which supports multiple tablespaces, then this function performs the necessary update operations. If we are in a crash recovery, this function uses a possible doublewrite buffer to restore half-written pages in the data files. */ UNIV_INTERN void trx_sys_doublewrite_init_or_restore_pages( /*======================================*/ ibool restore_corrupt_pages); /*!< in: TRUE=restore pages */ /****************************************************************//** Marks the trx sys header when we have successfully upgraded to the >= 4.1.x multiple tablespace format. */ UNIV_INTERN void trx_sys_mark_upgraded_to_multiple_tablespaces(void); /*===============================================*/ /****************************************************************//** Determines if a page number is located inside the doublewrite buffer. @return TRUE if the location is inside the two blocks of the doublewrite buffer */ UNIV_INTERN ibool trx_doublewrite_page_inside( /*========================*/ ulint page_no); /*!< in: page number */ /***************************************************************//** Checks if a page address is the trx sys header page. @return TRUE if trx sys header page */ UNIV_INLINE ibool trx_sys_hdr_page( /*=============*/ ulint space, /*!< in: space */ ulint page_no);/*!< in: page number */ /*****************************************************************//** Creates and initializes the central memory structures for the transaction system. This is called when the database is started. */ UNIV_INTERN void trx_sys_init_at_db_start( /*=====================*/ ib_recovery_t recovery); /*!< in: recovery flag */ /*****************************************************************//** Creates and initializes the transaction system at the database creation. */ UNIV_INTERN void trx_sys_create( /*===========*/ ib_recovery_t recovery); /*!< in: recovery flag */ /****************************************************************//** Looks for a free slot for a rollback segment in the trx system file copy. @return slot index or ULINT_UNDEFINED if not found */ UNIV_INTERN ulint trx_sysf_rseg_find_free( /*====================*/ mtr_t* mtr); /*!< in: mtr */ /***************************************************************//** Gets the pointer in the nth slot of the rseg array. @return pointer to rseg object, NULL if slot not in use */ UNIV_INLINE trx_rseg_t* trx_sys_get_nth_rseg( /*=================*/ trx_sys_t* sys, /*!< in: trx system */ ulint n); /*!< in: index of slot */ /***************************************************************//** Sets the pointer in the nth slot of the rseg array. */ UNIV_INLINE void trx_sys_set_nth_rseg( /*=================*/ trx_sys_t* sys, /*!< in: trx system */ ulint n, /*!< in: index of slot */ trx_rseg_t* rseg); /*!< in: pointer to rseg object, NULL if slot not in use */ /**********************************************************************//** Gets a pointer to the transaction system file copy and x-locks its page. @return pointer to system file copy, page x-locked */ UNIV_INLINE trx_sysf_t* trx_sysf_get( /*=========*/ mtr_t* mtr); /*!< in: mtr */ /*****************************************************************//** Gets the space of the nth rollback segment slot in the trx system file copy. @return space id */ UNIV_INLINE ulint trx_sysf_rseg_get_space( /*====================*/ trx_sysf_t* sys_header, /*!< in: trx sys file copy */ ulint i, /*!< in: slot index == rseg id */ mtr_t* mtr); /*!< in: mtr */ /*****************************************************************//** Gets the page number of the nth rollback segment slot in the trx system file copy. @return page number, FIL_NULL if slot unused */ UNIV_INLINE ulint trx_sysf_rseg_get_page_no( /*======================*/ trx_sysf_t* sys_header, /*!< in: trx sys file copy */ ulint i, /*!< in: slot index == rseg id */ mtr_t* mtr); /*!< in: mtr */ /*****************************************************************//** Sets the space id of the nth rollback segment slot in the trx system file copy. */ UNIV_INLINE void trx_sysf_rseg_set_space( /*====================*/ trx_sysf_t* sys_header, /*!< in: trx sys file copy */ ulint i, /*!< in: slot index == rseg id */ ulint space, /*!< in: space id */ mtr_t* mtr); /*!< in: mtr */ /*****************************************************************//** Sets the page number of the nth rollback segment slot in the trx system file copy. */ UNIV_INLINE void trx_sysf_rseg_set_page_no( /*======================*/ trx_sysf_t* sys_header, /*!< in: trx sys file copy */ ulint i, /*!< in: slot index == rseg id */ ulint page_no, /*!< in: page number, FIL_NULL if the slot is reset to unused */ mtr_t* mtr); /*!< in: mtr */ /*****************************************************************//** Allocates a new transaction id. @return new, allocated trx id */ UNIV_INLINE trx_id_t trx_sys_get_new_trx_id(void); /*========================*/ /*****************************************************************//** Allocates a new transaction number. @return new, allocated trx number */ UNIV_INLINE trx_id_t trx_sys_get_new_trx_no(void); /*========================*/ #endif /* !UNIV_HOTBACKUP */ /*****************************************************************//** Writes a trx id to an index page. In case that the id size changes in some future version, this function should be used instead of mach_write_... */ UNIV_INLINE void trx_write_trx_id( /*=============*/ byte* ptr, /*!< in: pointer to memory where written */ trx_id_t id); /*!< in: id */ #ifndef UNIV_HOTBACKUP /*****************************************************************//** Reads a trx id from an index page. In case that the id size changes in some future version, this function should be used instead of mach_read_... @return id */ UNIV_INLINE trx_id_t trx_read_trx_id( /*============*/ const byte* ptr); /*!< in: pointer to memory from where to read */ /****************************************************************//** Looks for the trx handle with the given id in trx_list. @return the trx handle or NULL if not found */ UNIV_INLINE trx_t* trx_get_on_id( /*==========*/ trx_id_t trx_id);/*!< in: trx id to search for */ /****************************************************************//** Returns the minumum trx id in trx list. This is the smallest id for which the trx can possibly be active. (But, you must look at the trx->conc_state to find out if the minimum trx id transaction itself is active, or already committed.) @return the minimum trx id, or trx_sys->max_trx_id if the trx list is empty */ UNIV_INLINE trx_id_t trx_list_get_min_trx_id(void); /*=========================*/ /****************************************************************//** Checks if a transaction with the given id is active. @return TRUE if active */ UNIV_INLINE ibool trx_is_active( /*==========*/ trx_id_t trx_id);/*!< in: trx id of the transaction */ /****************************************************************//** Checks that trx is in the trx list. @return TRUE if is in */ UNIV_INTERN ibool trx_in_trx_list( /*============*/ trx_t* in_trx);/*!< in: trx */ /*****************************************************************//** Initializes the tablespace tag system. */ UNIV_INTERN void trx_sys_file_format_init(void); /*==========================*/ /*****************************************************************//** Closes the tablespace tag system. */ UNIV_INTERN void trx_sys_file_format_close(void); /*===========================*/ /*******************************************************************//** Shutdown/Close the transaction system. */ UNIV_INTERN void trx_sys_close(void); /*===============*/ /********************************************************************//** Tags the system table space with minimum format id if it has not been tagged yet. WARNING: This function is only called during the startup and AFTER the redo log application during recovery has finished. */ UNIV_INTERN void trx_sys_file_format_tag_init(void); /*==============================*/ #ifndef UNIV_HOTBACKUP /*****************************************************************//** Shutdown/Close the transaction system. */ UNIV_INTERN void trx_sys_close(void); /*===============*/ #endif /* !UNIV_HOTBACKUP */ /*****************************************************************//** Get the name representation of the file format from its id. @return pointer to the name */ UNIV_INTERN const char* trx_sys_file_format_id_to_name( /*===========================*/ const ulint id); /*!< in: id of the file format */ /**************************************************************//** Validate the file format name and return its corresponding id. @return valid file format id or DICT_TF_FORMAT_MAX + 1 */ UNIV_INTERN ulint trx_sys_file_format_name_to_id( /*===========================*/ const char* format_name); /*!< in: pointer to file format name */ /*****************************************************************//** Set the file format id unconditionally except if it's already the same value. @return TRUE if value updated */ UNIV_INTERN ibool trx_sys_file_format_max_set( /*===========================*/ ulint format_id, /*!< in: file format id */ const char** name); /*!< out: max file format name or NULL if not needed. */ /*****************************************************************//** Get the name representation of the file format from its id. @return pointer to the max format name */ UNIV_INTERN const char* trx_sys_file_format_max_get(void); /*=============================*/ /*****************************************************************//** Check for the max file format tag stored on disk. @return DB_SUCCESS or error code */ UNIV_INTERN ulint trx_sys_file_format_max_check( /*==========================*/ ulint max_format_id); /*!< in: the max format id to check */ /********************************************************************//** Update the file format tag in the system tablespace only if the given format id is greater than the known max id. @return TRUE if format_id was bigger than the known max id */ UNIV_INTERN ibool trx_sys_file_format_max_upgrade( /*============================*/ const char** name, /*!< out: max file format name */ ulint format_id); /*!< in: file format identifier */ /******************************************************************//** Reset the variables. */ UNIV_INTERN void trx_sys_var_init(void); /*==================*/ /*****************************************************************//** Reads the file format id from the first system table space file. Even if the call succeeds and returns TRUE, the returned format id may be ULINT_UNDEFINED signalling that the format id was not present in the data file. @return TRUE if call succeeds */ UNIV_INTERN ibool trx_sys_read_file_format_id( /*========================*/ const char *pathname, /*!< in: pathname of the first system table space file */ ulint *format_id); /*!< out: file format of the system table space */ /*****************************************************************//** Reads the file format id from the given per-table data file. @return TRUE if call succeeds */ UNIV_INTERN ibool trx_sys_read_pertable_file_format_id( /*=================================*/ const char *pathname, /*!< in: pathname of a per-table datafile */ ulint *format_id); /*!< out: file format of the per-table data file */ /*****************************************************************//** Get the name representation of the file format from its id. @return pointer to the name */ UNIV_INTERN const char* trx_sys_file_format_id_to_name( /*===========================*/ const ulint id); /*!< in: id of the file format */ #endif /* !UNIV_HOTBACKUP */ /* The automatically created system rollback segment has this id */ #define TRX_SYS_SYSTEM_RSEG_ID 0 /* Space id and page no where the trx system file copy resides */ #define TRX_SYS_SPACE 0 /* the SYSTEM tablespace */ #include "fsp0fsp.h" #define TRX_SYS_PAGE_NO FSP_TRX_SYS_PAGE_NO /* The offset of the transaction system header on the page */ #define TRX_SYS FSEG_PAGE_DATA /** Transaction system header */ /*------------------------------------------------------------- @{ */ #define TRX_SYS_TRX_ID_STORE 0 /*!< the maximum trx id or trx number modulo TRX_SYS_TRX_ID_UPDATE_MARGIN written to a file page by any transaction; the assignment of transaction ids continues from this number rounded up by TRX_SYS_TRX_ID_UPDATE_MARGIN plus TRX_SYS_TRX_ID_UPDATE_MARGIN when the database is started */ #define TRX_SYS_FSEG_HEADER 8 /*!< segment header for the tablespace segment the trx system is created into */ #define TRX_SYS_RSEGS (8 + FSEG_HEADER_SIZE) /*!< the start of the array of rollback segment specification slots */ /*------------------------------------------------------------- @} */ /** Maximum number of rollback segments: the number of segment specification slots in the transaction system array; rollback segment id must fit in one byte, therefore 256; each slot is currently 8 bytes in size */ #define TRX_SYS_N_RSEGS 256 #if UNIV_PAGE_SIZE < 4096 # error "UNIV_PAGE_SIZE < 4096" #endif /** Doublewrite buffer */ /* @{ */ /** The offset of the doublewrite buffer header on the trx system header page */ #define TRX_SYS_DOUBLEWRITE (UNIV_PAGE_SIZE - 200) /*-------------------------------------------------------------*/ #define TRX_SYS_DOUBLEWRITE_FSEG 0 /*!< fseg header of the fseg containing the doublewrite buffer */ #define TRX_SYS_DOUBLEWRITE_MAGIC FSEG_HEADER_SIZE /*!< 4-byte magic number which shows if we already have created the doublewrite buffer */ #define TRX_SYS_DOUBLEWRITE_BLOCK1 (4 + FSEG_HEADER_SIZE) /*!< page number of the first page in the first sequence of 64 (= FSP_EXTENT_SIZE) consecutive pages in the doublewrite buffer */ #define TRX_SYS_DOUBLEWRITE_BLOCK2 (8 + FSEG_HEADER_SIZE) /*!< page number of the first page in the second sequence of 64 consecutive pages in the doublewrite buffer */ #define TRX_SYS_DOUBLEWRITE_REPEAT 12 /*!< we repeat TRX_SYS_DOUBLEWRITE_MAGIC, TRX_SYS_DOUBLEWRITE_BLOCK1, TRX_SYS_DOUBLEWRITE_BLOCK2 so that if the trx sys header is half-written to disk, we still may be able to recover the information */ /** If this is not yet set to TRX_SYS_DOUBLEWRITE_SPACE_ID_STORED_N, we must reset the doublewrite buffer, because starting from 4.1.x the space id of a data page is stored into FIL_PAGE_ARCH_LOG_NO_OR_SPACE_NO. */ #define TRX_SYS_DOUBLEWRITE_SPACE_ID_STORED (24 + FSEG_HEADER_SIZE) /*-------------------------------------------------------------*/ /** Contents of TRX_SYS_DOUBLEWRITE_MAGIC */ #define TRX_SYS_DOUBLEWRITE_MAGIC_N 536853855 /** Contents of TRX_SYS_DOUBLEWRITE_SPACE_ID_STORED */ #define TRX_SYS_DOUBLEWRITE_SPACE_ID_STORED_N 1783657386 /** Size of the doublewrite block in pages */ #define TRX_SYS_DOUBLEWRITE_BLOCK_SIZE FSP_EXTENT_SIZE /* @} */ #ifndef UNIV_HOTBACKUP /** File format tag */ /* @{ */ /** The offset of the file format tag on the trx system header page (TRX_SYS_PAGE_NO of TRX_SYS_SPACE) */ #define TRX_SYS_FILE_FORMAT_TAG (UNIV_PAGE_SIZE - 16) /** Contents of TRX_SYS_FILE_FORMAT_TAG when valid. The file format identifier is added to this constant. */ #define TRX_SYS_FILE_FORMAT_TAG_MAGIC_N_LOW 3645922177UL /** Contents of TRX_SYS_FILE_FORMAT_TAG+4 when valid */ #define TRX_SYS_FILE_FORMAT_TAG_MAGIC_N_HIGH 2745987765UL /* @} */ /** Doublewrite control struct */ struct trx_doublewrite_struct{ mutex_t mutex; /*!< mutex protecting the first_free field and write_buf */ ulint block1; /*!< the page number of the first doublewrite block (64 pages) */ ulint block2; /*!< page number of the second block */ ulint first_free; /*!< first free position in write_buf measured in units of UNIV_PAGE_SIZE */ byte* write_buf; /*!< write buffer used in writing to the doublewrite buffer, aligned to an address divisible by UNIV_PAGE_SIZE (which is required by Windows aio) */ byte* write_buf_unaligned; /*!< pointer to write_buf, but unaligned */ buf_page_t** buf_block_arr; /*!< array to store pointers to the buffer blocks which have been cached to write_buf */ }; /** The transaction system central memory data structure; protected by the kernel mutex */ struct trx_sys_struct{ trx_id_t max_trx_id; /*!< The smallest number not yet assigned as a transaction id or transaction number */ UT_LIST_BASE_NODE_T(trx_t) trx_list; /*!< List of active and committed in memory transactions, sorted on trx id, biggest first */ UT_LIST_BASE_NODE_T(trx_t) client_trx_list; /*!< List of transactions created for users */ UT_LIST_BASE_NODE_T(trx_rseg_t) rseg_list; /*!< List of rollback segment objects */ trx_rseg_t* latest_rseg; /*!< Latest rollback segment in the round-robin assignment of rollback segments to transactions */ trx_rseg_t* rseg_array[TRX_SYS_N_RSEGS]; /*!< Pointer array to rollback segments; NULL if slot not in use */ ulint rseg_history_len;/*!< Length of the TRX_RSEG_HISTORY list (update undo logs for committed transactions), protected by rseg->mutex */ UT_LIST_BASE_NODE_T(read_view_t) view_list; /*!< List of read views sorted on trx no, biggest first */ }; /** When a trx id which is zero modulo this number (which must be a power of two) is assigned, the field TRX_SYS_TRX_ID_STORE on the transaction system page is updated */ #define TRX_SYS_TRX_ID_WRITE_MARGIN 256 #endif /* !UNIV_HOTBACKUP */ #ifndef UNIV_NONINL #include "trx0sys.ic" #endif #endif haildb-2.3.2/include/pars0pars.h0000644000175000017500000006344611513177357017367 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/pars0pars.h SQL parser Created 11/19/1996 Heikki Tuuri *******************************************************/ #ifndef pars0pars_h #define pars0pars_h #include "univ.i" #include "que0types.h" #include "usr0types.h" #include "pars0types.h" #include "row0types.h" #include "trx0types.h" #include "ut0vec.h" /** Type of the user functions. The first argument is always InnoDB-supplied and varies in type, while 'user_arg' is a user-supplied argument. The meaning of the return type also varies. See the individual use cases, e.g. the FETCH statement, for details on them. */ typedef void* (*pars_user_func_cb_t)(void* arg, void* user_arg); /** If the following is set TRUE, the parser will emit debugging information */ extern int yydebug; #ifdef UNIV_SQL_DEBUG /** If the following is set TRUE, the lexer will print the SQL string as it tokenizes it */ extern ibool pars_print_lexed; #endif /* UNIV_SQL_DEBUG */ /* Global variable used while parsing a single procedure or query : the code is NOT re-entrant */ extern sym_tab_t* pars_sym_tab_global; extern pars_res_word_t pars_to_char_token; extern pars_res_word_t pars_to_number_token; extern pars_res_word_t pars_to_binary_token; extern pars_res_word_t pars_binary_to_number_token; extern pars_res_word_t pars_substr_token; extern pars_res_word_t pars_replstr_token; extern pars_res_word_t pars_concat_token; extern pars_res_word_t pars_length_token; extern pars_res_word_t pars_instr_token; extern pars_res_word_t pars_sysdate_token; extern pars_res_word_t pars_printf_token; extern pars_res_word_t pars_assert_token; extern pars_res_word_t pars_rnd_token; extern pars_res_word_t pars_rnd_str_token; extern pars_res_word_t pars_count_token; extern pars_res_word_t pars_sum_token; extern pars_res_word_t pars_distinct_token; extern pars_res_word_t pars_binary_token; extern pars_res_word_t pars_blob_token; extern pars_res_word_t pars_int_token; extern pars_res_word_t pars_char_token; extern pars_res_word_t pars_float_token; extern pars_res_word_t pars_update_token; extern pars_res_word_t pars_asc_token; extern pars_res_word_t pars_desc_token; extern pars_res_word_t pars_open_token; extern pars_res_word_t pars_close_token; extern pars_res_word_t pars_share_token; extern pars_res_word_t pars_unique_token; extern pars_res_word_t pars_clustered_token; extern ulint pars_star_denoter; /* Procedure parameter types */ #define PARS_INPUT 0 #define PARS_OUTPUT 1 #define PARS_NOT_PARAM 2 int yyparse(void); /*************************************************************//** Parses an SQL string returning the query graph. @return own: the query graph */ UNIV_INTERN que_t* pars_sql( /*=====*/ pars_info_t* info, /*!< in: extra information, or NULL */ const char* str); /*!< in: SQL string */ /*************************************************************//** Retrieves characters to the lexical analyzer. */ UNIV_INTERN void pars_get_lex_chars( /*===============*/ char* buf, /*!< in/out: buffer where to copy */ int* result, /*!< out: number of characters copied or EOF */ int max_size); /*!< in: maximum number of characters which fit in the buffer */ /*************************************************************//** Called by yyparse on error. */ UNIV_INTERN void yyerror( /*====*/ const char* s); /*!< in: error message string */ /*********************************************************************//** Parses a variable declaration. @return own: symbol table node of type SYM_VAR */ UNIV_INTERN sym_node_t* pars_variable_declaration( /*======================*/ sym_node_t* node, /*!< in: symbol table node allocated for the id of the variable */ pars_res_word_t* type); /*!< in: pointer to a type token */ /*********************************************************************//** Parses a function expression. @return own: function node in a query tree */ UNIV_INTERN func_node_t* pars_func( /*======*/ que_node_t* res_word,/*!< in: function name reserved word */ que_node_t* arg); /*!< in: first argument in the argument list */ /*********************************************************************//** Parses an operator expression. @return own: function node in a query tree */ UNIV_INTERN func_node_t* pars_op( /*====*/ int func, /*!< in: operator token code */ que_node_t* arg1, /*!< in: first argument */ que_node_t* arg2); /*!< in: second argument or NULL for an unary operator */ /*********************************************************************//** Parses an ORDER BY clause. Order by a single column only is supported. @return own: order-by node in a query tree */ UNIV_INTERN order_node_t* pars_order_by( /*==========*/ sym_node_t* column, /*!< in: column name */ pars_res_word_t* asc); /*!< in: &pars_asc_token or pars_desc_token */ /*********************************************************************//** Parses a select list; creates a query graph node for the whole SELECT statement. @return own: select node in a query tree */ UNIV_INTERN sel_node_t* pars_select_list( /*=============*/ que_node_t* select_list, /*!< in: select list */ sym_node_t* into_list); /*!< in: variables list or NULL */ /*********************************************************************//** Parses a cursor declaration. @return sym_node */ UNIV_INTERN que_node_t* pars_cursor_declaration( /*====================*/ sym_node_t* sym_node, /*!< in: cursor id node in the symbol table */ sel_node_t* select_node); /*!< in: select node */ /*********************************************************************//** Parses a function declaration. @return sym_node */ UNIV_INTERN que_node_t* pars_function_declaration( /*======================*/ sym_node_t* sym_node); /*!< in: function id node in the symbol table */ /*********************************************************************//** Parses a select statement. @return own: select node in a query tree */ UNIV_INTERN sel_node_t* pars_select_statement( /*==================*/ sel_node_t* select_node, /*!< in: select node already containing the select list */ sym_node_t* table_list, /*!< in: table list */ que_node_t* search_cond, /*!< in: search condition or NULL */ pars_res_word_t* for_update, /*!< in: NULL or &pars_update_token */ pars_res_word_t* consistent_read,/*!< in: NULL or &pars_consistent_token */ order_node_t* order_by); /*!< in: NULL or an order-by node */ /*********************************************************************//** Parses a column assignment in an update. @return column assignment node */ UNIV_INTERN col_assign_node_t* pars_column_assignment( /*===================*/ sym_node_t* column, /*!< in: column to assign */ que_node_t* exp); /*!< in: value to assign */ /*********************************************************************//** Parses a delete or update statement start. @return own: update node in a query tree */ UNIV_INTERN upd_node_t* pars_update_statement_start( /*========================*/ ibool is_delete, /*!< in: TRUE if delete */ sym_node_t* table_sym, /*!< in: table name node */ col_assign_node_t* col_assign_list);/*!< in: column assignment list, NULL if delete */ /*********************************************************************//** Parses an update or delete statement. @return own: update node in a query tree */ UNIV_INTERN upd_node_t* pars_update_statement( /*==================*/ upd_node_t* node, /*!< in: update node */ sym_node_t* cursor_sym, /*!< in: pointer to a cursor entry in the symbol table or NULL */ que_node_t* search_cond); /*!< in: search condition or NULL */ /*********************************************************************//** Parses an insert statement. @return own: update node in a query tree */ UNIV_INTERN ins_node_t* pars_insert_statement( /*==================*/ sym_node_t* table_sym, /*!< in: table name node */ que_node_t* values_list, /*!< in: value expression list or NULL */ sel_node_t* select); /*!< in: select condition or NULL */ /*********************************************************************//** Parses a procedure parameter declaration. @return own: symbol table node of type SYM_VAR */ UNIV_INTERN sym_node_t* pars_parameter_declaration( /*=======================*/ sym_node_t* node, /*!< in: symbol table node allocated for the id of the parameter */ ulint param_type, /*!< in: PARS_INPUT or PARS_OUTPUT */ pars_res_word_t* type); /*!< in: pointer to a type token */ /*********************************************************************//** Parses an elsif element. @return elsif node */ UNIV_INTERN elsif_node_t* pars_elsif_element( /*===============*/ que_node_t* cond, /*!< in: if-condition */ que_node_t* stat_list); /*!< in: statement list */ /*********************************************************************//** Parses an if-statement. @return if-statement node */ UNIV_INTERN if_node_t* pars_if_statement( /*==============*/ que_node_t* cond, /*!< in: if-condition */ que_node_t* stat_list, /*!< in: statement list */ que_node_t* else_part); /*!< in: else-part statement list */ /*********************************************************************//** Parses a for-loop-statement. @return for-statement node */ UNIV_INTERN for_node_t* pars_for_statement( /*===============*/ sym_node_t* loop_var, /*!< in: loop variable */ que_node_t* loop_start_limit,/*!< in: loop start expression */ que_node_t* loop_end_limit, /*!< in: loop end expression */ que_node_t* stat_list); /*!< in: statement list */ /*********************************************************************//** Parses a while-statement. @return while-statement node */ UNIV_INTERN while_node_t* pars_while_statement( /*=================*/ que_node_t* cond, /*!< in: while-condition */ que_node_t* stat_list); /*!< in: statement list */ /*********************************************************************//** Parses an exit statement. @return exit statement node */ UNIV_INTERN exit_node_t* pars_exit_statement(void); /*=====================*/ /*********************************************************************//** Parses a return-statement. @return return-statement node */ UNIV_INTERN return_node_t* pars_return_statement(void); /*=======================*/ /*********************************************************************//** Parses a procedure call. @return function node */ UNIV_INTERN func_node_t* pars_procedure_call( /*================*/ que_node_t* res_word,/*!< in: procedure name reserved word */ que_node_t* args); /*!< in: argument list */ /*********************************************************************//** Parses an assignment statement. @return assignment statement node */ UNIV_INTERN assign_node_t* pars_assignment_statement( /*======================*/ sym_node_t* var, /*!< in: variable to assign */ que_node_t* val); /*!< in: value to assign */ /*********************************************************************//** Parses a fetch statement. into_list or user_func (but not both) must be non-NULL. @return fetch statement node */ UNIV_INTERN fetch_node_t* pars_fetch_statement( /*=================*/ sym_node_t* cursor, /*!< in: cursor node */ sym_node_t* into_list, /*!< in: variables to set, or NULL */ sym_node_t* user_func); /*!< in: user function name, or NULL */ /*********************************************************************//** Parses an open or close cursor statement. @return fetch statement node */ UNIV_INTERN open_node_t* pars_open_statement( /*================*/ ulint type, /*!< in: ROW_SEL_OPEN_CURSOR or ROW_SEL_CLOSE_CURSOR */ sym_node_t* cursor); /*!< in: cursor node */ /*********************************************************************//** Parses a row_printf-statement. @return row_printf-statement node */ UNIV_INTERN row_printf_node_t* pars_row_printf_statement( /*======================*/ sel_node_t* sel_node); /*!< in: select node */ /*********************************************************************//** Parses a commit statement. @return own: commit node struct */ UNIV_INTERN commit_node_t* pars_commit_statement(void); /*=======================*/ /*********************************************************************//** Parses a rollback statement. @return own: rollback node struct */ UNIV_INTERN roll_node_t* pars_rollback_statement(void); /*=========================*/ /*********************************************************************//** Parses a column definition at a table creation. @return column sym table node */ UNIV_INTERN sym_node_t* pars_column_def( /*============*/ sym_node_t* sym_node, /*!< in: column node in the symbol table */ pars_res_word_t* type, /*!< in: data type */ sym_node_t* len, /*!< in: length of column, or NULL */ void* is_unsigned, /*!< in: if not NULL, column is of type UNSIGNED. */ void* is_not_null); /*!< in: if not NULL, column is of type NOT NULL. */ /*********************************************************************//** Parses a table creation operation. @return table create subgraph */ UNIV_INTERN tab_node_t* pars_create_table( /*==============*/ sym_node_t* table_sym, /*!< in: table name node in the symbol table */ sym_node_t* column_defs, /*!< in: list of column names */ void* not_fit_in_memory);/*!< in: a non-NULL pointer means that this is a table which in simulations should be simulated as not fitting in memory; thread is put to sleep to simulate disk accesses; NOTE that this flag is not stored to the data dictionary on disk, and the database will forget about non-NULL value if it has to reload the table definition from disk */ /*********************************************************************//** Parses an index creation operation. @return index create subgraph */ UNIV_INTERN ind_node_t* pars_create_index( /*==============*/ pars_res_word_t* unique_def, /*!< in: not NULL if a unique index */ pars_res_word_t* clustered_def, /*!< in: not NULL if a clustered index */ sym_node_t* index_sym, /*!< in: index name node in the symbol table */ sym_node_t* table_sym, /*!< in: table name node in the symbol table */ sym_node_t* column_list); /*!< in: list of column names */ /*********************************************************************//** Parses a procedure definition. @return query fork node */ UNIV_INTERN que_fork_t* pars_procedure_definition( /*======================*/ sym_node_t* sym_node, /*!< in: procedure id node in the symbol table */ sym_node_t* param_list, /*!< in: parameter declaration list */ que_node_t* stat_list); /*!< in: statement list */ /*************************************************************//** Parses a stored procedure call, when this is not within another stored procedure, that is, the client issues a procedure call directly. In InnoDB, stored InnoDB procedures are invoked via the parsed procedure tree, not via InnoDB SQL, so this function is not used. @return query graph */ UNIV_INTERN que_fork_t* pars_stored_procedure_call( /*=======================*/ sym_node_t* sym_node); /*!< in: stored procedure name */ /******************************************************************//** Completes a query graph by adding query thread and fork nodes above it and prepares the graph for running. The fork created is of type QUE_FORK_USER_INTERFACE. @return query thread node to run */ UNIV_INTERN que_thr_t* pars_complete_graph_for_exec( /*=========================*/ que_node_t* node, /*!< in: root node for an incomplete query graph */ trx_t* trx, /*!< in: transaction handle */ mem_heap_t* heap); /*!< in: memory heap from which allocated */ /****************************************************************//** Create parser info struct. @return own: info struct */ UNIV_INTERN pars_info_t* pars_info_create(void); /*==================*/ /****************************************************************//** Free info struct and everything it contains. */ UNIV_INTERN void pars_info_free( /*===========*/ pars_info_t* info); /*!< in, own: info struct */ /****************************************************************//** Add bound literal. */ UNIV_INTERN void pars_info_add_literal( /*==================*/ pars_info_t* info, /*!< in: info struct */ const char* name, /*!< in: name */ const void* address, /*!< in: address */ ulint length, /*!< in: length of data */ ulint type, /*!< in: type, e.g. DATA_FIXBINARY */ ulint prtype); /*!< in: precise type, e.g. DATA_UNSIGNED */ /****************************************************************//** Equivalent to pars_info_add_literal(info, name, str, strlen(str), DATA_VARCHAR, DATA_ENGLISH). */ UNIV_INTERN void pars_info_add_str_literal( /*======================*/ pars_info_t* info, /*!< in: info struct */ const char* name, /*!< in: name */ const char* str); /*!< in: string */ /****************************************************************//** Equivalent to: char buf[4]; mach_write_to_4(buf, val); pars_info_add_literal(info, name, buf, 4, DATA_INT, 0); except that the buffer is dynamically allocated from the info struct's heap. */ UNIV_INTERN void pars_info_add_int4_literal( /*=======================*/ pars_info_t* info, /*!< in: info struct */ const char* name, /*!< in: name */ lint val); /*!< in: value */ /****************************************************************//** Equivalent to: char buf[8]; mach_write_ull(buf, val); pars_info_add_literal(info, name, buf, 8, DATA_INT, 0); except that the buffer is dynamically allocated from the info struct's heap. */ UNIV_INTERN void pars_info_add_int8_literal( /*=======================*/ pars_info_t* info, /*!< in: info struct */ const char* name, /*!< in: name */ ib_uint64_t val); /*!< in: value */ /******************************************************************** Equivalent to: char buf[8]; mach_write_to_8(buf, val); pars_info_add_literal(info, name, buf, 8, DATA_BINARY, 0); except that the buffer is dynamically allocated from the info struct's heap. */ UNIV_INTERN void pars_info_add_dulint_literal( /*=========================*/ pars_info_t* info, /*!< in: info struct */ const char* name, /*!< in: name */ dulint val); /*!< in: value */ /****************************************************************//** Add user function. */ UNIV_INTERN void pars_info_add_function( /*===================*/ pars_info_t* info, /*!< in: info struct */ const char* name, /*!< in: function name */ pars_user_func_cb_t func, /*!< in: function address */ void* arg); /*!< in: user-supplied argument */ /****************************************************************//** Add bound id. */ UNIV_INTERN void pars_info_add_id( /*=============*/ pars_info_t* info, /*!< in: info struct */ const char* name, /*!< in: name */ const char* id); /*!< in: id */ /****************************************************************//** Get user function with the given name. @return user func, or NULL if not found */ UNIV_INTERN pars_user_func_t* pars_info_get_user_func( /*====================*/ pars_info_t* info, /*!< in: info struct */ const char* name); /*!< in: function name to find*/ /****************************************************************//** Get bound literal with the given name. @return bound literal, or NULL if not found */ UNIV_INTERN pars_bound_lit_t* pars_info_get_bound_lit( /*====================*/ pars_info_t* info, /*!< in: info struct */ const char* name); /*!< in: bound literal name to find */ /****************************************************************//** Get bound id with the given name. @return bound id, or NULL if not found */ UNIV_INTERN pars_bound_id_t* pars_info_get_bound_id( /*===================*/ pars_info_t* info, /*!< in: info struct */ const char* name); /*!< in: bound id name to find */ /********************************************************************** Release any resources used by the parser and lexer. */ UNIV_INTERN void pars_close(void); /*=============*/ /***********************************************************************//** Reset and check parser variables. */ UNIV_INTERN void pars_var_init(void); /*===============*/ /***********************************************************************//** Reset the lexing variables. */ UNIV_INTERN void pars_lexer_var_init(void); /*=====================*/ /********************************************************************//** Release any resources used by the lexer. */ UNIV_INTERN void pars_lexer_close(void); /*==================*/ /** Extra information supplied for pars_sql(). */ struct pars_info_struct { mem_heap_t* heap; /*!< our own memory heap */ ib_vector_t* funcs; /*!< user functions, or NUll (pars_user_func_t*) */ ib_vector_t* bound_lits; /*!< bound literals, or NULL (pars_bound_lit_t*) */ ib_vector_t* bound_ids; /*!< bound ids, or NULL (pars_bound_id_t*) */ ibool graph_owns_us; /*!< if TRUE (which is the default), que_graph_free() will free us */ }; /** User-supplied function and argument. */ struct pars_user_func_struct { const char* name; /*!< function name */ pars_user_func_cb_t func; /*!< function address */ void* arg; /*!< user-supplied argument */ }; /** Bound literal. */ struct pars_bound_lit_struct { const char* name; /*!< name */ const void* address; /*!< address */ ulint length; /*!< length of data */ ulint type; /*!< type, e.g. DATA_FIXBINARY */ ulint prtype; /*!< precise type, e.g. DATA_UNSIGNED */ }; /** Bound identifier. */ struct pars_bound_id_struct { const char* name; /*!< name */ const char* id; /*!< identifier */ }; /** Struct used to denote a reserved word in a parsing tree */ struct pars_res_word_struct{ int code; /*!< the token code for the reserved word from pars0grm.h */ }; /** A predefined function or operator node in a parsing tree; this construct is also used for some non-functions like the assignment ':=' */ struct func_node_struct{ que_common_t common; /*!< type: QUE_NODE_FUNC */ int func; /*!< token code of the function name */ ulint class; /*!< class of the function */ que_node_t* args; /*!< argument(s) of the function */ UT_LIST_NODE_T(func_node_t) cond_list; /*!< list of comparison conditions; defined only for comparison operator nodes except, presently, for OPT_SCROLL_TYPE ones */ UT_LIST_NODE_T(func_node_t) func_node_list; /*!< list of function nodes in a parsed query graph */ }; /** An order-by node in a select */ struct order_node_struct{ que_common_t common; /*!< type: QUE_NODE_ORDER */ sym_node_t* column; /*!< order-by column */ ibool asc; /*!< TRUE if ascending, FALSE if descending */ }; /** Procedure definition node */ struct proc_node_struct{ que_common_t common; /*!< type: QUE_NODE_PROC */ sym_node_t* proc_id; /*!< procedure name symbol in the symbol table of this same procedure */ sym_node_t* param_list; /*!< input and output parameters */ que_node_t* stat_list; /*!< statement list */ sym_tab_t* sym_tab; /*!< symbol table of this procedure */ }; /** elsif-element node */ struct elsif_node_struct{ que_common_t common; /*!< type: QUE_NODE_ELSIF */ que_node_t* cond; /*!< if condition */ que_node_t* stat_list; /*!< statement list */ }; /** if-statement node */ struct if_node_struct{ que_common_t common; /*!< type: QUE_NODE_IF */ que_node_t* cond; /*!< if condition */ que_node_t* stat_list; /*!< statement list */ que_node_t* else_part; /*!< else-part statement list */ elsif_node_t* elsif_list; /*!< elsif element list */ }; /** while-statement node */ struct while_node_struct{ que_common_t common; /*!< type: QUE_NODE_WHILE */ que_node_t* cond; /*!< while condition */ que_node_t* stat_list; /*!< statement list */ }; /** for-loop-statement node */ struct for_node_struct{ que_common_t common; /*!< type: QUE_NODE_FOR */ sym_node_t* loop_var; /*!< loop variable: this is the dereferenced symbol from the variable declarations, not the symbol occurrence in the for loop definition */ que_node_t* loop_start_limit;/*!< initial value of loop variable */ que_node_t* loop_end_limit; /*!< end value of loop variable */ lint loop_end_value; /*!< evaluated value for the end value: it is calculated only when the loop is entered, and will not change within the loop */ que_node_t* stat_list; /*!< statement list */ }; /** exit statement node */ struct exit_node_struct{ que_common_t common; /*!< type: QUE_NODE_EXIT */ }; /** return-statement node */ struct return_node_struct{ que_common_t common; /*!< type: QUE_NODE_RETURN */ }; /** Assignment statement node */ struct assign_node_struct{ que_common_t common; /*!< type: QUE_NODE_ASSIGNMENT */ sym_node_t* var; /*!< variable to set */ que_node_t* val; /*!< value to assign */ }; /** Column assignment node */ struct col_assign_node_struct{ que_common_t common; /*!< type: QUE_NODE_COL_ASSIGN */ sym_node_t* col; /*!< column to set */ que_node_t* val; /*!< value to assign */ }; /** Classes of functions */ /* @{ */ #define PARS_FUNC_ARITH 1 /*!< +, -, *, / */ #define PARS_FUNC_LOGICAL 2 /*!< AND, OR, NOT */ #define PARS_FUNC_CMP 3 /*!< comparison operators */ #define PARS_FUNC_PREDEFINED 4 /*!< TO_NUMBER, SUBSTR, ... */ #define PARS_FUNC_AGGREGATE 5 /*!< COUNT, DISTINCT, SUM */ #define PARS_FUNC_OTHER 6 /*!< these are not real functions, e.g., := */ /* @} */ #ifndef UNIV_NONINL #include "pars0pars.ic" #endif #endif haildb-2.3.2/include/ha0storage.h0000644000175000017500000001237411513177357017503 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 2007, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/ha0storage.h Hash storage. Provides a data structure that stores chunks of data in its own storage, avoiding duplicates. Created September 22, 2007 Vasil Dimov *******************************************************/ #ifndef ha0storage_h #define ha0storage_h #include "univ.i" /** This value is used by default by ha_storage_create(). More memory is allocated later when/if it is needed. */ #define HA_STORAGE_DEFAULT_HEAP_BYTES 1024 /** This value is used by default by ha_storage_create(). It is a constant per ha_storage's lifetime. */ #define HA_STORAGE_DEFAULT_HASH_CELLS 4096 /** Hash storage */ typedef struct ha_storage_struct ha_storage_t; /*******************************************************************//** Creates a hash storage. If any of the parameters is 0, then a default value is used. @return own: hash storage */ UNIV_INLINE ha_storage_t* ha_storage_create( /*==============*/ ulint initial_heap_bytes, /*!< in: initial heap's size */ ulint initial_hash_cells); /*!< in: initial number of cells in the hash table */ /*******************************************************************//** Copies data into the storage and returns a pointer to the copy. If the same data chunk is already present, then pointer to it is returned. Data chunks are considered to be equal if len1 == len2 and memcmp(data1, data2, len1) == 0. If "data" is not present (and thus data_len bytes need to be allocated) and the size of storage is going to become more than "memlim" then "data" is not added and NULL is returned. To disable this behavior "memlim" can be set to 0, which stands for "no limit". @return pointer to the copy */ UNIV_INTERN const void* ha_storage_put_memlim( /*==================*/ ha_storage_t* storage, /*!< in/out: hash storage */ const void* data, /*!< in: data to store */ ulint data_len, /*!< in: data length */ ulint memlim); /*!< in: memory limit to obey */ /*******************************************************************//** Same as ha_storage_put_memlim() but without memory limit. @param storage in/out: hash storage @param data in: data to store @param data_len in: data length @return pointer to the copy of the string */ #define ha_storage_put(storage, data, data_len) \ ha_storage_put_memlim((storage), (data), (data_len), 0) /*******************************************************************//** Copies string into the storage and returns a pointer to the copy. If the same string is already present, then pointer to it is returned. Strings are considered to be equal if strcmp(str1, str2) == 0. @param storage in/out: hash storage @param str in: string to put @return pointer to the copy of the string */ #define ha_storage_put_str(storage, str) \ ((const char*) ha_storage_put((storage), (str), strlen(str) + 1)) /*******************************************************************//** Copies string into the storage and returns a pointer to the copy obeying a memory limit. If the same string is already present, then pointer to it is returned. Strings are considered to be equal if strcmp(str1, str2) == 0. @param storage in/out: hash storage @param str in: string to put @param memlim in: memory limit to obey @return pointer to the copy of the string */ #define ha_storage_put_str_memlim(storage, str, memlim) \ ((const char*) ha_storage_put_memlim((storage), (str), \ strlen(str) + 1, (memlim))) /*******************************************************************//** Empties a hash storage, freeing memory occupied by data chunks. This invalidates any pointers previously returned by ha_storage_put(). The hash storage is not invalidated itself and can be used again. */ UNIV_INLINE void ha_storage_empty( /*=============*/ ha_storage_t** storage); /*!< in/out: hash storage */ /*******************************************************************//** Frees a hash storage and everything it contains, it cannot be used after this call. This invalidates any pointers previously returned by ha_storage_put(). */ UNIV_INLINE void ha_storage_free( /*============*/ ha_storage_t* storage); /*!< in, own: hash storage */ /*******************************************************************//** Gets the size of the memory used by a storage. @return bytes used */ UNIV_INLINE ulint ha_storage_get_size( /*================*/ const ha_storage_t* storage); /*!< in: hash storage */ #ifndef UNIV_NONINL #include "ha0storage.ic" #endif #endif /* ha0storage_h */ haildb-2.3.2/include/mach0data.ic0000644000175000017500000005501611513177357017434 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /******************************************************************//** @file include/mach0data.ic Utilities for converting data from the database file to the machine format. Created 11/28/1995 Heikki Tuuri ***********************************************************************/ #include "ut0mem.h" /*******************************************************//** The following function is used to store data in one byte. */ UNIV_INLINE void mach_write_to_1( /*============*/ byte* b, /*!< in: pointer to byte where to store */ ulint n) /*!< in: ulint integer to be stored, >= 0, < 256 */ { ut_ad(b); ut_ad(n <= 0xFFUL); b[0] = (byte)n; } /********************************************************//** The following function is used to fetch data from one byte. @return ulint integer, >= 0, < 256 */ UNIV_INLINE ulint mach_read_from_1( /*=============*/ const byte* b) /*!< in: pointer to byte */ { ut_ad(b); return((ulint)(b[0])); } /*******************************************************//** The following function is used to store data in two consecutive bytes. We store the most significant byte to the lowest address. */ UNIV_INLINE void mach_write_to_2( /*============*/ byte* b, /*!< in: pointer to two bytes where to store */ ulint n) /*!< in: ulint integer to be stored */ { ut_ad(b); ut_ad(n <= 0xFFFFUL); b[0] = (byte)(n >> 8); b[1] = (byte)(n); } /********************************************************//** The following function is used to fetch data from 2 consecutive bytes. The most significant byte is at the lowest address. @return ulint integer */ UNIV_INLINE ulint mach_read_from_2( /*=============*/ const byte* b) /*!< in: pointer to 2 bytes */ { ut_ad(b); return( ((ulint)(b[0]) << 8) + (ulint)(b[1]) ); } /********************************************************//** The following function is used to convert a 16-bit data item to the canonical format, for fast bytewise equality test against memory. @return 16-bit integer in canonical format */ UNIV_INLINE ib_uint16_t mach_encode_2( /*==========*/ ulint n) /*!< in: integer in machine-dependent format */ { ib_uint16_t ret; ut_ad(2 == sizeof ret); mach_write_to_2((byte*) &ret, n); return(ret); } /********************************************************//** The following function is used to convert a 16-bit data item from the canonical format, for fast bytewise equality test against memory. @return integer in machine-dependent format */ UNIV_INLINE ulint mach_decode_2( /*==========*/ ib_uint16_t n) /*!< in: 16-bit integer in canonical format */ { ut_ad(2 == sizeof n); return(mach_read_from_2((const byte*) &n)); } /*******************************************************//** The following function is used to store data in 3 consecutive bytes. We store the most significant byte to the lowest address. */ UNIV_INLINE void mach_write_to_3( /*============*/ byte* b, /*!< in: pointer to 3 bytes where to store */ ulint n) /*!< in: ulint integer to be stored */ { ut_ad(b); ut_ad(n <= 0xFFFFFFUL); b[0] = (byte)(n >> 16); b[1] = (byte)(n >> 8); b[2] = (byte)(n); } /********************************************************//** The following function is used to fetch data from 3 consecutive bytes. The most significant byte is at the lowest address. @return ulint integer */ UNIV_INLINE ulint mach_read_from_3( /*=============*/ const byte* b) /*!< in: pointer to 3 bytes */ { ut_ad(b); return( ((ulint)(b[0]) << 16) + ((ulint)(b[1]) << 8) + (ulint)(b[2]) ); } /*******************************************************//** The following function is used to store data in four consecutive bytes. We store the most significant byte to the lowest address. */ UNIV_INLINE void mach_write_to_4( /*============*/ byte* b, /*!< in: pointer to four bytes where to store */ ulint n) /*!< in: ulint integer to be stored */ { ut_ad(b); b[0] = (byte)(n >> 24); b[1] = (byte)(n >> 16); b[2] = (byte)(n >> 8); b[3] = (byte)n; } /********************************************************//** The following function is used to fetch data from 4 consecutive bytes. The most significant byte is at the lowest address. @return ulint integer */ UNIV_INLINE ulint mach_read_from_4( /*=============*/ const byte* b) /*!< in: pointer to four bytes */ { ut_ad(b); return( ((ulint)(b[0]) << 24) + ((ulint)(b[1]) << 16) + ((ulint)(b[2]) << 8) + (ulint)(b[3]) ); } /*********************************************************//** Writes a ulint in a compressed form where the first byte codes the length of the stored ulint. We look at the most significant bits of the byte. If the most significant bit is zero, it means 1-byte storage, else if the 2nd bit is 0, it means 2-byte storage, else if 3rd is 0, it means 3-byte storage, else if 4th is 0, it means 4-byte storage, else the storage is 5-byte. @return compressed size in bytes */ UNIV_INLINE ulint mach_write_compressed( /*==================*/ byte* b, /*!< in: pointer to memory where to store */ ulint n) /*!< in: ulint integer (< 2^32) to be stored */ { ut_ad(b); if (n < 0x80UL) { mach_write_to_1(b, n); return(1); } else if (n < 0x4000UL) { mach_write_to_2(b, n | 0x8000UL); return(2); } else if (n < 0x200000UL) { mach_write_to_3(b, n | 0xC00000UL); return(3); } else if (n < 0x10000000UL) { mach_write_to_4(b, n | 0xE0000000UL); return(4); } else { mach_write_to_1(b, 0xF0UL); mach_write_to_4(b + 1, n); return(5); } } /*********************************************************//** Returns the size of a ulint when written in the compressed form. @return compressed size in bytes */ UNIV_INLINE ulint mach_get_compressed_size( /*=====================*/ ulint n) /*!< in: ulint integer (< 2^32) to be stored */ { if (n < 0x80UL) { return(1); } else if (n < 0x4000UL) { return(2); } else if (n < 0x200000UL) { return(3); } else if (n < 0x10000000UL) { return(4); } else { return(5); } } /*********************************************************//** Reads a ulint in a compressed form. @return read integer (< 2^32) */ UNIV_INLINE ulint mach_read_compressed( /*=================*/ const byte* b) /*!< in: pointer to memory from where to read */ { ulint flag; ut_ad(b); flag = mach_read_from_1(b); if (flag < 0x80UL) { return(flag); } else if (flag < 0xC0UL) { return(mach_read_from_2(b) & 0x7FFFUL); } else if (flag < 0xE0UL) { return(mach_read_from_3(b) & 0x3FFFFFUL); } else if (flag < 0xF0UL) { return(mach_read_from_4(b) & 0x1FFFFFFFUL); } else { ut_ad(flag == 0xF0UL); return(mach_read_from_4(b + 1)); } } /*******************************************************//** The following function is used to store data in 8 consecutive bytes. We store the most significant byte to the lowest address. */ UNIV_INLINE void mach_write_to_8( /*============*/ byte* b, /*!< in: pointer to 8 bytes where to store */ dulint n) /*!< in: dulint integer to be stored */ { ut_ad(b); mach_write_to_4(b, ut_dulint_get_high(n)); mach_write_to_4(b + 4, ut_dulint_get_low(n)); } /*******************************************************//** The following function is used to store data in 8 consecutive bytes. We store the most significant byte to the lowest address. */ UNIV_INLINE void mach_write_ull( /*===========*/ byte* b, /*!< in: pointer to 8 bytes where to store */ ib_uint64_t n) /*!< in: 64-bit integer to be stored */ { ut_ad(b); mach_write_to_4(b, (ulint) (n >> 32)); mach_write_to_4(b + 4, (ulint) n); } /********************************************************//** The following function is used to fetch data from 8 consecutive bytes. The most significant byte is at the lowest address. @return dulint integer */ UNIV_INLINE dulint mach_read_from_8( /*=============*/ const byte* b) /*!< in: pointer to 8 bytes */ { ulint high; ulint low; ut_ad(b); high = mach_read_from_4(b); low = mach_read_from_4(b + 4); return(ut_dulint_create(high, low)); } /********************************************************//** The following function is used to fetch data from 8 consecutive bytes. The most significant byte is at the lowest address. @return 64-bit integer */ UNIV_INLINE ib_uint64_t mach_read_ull( /*==========*/ const byte* b) /*!< in: pointer to 8 bytes */ { ib_uint64_t ull; ull = ((ib_uint64_t) mach_read_from_4(b)) << 32; ull |= (ib_uint64_t) mach_read_from_4(b + 4); return(ull); } /*******************************************************//** The following function is used to store data in 7 consecutive bytes. We store the most significant byte to the lowest address. */ UNIV_INLINE void mach_write_to_7( /*============*/ byte* b, /*!< in: pointer to 7 bytes where to store */ dulint n) /*!< in: dulint integer to be stored */ { ut_ad(b); mach_write_to_3(b, ut_dulint_get_high(n)); mach_write_to_4(b + 3, ut_dulint_get_low(n)); } /********************************************************//** The following function is used to fetch data from 7 consecutive bytes. The most significant byte is at the lowest address. @return dulint integer */ UNIV_INLINE dulint mach_read_from_7( /*=============*/ const byte* b) /*!< in: pointer to 7 bytes */ { ulint high; ulint low; ut_ad(b); high = mach_read_from_3(b); low = mach_read_from_4(b + 3); return(ut_dulint_create(high, low)); } /*******************************************************//** The following function is used to store data in 6 consecutive bytes. We store the most significant byte to the lowest address. */ UNIV_INLINE void mach_write_to_6( /*============*/ byte* b, /*!< in: pointer to 6 bytes where to store */ dulint n) /*!< in: dulint integer to be stored */ { ut_ad(b); mach_write_to_2(b, ut_dulint_get_high(n)); mach_write_to_4(b + 2, ut_dulint_get_low(n)); } /********************************************************//** The following function is used to fetch data from 6 consecutive bytes. The most significant byte is at the lowest address. @return dulint integer */ UNIV_INLINE dulint mach_read_from_6( /*=============*/ const byte* b) /*!< in: pointer to 6 bytes */ { ulint high; ulint low; ut_ad(b); high = mach_read_from_2(b); low = mach_read_from_4(b + 2); return(ut_dulint_create(high, low)); } /*********************************************************//** Writes a dulint in a compressed form (5..9 bytes). @return size in bytes */ UNIV_INLINE ulint mach_dulint_write_compressed( /*=========================*/ byte* b, /*!< in: pointer to memory where to store */ dulint n) /*!< in: dulint integer to be stored */ { ulint size; ut_ad(b); size = mach_write_compressed(b, ut_dulint_get_high(n)); mach_write_to_4(b + size, ut_dulint_get_low(n)); return(size + 4); } /*********************************************************//** Returns the size of a dulint when written in the compressed form. @return compressed size in bytes */ UNIV_INLINE ulint mach_dulint_get_compressed_size( /*============================*/ dulint n) /*!< in: dulint integer to be stored */ { return(4 + mach_get_compressed_size(ut_dulint_get_high(n))); } /*********************************************************//** Reads a dulint in a compressed form. @return read dulint */ UNIV_INLINE dulint mach_dulint_read_compressed( /*========================*/ const byte* b) /*!< in: pointer to memory from where to read */ { ulint high; ulint low; ulint size; ut_ad(b); high = mach_read_compressed(b); size = mach_get_compressed_size(high); low = mach_read_from_4(b + size); return(ut_dulint_create(high, low)); } /*********************************************************//** Writes a dulint in a compressed form (1..11 bytes). @return size in bytes */ UNIV_INLINE ulint mach_dulint_write_much_compressed( /*==============================*/ byte* b, /*!< in: pointer to memory where to store */ dulint n) /*!< in: dulint integer to be stored */ { ulint size; ut_ad(b); if (ut_dulint_get_high(n) == 0) { return(mach_write_compressed(b, ut_dulint_get_low(n))); } *b = (byte)0xFF; size = 1 + mach_write_compressed(b + 1, ut_dulint_get_high(n)); size += mach_write_compressed(b + size, ut_dulint_get_low(n)); return(size); } /*********************************************************//** Returns the size of a dulint when written in the compressed form. @return compressed size in bytes */ UNIV_INLINE ulint mach_dulint_get_much_compressed_size( /*=================================*/ dulint n) /*!< in: dulint integer to be stored */ { if (0 == ut_dulint_get_high(n)) { return(mach_get_compressed_size(ut_dulint_get_low(n))); } return(1 + mach_get_compressed_size(ut_dulint_get_high(n)) + mach_get_compressed_size(ut_dulint_get_low(n))); } /*********************************************************//** Reads a dulint in a compressed form. @return read dulint */ UNIV_INLINE dulint mach_dulint_read_much_compressed( /*=============================*/ const byte* b) /*!< in: pointer to memory from where to read */ { ulint high; ulint low; ulint size; ut_ad(b); if (*b != (byte)0xFF) { high = 0; size = 0; } else { high = mach_read_compressed(b + 1); size = 1 + mach_get_compressed_size(high); } low = mach_read_compressed(b + size); return(ut_dulint_create(high, low)); } #ifndef UNIV_HOTBACKUP /*********************************************************//** Reads a double. It is stored in a little-endian format. @return double read */ UNIV_INLINE double mach_double_read( /*=============*/ const byte* b) /*!< in: pointer to memory from where to read */ { double d; ulint i; byte* ptr; ptr = (byte*)&d; for (i = 0; i < sizeof(double); i++) { #ifdef WORDS_BIGENDIAN ptr[sizeof(double) - i - 1] = b[i]; #else ptr[i] = b[i]; #endif } return(d); } /*********************************************************//** Writes a pointer to a double. It is stored in a little-endian format. */ UNIV_INLINE void mach_double_ptr_write( /*==================*/ byte* b, /*!< in: pointer to memory where to write */ const byte* ptr) /*!< in: pointer to a double */ { ulint i; for (i = 0; i < sizeof(double); i++) { #ifdef WORDS_BIGENDIAN b[i] = ptr[sizeof(double) - i - 1]; #else b[i] = ptr[i]; #endif } } /*********************************************************//** Writes a double. It is stored in a little-endian format. */ UNIV_INLINE void mach_double_write( /*==============*/ byte* b, /*!< in: pointer to memory where to write */ double d) /*!< in: double */ { mach_double_ptr_write(b, (byte*) &d); } /************************************************************* Reads a float. It is stored in a little-endian format. @return float read */ UNIV_INLINE float mach_float_read( /*============*/ const byte* b) /*!< in: pointer to memory from where to read */ { float d; ulint i; byte* ptr; ptr = (byte*)&d; for (i = 0; i < sizeof(float); i++) { #ifdef WORDS_BIGENDIAN ptr[sizeof(float) - i - 1] = b[i]; #else ptr[i] = b[i]; #endif } return(d); } /*********************************************************//** Writes a pointer to float. It is stored in a little-endian format. */ UNIV_INLINE void mach_float_ptr_write( /*=================*/ byte* b, /*!< in: pointer to memory where to write */ const byte* ptr) /*!< in: pointer to float */ { ulint i; for (i = 0; i < sizeof(float); i++) { #ifdef WORDS_BIGENDIAN b[i] = ptr[sizeof(float) - i - 1]; #else b[i] = ptr[i]; #endif } } /*********************************************************//** Writes a float. It is stored in a little-endian format. */ UNIV_INLINE void mach_float_write( /*=============*/ byte* b, /*!< in: pointer to memory where to write */ float d) /*!< in: float */ { mach_float_ptr_write(b, (byte*)&d); } /************************************************************* Reads a ulint stored in the little-endian format. @return unsigned long int */ UNIV_INLINE ulint mach_read_from_n_little_endian( /*===========================*/ const byte* buf, /*!< in: from where to read */ ulint buf_size) /*!< in: from how many bytes to read */ { ulint n = 0; const byte* ptr; ut_ad(buf_size <= sizeof(ulint)); ut_ad(buf_size > 0); ptr = buf + buf_size; for (;;) { ptr--; n = n << 8; n += (ulint)(*ptr); if (ptr == buf) { break; } } return(n); } /*********************************************************//** Writes a ulint in the little-endian format. */ UNIV_INLINE void mach_write_to_n_little_endian( /*==========================*/ byte* dest, /*!< in: where to write */ ulint dest_size, /*!< in: into how many bytes to write */ ulint n) /*!< in: unsigned long int to write */ { byte* end; ut_ad(dest_size <= sizeof(ulint)); ut_ad(dest_size > 0); end = dest + dest_size; for (;;) { *dest = (byte)(n & 0xFF); n = n >> 8; dest++; if (dest == end) { break; } } ut_ad(n == 0); } /*********************************************************//** Reads a ulint stored in the little-endian format. @return unsigned long int */ UNIV_INLINE ulint mach_read_from_2_little_endian( /*===========================*/ const byte* buf) /*!< in: from where to read */ { return((ulint)(*buf) + ((ulint)(*(buf + 1))) * 256); } /*********************************************************//** Writes a ulint in the little-endian format. */ UNIV_INLINE void mach_write_to_2_little_endian( /*==========================*/ byte* dest, /*!< in: where to write */ ulint n) /*!< in: unsigned long int to write */ { ut_ad(n < 256 * 256); *dest = (byte)(n & 0xFFUL); n = n >> 8; dest++; *dest = (byte)(n & 0xFFUL); } /*********************************************************//** Swap byte ordering. */ UNIV_INLINE void mach_swap_byte_order( /*=================*/ byte* dest, /*!< out: where to write */ const byte* from, /*!< in: where to read from */ ulint len) /*!< in: length of src */ { ut_ad(len > 0); ut_ad(len <= 8); dest += len; switch (len & 0x7) { case 0: *--dest = *from++; case 7: *--dest = *from++; case 6: *--dest = *from++; case 5: *--dest = *from++; case 4: *--dest = *from++; case 3: *--dest = *from++; case 2: *--dest = *from++; case 1: *--dest = *from; } } /************************************************************* Convert integral type from storage byte order (big-endian) to host byte order. @return value in host byte order */ UNIV_INLINE void mach_read_int_type( /*===============*/ void* dst, /*!< out: where to write */ const byte* src, /*!< in: where to read from */ ulint len, /*!< in: length of src */ ibool usign) /*!< in: signed or unsigned flag */ { #ifdef WORDS_BIGENDIAN memcpy(dst, src, len); #else mach_swap_byte_order(dst, src, len); if (usign) { *(((byte*) dst) + len - 1) ^= 0x80; } #endif } /************************************************************* Convert integral type from host byte order (big-endian) storage byte order. */ UNIV_INLINE void mach_write_int_type( /*================*/ byte* dest, /*!< in: where to write*/ const byte* src, /*!< in: where to read from */ ulint len, /*!< in: length of src */ ibool usign) /*!< in: signed or unsigned flag */ { #ifdef WORDS_BIGENDIAN memcpy(dest, src, len); #else mach_swap_byte_order(dest, src, len); if (usign) { *dest ^= 0x80; } #endif } /************************************************************* Convert a 64 bit big endian unsigned integral type to the host byte order. @return value in host byte order */ UNIV_INLINE ib_uint64_t mach_read_uint64( /*=============*/ const byte* src) /*!< in: where to read from */ { ib_uint64_t dst; mach_read_int_type(&dst, src, sizeof(dst), TRUE); return(dst); } /************************************************************* Convert a 64 bit big endian signed integral type to the host byte order. @return value in host byte order */ UNIV_INLINE ib_int64_t mach_read_int64( /*============*/ const byte* src) /*!< in: where to read from */ { ib_uint64_t dst; mach_read_int_type(&dst, src, sizeof(dst), FALSE); return(dst); } /************************************************************* Convert a 32 bit big endian unsigned integral type to the host byte order. @return value in host byte order */ UNIV_INLINE ib_uint32_t mach_read_uint32( /*=============*/ const byte* src) /*!< in: where to read from */ { ib_uint32_t dst; mach_read_int_type(&dst, src, sizeof(dst), TRUE); return(dst); } /************************************************************* Convert a 32 bit big endian signed integral type to the host byte order. @return value in host byte order */ UNIV_INLINE ib_int32_t mach_read_int32( /*============*/ const byte* src) /*!< in: where to read from */ { ib_int32_t dst; mach_read_int_type(&dst, src, sizeof(dst), FALSE); return(dst); } /************************************************************* Convert a 64 bit unsigned integral type to big endian from host byte order. */ UNIV_INLINE void mach_write_uint64( /*==============*/ byte* dest, /*!< out: where to write */ ib_uint64_t n) /*!< in: where to read from */ { ut_ad(dest != NULL); mach_write_int_type(dest, (const byte*) &n, sizeof(n), TRUE); } /************************************************************* Convert a 64 bit signed integral type to big endian from host byte order. */ UNIV_INLINE void mach_write_int64( /*=============*/ byte* dest, /*!< out: where to write */ ib_int64_t n) /*!< in: where to read from */ { mach_write_int_type(dest, (const byte*) &n, sizeof(n), FALSE); } /************************************************************* Convert a 32 bit unsigned integral type to big endian from host byte order. */ UNIV_INLINE void mach_write_uint32( /*==============*/ byte* dest, /*!< out: where to write */ ib_uint32_t n) /*!< in: where to read from */ { mach_write_int_type(dest, (const byte*) &n, sizeof(n), TRUE); } /************************************************************* Convert a 32 bit signed integral type to big endian from host byte order. */ UNIV_INLINE void mach_write_int32( /*=============*/ byte* dest, /*!< out: where to write */ ib_int32_t n) /*!< in: where to read from */ { mach_write_int_type(dest, (const byte*) &n, sizeof(n), FALSE); } #endif /* !UNIV_HOTBACKUP */ haildb-2.3.2/include/buf0types.h0000644000175000017500000000554711513177357017373 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/buf0types.h The database buffer pool global types for the directory Created 11/17/1995 Heikki Tuuri *******************************************************/ #ifndef buf0types_h #define buf0types_h /** Buffer page (uncompressed or compressed) */ typedef struct buf_page_struct buf_page_t; /** Buffer block for which an uncompressed page exists */ typedef struct buf_block_struct buf_block_t; /** Buffer pool chunk comprising buf_block_t */ typedef struct buf_chunk_struct buf_chunk_t; /** Buffer pool comprising buf_chunk_t */ typedef struct buf_pool_struct buf_pool_t; /** Buffer pool statistics struct */ typedef struct buf_pool_stat_struct buf_pool_stat_t; /** A buffer frame. @see page_t */ typedef byte buf_frame_t; /** Flags for flush types */ enum buf_flush { BUF_FLUSH_LRU = 0, /*!< flush via the LRU list */ BUF_FLUSH_SINGLE_PAGE, /*!< flush a single page */ BUF_FLUSH_LIST, /*!< flush via the flush list of dirty blocks */ BUF_FLUSH_N_TYPES /*!< index of last element + 1 */ }; /** Flags for io_fix types */ enum buf_io_fix { BUF_IO_NONE = 0, /**< no pending I/O */ BUF_IO_READ, /**< read pending */ BUF_IO_WRITE /**< write pending */ }; /** Parameters of binary buddy system for compressed pages (buf0buddy.h) */ /* @{ */ #if UNIV_WORD_SIZE <= 4 /* 32-bit system */ /** Base-2 logarithm of the smallest buddy block size */ # define BUF_BUDDY_LOW_SHIFT 6 #else /* 64-bit system */ /** Base-2 logarithm of the smallest buddy block size */ # define BUF_BUDDY_LOW_SHIFT 7 #endif #define BUF_BUDDY_LOW (1 << BUF_BUDDY_LOW_SHIFT) /*!< minimum block size in the binary buddy system; must be at least sizeof(buf_page_t) */ #define BUF_BUDDY_SIZES (UNIV_PAGE_SIZE_SHIFT - BUF_BUDDY_LOW_SHIFT) /*!< number of buddy sizes */ /** twice the maximum block size of the buddy system; the underlying memory is aligned by this amount: this must be equal to UNIV_PAGE_SIZE */ #define BUF_BUDDY_HIGH (BUF_BUDDY_LOW << BUF_BUDDY_SIZES) /* @} */ #endif haildb-2.3.2/include/ut0mem.h0000644000175000017500000002520411513177357016651 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1994, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /*******************************************************************//** @file include/ut0mem.h Memory primitives Created 5/30/1994 Heikki Tuuri ************************************************************************/ #ifndef ut0mem_h #define ut0mem_h #include "univ.i" #include #ifndef UNIV_HOTBACKUP # include "os0sync.h" /** The total amount of memory currently allocated from the operating system with os_mem_alloc_large() or malloc(). Does not count malloc() if srv_use_sys_malloc is set. Protected by ut_list_mutex. */ extern ulint ut_total_allocated_memory; /** Mutex protecting ut_total_allocated_memory and ut_mem_block_list */ extern os_fast_mutex_t ut_list_mutex; #endif /* !UNIV_HOTBACKUP */ /** Wrapper for memcpy(3). Copy memory area when the source and target are not overlapping. * @param dest in: copy to * @param sour in: copy from * @param n in: number of bytes to copy * @return dest */ UNIV_INLINE void* ut_memcpy(void* dest, const void* sour, ulint n); /** Wrapper for memmove(3). Copy memory area when the source and target are overlapping. * @param dest in: copy to * @param sour in: copy from * @param n in: number of bytes to copy * @return dest */ UNIV_INLINE void* ut_memmove(void* dest, const void* sour, ulint n); /** Wrapper for memcmp(3). Compare memory areas. * @param str1 in: first memory block to compare * @param str2 in: second memory block to compare * @param n in: number of bytes to compare * @return negative, 0, or positive if str1 is smaller, equal, or greater than str2, respectively. */ UNIV_INLINE int ut_memcmp(const void* str1, const void* str2, ulint n); /**********************************************************************//** Initializes the mem block list at database startup. */ UNIV_INTERN void ut_mem_init(void); /*=============*/ /**********************************************************************//** Allocates memory. Sets it also to zero if UNIV_SET_MEM_TO_ZERO is defined and set_to_zero is TRUE. @return own: allocated memory */ UNIV_INTERN void* ut_malloc_low( /*==========*/ ulint n, /*!< in: number of bytes to allocate */ ibool set_to_zero, /*!< in: TRUE if allocated memory should be set to zero if UNIV_SET_MEM_TO_ZERO is defined */ ibool assert_on_error); /*!< in: if TRUE, we crash the engine if the memory cannot be allocated */ /**********************************************************************//** Allocates memory. Sets it also to zero if UNIV_SET_MEM_TO_ZERO is defined. @return own: allocated memory */ UNIV_INTERN void* ut_malloc( /*======*/ ulint n); /*!< in: number of bytes to allocate */ #ifndef UNIV_HOTBACKUP /**********************************************************************//** Tests if malloc of n bytes would succeed. ut_malloc() asserts if memory runs out. It cannot be used if we want to return an error message. Prints to stderr a message if fails. @return TRUE if succeeded */ UNIV_INTERN ibool ut_test_malloc( /*===========*/ ulint n); /*!< in: try to allocate this many bytes */ #endif /* !UNIV_HOTBACKUP */ /**********************************************************************//** Frees a memory block allocated with ut_malloc. */ UNIV_INTERN void ut_free( /*====*/ void* ptr); /*!< in, own: memory block */ #ifndef UNIV_HOTBACKUP /**********************************************************************//** Implements realloc. This is needed by /pars/lexyy.c. Otherwise, you should not use this function because the allocation functions in mem0mem.h are the recommended ones in InnoDB. man realloc in Linux, 2004: realloc() changes the size of the memory block pointed to by ptr to size bytes. The contents will be unchanged to the minimum of the old and new sizes; newly allocated mem­ ory will be uninitialized. If ptr is NULL, the call is equivalent to malloc(size); if size is equal to zero, the call is equivalent to free(ptr). Unless ptr is NULL, it must have been returned by an earlier call to malloc(), calloc() or realloc(). RETURN VALUE realloc() returns a pointer to the newly allocated memory, which is suitably aligned for any kind of variable and may be different from ptr, or NULL if the request fails. If size was equal to 0, either NULL or a pointer suitable to be passed to free() is returned. If realloc() fails the original block is left untouched - it is not freed or moved. @return own: pointer to new mem block or NULL */ UNIV_INTERN void* ut_realloc( /*=======*/ void* ptr, /*!< in: pointer to old block or NULL */ ulint size); /*!< in: desired size */ /**********************************************************************//** Frees in shutdown all allocated memory not freed yet. */ UNIV_INTERN void ut_free_all_mem(void); /*=================*/ #endif /* !UNIV_HOTBACKUP */ /** Wrapper for strcpy(3). Copy a NUL-terminated string. * @param dest in: copy to * @param sour in: copy from * @return dest */ UNIV_INLINE char* ut_strcpy(char* dest, const char* sour); /** Wrapper for strlen(3). Determine the length of a NUL-terminated string. * @param str in: string * @return length of the string in bytes, excluding the terminating NUL */ UNIV_INLINE ulint ut_strlen(const char* str); /** Wrapper for strcmp(3). Compare NUL-terminated strings. * @param str1 in: first string to compare * @param str2 in: second string to compare * @return negative, 0, or positive if str1 is smaller, equal, or greater than str2, respectively. */ UNIV_INLINE int ut_strcmp(const char* str1, const char* str2); /**********************************************************************//** Copies up to size - 1 characters from the NUL-terminated string src to dst, NUL-terminating the result. Returns strlen(src), so truncation occurred if the return value >= size. @return strlen(src) */ UNIV_INTERN ulint ut_strlcpy( /*=======*/ char* dst, /*!< in: destination buffer */ const char* src, /*!< in: source buffer */ ulint size); /*!< in: size of destination buffer */ /**********************************************************************//** Like ut_strlcpy, but if src doesn't fit in dst completely, copies the last (size - 1) bytes of src, not the first. @return strlen(src) */ UNIV_INTERN ulint ut_strlcpy_rev( /*===========*/ char* dst, /*!< in: destination buffer */ const char* src, /*!< in: source buffer */ ulint size); /*!< in: size of destination buffer */ /**********************************************************************//** Compute strlen(ut_strcpyq(str, q)). @return length of the string when quoted */ UNIV_INLINE ulint ut_strlenq( /*=======*/ const char* str, /*!< in: null-terminated string */ char q); /*!< in: the quote character */ /**********************************************************************//** Make a quoted copy of a NUL-terminated string. Leading and trailing quotes will not be included; only embedded quotes will be escaped. See also ut_strlenq() and ut_memcpyq(). @return pointer to end of dest */ UNIV_INTERN char* ut_strcpyq( /*=======*/ char* dest, /*!< in: output buffer */ char q, /*!< in: the quote character */ const char* src); /*!< in: null-terminated string */ /**********************************************************************//** Make a quoted copy of a fixed-length string. Leading and trailing quotes will not be included; only embedded quotes will be escaped. See also ut_strlenq() and ut_strcpyq(). @return pointer to end of dest */ UNIV_INTERN char* ut_memcpyq( /*=======*/ char* dest, /*!< in: output buffer */ char q, /*!< in: the quote character */ const char* src, /*!< in: string to be quoted */ ulint len); /*!< in: length of src */ /**********************************************************************//** Return the number of times s2 occurs in s1. Overlapping instances of s2 are only counted once. @return the number of times s2 occurs in s1 */ UNIV_INTERN ulint ut_strcount( /*========*/ const char* s1, /*!< in: string to search in */ const char* s2); /*!< in: string to search for */ /**********************************************************************//** Replace every occurrence of s1 in str with s2. Overlapping instances of s1 are only replaced once. @return own: modified string, must be freed with mem_free() */ UNIV_INTERN char* ut_strreplace( /*==========*/ const char* str, /*!< in: string to operate on */ const char* s1, /*!< in: string to replace */ const char* s2); /*!< in: string to replace s1 with */ /**********************************************************************//** Converts a raw binary data to a NUL-terminated hex string. The output is truncated if there is not enough space in "hex", make sure "hex_size" is at least (2 * raw_size + 1) if you do not want this to happen. Returns the actual number of characters written to "hex" (including the NUL). @return number of chars written */ UNIV_INLINE ulint ut_raw_to_hex( /*==========*/ const void* raw, /*!< in: raw data */ ulint raw_size, /*!< in: "raw" length in bytes */ char* hex, /*!< out: hex string */ ulint hex_size); /*!< in: "hex" size in bytes */ /*******************************************************************//** Adds single quotes to the start and end of string and escapes any quotes by doubling them. Returns the number of bytes that were written to "buf" (including the terminating NUL). If buf_size is too small then the trailing bytes from "str" are discarded. @return number of bytes that were written */ UNIV_INLINE ulint ut_str_sql_format( /*==============*/ const char* str, /*!< in: string */ ulint str_len, /*!< in: string length in bytes */ char* buf, /*!< out: output buffer */ ulint buf_size); /*!< in: output buffer size in bytes */ /************************************************************************** Reset the variables. */ UNIV_INTERN void ut_mem_var_init(void); /*=================*/ #ifndef UNIV_NONINL #include "ut0mem.ic" #endif #endif haildb-2.3.2/include/dict0mem.ic0000644000175000017500000000210411513177357017302 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /******************************************************************//** @file include/dict0mem.ic Data dictionary memory object creation Created 1/8/1996 Heikki Tuuri ***********************************************************************/ haildb-2.3.2/include/ut0vec.h0000644000175000017500000001042311513177357016645 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 2006, 2010, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /*******************************************************************//** @file include/ut0vec.h A vector of pointers to data items Created 4/6/2006 Osku Salerma ************************************************************************/ #ifndef IB_VECTOR_H #define IB_VECTOR_H #include "univ.i" #include "mem0mem.h" /** An automatically resizing vector data type. */ typedef struct ib_vector_struct ib_vector_t; /* An automatically resizing vector datatype with the following properties: -Contains void* items. -The items are owned by the caller. -All memory allocation is done through a heap owned by the caller, who is responsible for freeing it when done with the vector. -When the vector is resized, the old memory area is left allocated since it uses the same heap as the new memory area, so this is best used for relatively small or short-lived uses. */ /****************************************************************//** Create a new vector with the given initial size. @return vector */ UNIV_INTERN ib_vector_t* ib_vector_create( /*=============*/ mem_heap_t* heap, /*!< in: heap */ ulint size); /*!< in: initial size */ /****************************************************************//** Push a new element to the vector, increasing its size if necessary. */ UNIV_INTERN void ib_vector_push( /*===========*/ ib_vector_t* vec, /*!< in: vector */ void* elem); /*!< in: data element */ /****************************************************************//** Get the number of elements in the vector. @return number of elements in vector */ UNIV_INLINE ulint ib_vector_size( /*===========*/ const ib_vector_t* vec); /*!< in: vector */ /****************************************************************//** Test whether a vector is empty or not. @return TRUE if empty */ UNIV_INLINE ibool ib_vector_is_empty( /*===============*/ const ib_vector_t* vec); /*!< in: vector */ /****************************************************************//** Get the n'th element. @return n'th element */ UNIV_INLINE void* ib_vector_get( /*==========*/ ib_vector_t* vec, /*!< in: vector */ ulint n); /*!< in: element index to get */ /****************************************************************//** Get n'th element as a const pointer. @return n'th element */ UNIV_INLINE const void* ib_vector_get_const( /*================*/ const ib_vector_t* vec, /*!< in: vector */ ulint n); /*!< in: element index to get */ /****************************************************************//** Set the n'th element and return the previous value. @return n'th element */ UNIV_INLINE void* ib_vector_set( /*==========*/ ib_vector_t* vec, /*!< in: vector */ ulint n, /*!< in: element index to set */ void* p); /*!< in: new value to set */ /******************************************************************** Remove the last element from the vector. */ UNIV_INLINE void* ib_vector_pop( /*==========*/ ib_vector_t* vec); /*!< in: vector */ /****************************************************************//** Free the underlying heap of the vector. Note that vec is invalid after this call. */ UNIV_INLINE void ib_vector_free( /*===========*/ ib_vector_t* vec); /*!< in,own: vector */ /** An automatically resizing vector data type. */ struct ib_vector_struct { mem_heap_t* heap; /*!< heap */ void** data; /*!< data elements */ ulint used; /*!< number of elements currently used */ ulint total; /*!< number of elements allocated */ }; #ifndef UNIV_NONINL #include "ut0vec.ic" #endif #endif haildb-2.3.2/include/row0uins.ic0000644000175000017500000000201711513177357017371 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1997, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/row0uins.ic Fresh insert undo Created 2/25/1997 Heikki Tuuri *******************************************************/ haildb-2.3.2/include/btr0cur.ic0000644000175000017500000001350211513177357017165 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1994, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/btr0cur.ic The index tree cursor Created 10/16/1994 Heikki Tuuri *******************************************************/ #ifndef UNIV_HOTBACKUP #include "btr0btr.h" #ifdef UNIV_DEBUG /*********************************************************//** Returns the page cursor component of a tree cursor. @return pointer to page cursor component */ UNIV_INLINE page_cur_t* btr_cur_get_page_cur( /*=================*/ const btr_cur_t* cursor) /*!< in: tree cursor */ { return(&((btr_cur_t*) cursor)->page_cur); } #endif /* UNIV_DEBUG */ /*********************************************************//** Returns the buffer block on which the tree cursor is positioned. @return pointer to buffer block */ UNIV_INLINE buf_block_t* btr_cur_get_block( /*==============*/ btr_cur_t* cursor) /*!< in: tree cursor */ { return(page_cur_get_block(btr_cur_get_page_cur(cursor))); } /*********************************************************//** Returns the record pointer of a tree cursor. @return pointer to record */ UNIV_INLINE rec_t* btr_cur_get_rec( /*============*/ btr_cur_t* cursor) /*!< in: tree cursor */ { return(page_cur_get_rec(&(cursor->page_cur))); } #ifdef WITH_ZIP /*********************************************************//** Returns the compressed page on which the tree cursor is positioned. @return pointer to compressed page, or NULL if the page is not compressed */ UNIV_INLINE page_zip_des_t* btr_cur_get_page_zip( /*=================*/ btr_cur_t* cursor) /*!< in: tree cursor */ { return(buf_block_get_page_zip(btr_cur_get_block(cursor))); } #endif /* WITH_ZIP */ /*********************************************************//** Invalidates a tree cursor by setting record pointer to NULL. */ UNIV_INLINE void btr_cur_invalidate( /*===============*/ btr_cur_t* cursor) /*!< in: tree cursor */ { page_cur_invalidate(&(cursor->page_cur)); } /*********************************************************//** Returns the page of a tree cursor. @return pointer to page */ UNIV_INLINE page_t* btr_cur_get_page( /*=============*/ btr_cur_t* cursor) /*!< in: tree cursor */ { return(page_align(page_cur_get_rec(&(cursor->page_cur)))); } /*********************************************************//** Returns the index of a cursor. @return index */ UNIV_INLINE dict_index_t* btr_cur_get_index( /*==============*/ btr_cur_t* cursor) /*!< in: B-tree cursor */ { return(cursor->index); } /*********************************************************//** Positions a tree cursor at a given record. */ UNIV_INLINE void btr_cur_position( /*=============*/ dict_index_t* dict_index, /*!< in: dict_index */ rec_t* rec, /*!< in: record in tree */ buf_block_t* block, /*!< in: buffer block of rec */ btr_cur_t* cursor) /*!< out: cursor */ { ut_ad(page_align(rec) == block->frame); page_cur_position(rec, block, btr_cur_get_page_cur(cursor)); cursor->index = dict_index; } /*********************************************************************//** Checks if compressing an index page where a btr cursor is placed makes sense. @return TRUE if compression is recommended */ UNIV_INLINE ibool btr_cur_compress_recommendation( /*============================*/ btr_cur_t* cursor, /*!< in: btr cursor */ mtr_t* mtr) /*!< in: mtr */ { page_t* page; ut_ad(mtr_memo_contains(mtr, btr_cur_get_block(cursor), MTR_MEMO_PAGE_X_FIX)); page = btr_cur_get_page(cursor); if ((page_get_data_size(page) < BTR_CUR_PAGE_COMPRESS_LIMIT) || ((btr_page_get_next(page, mtr) == FIL_NULL) && (btr_page_get_prev(page, mtr) == FIL_NULL))) { /* The page fillfactor has dropped below a predefined minimum value OR the level in the B-tree contains just one page: we recommend compression if this is not the root page. */ return(dict_index_get_page(cursor->index) != page_get_page_no(page)); } return(FALSE); } /*********************************************************************//** Checks if the record on which the cursor is placed can be deleted without making tree compression necessary (or, recommended). @return TRUE if can be deleted without recommended compression */ UNIV_INLINE ibool btr_cur_can_delete_without_compress( /*================================*/ btr_cur_t* cursor, /*!< in: btr cursor */ ulint rec_size,/*!< in: rec_get_size(btr_cur_get_rec(cursor))*/ mtr_t* mtr) /*!< in: mtr */ { page_t* page; ut_ad(mtr_memo_contains(mtr, btr_cur_get_block(cursor), MTR_MEMO_PAGE_X_FIX)); page = btr_cur_get_page(cursor); if ((page_get_data_size(page) - rec_size < BTR_CUR_PAGE_COMPRESS_LIMIT) || ((btr_page_get_next(page, mtr) == FIL_NULL) && (btr_page_get_prev(page, mtr) == FIL_NULL)) || (page_get_n_recs(page) < 2)) { /* The page fillfactor will drop below a predefined minimum value, OR the level in the B-tree contains just one page, OR the page will become empty: we recommend compression if this is not the root page. */ return(dict_index_get_page(cursor->index) == page_get_page_no(page)); } return(TRUE); } #endif /* !UNIV_HOTBACKUP */ haildb-2.3.2/include/read0read.ic0000644000175000017500000000524211513177357017435 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1997, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/read0read.ic Cursor read Created 2/16/1997 Heikki Tuuri *******************************************************/ /*********************************************************************//** Gets the nth trx id in a read view. @return trx id */ UNIV_INLINE trx_id_t read_view_get_nth_trx_id( /*=====================*/ const read_view_t* view, /*!< in: read view */ ulint n) /*!< in: position */ { ut_ad(n < view->n_trx_ids); return(*(view->trx_ids + n)); } /*********************************************************************//** Sets the nth trx id in a read view. */ UNIV_INLINE void read_view_set_nth_trx_id( /*=====================*/ read_view_t* view, /*!< in: read view */ ulint n, /*!< in: position */ trx_id_t trx_id) /*!< in: trx id to set */ { ut_ad(n < view->n_trx_ids); *(view->trx_ids + n) = trx_id; } /*********************************************************************//** Checks if a read view sees the specified transaction. @return TRUE if sees */ UNIV_INLINE ibool read_view_sees_trx_id( /*==================*/ const read_view_t* view, /*!< in: read view */ trx_id_t trx_id) /*!< in: trx id */ { ulint n_ids; int cmp; ulint i; if (ut_dulint_cmp(trx_id, view->up_limit_id) < 0) { return(TRUE); } if (ut_dulint_cmp(trx_id, view->low_limit_id) >= 0) { return(FALSE); } /* We go through the trx ids in the array smallest first: this order may save CPU time, because if there was a very long running transaction in the trx id array, its trx id is looked at first, and the first two comparisons may well decide the visibility of trx_id. */ n_ids = view->n_trx_ids; for (i = 0; i < n_ids; i++) { cmp = ut_dulint_cmp( trx_id, read_view_get_nth_trx_id(view, n_ids - i - 1)); if (cmp <= 0) { return(cmp < 0); } } return(TRUE); } haildb-2.3.2/include/ut0dbg.h0000644000175000017500000001340511513177357016627 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1994, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /*****************************************************************//** @file include/ut0dbg.h Debug utilities for Innobase Created 1/30/1994 Heikki Tuuri **********************************************************************/ #ifndef ut0dbg_h #define ut0dbg_h #include "univ.i" #include #include "os0thread.h" #if defined(__GNUC__) && (__GNUC__ > 2) /** Test if an assertion fails. @param EXPR assertion expression @return nonzero if EXPR holds, zero if not */ # define UT_DBG_FAIL(EXPR) UNIV_UNLIKELY(!((ulint)(EXPR))) #else /** This is used to eliminate compiler warnings */ extern ulint ut_dbg_zero; /** Test if an assertion fails. @param EXPR assertion expression @return nonzero if EXPR holds, zero if not */ # define UT_DBG_FAIL(EXPR) !((ulint)(EXPR) + ut_dbg_zero) #endif #if __STDC_VERSION__ < 199901L # if __GNUC__ >= 2 # define __func__ __FUNCTION__ # else # define __func__ "" # endif #endif #define UT_DBG_PRINT_FUNC printf("%s\n", __func__) /* you must #define UT_DBG_ENTER_FUNC_ENABLED to something before using this macro */ #define UT_DBG_ENTER_FUNC \ do { \ if (UT_DBG_ENTER_FUNC_ENABLED) { \ UT_DBG_PRINT_FUNC; \ } \ } while (0) /*************************************************************//** Report a failed assertion. */ UNIV_INTERN void ut_dbg_assertion_failed( /*====================*/ const char* expr, /*!< in: the failed assertion */ const char* file, /*!< in: source file containing the assertion */ ulint line); /*!< in: line number of the assertion */ #ifdef __NETWARE__ /** Flag for ignoring further assertion failures. This is set to TRUE when on NetWare there happens an InnoDB assertion failure or other fatal error condition that requires an immediate shutdown. */ extern ibool panic_shutdown; /* Abort the execution. */ void ut_dbg_panic(void); # define UT_DBG_PANIC ut_dbg_panic() /* Stop threads in ut_a(). */ # define UT_DBG_STOP do {} while (0) /* We do not do this on NetWare */ #else /* __NETWARE__ */ # if defined(__WIN__) || defined(__INTEL_COMPILER) # undef UT_DBG_USE_ABORT # elif defined(__GNUC__) && (__GNUC__ > 2) # define UT_DBG_USE_ABORT # endif # ifndef UT_DBG_USE_ABORT /** A null pointer that will be dereferenced to trigger a memory trap */ extern ulint* ut_dbg_null_ptr; # endif # if defined(UNIV_SYNC_DEBUG) || !defined(UT_DBG_USE_ABORT) /** If this is set to TRUE by ut_dbg_assertion_failed(), all threads will stop at the next ut_a() or ut_ad(). */ extern ibool ut_dbg_stop_threads; /*************************************************************//** Stop a thread after assertion failure. */ UNIV_INTERN void ut_dbg_stop_thread( /*===============*/ const char* file, ulint line); # endif # ifdef UT_DBG_USE_ABORT /** Abort the execution. */ # define UT_DBG_PANIC abort() /** Stop threads (null operation) */ # define UT_DBG_STOP do {} while (0) # else /* UT_DBG_USE_ABORT */ /** Abort the execution. */ # define UT_DBG_PANIC \ if (*(ut_dbg_null_ptr)) ut_dbg_null_ptr = NULL /** Stop threads in ut_a(). */ # define UT_DBG_STOP do \ if (UNIV_UNLIKELY(ut_dbg_stop_threads)) { \ ut_dbg_stop_thread(__FILE__, (ulint) __LINE__); \ } while (0) # endif /* UT_DBG_USE_ABORT */ #endif /* __NETWARE__ */ /** Abort execution if EXPR does not evaluate to nonzero. @param EXPR assertion expression that should hold */ #define ut_a(EXPR) do { \ if (UT_DBG_FAIL(EXPR)) { \ ut_dbg_assertion_failed(#EXPR, \ __FILE__, (ulint) __LINE__); \ UT_DBG_PANIC; \ } \ UT_DBG_STOP; \ } while (0) /** Abort execution. */ #define ut_error do { \ ut_dbg_assertion_failed(0, __FILE__, (ulint) __LINE__); \ UT_DBG_PANIC; \ } while (0) #ifdef UNIV_DEBUG /** Debug assertion. Does nothing unless UNIV_DEBUG is defined. */ #define ut_ad(EXPR) ut_a(EXPR) /** Debug statement. Does nothing unless UNIV_DEBUG is defined. */ #define ut_d(EXPR) do {EXPR;} while (0) #else /** Debug assertion. Does nothing unless UNIV_DEBUG is defined. */ #define ut_ad(EXPR) /** Debug statement. Does nothing unless UNIV_DEBUG is defined. */ #define ut_d(EXPR) #endif /** Silence warnings about an unused variable by doing a null assignment. @param A the unused variable */ #define UT_NOT_USED(A) A = A #ifdef UNIV_COMPILE_TEST_FUNCS #include #include #include /** structure used for recording usage statistics */ typedef struct speedo_struct { struct rusage ru; /*!< getrusage() result */ struct timeval tv; /*!< gettimeofday() result */ } speedo_t; /*******************************************************************//** Resets a speedo (records the current time in it). */ UNIV_INTERN void speedo_reset( /*=========*/ speedo_t* speedo); /*!< out: speedo */ /*******************************************************************//** Shows the time elapsed and usage statistics since the last reset of a speedo. */ UNIV_INTERN void speedo_show( /*========*/ const speedo_t* speedo); /*!< in: speedo */ #endif /* UNIV_COMPILE_TEST_FUNCS */ #endif haildb-2.3.2/include/usr0sess.ic0000644000175000017500000000200511513177357017367 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/usr0sess.ic Sessions Created 6/25/1996 Heikki Tuuri *******************************************************/ haildb-2.3.2/include/row0upd.h0000644000175000017500000004527311513177357017052 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/row0upd.h Update of a row Created 12/27/1996 Heikki Tuuri *******************************************************/ #ifndef row0upd_h #define row0upd_h #include "univ.i" #include "data0data.h" #include "row0types.h" #include "btr0types.h" #include "dict0types.h" #include "trx0types.h" #ifndef UNIV_HOTBACKUP # include "btr0pcur.h" # include "que0types.h" # include "pars0types.h" #endif /* !UNIV_HOTBACKUP */ /*********************************************************************//** Creates an update vector object. @return own: update vector object */ UNIV_INLINE upd_t* upd_create( /*=======*/ ulint n, /*!< in: number of fields */ mem_heap_t* heap); /*!< in: heap from which memory allocated */ /*********************************************************************//** Returns the number of fields in the update vector == number of columns to be updated by an update vector. @return number of fields */ UNIV_INLINE ulint upd_get_n_fields( /*=============*/ const upd_t* update); /*!< in: update vector */ #ifdef UNIV_DEBUG /*********************************************************************//** Returns the nth field of an update vector. @return update vector field */ UNIV_INLINE upd_field_t* upd_get_nth_field( /*==============*/ const upd_t* update, /*!< in: update vector */ ulint n); /*!< in: field position in update vector */ #else # define upd_get_nth_field(update, n) ((update)->fields + (n)) #endif #ifndef UNIV_HOTBACKUP /*********************************************************************//** Sets an index field number to be updated by an update vector field. */ UNIV_INLINE void upd_field_set_field_no( /*===================*/ upd_field_t* upd_field, /*!< in: update vector field */ ulint field_no, /*!< in: field number in a clustered index */ dict_index_t* index, /*!< in: index */ trx_t* trx); /*!< in: transaction */ /*********************************************************************//** Returns a field of an update vector by field_no. @return update vector field, or NULL */ UNIV_INLINE const upd_field_t* upd_get_field_by_field_no( /*======================*/ const upd_t* update, /*!< in: update vector */ ulint no) /*!< in: field_no */ __attribute__((nonnull, pure)); /*********************************************************************//** Writes into the redo log the values of trx id and roll ptr and enough info to determine their positions within a clustered index record. @return new pointer to mlog */ UNIV_INTERN byte* row_upd_write_sys_vals_to_log( /*==========================*/ dict_index_t* index, /*!< in: clustered index */ trx_t* trx, /*!< in: transaction */ roll_ptr_t roll_ptr,/*!< in: roll ptr of the undo log record */ byte* log_ptr,/*!< pointer to a buffer of size > 20 opened in mlog */ mtr_t* mtr); /*!< in: mtr */ /*********************************************************************//** Updates the trx id and roll ptr field in a clustered index record when a row is updated or marked deleted. */ UNIV_INLINE void row_upd_rec_sys_fields( /*===================*/ rec_t* rec, /*!< in/out: record */ page_zip_des_t* page_zip,/*!< in/out: compressed page whose uncompressed part will be updated, or NULL */ dict_index_t* index, /*!< in: clustered index */ const ulint* offsets,/*!< in: rec_get_offsets(rec, index) */ trx_t* trx, /*!< in: transaction */ roll_ptr_t roll_ptr);/*!< in: roll ptr of the undo log record */ /*********************************************************************//** Sets the trx id or roll ptr field of a clustered index entry. */ UNIV_INTERN void row_upd_index_entry_sys_field( /*==========================*/ const dtuple_t* entry, /*!< in: index entry, where the memory buffers for sys fields are already allocated: the function just copies the new values to them */ dict_index_t* index, /*!< in: clustered index */ ulint type, /*!< in: DATA_TRX_ID or DATA_ROLL_PTR */ dulint val); /*!< in: value to write */ /*********************************************************************//** Creates an update node for a query graph. @return own: update node */ UNIV_INTERN upd_node_t* upd_node_create( /*============*/ mem_heap_t* heap); /*!< in: mem heap where created */ /***********************************************************//** Writes to the redo log the new values of the fields occurring in the index. */ UNIV_INTERN void row_upd_index_write_log( /*====================*/ const upd_t* update, /*!< in: update vector */ byte* log_ptr,/*!< in: pointer to mlog buffer: must contain at least MLOG_BUF_MARGIN bytes of free space; the buffer is closed within this function */ mtr_t* mtr); /*!< in: mtr into whose log to write */ /***********************************************************//** Returns TRUE if row update changes size of some field in index or if some field to be updated is stored externally in rec or update. @return TRUE if the update changes the size of some field in index or the field is external in rec or update */ UNIV_INTERN ibool row_upd_changes_field_size_or_external( /*===================================*/ dict_index_t* index, /*!< in: index */ const ulint* offsets,/*!< in: rec_get_offsets(rec, index) */ const upd_t* update);/*!< in: update vector */ #endif /* !UNIV_HOTBACKUP */ /***********************************************************//** Replaces the new column values stored in the update vector to the record given. No field size changes are allowed. */ UNIV_INTERN void row_upd_rec_in_place( /*=================*/ rec_t* rec, /*!< in/out: record where replaced */ dict_index_t* index, /*!< in: the index the record belongs to */ const ulint* offsets,/*!< in: array returned by rec_get_offsets() */ const upd_t* update, /*!< in: update vector */ page_zip_des_t* page_zip);/*!< in: compressed page with enough space available, or NULL */ #ifndef UNIV_HOTBACKUP /***************************************************************//** Builds an update vector from those fields which in a secondary index entry differ from a record that has the equal ordering fields. NOTE: we compare the fields as binary strings! @return own: update vector of differing fields */ UNIV_INTERN upd_t* row_upd_build_sec_rec_difference_binary( /*====================================*/ dict_index_t* index, /*!< in: index */ const dtuple_t* entry, /*!< in: entry to insert */ const rec_t* rec, /*!< in: secondary index record */ trx_t* trx, /*!< in: transaction */ mem_heap_t* heap); /*!< in: memory heap from which allocated */ /***************************************************************//** Builds an update vector from those fields, excluding the roll ptr and trx id fields, which in an index entry differ from a record that has the equal ordering fields. NOTE: we compare the fields as binary strings! @return own: update vector of differing fields, excluding roll ptr and trx id */ UNIV_INTERN upd_t* row_upd_build_difference_binary( /*============================*/ dict_index_t* index, /*!< in: clustered index */ const dtuple_t* entry, /*!< in: entry to insert */ const rec_t* rec, /*!< in: clustered index record */ trx_t* trx, /*!< in: transaction */ mem_heap_t* heap); /*!< in: memory heap from which allocated */ /***********************************************************//** Replaces the new column values stored in the update vector to the index entry given. */ UNIV_INTERN void row_upd_index_replace_new_col_vals_index_pos( /*=========================================*/ dtuple_t* entry, /*!< in/out: index entry where replaced; the clustered index record must be covered by a lock or a page latch to prevent deletion (rollback or purge) */ dict_index_t* index, /*!< in: index; NOTE that this may also be a non-clustered index */ const upd_t* update, /*!< in: an update vector built for the index so that the field number in an upd_field is the index position */ ibool order_only, /*!< in: if TRUE, limit the replacement to ordering fields of index; note that this does not work for non-clustered indexes. */ mem_heap_t* heap) /*!< in: memory heap for allocating and copying the new values */ __attribute__((nonnull)); /***********************************************************//** Replaces the new column values stored in the update vector to the index entry given. */ UNIV_INTERN void row_upd_index_replace_new_col_vals( /*===============================*/ dtuple_t* entry, /*!< in/out: index entry where replaced; the clustered index record must be covered by a lock or a page latch to prevent deletion (rollback or purge) */ dict_index_t* index, /*!< in: index; NOTE that this may also be a non-clustered index */ const upd_t* update, /*!< in: an update vector built for the CLUSTERED index so that the field number in an upd_field is the clustered index position */ mem_heap_t* heap) /*!< in: memory heap for allocating and copying the new values */ __attribute__((nonnull)); /***********************************************************//** Replaces the new column values stored in the update vector. */ UNIV_INTERN void row_upd_replace( /*============*/ dtuple_t* row, /*!< in/out: row where replaced, indexed by col_no; the clustered index record must be covered by a lock or a page latch to prevent deletion (rollback or purge) */ row_ext_t** ext, /*!< out, own: NULL, or externally stored column prefixes */ const dict_index_t* index, /*!< in: clustered index */ const upd_t* update, /*!< in: an update vector built for the clustered index */ mem_heap_t* heap); /*!< in: memory heap */ /***********************************************************//** Checks if an update vector changes an ordering field of an index record. This function is fast if the update vector is short or the number of ordering fields in the index is small. Otherwise, this can be quadratic. NOTE: we compare the fields as binary strings! @return TRUE if update vector changes an ordering field in the index record */ UNIV_INTERN ibool row_upd_changes_ord_field_binary( /*=============================*/ const dtuple_t* row, /*!< in: old value of row, or NULL if the row and the data values in update are not known when this function is called, e.g., at compile time */ dict_index_t* index, /*!< in: index of the record */ const upd_t* update);/*!< in: update vector for the row; NOTE: the field numbers in this MUST be clustered index positions! */ /***********************************************************//** Checks if an update vector changes an ordering field of an index record. This function is fast if the update vector is short or the number of ordering fields in the index is small. Otherwise, this can be quadratic. NOTE: we compare the fields as binary strings! @return TRUE if update vector may change an ordering field in an index record */ UNIV_INTERN ibool row_upd_changes_some_index_ord_field_binary( /*========================================*/ const dict_table_t* table, /*!< in: table */ const upd_t* update);/*!< in: update vector for the row */ /***********************************************************//** Updates a row in a table. This is a high-level function used in SQL execution graphs. @return query thread to run next or NULL */ UNIV_INTERN que_thr_t* row_upd_step( /*=========*/ que_thr_t* thr); /*!< in: query thread */ #endif /* !UNIV_HOTBACKUP */ /*********************************************************************//** Parses the log data of system field values. @return log data end or NULL */ UNIV_INTERN byte* row_upd_parse_sys_vals( /*===================*/ byte* ptr, /*!< in: buffer */ byte* end_ptr,/*!< in: buffer end */ ulint* pos, /*!< out: TRX_ID position in record */ trx_id_t* trx_id, /*!< out: trx id */ roll_ptr_t* roll_ptr);/*!< out: roll ptr */ /*********************************************************************//** Updates the trx id and roll ptr field in a clustered index record in database recovery. */ UNIV_INTERN void row_upd_rec_sys_fields_in_recovery( /*===============================*/ rec_t* rec, /*!< in/out: record */ page_zip_des_t* page_zip,/*!< in/out: compressed page, or NULL */ const ulint* offsets,/*!< in: array returned by rec_get_offsets() */ ulint pos, /*!< in: TRX_ID position in rec */ trx_id_t trx_id, /*!< in: transaction id */ roll_ptr_t roll_ptr);/*!< in: roll ptr of the undo log record */ /*********************************************************************//** Parses the log data written by row_upd_index_write_log. @return log data end or NULL */ UNIV_INTERN byte* row_upd_index_parse( /*================*/ byte* ptr, /*!< in: buffer */ byte* end_ptr,/*!< in: buffer end */ mem_heap_t* heap, /*!< in: memory heap where update vector is built */ upd_t** update_out);/*!< out: update vector */ /************************************************************************* Creates an query graph node of 'update' type to be used in the engine interface. @return own: update node */ UNIV_INTERN upd_node_t* row_create_update_node( /*===================*/ dict_table_t* table, /*!< in: table to update */ mem_heap_t* heap); /*!< in: mem heap from which allocated */ /* Update vector field */ struct upd_field_struct{ unsigned field_no:16; /*!< field number in an index, usually the clustered index, but in updating a secondary index record in btr0cur.c this is the position in the secondary index */ #ifndef UNIV_HOTBACKUP unsigned orig_len:16; /*!< original length of the locally stored part of an externally stored column, or 0 */ que_node_t* exp; /*!< expression for calculating a new value: it refers to column values and constants in the symbol table of the query graph */ #endif /* !UNIV_HOTBACKUP */ dfield_t new_val; /*!< new value for the column */ }; /* Update vector structure */ struct upd_struct{ ulint info_bits; /*!< new value of info bits to record; default is 0 */ ulint n_fields; /*!< number of update fields */ upd_field_t* fields; /*!< array of update fields */ }; #ifndef UNIV_HOTBACKUP /* Update node structure which also implements the delete operation of a row */ struct upd_node_struct{ que_common_t common; /*!< node type: QUE_NODE_UPDATE */ ibool is_delete;/* TRUE if delete, FALSE if update */ ibool searched_update; /* TRUE if searched update, FALSE if positioned */ ibool in_client_interface; /* TRUE if the update node was created for the client interface */ dict_foreign_t* foreign;/* NULL or pointer to a foreign key constraint if this update node is used in doing an ON DELETE or ON UPDATE operation */ upd_node_t* cascade_node;/* NULL or an update node template which is used to implement ON DELETE/UPDATE CASCADE or ... SET NULL for foreign keys */ mem_heap_t* cascade_heap;/* NULL or a mem heap where the cascade node is created */ sel_node_t* select; /*!< query graph subtree implementing a base table cursor: the rows returned will be updated */ btr_pcur_t* pcur; /*!< persistent cursor placed on the clustered index record which should be updated or deleted; the cursor is stored in the graph of 'select' field above, except in the case of the client interface */ dict_table_t* table; /*!< table where updated */ upd_t* update; /*!< update vector for the row */ ulint update_n_fields; /* when this struct is used to implement a cascade operation for foreign keys, we store here the size of the buffer allocated for use as the update vector */ sym_node_list_t columns;/* symbol table nodes for the columns to retrieve from the table */ ibool has_clust_rec_x_lock; /* TRUE if the select which retrieves the records to update already sets an x-lock on the clustered record; note that it must always set at least an s-lock */ ulint cmpl_info;/* information extracted during query compilation; speeds up execution: UPD_NODE_NO_ORD_CHANGE and UPD_NODE_NO_SIZE_CHANGE, ORed */ /*----------------------*/ /* Local storage for this graph node */ ulint state; /*!< node execution state */ dict_index_t* index; /*!< NULL, or the next index whose record should be updated */ dtuple_t* row; /*!< NULL, or a copy (also fields copied to heap) of the row to update; this must be reset to NULL after a successful update */ row_ext_t* ext; /*!< NULL, or prefixes of the externally stored columns in the old row */ dtuple_t* upd_row;/* NULL, or a copy of the updated row */ row_ext_t* upd_ext;/* NULL, or prefixes of the externally stored columns in upd_row */ mem_heap_t* heap; /*!< memory heap used as auxiliary storage; this must be emptied after a successful update */ /*----------------------*/ sym_node_t* table_sym;/* table node in symbol table */ que_node_t* col_assign_list; /* column assignment list */ ulint magic_n; }; #define UPD_NODE_MAGIC_N 1579975 /* Node execution states */ #define UPD_NODE_SET_IX_LOCK 1 /* execution came to the node from a node above and if the field has_clust_rec_x_lock is FALSE, we should set an intention x-lock on the table */ #define UPD_NODE_UPDATE_CLUSTERED 2 /* clustered index record should be updated */ #define UPD_NODE_INSERT_CLUSTERED 3 /* clustered index record should be inserted, old record is already delete marked */ #define UPD_NODE_UPDATE_ALL_SEC 4 /* an ordering field of the clustered index record was changed, or this is a delete operation: should update all the secondary index records */ #define UPD_NODE_UPDATE_SOME_SEC 5 /* secondary index entries should be looked at and updated if an ordering field changed */ /* Compilation info flags: these must fit within 3 bits; see trx0rec.h */ #define UPD_NODE_NO_ORD_CHANGE 1 /* no secondary index record will be changed in the update and no ordering field of the clustered index */ #define UPD_NODE_NO_SIZE_CHANGE 2 /* no record field size will be changed in the update */ #endif /* !UNIV_HOTBACKUP */ #ifndef UNIV_NONINL #include "row0upd.ic" #endif #endif haildb-2.3.2/include/buf0flu.h0000644000175000017500000002027611513177357017011 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2010, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/buf0flu.h The database buffer pool flush algorithm Created 11/5/1995 Heikki Tuuri *******************************************************/ #ifndef buf0flu_h #define buf0flu_h #include "univ.i" #include "ut0byte.h" #ifndef UNIV_HOTBACKUP #include "mtr0types.h" #include "buf0types.h" /********************************************************************//** Remove a block from the flush list of modified blocks. */ UNIV_INTERN void buf_flush_remove( /*=============*/ buf_page_t* bpage); /*!< in: pointer to the block in question */ /********************************************************************//** Relocates a buffer control block on the flush_list. Note that it is assumed that the contents of bpage has already been copied to dpage. */ UNIV_INTERN void buf_flush_relocate_on_flush_list( /*=============================*/ buf_page_t* bpage, /*!< in/out: control block being moved */ buf_page_t* dpage); /*!< in/out: destination block */ /********************************************************************//** Updates the flush system data structures when a write is completed. */ UNIV_INTERN void buf_flush_write_complete( /*=====================*/ buf_page_t* bpage); /*!< in: pointer to the block in question */ /*********************************************************************//** Flushes pages from the end of the LRU list if there is too small a margin of replaceable pages there. */ UNIV_INTERN void buf_flush_free_margin(void); /*=======================*/ #endif /* !UNIV_HOTBACKUP */ /********************************************************************//** Initializes a page for writing to the tablespace. */ UNIV_INTERN void buf_flush_init_for_writing( /*=======================*/ byte* page, /*!< in/out: page */ void* page_zip_, /*!< in/out: compressed page, or NULL */ ib_uint64_t newest_lsn); /*!< in: newest modification lsn to the page */ #ifndef UNIV_HOTBACKUP /*******************************************************************//** This utility flushes dirty blocks from the end of the LRU list or flush_list. NOTE 1: in the case of an LRU flush the calling thread may own latches to pages: to avoid deadlocks, this function must be written so that it cannot end up waiting for these latches! NOTE 2: in the case of a flush list flush, the calling thread is not allowed to own any latches on pages! @return number of blocks for which the write request was queued; ULINT_UNDEFINED if there was a flush of the same type already running */ UNIV_INTERN ulint buf_flush_batch( /*============*/ enum buf_flush flush_type, /*!< in: BUF_FLUSH_LRU or BUF_FLUSH_LIST; if BUF_FLUSH_LIST, then the caller must not own any latches on pages */ ulint min_n, /*!< in: wished minimum mumber of blocks flushed (it is not guaranteed that the actual number is that big, though) */ ib_uint64_t lsn_limit); /*!< in the case BUF_FLUSH_LIST all blocks whose oldest_modification is smaller than this should be flushed (if their number does not exceed min_n), otherwise ignored */ /******************************************************************//** Waits until a flush batch of the given type ends */ UNIV_INTERN void buf_flush_wait_batch_end( /*=====================*/ enum buf_flush type); /*!< in: BUF_FLUSH_LRU or BUF_FLUSH_LIST */ /********************************************************************//** This function should be called at a mini-transaction commit, if a page was modified in it. Puts the block to the list of modified blocks, if it not already in it. */ UNIV_INLINE void buf_flush_note_modification( /*========================*/ buf_block_t* block, /*!< in: block which is modified */ mtr_t* mtr); /*!< in: mtr */ /********************************************************************//** This function should be called when recovery has modified a buffer page. */ UNIV_INLINE void buf_flush_recv_note_modification( /*=============================*/ buf_block_t* block, /*!< in: block which is modified */ ib_uint64_t start_lsn, /*!< in: start lsn of the first mtr in a set of mtr's */ ib_uint64_t end_lsn); /*!< in: end lsn of the last mtr in the set of mtr's */ /********************************************************************//** Returns TRUE if the file page block is immediately suitable for replacement, i.e., transition FILE_PAGE => NOT_USED allowed. @return TRUE if can replace immediately */ UNIV_INTERN ibool buf_flush_ready_for_replace( /*========================*/ buf_page_t* bpage); /*!< in: buffer control block, must be buf_page_in_file(bpage) and in the LRU list */ /** @brief Statistics for selecting flush rate based on redo log generation speed. These statistics are generated for heuristics used in estimating the rate at which we should flush the dirty blocks to avoid bursty IO activity. Note that the rate of flushing not only depends on how many dirty pages we have in the buffer pool but it is also a fucntion of how much redo the workload is generating and at what rate. */ struct buf_flush_stat_struct { ib_uint64_t redo; /*!< amount of redo generated. */ ulint n_flushed; /*!< number of pages flushed. */ }; /** Statistics for selecting flush rate of dirty pages. */ typedef struct buf_flush_stat_struct buf_flush_stat_t; /********************************************************************* Update the historical stats that we are collecting for flush rate heuristics at the end of each interval. */ UNIV_INTERN void buf_flush_stat_update(void); /*=======================*/ /********************************************************************* Determines the fraction of dirty pages that need to be flushed based on the speed at which we generate redo log. Note that if redo log is generated at significant rate without a corresponding increase in the number of dirty pages (for example, an in-memory workload) it can cause IO bursts of flushing. This function implements heuristics to avoid this burstiness. @return number of dirty pages to be flushed / second */ UNIV_INTERN ulint buf_flush_get_desired_flush_rate(void); /*==================================*/ #if defined UNIV_DEBUG || defined UNIV_BUF_DEBUG /******************************************************************//** Validates the flush list. @return TRUE if ok */ UNIV_INTERN ibool buf_flush_validate(void); /*====================*/ #endif /* UNIV_DEBUG || UNIV_BUF_DEBUG */ /******************************************************************//** Initialize the red-black tree to speed up insertions into the flush_list during recovery process. Should be called at the start of recovery process before any page has been read/written. */ UNIV_INTERN void buf_flush_init_flush_rbt(void); /*==========================*/ /******************************************************************//** Frees up the red-black tree. */ UNIV_INTERN void buf_flush_free_flush_rbt(void); /*==========================*/ /** When buf_flush_free_margin is called, it tries to make this many blocks available to replacement in the free list and at the end of the LRU list (to make sure that a read-ahead batch can be read efficiently in a single sweep). */ #define BUF_FLUSH_FREE_BLOCK_MARGIN (5 + BUF_READ_AHEAD_AREA) /** Extra margin to apply above BUF_FLUSH_FREE_BLOCK_MARGIN */ #define BUF_FLUSH_EXTRA_MARGIN (BUF_FLUSH_FREE_BLOCK_MARGIN / 4 + 100) #endif /* !UNIV_HOTBACKUP */ #ifndef UNIV_NONINL #include "buf0flu.ic" #endif #endif haildb-2.3.2/include/que0que.h0000644000175000017500000004514411513177357017034 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2010, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/que0que.h Query graph Created 5/27/1996 Heikki Tuuri *******************************************************/ #ifndef que0que_h #define que0que_h #include "univ.i" #include "data0data.h" #include "dict0types.h" #include "trx0trx.h" #include "trx0roll.h" #include "srv0srv.h" #include "usr0types.h" #include "que0types.h" #include "row0types.h" #include "pars0types.h" /* If the following flag is set TRUE, the module will print trace info of SQL execution in the UNIV_SQL_DEBUG version */ extern ibool que_trace_on; /***********************************************************************//** Adds a query graph to the session's list of graphs. */ UNIV_INTERN void que_graph_publish( /*==============*/ que_t* graph, /*!< in: graph */ sess_t* sess); /*!< in: session */ /***********************************************************************//** Creates a query graph fork node. @return own: fork node */ UNIV_INTERN que_fork_t* que_fork_create( /*============*/ que_t* graph, /*!< in: graph, if NULL then this fork node is assumed to be the graph root */ que_node_t* parent, /*!< in: parent node */ ulint fork_type, /*!< in: fork type */ mem_heap_t* heap); /*!< in: memory heap where created */ /***********************************************************************//** Gets the first thr in a fork. */ UNIV_INLINE que_thr_t* que_fork_get_first_thr( /*===================*/ que_fork_t* fork); /*!< in: query fork */ /***********************************************************************//** Gets the child node of the first thr in a fork. */ UNIV_INLINE que_node_t* que_fork_get_child( /*===============*/ que_fork_t* fork); /*!< in: query fork */ /***********************************************************************//** Sets the parent of a graph node. */ UNIV_INLINE void que_node_set_parent( /*================*/ que_node_t* node, /*!< in: graph node */ que_node_t* parent);/*!< in: parent */ /***********************************************************************//** Creates a query graph thread node. @return own: query thread node */ UNIV_INTERN que_thr_t* que_thr_create( /*===========*/ que_fork_t* parent, /*!< in: parent node, i.e., a fork node */ mem_heap_t* heap); /*!< in: memory heap where created */ /**********************************************************************//** Frees a query graph, but not the heap where it was created. Does not free explicit cursor declarations, they are freed in que_graph_free. */ UNIV_INTERN void que_graph_free_recursive( /*=====================*/ que_node_t* node); /*!< in: query graph node */ /**********************************************************************//** Frees a query graph. */ UNIV_INTERN void que_graph_free( /*===========*/ que_t* graph); /*!< in: query graph; we assume that the memory heap where this graph was created is private to this graph: if not, then use que_graph_free_recursive and free the heap afterwards! */ /**********************************************************************//** Stops a query thread if graph or trx is in a state requiring it. The conditions are tested in the order (1) graph, (2) trx. The kernel mutex has to be reserved. @return TRUE if stopped */ UNIV_INTERN ibool que_thr_stop( /*=========*/ que_thr_t* thr); /*!< in: query thread */ /**********************************************************************//** Moves a thread from another state to the QUE_THR_RUNNING state. Increments the n_active_thrs counters of the query graph and transaction if thr was not active. */ UNIV_INTERN void que_thr_move_to_run_state_for_client( /*=================================*/ que_thr_t* thr, /*!< in: an query thread */ trx_t* trx); /*!< in: transaction */ /**********************************************************************//** The query thread is stopped and made inactive, except in the case where it was put to the lock wait state in lock0lock.c, but the lock has already been granted or the transaction chosen as a victim in deadlock resolution. */ UNIV_INTERN void que_thr_stop_client( /*================*/ que_thr_t* thr); /*!< in: query thread */ /**********************************************************************//** Run a query thread. Handles lock waits. */ UNIV_INTERN void que_run_threads( /*============*/ que_thr_t* thr); /*!< in: query thread */ /**********************************************************************//** After signal handling is finished, returns control to a query graph error handling routine. (Currently, just returns the control to the root of the graph so that the graph can communicate an error message to the client.) */ UNIV_INTERN void que_fork_error_handle( /*==================*/ trx_t* trx, /*!< in: trx */ que_t* fork); /*!< in: query graph which was run before signal handling started, NULL not allowed */ /**********************************************************************//** Moves a suspended query thread to the QUE_THR_RUNNING state and releases a single worker thread to execute it. This function should be used to end the wait state of a query thread waiting for a lock or a stored procedure completion. */ UNIV_INTERN void que_thr_end_wait( /*=============*/ que_thr_t* thr, /*!< in: query thread in the QUE_THR_LOCK_WAIT, or QUE_THR_PROCEDURE_WAIT, or QUE_THR_SIG_REPLY_WAIT state */ que_thr_t** next_thr); /*!< in/out: next query thread to run; if the value which is passed in is a pointer to a NULL pointer, then the calling function can start running a new query thread */ /**********************************************************************//** Same as que_thr_end_wait, but no parameter next_thr available. */ UNIV_INTERN void que_thr_end_wait_no_next_thr( /*=========================*/ que_thr_t* thr); /*!< in: query thread in the QUE_THR_LOCK_WAIT, or QUE_THR_PROCEDURE_WAIT, or QUE_THR_SIG_REPLY_WAIT state */ /**********************************************************************//** Starts execution of a command in a query fork. Picks a query thread which is not in the QUE_THR_RUNNING state and moves it to that state. If none can be chosen, a situation which may arise in parallelized fetches, NULL is returned. @return a query thread of the graph moved to QUE_THR_RUNNING state, or NULL; the query thread should be executed by que_run_threads by the caller */ UNIV_INTERN que_thr_t* que_fork_start_command( /*===================*/ que_fork_t* fork); /*!< in: a query fork */ /***********************************************************************//** Gets the trx of a query thread. */ UNIV_INLINE trx_t* thr_get_trx( /*========*/ que_thr_t* thr); /*!< in: query thread */ /*******************************************************************//** Determines if this thread is rolling back an incomplete transaction in crash recovery. @return TRUE if thr is rolling back an incomplete transaction in crash recovery */ UNIV_INLINE ibool thr_is_recv( /*========*/ const que_thr_t* thr); /*!< in: query thread */ /***********************************************************************//** Gets the type of a graph node. */ UNIV_INLINE ulint que_node_get_type( /*==============*/ que_node_t* node); /*!< in: graph node */ /***********************************************************************//** Gets pointer to the value data type field of a graph node. */ UNIV_INLINE dtype_t* que_node_get_data_type( /*===================*/ que_node_t* node); /*!< in: graph node */ /***********************************************************************//** Gets pointer to the value dfield of a graph node. */ UNIV_INLINE dfield_t* que_node_get_val( /*=============*/ que_node_t* node); /*!< in: graph node */ /***********************************************************************//** Gets the value buffer size of a graph node. @return val buffer size, not defined if val.data == NULL in node */ UNIV_INLINE ulint que_node_get_val_buf_size( /*======================*/ que_node_t* node); /*!< in: graph node */ /***********************************************************************//** Sets the value buffer size of a graph node. */ UNIV_INLINE void que_node_set_val_buf_size( /*======================*/ que_node_t* node, /*!< in: graph node */ ulint size); /*!< in: size */ /*********************************************************************//** Gets the next list node in a list of query graph nodes. */ UNIV_INLINE que_node_t* que_node_get_next( /*==============*/ que_node_t* node); /*!< in: node in a list */ /*********************************************************************//** Gets the parent node of a query graph node. @return parent node or NULL */ UNIV_INLINE que_node_t* que_node_get_parent( /*================*/ que_node_t* node); /*!< in: node */ /****************************************************************//** Get the first containing loop node (e.g. while_node_t or for_node_t) for the given node, or NULL if the node is not within a loop. @return containing loop node, or NULL. */ UNIV_INTERN que_node_t* que_node_get_containing_loop_node( /*==============================*/ que_node_t* node); /*!< in: node */ /*********************************************************************//** Catenates a query graph node to a list of them, possible empty list. @return one-way list of nodes */ UNIV_INLINE que_node_t* que_node_list_add_last( /*===================*/ que_node_t* node_list, /*!< in: node list, or NULL */ que_node_t* node); /*!< in: node */ /*********************************************************************//** Gets a query graph node list length. @return length, for NULL list 0 */ UNIV_INLINE ulint que_node_list_get_len( /*==================*/ que_node_t* node_list); /*!< in: node list, or NULL */ /**********************************************************************//** Checks if graph, trx, or session is in a state where the query thread should be stopped. @return TRUE if should be stopped; NOTE that if the peek is made without reserving the kernel mutex, then another peek with the mutex reserved is necessary before deciding the actual stopping */ UNIV_INLINE ibool que_thr_peek_stop( /*==============*/ que_thr_t* thr); /*!< in: query thread */ /***********************************************************************//** Returns TRUE if the query graph is for a SELECT statement. @return TRUE if a select */ UNIV_INLINE ibool que_graph_is_select( /*================*/ que_t* graph); /*!< in: graph */ /**********************************************************************//** Prints info of an SQL query graph node. */ UNIV_INTERN void que_node_print_info( /*================*/ que_node_t* node); /*!< in: query graph node */ /*********************************************************************//** Evaluate the given SQL @return error code or DB_SUCCESS */ UNIV_INTERN ulint que_eval_sql( /*=========*/ pars_info_t* info, /*!< in: info struct, or NULL */ const char* sql, /*!< in: SQL string */ ibool reserve_dict_mutex, /*!< in: if TRUE, acquire/release dict_sys->mutex around call to pars_sql. */ trx_t* trx); /*!< in: trx */ /************************************************************************//** Moves a thread from another state to the QUE_THR_RUNNING state. Increments the n_active_thrs counters of the query graph and transaction if thr was not active. ***NOTE***: This is the only functions in which such a transition is allowed to happen! */ UNIV_INTERN void que_thr_move_to_run_state( /*======================*/ que_thr_t* thr); /*!< in: an query thread */ /************************************************************************//** A patch for a client used to 'stop' a dummy query thread used in client select, when there is no error or lock wait. TODO: Currently only called from row0merge, needs to be removed. */ UNIV_INTERN void que_thr_stop_for_client_no_error( /*=============================*/ que_thr_t* thr, /*!< in: query thread */ trx_t* trx); /*!< in: transaction */ /*************************************************************************//** Reset the variables. */ UNIV_INTERN void que_var_init(void); /*==============*/ /* Query graph query thread node: the fields are protected by the kernel mutex with the exceptions named below */ struct que_thr_struct{ que_common_t common; /*!< type: QUE_NODE_THR */ ulint magic_n; /*!< magic number to catch memory corruption */ que_node_t* child; /*!< graph child node */ que_t* graph; /*!< graph where this node belongs */ ibool is_active; /*!< TRUE if the thread has been set to the run state in que_thr_move_to_run_state, but not deactivated in que_thr_dec_reference_count */ ulint state; /*!< state of the query thread */ UT_LIST_NODE_T(que_thr_t) thrs; /*!< list of thread nodes of the fork node */ UT_LIST_NODE_T(que_thr_t) trx_thrs; /*!< lists of threads in wait list of the trx */ UT_LIST_NODE_T(que_thr_t) queue; /*!< list of runnable thread nodes in the server task queue */ /*------------------------------*/ /* The following fields are private to the OS thread executing the query thread, and are not protected by the kernel mutex: */ que_node_t* run_node; /*!< pointer to the node where the subgraph down from this node is currently executed */ que_node_t* prev_node; /*!< pointer to the node from which the control came */ ulint resource; /*!< resource usage of the query thread thus far */ ulint lock_state; /*!< lock state of thread (table or row) */ }; #define QUE_THR_MAGIC_N 8476583 #define QUE_THR_MAGIC_FREED 123461526 /* Query graph fork node: its fields are protected by the kernel mutex */ struct que_fork_struct{ que_common_t common; /*!< type: QUE_NODE_FORK */ que_t* graph; /*!< query graph of this node */ ulint fork_type; /*!< fork type */ ulint n_active_thrs; /*!< if this is the root of a graph, the number query threads that have been started in que_thr_move_to_run_state but for which que_thr_dec_refer_count has not yet been called */ trx_t* trx; /*!< transaction: this is set only in the root node */ ulint state; /*!< state of the fork node */ que_thr_t* caller; /*!< pointer to a possible calling query thread */ UT_LIST_BASE_NODE_T(que_thr_t) thrs; /*!< list of query threads */ /*------------------------------*/ /* The fields in this section are defined only in the root node */ sym_tab_t* sym_tab; /*!< symbol table of the query, generated by the parser, or NULL if the graph was created 'by hand' */ pars_info_t* info; /*!< info struct, or NULL */ /* The following cur_... fields are relevant only in a select graph */ ulint cur_end; /*!< QUE_CUR_NOT_DEFINED, QUE_CUR_START, QUE_CUR_END */ ulint cur_pos; /*!< if there are n rows in the result set, values 0 and n + 1 mean before first row, or after last row, depending on cur_end; values 1...n mean a row index */ ibool cur_on_row; /*!< TRUE if cursor is on a row, i.e., it is not before the first row or after the last row */ dulint n_inserts; /*!< number of rows inserted */ dulint n_updates; /*!< number of rows updated */ dulint n_deletes; /*!< number of rows deleted */ sel_node_t* last_sel_node; /*!< last executed select node, or NULL if none */ UT_LIST_NODE_T(que_fork_t) graphs; /*!< list of query graphs of a session or a stored procedure */ /*------------------------------*/ mem_heap_t* heap; /*!< memory heap where the fork was created */ }; /* Query fork (or graph) types */ #define QUE_FORK_SELECT_NON_SCROLL 1 /* forward-only cursor */ #define QUE_FORK_SELECT_SCROLL 2 /* scrollable cursor */ #define QUE_FORK_INSERT 3 #define QUE_FORK_UPDATE 4 #define QUE_FORK_ROLLBACK 5 /* This is really the undo graph used in rollback, no signal-sending roll_node in this graph */ #define QUE_FORK_PURGE 6 #define QUE_FORK_EXECUTE 7 #define QUE_FORK_PROCEDURE 8 #define QUE_FORK_PROCEDURE_CALL 9 #define QUE_FORK_USER_INTERFACE 10 #define QUE_FORK_RECOVERY 11 /* Query fork (or graph) states */ #define QUE_FORK_ACTIVE 1 #define QUE_FORK_COMMAND_WAIT 2 #define QUE_FORK_INVALID 3 #define QUE_FORK_BEING_FREED 4 /* Flag which is ORed to control structure statement node types */ #define QUE_NODE_CONTROL_STAT 1024 /* Query graph node types */ #define QUE_NODE_LOCK 1 #define QUE_NODE_INSERT 2 #define QUE_NODE_UPDATE 4 #define QUE_NODE_CURSOR 5 #define QUE_NODE_SELECT 6 #define QUE_NODE_AGGREGATE 7 #define QUE_NODE_FORK 8 #define QUE_NODE_THR 9 #define QUE_NODE_UNDO 10 #define QUE_NODE_COMMIT 11 #define QUE_NODE_ROLLBACK 12 #define QUE_NODE_PURGE 13 #define QUE_NODE_CREATE_TABLE 14 #define QUE_NODE_CREATE_INDEX 15 #define QUE_NODE_SYMBOL 16 #define QUE_NODE_RES_WORD 17 #define QUE_NODE_FUNC 18 #define QUE_NODE_ORDER 19 #define QUE_NODE_PROC (20 + QUE_NODE_CONTROL_STAT) #define QUE_NODE_IF (21 + QUE_NODE_CONTROL_STAT) #define QUE_NODE_WHILE (22 + QUE_NODE_CONTROL_STAT) #define QUE_NODE_ASSIGNMENT 23 #define QUE_NODE_FETCH 24 #define QUE_NODE_OPEN 25 #define QUE_NODE_COL_ASSIGNMENT 26 #define QUE_NODE_FOR (27 + QUE_NODE_CONTROL_STAT) #define QUE_NODE_RETURN 28 #define QUE_NODE_ROW_PRINTF 29 #define QUE_NODE_ELSIF 30 #define QUE_NODE_CALL 31 #define QUE_NODE_EXIT 32 /* Query thread states */ #define QUE_THR_RUNNING 1 #define QUE_THR_PROCEDURE_WAIT 2 #define QUE_THR_COMPLETED 3 /* in selects this means that the thread is at the end of its result set (or start, in case of a scroll cursor); in other statements, this means the thread has done its task */ #define QUE_THR_COMMAND_WAIT 4 #define QUE_THR_LOCK_WAIT 5 #define QUE_THR_SIG_REPLY_WAIT 6 #define QUE_THR_SUSPENDED 7 #define QUE_THR_ERROR 8 /* Query thread lock states */ #define QUE_THR_LOCK_NOLOCK 0 #define QUE_THR_LOCK_ROW 1 #define QUE_THR_LOCK_TABLE 2 /* From where the cursor position is counted */ #define QUE_CUR_NOT_DEFINED 1 #define QUE_CUR_START 2 #define QUE_CUR_END 3 #ifndef UNIV_NONINL #include "que0que.ic" #endif #endif haildb-2.3.2/include/trx0roll.h0000644000175000017500000002375211513177357017236 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/trx0roll.h Transaction rollback Created 3/26/1996 Heikki Tuuri *******************************************************/ #ifndef trx0roll_h #define trx0roll_h #include "univ.i" #include "trx0trx.h" #include "trx0types.h" #include "mtr0mtr.h" #include "trx0sys.h" #define trx_roll_free_all_savepoints(s) trx_roll_savepoints_free((s), NULL) /*******************************************************************//** Determines if this transaction is rolling back an incomplete transaction in crash recovery. @return TRUE if trx is an incomplete transaction that is being rolled back in crash recovery */ UNIV_INTERN ibool trx_is_recv( /*========*/ const trx_t* trx); /*!< in: transaction */ /*******************************************************************//** Returns a transaction savepoint taken at this point in time. @return savepoint */ UNIV_INTERN trx_savept_t trx_savept_take( /*============*/ trx_t* trx); /*!< in: transaction */ /*******************************************************************//** Creates an undo number array. */ UNIV_INTERN trx_undo_arr_t* trx_undo_arr_create(void); /*=====================*/ /*******************************************************************//** Frees an undo number array. */ UNIV_INTERN void trx_undo_arr_free( /*==============*/ trx_undo_arr_t* arr); /*!< in: undo number array */ /*******************************************************************//** Returns pointer to nth element in an undo number array. @return pointer to the nth element */ UNIV_INLINE trx_undo_inf_t* trx_undo_arr_get_nth_info( /*======================*/ trx_undo_arr_t* arr, /*!< in: undo number array */ ulint n); /*!< in: position */ /***********************************************************************//** Tries truncate the undo logs. */ UNIV_INTERN void trx_roll_try_truncate( /*==================*/ trx_t* trx); /*!< in/out: transaction */ /********************************************************************//** Pops the topmost record when the two undo logs of a transaction are seen as a single stack of records ordered by their undo numbers. Inserts the undo number of the popped undo record to the array of currently processed undo numbers in the transaction. When the query thread finishes processing of this undo record, it must be released with trx_undo_rec_release. @return undo log record copied to heap, NULL if none left, or if the undo number of the top record would be less than the limit */ UNIV_INTERN trx_undo_rec_t* trx_roll_pop_top_rec_of_trx( /*========================*/ trx_t* trx, /*!< in: transaction */ undo_no_t limit, /*!< in: least undo number we need */ roll_ptr_t* roll_ptr,/*!< out: roll pointer to undo record */ mem_heap_t* heap); /*!< in: memory heap where copied */ /********************************************************************//** Reserves an undo log record for a query thread to undo. This should be called if the query thread gets the undo log record not using the pop function above. @return TRUE if succeeded */ UNIV_INTERN ibool trx_undo_rec_reserve( /*=================*/ trx_t* trx, /*!< in/out: transaction */ undo_no_t undo_no);/*!< in: undo number of the record */ /*******************************************************************//** Releases a reserved undo record. */ UNIV_INTERN void trx_undo_rec_release( /*=================*/ trx_t* trx, /*!< in/out: transaction */ undo_no_t undo_no);/*!< in: undo number */ /*********************************************************************//** Starts a rollback operation. */ UNIV_INTERN void trx_rollback( /*=========*/ trx_t* trx, /*!< in: transaction */ trx_sig_t* sig, /*!< in: signal starting the rollback */ que_thr_t** next_thr);/*!< in/out: next query thread to run; if the value which is passed in is a pointer to a NULL pointer, then the calling function can start running a new query thread */ /*******************************************************************//** Rollback or clean up any incomplete transactions which were encountered in crash recovery. If the transaction already was committed, then we clean up a possible insert undo log. If the transaction was not yet committed, then we roll it back. */ UNIV_INTERN void trx_rollback_or_clean_recovered( /*============================*/ ibool all); /*!< in: FALSE=roll back dictionary transactions; TRUE=roll back all non-PREPARED transactions */ /*******************************************************************//** Rollback or clean up any incomplete transactions which were encountered in crash recovery. If the transaction already was committed, then we clean up a possible insert undo log. If the transaction was not yet committed, then we roll it back. Note: this is done in a background thread. @return a dummy parameter */ UNIV_INTERN os_thread_ret_t trx_rollback_or_clean_all_recovered( /*================================*/ void* arg __attribute__((unused))); /*!< in: a dummy parameter required by os_thread_create */ /****************************************************************//** Finishes a transaction rollback. */ UNIV_INTERN void trx_finish_rollback_off_kernel( /*===========================*/ que_t* graph, /*!< in: undo graph which can now be freed */ trx_t* trx, /*!< in: transaction */ que_thr_t** next_thr);/*!< in/out: next query thread to run; if the value which is passed in is a pointer to a NULL pointer, then the calling function can start running a new query thread; if this parameter is NULL, it is ignored */ /****************************************************************//** Builds an undo 'query' graph for a transaction. The actual rollback is performed by executing this query graph like a query subprocedure call. The reply about the completion of the rollback will be sent by this graph. @return own: the query graph */ UNIV_INTERN que_t* trx_roll_graph_build( /*=================*/ trx_t* trx); /*!< in: trx handle */ /*********************************************************************//** Creates a rollback command node struct. @return own: rollback node struct */ UNIV_INTERN roll_node_t* roll_node_create( /*=============*/ mem_heap_t* heap); /*!< in: mem heap where created */ /***********************************************************//** Performs an execution step for a rollback command node in a query graph. @return query thread to run next, or NULL */ UNIV_INTERN que_thr_t* trx_rollback_step( /*==============*/ que_thr_t* thr); /*!< in: query thread */ /*******************************************************************//** Rollback a user transaction. @return error code or DB_SUCCESS */ UNIV_INTERN int trx_general_rollback( /*=================*/ trx_t* trx, /*!< in: transaction handle */ ibool partial,/*!< in: TRUE if partial rollback requested */ trx_savept_t* savept);/*!< in: pointer to savepoint undo number, if partial rollback requested */ /*******************************************************************//** Frees savepoint structs starting from savep, if savep == NULL then free all savepoints. */ UNIV_INTERN void trx_roll_savepoints_free( /*=====================*/ trx_t* trx, /*!< in: transaction handle */ trx_named_savept_t* savep); /*!< in: free all savepoints > this one; if this is NULL, free all savepoints of trx */ /** A cell of trx_undo_arr_struct; used during a rollback and a purge */ struct trx_undo_inf_struct{ trx_id_t trx_no; /*!< transaction number: not defined during a rollback */ undo_no_t undo_no;/*!< undo number of an undo record */ ibool in_use; /*!< TRUE if the cell is in use */ }; /** During a rollback and a purge, undo numbers of undo records currently being processed are stored in this array */ struct trx_undo_arr_struct{ ulint n_cells; /*!< number of cells in the array */ ulint n_used; /*!< number of cells currently in use */ trx_undo_inf_t* infos; /*!< the array of undo infos */ mem_heap_t* heap; /*!< memory heap from which allocated */ }; /** Rollback node states */ enum roll_node_state { ROLL_NODE_SEND = 1, /*!< about to send a rollback signal to the transaction */ ROLL_NODE_WAIT /*!< rollback signal sent to the transaction, waiting for completion */ }; /** Rollback command node in a query graph */ struct roll_node_struct{ que_common_t common; /*!< node type: QUE_NODE_ROLLBACK */ enum roll_node_state state; /*!< node execution state */ ibool partial;/*!< TRUE if we want a partial rollback */ trx_savept_t savept; /*!< savepoint to which to roll back, in the case of a partial rollback */ }; /** A savepoint set with SQL's "SAVEPOINT savepoint_id" command */ struct trx_named_savept_struct{ void* name; /*!< savepoint name, it can be any arbitrary set of characters. Note: When allocating memory for trx_named structure allocate an additional name_len bytes and set the value of name to point to the first byte past an instance of this structure */ ulint name_len; /*!< length of name in bytes */ trx_savept_t savept; /*!< the undo number corresponding to the savepoint */ UT_LIST_NODE_T(trx_named_savept_t) trx_savepoints; /*!< the list of savepoints of a transaction */ }; #ifndef UNIV_NONINL #include "trx0roll.ic" #endif #endif haildb-2.3.2/include/rem0rec.ic0000644000175000017500000013321111513177357017141 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1994, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /********************************************************************//** @file include/rem0rec.ic Record manager Created 5/30/1994 Heikki Tuuri *************************************************************************/ #include "mach0data.h" #include "ut0byte.h" #include "dict0dict.h" /* Compact flag ORed to the extra size returned by rec_get_offsets() */ #define REC_OFFS_COMPACT ((ulint) 1 << 31) /* SQL NULL flag in offsets returned by rec_get_offsets() */ #define REC_OFFS_SQL_NULL ((ulint) 1 << 31) /* External flag in offsets returned by rec_get_offsets() */ #define REC_OFFS_EXTERNAL ((ulint) 1 << 30) /* Mask for offsets returned by rec_get_offsets() */ #define REC_OFFS_MASK (REC_OFFS_EXTERNAL - 1) /* Offsets of the bit-fields in an old-style record. NOTE! In the table the most significant bytes and bits are written below less significant. (1) byte offset (2) bit usage within byte downward from origin -> 1 8 bits pointer to next record 2 8 bits pointer to next record 3 1 bit short flag 7 bits number of fields 4 3 bits number of fields 5 bits heap number 5 8 bits heap number 6 4 bits n_owned 4 bits info bits */ /* Offsets of the bit-fields in a new-style record. NOTE! In the table the most significant bytes and bits are written below less significant. (1) byte offset (2) bit usage within byte downward from origin -> 1 8 bits relative offset of next record 2 8 bits relative offset of next record the relative offset is an unsigned 16-bit integer: (offset_of_next_record - offset_of_this_record) mod 64Ki, where mod is the modulo as a non-negative number; we can calculate the offset of the next record with the formula: relative_offset + offset_of_this_record mod UNIV_PAGE_SIZE 3 3 bits status: 000=conventional record 001=node pointer record (inside B-tree) 010=infimum record 011=supremum record 1xx=reserved 5 bits heap number 4 8 bits heap number 5 4 bits n_owned 4 bits info bits */ /* We list the byte offsets from the origin of the record, the mask, and the shift needed to obtain each bit-field of the record. */ #define REC_NEXT 2 #define REC_NEXT_MASK 0xFFFFUL #define REC_NEXT_SHIFT 0 #define REC_OLD_SHORT 3 /* This is single byte bit-field */ #define REC_OLD_SHORT_MASK 0x1UL #define REC_OLD_SHORT_SHIFT 0 #define REC_OLD_N_FIELDS 4 #define REC_OLD_N_FIELDS_MASK 0x7FEUL #define REC_OLD_N_FIELDS_SHIFT 1 #define REC_NEW_STATUS 3 /* This is single byte bit-field */ #define REC_NEW_STATUS_MASK 0x7UL #define REC_NEW_STATUS_SHIFT 0 #define REC_OLD_HEAP_NO 5 #define REC_HEAP_NO_MASK 0xFFF8UL #if 0 /* defined in rem0rec.h for use of page0zip.c */ #define REC_NEW_HEAP_NO 4 #define REC_HEAP_NO_SHIFT 3 #endif #define REC_OLD_N_OWNED 6 /* This is single byte bit-field */ #define REC_NEW_N_OWNED 5 /* This is single byte bit-field */ #define REC_N_OWNED_MASK 0xFUL #define REC_N_OWNED_SHIFT 0 #define REC_OLD_INFO_BITS 6 /* This is single byte bit-field */ #define REC_NEW_INFO_BITS 5 /* This is single byte bit-field */ #define REC_INFO_BITS_MASK 0xF0UL #define REC_INFO_BITS_SHIFT 0 /* The following masks are used to filter the SQL null bit from one-byte and two-byte offsets */ #define REC_1BYTE_SQL_NULL_MASK 0x80UL #define REC_2BYTE_SQL_NULL_MASK 0x8000UL /* In a 2-byte offset the second most significant bit denotes a field stored to another page: */ #define REC_2BYTE_EXTERN_MASK 0x4000UL #if REC_OLD_SHORT_MASK << (8 * (REC_OLD_SHORT - 3)) \ ^ REC_OLD_N_FIELDS_MASK << (8 * (REC_OLD_N_FIELDS - 4)) \ ^ REC_HEAP_NO_MASK << (8 * (REC_OLD_HEAP_NO - 4)) \ ^ REC_N_OWNED_MASK << (8 * (REC_OLD_N_OWNED - 3)) \ ^ REC_INFO_BITS_MASK << (8 * (REC_OLD_INFO_BITS - 3)) \ ^ 0xFFFFFFFFUL # error "sum of old-style masks != 0xFFFFFFFFUL" #endif #if REC_NEW_STATUS_MASK << (8 * (REC_NEW_STATUS - 3)) \ ^ REC_HEAP_NO_MASK << (8 * (REC_NEW_HEAP_NO - 4)) \ ^ REC_N_OWNED_MASK << (8 * (REC_NEW_N_OWNED - 3)) \ ^ REC_INFO_BITS_MASK << (8 * (REC_NEW_INFO_BITS - 3)) \ ^ 0xFFFFFFUL # error "sum of new-style masks != 0xFFFFFFUL" #endif /***********************************************************//** Sets the value of the ith field SQL null bit of an old-style record. */ UNIV_INTERN void rec_set_nth_field_null_bit( /*=======================*/ rec_t* rec, /*!< in: record */ ulint i, /*!< in: ith field */ ibool val); /*!< in: value to set */ /***********************************************************//** Sets an old-style record field to SQL null. The physical size of the field is not changed. */ UNIV_INTERN void rec_set_nth_field_sql_null( /*=======================*/ rec_t* rec, /*!< in: record */ ulint n); /*!< in: index of the field */ /******************************************************//** Gets a bit field from within 1 byte. */ UNIV_INLINE ulint rec_get_bit_field_1( /*================*/ const rec_t* rec, /*!< in: pointer to record origin */ ulint offs, /*!< in: offset from the origin down */ ulint mask, /*!< in: mask used to filter bits */ ulint shift) /*!< in: shift right applied after masking */ { ut_ad(rec); return((mach_read_from_1(rec - offs) & mask) >> shift); } /******************************************************//** Sets a bit field within 1 byte. */ UNIV_INLINE void rec_set_bit_field_1( /*================*/ rec_t* rec, /*!< in: pointer to record origin */ ulint val, /*!< in: value to set */ ulint offs, /*!< in: offset from the origin down */ ulint mask, /*!< in: mask used to filter bits */ ulint shift) /*!< in: shift right applied after masking */ { ut_ad(rec); ut_ad(offs <= REC_N_OLD_EXTRA_BYTES); ut_ad(mask); ut_ad(mask <= 0xFFUL); ut_ad(((mask >> shift) << shift) == mask); ut_ad(((val << shift) & mask) == (val << shift)); mach_write_to_1(rec - offs, (mach_read_from_1(rec - offs) & ~mask) | (val << shift)); } /******************************************************//** Gets a bit field from within 2 bytes. */ UNIV_INLINE ulint rec_get_bit_field_2( /*================*/ const rec_t* rec, /*!< in: pointer to record origin */ ulint offs, /*!< in: offset from the origin down */ ulint mask, /*!< in: mask used to filter bits */ ulint shift) /*!< in: shift right applied after masking */ { ut_ad(rec); return((mach_read_from_2(rec - offs) & mask) >> shift); } /******************************************************//** Sets a bit field within 2 bytes. */ UNIV_INLINE void rec_set_bit_field_2( /*================*/ rec_t* rec, /*!< in: pointer to record origin */ ulint val, /*!< in: value to set */ ulint offs, /*!< in: offset from the origin down */ ulint mask, /*!< in: mask used to filter bits */ ulint shift) /*!< in: shift right applied after masking */ { ut_ad(rec); ut_ad(offs <= REC_N_OLD_EXTRA_BYTES); ut_ad(mask > 0xFFUL); ut_ad(mask <= 0xFFFFUL); ut_ad((mask >> shift) & 1); ut_ad(0 == ((mask >> shift) & ((mask >> shift) + 1))); ut_ad(((mask >> shift) << shift) == mask); ut_ad(((val << shift) & mask) == (val << shift)); mach_write_to_2(rec - offs, (mach_read_from_2(rec - offs) & ~mask) | (val << shift)); } /******************************************************//** The following function is used to get the pointer of the next chained record on the same page. @return pointer to the next chained record, or NULL if none */ UNIV_INLINE const rec_t* rec_get_next_ptr_const( /*===================*/ const rec_t* rec, /*!< in: physical record */ ulint comp) /*!< in: nonzero=compact page format */ { ulint field_value; ut_ad(REC_NEXT_MASK == 0xFFFFUL); ut_ad(REC_NEXT_SHIFT == 0); field_value = mach_read_from_2(rec - REC_NEXT); if (UNIV_UNLIKELY(field_value == 0)) { return(NULL); } if (UNIV_EXPECT(comp, REC_OFFS_COMPACT)) { #if UNIV_PAGE_SIZE <= 32768 /* Note that for 64 KiB pages, field_value can 'wrap around' and the debug assertion is not valid */ /* In the following assertion, field_value is interpreted as signed 16-bit integer in 2's complement arithmetics. If all platforms defined int16_t in the standard headers, the expression could be written simpler as (int16_t) field_value + ut_align_offset(...) < UNIV_PAGE_SIZE */ ut_ad((field_value >= 32768 ? field_value - 65536 : field_value) + ut_align_offset(rec, UNIV_PAGE_SIZE) < UNIV_PAGE_SIZE); #endif /* There must be at least REC_N_NEW_EXTRA_BYTES + 1 between each record. */ ut_ad((field_value > REC_N_NEW_EXTRA_BYTES && field_value < 32768) || field_value < (ib_uint16_t) -REC_N_NEW_EXTRA_BYTES); return((byte*) ut_align_down(rec, UNIV_PAGE_SIZE) + ut_align_offset(rec + field_value, UNIV_PAGE_SIZE)); } else { ut_ad(field_value < UNIV_PAGE_SIZE); return((byte*) ut_align_down(rec, UNIV_PAGE_SIZE) + field_value); } } /******************************************************//** The following function is used to get the pointer of the next chained record on the same page. @return pointer to the next chained record, or NULL if none */ UNIV_INLINE rec_t* rec_get_next_ptr( /*=============*/ rec_t* rec, /*!< in: physical record */ ulint comp) /*!< in: nonzero=compact page format */ { return((rec_t*) rec_get_next_ptr_const(rec, comp)); } /******************************************************//** The following function is used to get the offset of the next chained record on the same page. @return the page offset of the next chained record, or 0 if none */ UNIV_INLINE ulint rec_get_next_offs( /*==============*/ const rec_t* rec, /*!< in: physical record */ ulint comp) /*!< in: nonzero=compact page format */ { ulint field_value; #if REC_NEXT_MASK != 0xFFFFUL # error "REC_NEXT_MASK != 0xFFFFUL" #endif #if REC_NEXT_SHIFT # error "REC_NEXT_SHIFT != 0" #endif field_value = mach_read_from_2(rec - REC_NEXT); if (UNIV_EXPECT(comp, REC_OFFS_COMPACT)) { #if UNIV_PAGE_SIZE <= 32768 /* Note that for 64 KiB pages, field_value can 'wrap around' and the debug assertion is not valid */ /* In the following assertion, field_value is interpreted as signed 16-bit integer in 2's complement arithmetics. If all platforms defined int16_t in the standard headers, the expression could be written simpler as (int16_t) field_value + ut_align_offset(...) < UNIV_PAGE_SIZE */ ut_ad((field_value >= 32768 ? field_value - 65536 : field_value) + ut_align_offset(rec, UNIV_PAGE_SIZE) < UNIV_PAGE_SIZE); #endif if (UNIV_UNLIKELY(field_value == 0)) { return(0); } /* There must be at least REC_N_NEW_EXTRA_BYTES + 1 between each record. */ ut_ad((field_value > REC_N_NEW_EXTRA_BYTES && field_value < 32768) || field_value < (ib_uint16_t) -REC_N_NEW_EXTRA_BYTES); return(ut_align_offset(rec + field_value, UNIV_PAGE_SIZE)); } else { ut_ad(field_value < UNIV_PAGE_SIZE); return(field_value); } } /******************************************************//** The following function is used to set the next record offset field of an old-style record. */ UNIV_INLINE void rec_set_next_offs_old( /*==================*/ rec_t* rec, /*!< in: old-style physical record */ ulint next) /*!< in: offset of the next record */ { ut_ad(rec); ut_ad(UNIV_PAGE_SIZE > next); #if REC_NEXT_MASK != 0xFFFFUL # error "REC_NEXT_MASK != 0xFFFFUL" #endif #if REC_NEXT_SHIFT # error "REC_NEXT_SHIFT != 0" #endif mach_write_to_2(rec - REC_NEXT, next); } /******************************************************//** The following function is used to set the next record offset field of a new-style record. */ UNIV_INLINE void rec_set_next_offs_new( /*==================*/ rec_t* rec, /*!< in/out: new-style physical record */ ulint next) /*!< in: offset of the next record */ { ulint field_value; ut_ad(rec); ut_ad(UNIV_PAGE_SIZE > next); if (UNIV_UNLIKELY(!next)) { field_value = 0; } else { /* The following two statements calculate next - offset_of_rec mod 64Ki, where mod is the modulo as a non-negative number */ field_value = (ulint) ((lint) next - (lint) ut_align_offset(rec, UNIV_PAGE_SIZE)); field_value &= REC_NEXT_MASK; } mach_write_to_2(rec - REC_NEXT, field_value); } /******************************************************//** The following function is used to get the number of fields in an old-style record. @return number of data fields */ UNIV_INLINE ulint rec_get_n_fields_old( /*=================*/ const rec_t* rec) /*!< in: physical record */ { ulint ret; ut_ad(rec); ret = rec_get_bit_field_2(rec, REC_OLD_N_FIELDS, REC_OLD_N_FIELDS_MASK, REC_OLD_N_FIELDS_SHIFT); ut_ad(ret <= REC_MAX_N_FIELDS); ut_ad(ret > 0); return(ret); } /******************************************************//** The following function is used to set the number of fields in an old-style record. */ UNIV_INLINE void rec_set_n_fields_old( /*=================*/ rec_t* rec, /*!< in: physical record */ ulint n_fields) /*!< in: the number of fields */ { ut_ad(rec); ut_ad(n_fields <= REC_MAX_N_FIELDS); ut_ad(n_fields > 0); rec_set_bit_field_2(rec, n_fields, REC_OLD_N_FIELDS, REC_OLD_N_FIELDS_MASK, REC_OLD_N_FIELDS_SHIFT); } /******************************************************//** The following function retrieves the status bits of a new-style record. @return status bits */ UNIV_INLINE ulint rec_get_status( /*===========*/ const rec_t* rec) /*!< in: physical record */ { ulint ret; ut_ad(rec); ret = rec_get_bit_field_1(rec, REC_NEW_STATUS, REC_NEW_STATUS_MASK, REC_NEW_STATUS_SHIFT); ut_ad((ret & ~REC_NEW_STATUS_MASK) == 0); return(ret); } /******************************************************//** The following function is used to get the number of fields in a record. @return number of data fields */ UNIV_INLINE ulint rec_get_n_fields( /*=============*/ const rec_t* rec, /*!< in: physical record */ const dict_index_t* dict_index) /*!< in: record descriptor */ { ut_ad(rec); ut_ad(dict_index); if (!dict_table_is_comp(dict_index->table)) { return(rec_get_n_fields_old(rec)); } switch (rec_get_status(rec)) { case REC_STATUS_ORDINARY: return(dict_index_get_n_fields(dict_index)); case REC_STATUS_NODE_PTR: return(dict_index_get_n_unique_in_tree(dict_index) + 1); case REC_STATUS_INFIMUM: case REC_STATUS_SUPREMUM: return(1); default: ut_error; return(ULINT_UNDEFINED); } } /******************************************************//** The following function is used to get the number of records owned by the previous directory record. @return number of owned records */ UNIV_INLINE ulint rec_get_n_owned_old( /*================*/ const rec_t* rec) /*!< in: old-style physical record */ { return(rec_get_bit_field_1(rec, REC_OLD_N_OWNED, REC_N_OWNED_MASK, REC_N_OWNED_SHIFT)); } /******************************************************//** The following function is used to set the number of owned records. */ UNIV_INLINE void rec_set_n_owned_old( /*================*/ rec_t* rec, /*!< in: old-style physical record */ ulint n_owned) /*!< in: the number of owned */ { rec_set_bit_field_1(rec, n_owned, REC_OLD_N_OWNED, REC_N_OWNED_MASK, REC_N_OWNED_SHIFT); } /******************************************************//** The following function is used to get the number of records owned by the previous directory record. @return number of owned records */ UNIV_INLINE ulint rec_get_n_owned_new( /*================*/ const rec_t* rec) /*!< in: new-style physical record */ { return(rec_get_bit_field_1(rec, REC_NEW_N_OWNED, REC_N_OWNED_MASK, REC_N_OWNED_SHIFT)); } /******************************************************//** The following function is used to set the number of owned records. */ UNIV_INLINE void rec_set_n_owned_new( /*================*/ rec_t* rec, /*!< in/out: new-style physical record */ page_zip_des_t* page_zip,/*!< in/out: compressed page, or NULL */ ulint n_owned)/*!< in: the number of owned */ { rec_set_bit_field_1(rec, n_owned, REC_NEW_N_OWNED, REC_N_OWNED_MASK, REC_N_OWNED_SHIFT); #ifdef WITH_ZIP if (UNIV_LIKELY_NULL(page_zip) && UNIV_LIKELY(rec_get_status(rec) != REC_STATUS_SUPREMUM)) { page_zip_rec_set_owned(page_zip, rec, n_owned); } #endif /* WITH_ZIP */ } /******************************************************//** The following function is used to retrieve the info bits of a record. @return info bits */ UNIV_INLINE ulint rec_get_info_bits( /*==============*/ const rec_t* rec, /*!< in: physical record */ ulint comp) /*!< in: nonzero=compact page format */ { return(rec_get_bit_field_1( rec, comp ? REC_NEW_INFO_BITS : REC_OLD_INFO_BITS, REC_INFO_BITS_MASK, REC_INFO_BITS_SHIFT)); } /******************************************************//** The following function is used to set the info bits of a record. */ UNIV_INLINE void rec_set_info_bits_old( /*==================*/ rec_t* rec, /*!< in: old-style physical record */ ulint bits) /*!< in: info bits */ { rec_set_bit_field_1(rec, bits, REC_OLD_INFO_BITS, REC_INFO_BITS_MASK, REC_INFO_BITS_SHIFT); } /******************************************************//** The following function is used to set the info bits of a record. */ UNIV_INLINE void rec_set_info_bits_new( /*==================*/ rec_t* rec, /*!< in/out: new-style physical record */ ulint bits) /*!< in: info bits */ { rec_set_bit_field_1(rec, bits, REC_NEW_INFO_BITS, REC_INFO_BITS_MASK, REC_INFO_BITS_SHIFT); } /******************************************************//** The following function is used to set the status bits of a new-style record. */ UNIV_INLINE void rec_set_status( /*===========*/ rec_t* rec, /*!< in/out: physical record */ ulint bits) /*!< in: info bits */ { rec_set_bit_field_1(rec, bits, REC_NEW_STATUS, REC_NEW_STATUS_MASK, REC_NEW_STATUS_SHIFT); } /******************************************************//** The following function is used to retrieve the info and status bits of a record. (Only compact records have status bits.) @return info bits */ UNIV_INLINE ulint rec_get_info_and_status_bits( /*=========================*/ const rec_t* rec, /*!< in: physical record */ ulint comp) /*!< in: nonzero=compact page format */ { ulint bits; #if (REC_NEW_STATUS_MASK >> REC_NEW_STATUS_SHIFT) \ & (REC_INFO_BITS_MASK >> REC_INFO_BITS_SHIFT) # error "REC_NEW_STATUS_MASK and REC_INFO_BITS_MASK overlap" #endif if (UNIV_EXPECT(comp, REC_OFFS_COMPACT)) { bits = rec_get_info_bits(rec, TRUE) | rec_get_status(rec); } else { bits = rec_get_info_bits(rec, FALSE); ut_ad(!(bits & ~(REC_INFO_BITS_MASK >> REC_INFO_BITS_SHIFT))); } return(bits); } /******************************************************//** The following function is used to set the info and status bits of a record. (Only compact records have status bits.) */ UNIV_INLINE void rec_set_info_and_status_bits( /*=========================*/ rec_t* rec, /*!< in/out: physical record */ ulint bits) /*!< in: info bits */ { #if (REC_NEW_STATUS_MASK >> REC_NEW_STATUS_SHIFT) \ & (REC_INFO_BITS_MASK >> REC_INFO_BITS_SHIFT) # error "REC_NEW_STATUS_MASK and REC_INFO_BITS_MASK overlap" #endif rec_set_status(rec, bits & REC_NEW_STATUS_MASK); rec_set_info_bits_new(rec, bits & ~REC_NEW_STATUS_MASK); } /******************************************************//** The following function tells if record is delete marked. @return nonzero if delete marked */ UNIV_INLINE ulint rec_get_deleted_flag( /*=================*/ const rec_t* rec, /*!< in: physical record */ ulint comp) /*!< in: nonzero=compact page format */ { if (UNIV_EXPECT(comp, REC_OFFS_COMPACT)) { return(UNIV_UNLIKELY( rec_get_bit_field_1(rec, REC_NEW_INFO_BITS, REC_INFO_DELETED_FLAG, REC_INFO_BITS_SHIFT))); } else { return(UNIV_UNLIKELY( rec_get_bit_field_1(rec, REC_OLD_INFO_BITS, REC_INFO_DELETED_FLAG, REC_INFO_BITS_SHIFT))); } } /******************************************************//** The following function is used to set the deleted bit. */ UNIV_INLINE void rec_set_deleted_flag_old( /*=====================*/ rec_t* rec, /*!< in: old-style physical record */ ulint flag) /*!< in: nonzero if delete marked */ { ulint val; val = rec_get_info_bits(rec, FALSE); if (flag) { val |= REC_INFO_DELETED_FLAG; } else { val &= ~REC_INFO_DELETED_FLAG; } rec_set_info_bits_old(rec, val); } /******************************************************//** The following function is used to set the deleted bit. */ UNIV_INLINE void rec_set_deleted_flag_new( /*=====================*/ rec_t* rec, /*!< in/out: new-style physical record */ page_zip_des_t* page_zip,/*!< in/out: compressed page, or NULL */ ulint flag) /*!< in: nonzero if delete marked */ { ulint val; val = rec_get_info_bits(rec, TRUE); if (flag) { val |= REC_INFO_DELETED_FLAG; } else { val &= ~REC_INFO_DELETED_FLAG; } rec_set_info_bits_new(rec, val); #ifdef WITH_ZIP if (UNIV_LIKELY_NULL(page_zip)) { page_zip_rec_set_deleted(page_zip, rec, flag); } #endif /* WITH_ZIP */ } /******************************************************//** The following function tells if a new-style record is a node pointer. @return TRUE if node pointer */ UNIV_INLINE ibool rec_get_node_ptr_flag( /*==================*/ const rec_t* rec) /*!< in: physical record */ { return(REC_STATUS_NODE_PTR == rec_get_status(rec)); } /******************************************************//** The following function is used to get the order number of an old-style record in the heap of the index page. @return heap order number */ UNIV_INLINE ulint rec_get_heap_no_old( /*================*/ const rec_t* rec) /*!< in: physical record */ { return(rec_get_bit_field_2(rec, REC_OLD_HEAP_NO, REC_HEAP_NO_MASK, REC_HEAP_NO_SHIFT)); } /******************************************************//** The following function is used to set the heap number field in an old-style record. */ UNIV_INLINE void rec_set_heap_no_old( /*================*/ rec_t* rec, /*!< in: physical record */ ulint heap_no)/*!< in: the heap number */ { rec_set_bit_field_2(rec, heap_no, REC_OLD_HEAP_NO, REC_HEAP_NO_MASK, REC_HEAP_NO_SHIFT); } /******************************************************//** The following function is used to get the order number of a new-style record in the heap of the index page. @return heap order number */ UNIV_INLINE ulint rec_get_heap_no_new( /*================*/ const rec_t* rec) /*!< in: physical record */ { return(rec_get_bit_field_2(rec, REC_NEW_HEAP_NO, REC_HEAP_NO_MASK, REC_HEAP_NO_SHIFT)); } /******************************************************//** The following function is used to set the heap number field in a new-style record. */ UNIV_INLINE void rec_set_heap_no_new( /*================*/ rec_t* rec, /*!< in/out: physical record */ ulint heap_no)/*!< in: the heap number */ { rec_set_bit_field_2(rec, heap_no, REC_NEW_HEAP_NO, REC_HEAP_NO_MASK, REC_HEAP_NO_SHIFT); } /******************************************************//** The following function is used to test whether the data offsets in the record are stored in one-byte or two-byte format. @return TRUE if 1-byte form */ UNIV_INLINE ibool rec_get_1byte_offs_flag( /*====================*/ const rec_t* rec) /*!< in: physical record */ { #if TRUE != 1 #error "TRUE != 1" #endif return(rec_get_bit_field_1(rec, REC_OLD_SHORT, REC_OLD_SHORT_MASK, REC_OLD_SHORT_SHIFT)); } /******************************************************//** The following function is used to set the 1-byte offsets flag. */ UNIV_INLINE void rec_set_1byte_offs_flag( /*====================*/ rec_t* rec, /*!< in: physical record */ ibool flag) /*!< in: TRUE if 1byte form */ { #if TRUE != 1 #error "TRUE != 1" #endif ut_ad(flag <= TRUE); rec_set_bit_field_1(rec, flag, REC_OLD_SHORT, REC_OLD_SHORT_MASK, REC_OLD_SHORT_SHIFT); } /******************************************************//** Returns the offset of nth field end if the record is stored in the 1-byte offsets form. If the field is SQL null, the flag is ORed in the returned value. @return offset of the start of the field, SQL null flag ORed */ UNIV_INLINE ulint rec_1_get_field_end_info( /*=====================*/ const rec_t* rec, /*!< in: record */ ulint n) /*!< in: field index */ { ut_ad(rec_get_1byte_offs_flag(rec)); ut_ad(n < rec_get_n_fields_old(rec)); return(mach_read_from_1(rec - (REC_N_OLD_EXTRA_BYTES + n + 1))); } /******************************************************//** Returns the offset of nth field end if the record is stored in the 2-byte offsets form. If the field is SQL null, the flag is ORed in the returned value. @return offset of the start of the field, SQL null flag and extern storage flag ORed */ UNIV_INLINE ulint rec_2_get_field_end_info( /*=====================*/ const rec_t* rec, /*!< in: record */ ulint n) /*!< in: field index */ { ut_ad(!rec_get_1byte_offs_flag(rec)); ut_ad(n < rec_get_n_fields_old(rec)); return(mach_read_from_2(rec - (REC_N_OLD_EXTRA_BYTES + 2 * n + 2))); } /* Get the base address of offsets. The extra_size is stored at this position, and following positions hold the end offsets of the fields. */ #define rec_offs_base(offsets) (offsets + REC_OFFS_HEADER_SIZE) /**********************************************************//** The following function returns the number of allocated elements for an array of offsets. @return number of elements */ UNIV_INLINE ulint rec_offs_get_n_alloc( /*=================*/ const ulint* offsets)/*!< in: array for rec_get_offsets() */ { ulint n_alloc; ut_ad(offsets); n_alloc = offsets[0]; ut_ad(n_alloc > REC_OFFS_HEADER_SIZE); UNIV_MEM_ASSERT_W(offsets, n_alloc * sizeof *offsets); return(n_alloc); } /**********************************************************//** The following function sets the number of allocated elements for an array of offsets. */ UNIV_INLINE void rec_offs_set_n_alloc( /*=================*/ ulint* offsets, /*!< out: array for rec_get_offsets(), must be allocated */ ulint n_alloc) /*!< in: number of elements */ { ut_ad(offsets); ut_ad(n_alloc > REC_OFFS_HEADER_SIZE); UNIV_MEM_ASSERT_AND_ALLOC(offsets, n_alloc * sizeof *offsets); offsets[0] = n_alloc; } /**********************************************************//** The following function returns the number of fields in a record. @return number of fields */ UNIV_INLINE ulint rec_offs_n_fields( /*==============*/ const ulint* offsets)/*!< in: array returned by rec_get_offsets() */ { ulint n_fields; ut_ad(offsets); n_fields = offsets[1]; ut_ad(n_fields > 0); ut_ad(n_fields <= REC_MAX_N_FIELDS); ut_ad(n_fields + REC_OFFS_HEADER_SIZE <= rec_offs_get_n_alloc(offsets)); return(n_fields); } /************************************************************//** Validates offsets returned by rec_get_offsets(). @return TRUE if valid */ UNIV_INLINE ibool rec_offs_validate( /*==============*/ const rec_t* rec, /*!< in: record or NULL */ const dict_index_t* dict_index,/*!< in: record descriptor or NULL */ const ulint* offsets)/*!< in: array returned by rec_get_offsets() */ { ulint i = rec_offs_n_fields(offsets); ulint last = ULINT_MAX; ulint comp = *rec_offs_base(offsets) & REC_OFFS_COMPACT; if (rec) { ut_ad((ulint) rec == offsets[2]); if (!comp) { ut_a(rec_get_n_fields_old(rec) >= i); } } if (dict_index) { ulint max_n_fields; ut_ad((ulint) dict_index == offsets[3]); max_n_fields = ut_max( dict_index_get_n_fields(dict_index), dict_index_get_n_unique_in_tree(dict_index) + 1); if (comp && rec) { switch (rec_get_status(rec)) { case REC_STATUS_ORDINARY: break; case REC_STATUS_NODE_PTR: max_n_fields = dict_index_get_n_unique_in_tree( dict_index) + 1; break; case REC_STATUS_INFIMUM: case REC_STATUS_SUPREMUM: max_n_fields = 1; break; default: ut_error; } } /* dict_index->n_def == 0 for dummy indexes if !comp */ ut_a(!comp || dict_index->n_def); ut_a(!dict_index->n_def || i <= max_n_fields); } while (i--) { ulint curr = rec_offs_base(offsets)[1 + i] & REC_OFFS_MASK; ut_a(curr <= last); last = curr; } return(TRUE); } #ifdef UNIV_DEBUG /************************************************************//** Updates debug data in offsets, in order to avoid bogus rec_offs_validate() failures. */ UNIV_INLINE void rec_offs_make_valid( /*================*/ const rec_t* rec, /*!< in: record */ const dict_index_t* index, /*!< in: record descriptor */ ulint* offsets)/*!< in: array returned by rec_get_offsets() */ { ut_ad(rec); ut_ad(index); ut_ad(offsets); ut_ad(rec_get_n_fields(rec, index) >= rec_offs_n_fields(offsets)); offsets[2] = (ulint) rec; offsets[3] = (ulint) index; } #endif /* UNIV_DEBUG */ /************************************************************//** The following function is used to get an offset to the nth data field in a record. @return offset from the origin of rec */ UNIV_INLINE ulint rec_get_nth_field_offs( /*===================*/ const ulint* offsets,/*!< in: array returned by rec_get_offsets() */ ulint n, /*!< in: index of the field */ ulint* len) /*!< out: length of the field; UNIV_SQL_NULL if SQL null */ { ulint offs; ulint length; ut_ad(n < rec_offs_n_fields(offsets)); ut_ad(len); if (UNIV_UNLIKELY(n == 0)) { offs = 0; } else { offs = rec_offs_base(offsets)[n] & REC_OFFS_MASK; } length = rec_offs_base(offsets)[1 + n]; if (length & REC_OFFS_SQL_NULL) { length = UNIV_SQL_NULL; } else { length &= REC_OFFS_MASK; length -= offs; } *len = length; return(offs); } /******************************************************//** Determine if the offsets are for a record in the new compact format. @return nonzero if compact format */ UNIV_INLINE ulint rec_offs_comp( /*==========*/ const ulint* offsets)/*!< in: array returned by rec_get_offsets() */ { ut_ad(rec_offs_validate(NULL, NULL, offsets)); return(*rec_offs_base(offsets) & REC_OFFS_COMPACT); } /******************************************************//** Determine if the offsets are for a record containing externally stored columns. @return nonzero if externally stored */ UNIV_INLINE ulint rec_offs_any_extern( /*================*/ const ulint* offsets)/*!< in: array returned by rec_get_offsets() */ { ut_ad(rec_offs_validate(NULL, NULL, offsets)); return(UNIV_UNLIKELY(*rec_offs_base(offsets) & REC_OFFS_EXTERNAL)); } /******************************************************//** Returns nonzero if the extern bit is set in nth field of rec. @return nonzero if externally stored */ UNIV_INLINE ulint rec_offs_nth_extern( /*================*/ const ulint* offsets,/*!< in: array returned by rec_get_offsets() */ ulint n) /*!< in: nth field */ { ut_ad(rec_offs_validate(NULL, NULL, offsets)); ut_ad(n < rec_offs_n_fields(offsets)); return(UNIV_UNLIKELY(rec_offs_base(offsets)[1 + n] & REC_OFFS_EXTERNAL)); } /******************************************************//** Returns nonzero if the SQL NULL bit is set in nth field of rec. @return nonzero if SQL NULL */ UNIV_INLINE ulint rec_offs_nth_sql_null( /*==================*/ const ulint* offsets,/*!< in: array returned by rec_get_offsets() */ ulint n) /*!< in: nth field */ { ut_ad(rec_offs_validate(NULL, NULL, offsets)); ut_ad(n < rec_offs_n_fields(offsets)); return(UNIV_UNLIKELY(rec_offs_base(offsets)[1 + n] & REC_OFFS_SQL_NULL)); } /******************************************************//** Gets the physical size of a field. @return length of field */ UNIV_INLINE ulint rec_offs_nth_size( /*==============*/ const ulint* offsets,/*!< in: array returned by rec_get_offsets() */ ulint n) /*!< in: nth field */ { ut_ad(rec_offs_validate(NULL, NULL, offsets)); ut_ad(n < rec_offs_n_fields(offsets)); if (!n) { return(rec_offs_base(offsets)[1 + n] & REC_OFFS_MASK); } return((rec_offs_base(offsets)[1 + n] - rec_offs_base(offsets)[n]) & REC_OFFS_MASK); } /******************************************************//** Returns the number of extern bits set in a record. @return number of externally stored fields */ UNIV_INLINE ulint rec_offs_n_extern( /*==============*/ const ulint* offsets)/*!< in: array returned by rec_get_offsets() */ { ulint n = 0; if (rec_offs_any_extern(offsets)) { ulint i; for (i = rec_offs_n_fields(offsets); i--; ) { if (rec_offs_nth_extern(offsets, i)) { n++; } } } return(n); } /******************************************************//** Returns the offset of n - 1th field end if the record is stored in the 1-byte offsets form. If the field is SQL null, the flag is ORed in the returned value. This function and the 2-byte counterpart are defined here because the C-compiler was not able to sum negative and positive constant offsets, and warned of constant arithmetic overflow within the compiler. @return offset of the start of the PREVIOUS field, SQL null flag ORed */ UNIV_INLINE ulint rec_1_get_prev_field_end_info( /*==========================*/ const rec_t* rec, /*!< in: record */ ulint n) /*!< in: field index */ { ut_ad(rec_get_1byte_offs_flag(rec)); ut_ad(n <= rec_get_n_fields_old(rec)); return(mach_read_from_1(rec - (REC_N_OLD_EXTRA_BYTES + n))); } /******************************************************//** Returns the offset of n - 1th field end if the record is stored in the 2-byte offsets form. If the field is SQL null, the flag is ORed in the returned value. @return offset of the start of the PREVIOUS field, SQL null flag ORed */ UNIV_INLINE ulint rec_2_get_prev_field_end_info( /*==========================*/ const rec_t* rec, /*!< in: record */ ulint n) /*!< in: field index */ { ut_ad(!rec_get_1byte_offs_flag(rec)); ut_ad(n <= rec_get_n_fields_old(rec)); return(mach_read_from_2(rec - (REC_N_OLD_EXTRA_BYTES + 2 * n))); } /******************************************************//** Sets the field end info for the nth field if the record is stored in the 1-byte format. */ UNIV_INLINE void rec_1_set_field_end_info( /*=====================*/ rec_t* rec, /*!< in: record */ ulint n, /*!< in: field index */ ulint info) /*!< in: value to set */ { ut_ad(rec_get_1byte_offs_flag(rec)); ut_ad(n < rec_get_n_fields_old(rec)); mach_write_to_1(rec - (REC_N_OLD_EXTRA_BYTES + n + 1), info); } /******************************************************//** Sets the field end info for the nth field if the record is stored in the 2-byte format. */ UNIV_INLINE void rec_2_set_field_end_info( /*=====================*/ rec_t* rec, /*!< in: record */ ulint n, /*!< in: field index */ ulint info) /*!< in: value to set */ { ut_ad(!rec_get_1byte_offs_flag(rec)); ut_ad(n < rec_get_n_fields_old(rec)); mach_write_to_2(rec - (REC_N_OLD_EXTRA_BYTES + 2 * n + 2), info); } /******************************************************//** Returns the offset of nth field start if the record is stored in the 1-byte offsets form. @return offset of the start of the field */ UNIV_INLINE ulint rec_1_get_field_start_offs( /*=======================*/ const rec_t* rec, /*!< in: record */ ulint n) /*!< in: field index */ { ut_ad(rec_get_1byte_offs_flag(rec)); ut_ad(n <= rec_get_n_fields_old(rec)); if (n == 0) { return(0); } return(rec_1_get_prev_field_end_info(rec, n) & ~REC_1BYTE_SQL_NULL_MASK); } /******************************************************//** Returns the offset of nth field start if the record is stored in the 2-byte offsets form. @return offset of the start of the field */ UNIV_INLINE ulint rec_2_get_field_start_offs( /*=======================*/ const rec_t* rec, /*!< in: record */ ulint n) /*!< in: field index */ { ut_ad(!rec_get_1byte_offs_flag(rec)); ut_ad(n <= rec_get_n_fields_old(rec)); if (n == 0) { return(0); } return(rec_2_get_prev_field_end_info(rec, n) & ~(REC_2BYTE_SQL_NULL_MASK | REC_2BYTE_EXTERN_MASK)); } /******************************************************//** The following function is used to read the offset of the start of a data field in the record. The start of an SQL null field is the end offset of the previous non-null field, or 0, if none exists. If n is the number of the last field + 1, then the end offset of the last field is returned. @return offset of the start of the field */ UNIV_INLINE ulint rec_get_field_start_offs( /*=====================*/ const rec_t* rec, /*!< in: record */ ulint n) /*!< in: field index */ { ut_ad(rec); ut_ad(n <= rec_get_n_fields_old(rec)); if (n == 0) { return(0); } if (rec_get_1byte_offs_flag(rec)) { return(rec_1_get_field_start_offs(rec, n)); } return(rec_2_get_field_start_offs(rec, n)); } /************************************************************//** Gets the physical size of an old-style field. Also an SQL null may have a field of size > 0, if the data type is of a fixed size. @return field size in bytes */ UNIV_INLINE ulint rec_get_nth_field_size( /*===================*/ const rec_t* rec, /*!< in: record */ ulint n) /*!< in: index of the field */ { ulint os; ulint next_os; os = rec_get_field_start_offs(rec, n); next_os = rec_get_field_start_offs(rec, n + 1); ut_ad(next_os - os < UNIV_PAGE_SIZE); return(next_os - os); } /***********************************************************//** This is used to modify the value of an already existing field in a record. The previous value must have exactly the same size as the new value. If len is UNIV_SQL_NULL then the field is treated as an SQL null. For records in ROW_FORMAT=COMPACT (new-style records), len must not be UNIV_SQL_NULL unless the field already is SQL null. */ UNIV_INLINE void rec_set_nth_field( /*==============*/ rec_t* rec, /*!< in: record */ const ulint* offsets,/*!< in: array returned by rec_get_offsets() */ ulint n, /*!< in: index number of the field */ const void* data, /*!< in: pointer to the data if not SQL null */ ulint len) /*!< in: length of the data or UNIV_SQL_NULL */ { byte* data2; ulint len2; ut_ad(rec); ut_ad(rec_offs_validate(rec, NULL, offsets)); if (UNIV_UNLIKELY(len == UNIV_SQL_NULL)) { if (!rec_offs_nth_sql_null(offsets, n)) { ut_a(!rec_offs_comp(offsets)); rec_set_nth_field_sql_null(rec, n); } return; } data2 = rec_get_nth_field(rec, offsets, n, &len2); if (len2 == UNIV_SQL_NULL) { ut_ad(!rec_offs_comp(offsets)); rec_set_nth_field_null_bit(rec, n, FALSE); ut_ad(len == rec_get_nth_field_size(rec, n)); } else { ut_ad(len2 == len); } ut_memcpy(data2, data, len); } /**********************************************************//** The following function returns the data size of an old-style physical record, that is the sum of field lengths. SQL null fields are counted as length 0 fields. The value returned by the function is the distance from record origin to record end in bytes. @return size */ UNIV_INLINE ulint rec_get_data_size_old( /*==================*/ const rec_t* rec) /*!< in: physical record */ { ut_ad(rec); return(rec_get_field_start_offs(rec, rec_get_n_fields_old(rec))); } /**********************************************************//** The following function sets the number of fields in offsets. */ UNIV_INLINE void rec_offs_set_n_fields( /*==================*/ ulint* offsets, /*!< in/out: array returned by rec_get_offsets() */ ulint n_fields) /*!< in: number of fields */ { ut_ad(offsets); ut_ad(n_fields > 0); ut_ad(n_fields <= REC_MAX_N_FIELDS); ut_ad(n_fields + REC_OFFS_HEADER_SIZE <= rec_offs_get_n_alloc(offsets)); offsets[1] = n_fields; } /**********************************************************//** The following function returns the data size of a physical record, that is the sum of field lengths. SQL null fields are counted as length 0 fields. The value returned by the function is the distance from record origin to record end in bytes. @return size */ UNIV_INLINE ulint rec_offs_data_size( /*===============*/ const ulint* offsets)/*!< in: array returned by rec_get_offsets() */ { ulint size; ut_ad(rec_offs_validate(NULL, NULL, offsets)); size = rec_offs_base(offsets)[rec_offs_n_fields(offsets)] & REC_OFFS_MASK; ut_ad(size < UNIV_PAGE_SIZE); return(size); } /**********************************************************//** Returns the total size of record minus data size of record. The value returned by the function is the distance from record start to record origin in bytes. @return size */ UNIV_INLINE ulint rec_offs_extra_size( /*================*/ const ulint* offsets)/*!< in: array returned by rec_get_offsets() */ { ulint size; ut_ad(rec_offs_validate(NULL, NULL, offsets)); size = *rec_offs_base(offsets) & ~(REC_OFFS_COMPACT | REC_OFFS_EXTERNAL); ut_ad(size < UNIV_PAGE_SIZE); return(size); } /**********************************************************//** Returns the total size of a physical record. @return size */ UNIV_INLINE ulint rec_offs_size( /*==========*/ const ulint* offsets)/*!< in: array returned by rec_get_offsets() */ { return(rec_offs_data_size(offsets) + rec_offs_extra_size(offsets)); } /**********************************************************//** Returns a pointer to the end of the record. @return pointer to end */ UNIV_INLINE byte* rec_get_end( /*========*/ rec_t* rec, /*!< in: pointer to record */ const ulint* offsets)/*!< in: array returned by rec_get_offsets() */ { ut_ad(rec_offs_validate(rec, NULL, offsets)); return(rec + rec_offs_data_size(offsets)); } /**********************************************************//** Returns a pointer to the start of the record. @return pointer to start */ UNIV_INLINE byte* rec_get_start( /*==========*/ rec_t* rec, /*!< in: pointer to record */ const ulint* offsets)/*!< in: array returned by rec_get_offsets() */ { ut_ad(rec_offs_validate(rec, NULL, offsets)); return(rec - rec_offs_extra_size(offsets)); } /***************************************************************//** Copies a physical record to a buffer. @return pointer to the origin of the copy */ UNIV_INLINE rec_t* rec_copy( /*=====*/ void* buf, /*!< in: buffer */ const rec_t* rec, /*!< in: physical record */ const ulint* offsets)/*!< in: array returned by rec_get_offsets() */ { ulint extra_len; ulint data_len; ut_ad(rec && buf); ut_ad(rec_offs_validate((rec_t*) rec, NULL, offsets)); ut_ad(rec_validate(rec, offsets)); extra_len = rec_offs_extra_size(offsets); data_len = rec_offs_data_size(offsets); ut_memcpy(buf, rec - extra_len, extra_len + data_len); return((byte*)buf + extra_len); } /**********************************************************//** Returns the extra size of an old-style physical record if we know its data size and number of fields. @return extra size */ UNIV_INLINE ulint rec_get_converted_extra_size( /*=========================*/ ulint data_size, /*!< in: data size */ ulint n_fields, /*!< in: number of fields */ ulint n_ext) /*!< in: number of externally stored columns */ { if (!n_ext && data_size <= REC_1BYTE_OFFS_LIMIT) { return(REC_N_OLD_EXTRA_BYTES + n_fields); } return(REC_N_OLD_EXTRA_BYTES + 2 * n_fields); } /**********************************************************//** The following function returns the size of a data tuple when converted to a physical record. @return size */ UNIV_INLINE ulint rec_get_converted_size( /*===================*/ dict_index_t* dict_index, /*!< in: record descriptor */ const dtuple_t* dtuple, /*!< in: data tuple */ ulint n_ext) /*!< in: number of externally stored columns */ { ulint data_size; ulint extra_size; ut_ad(dict_index); ut_ad(dtuple); ut_ad(dtuple_check_typed(dtuple)); ut_ad(dict_index->type & DICT_UNIVERSAL || dtuple_get_n_fields(dtuple) == (((dtuple_get_info_bits(dtuple) & REC_NEW_STATUS_MASK) == REC_STATUS_NODE_PTR) ? dict_index_get_n_unique_in_tree(dict_index) + 1 : dict_index_get_n_fields(dict_index))); if (dict_table_is_comp(dict_index->table)) { return(rec_get_converted_size_comp(dict_index, dtuple_get_info_bits(dtuple) & REC_NEW_STATUS_MASK, dtuple->fields, dtuple->n_fields, NULL)); } data_size = dtuple_get_data_size(dtuple, 0); extra_size = rec_get_converted_extra_size( data_size, dtuple_get_n_fields(dtuple), n_ext); return(data_size + extra_size); } #ifndef UNIV_HOTBACKUP /************************************************************//** Folds a prefix of a physical record to a ulint. Folds only existing fields, that is, checks that we do not run out of the record. @return the folded value */ UNIV_INLINE ulint rec_fold( /*=====*/ const rec_t* rec, /*!< in: the physical record */ const ulint* offsets, /*!< in: array returned by rec_get_offsets() */ ulint n_fields, /*!< in: number of complete fields to fold */ ulint n_bytes, /*!< in: number of bytes to fold in an incomplete last field */ dulint tree_id) /*!< in: index tree id */ { ulint i; const byte* data; ulint len; ulint fold; ulint n_fields_rec; ut_ad(rec_offs_validate(rec, NULL, offsets)); ut_ad(rec_validate(rec, offsets)); ut_ad(n_fields + n_bytes > 0); n_fields_rec = rec_offs_n_fields(offsets); ut_ad(n_fields <= n_fields_rec); ut_ad(n_fields < n_fields_rec || n_bytes == 0); if (n_fields > n_fields_rec) { n_fields = n_fields_rec; } if (n_fields == n_fields_rec) { n_bytes = 0; } fold = ut_fold_dulint(tree_id); for (i = 0; i < n_fields; i++) { data = rec_get_nth_field(rec, offsets, i, &len); if (len != UNIV_SQL_NULL) { fold = ut_fold_ulint_pair(fold, ut_fold_binary(data, len)); } } if (n_bytes > 0) { data = rec_get_nth_field(rec, offsets, i, &len); if (len != UNIV_SQL_NULL) { if (len > n_bytes) { len = n_bytes; } fold = ut_fold_ulint_pair(fold, ut_fold_binary(data, len)); } } return(fold); } #endif /* !UNIV_HOTBACKUP */ haildb-2.3.2/include/sync0sync.h0000644000175000017500000005012111513177357017367 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2010, Innobase Oy. All Rights Reserved. Copyright (c) 2008, Google Inc. Portions of this file contain modifications contributed and copyrighted by Google, Inc. Those modifications are gratefully acknowledged and are described briefly in the InnoDB documentation. The contributions by Google are incorporated with their permission, and subject to the conditions contained in the file COPYING.Google. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/sync0sync.h Mutex, the basic synchronization primitive Created 9/5/1995 Heikki Tuuri *******************************************************/ #ifndef sync0sync_h #define sync0sync_h #include "univ.i" #include "sync0types.h" #include "ut0lst.h" #include "ut0mem.h" #include "os0thread.h" #include "os0sync.h" #include "sync0arr.h" #ifdef HAVE_WINDOWS_ATOMICS typedef LONG lock_word_t; /*!< On Windows, InterlockedExchange operates on LONG variable */ #else typedef byte lock_word_t; #endif /******************************************************************//** Initializes the synchronization data structures. */ UNIV_INTERN void sync_init(void); /*===========*/ /******************************************************************//** Frees the resources in synchronization data structures. */ UNIV_INTERN void sync_close(void); /*===========*/ /******************************************************************//** Creates, or rather, initializes a mutex object to a specified memory location (which must be appropriately aligned). The mutex is initialized in the reset state. Explicit freeing of the mutex with mutex_free is necessary only if the memory block containing it is freed. */ #ifdef UNIV_DEBUG # ifdef UNIV_SYNC_DEBUG # define mutex_create(M, level) \ mutex_create_func((M), #M, (level), __FILE__, __LINE__) # else # define mutex_create(M, level) \ mutex_create_func((M), #M, __FILE__, __LINE__) # endif #else # define mutex_create(M, level) \ mutex_create_func((M), __FILE__, __LINE__) #endif /******************************************************************//** Creates, or rather, initializes a mutex object in a specified memory location (which must be appropriately aligned). The mutex is initialized in the reset state. Explicit freeing of the mutex with mutex_free is necessary only if the memory block containing it is freed. */ UNIV_INTERN void mutex_create_func( /*==============*/ mutex_t* mutex, /*!< in: pointer to memory */ #ifdef UNIV_DEBUG const char* cmutex_name, /*!< in: mutex name */ # ifdef UNIV_SYNC_DEBUG ulint level, /*!< in: level */ # endif /* UNIV_SYNC_DEBUG */ #endif /* UNIV_DEBUG */ const char* cfile_name, /*!< in: file name where created */ ulint cline); /*!< in: file line where created */ #undef mutex_free /* Fix for MacOS X */ /******************************************************************//** Calling this function is obligatory only if the memory buffer containing the mutex is freed. Removes a mutex object from the mutex list. The mutex is checked to be in the reset state. */ UNIV_INTERN void mutex_free( /*=======*/ mutex_t* mutex); /*!< in: mutex */ /**************************************************************//** NOTE! The following macro should be used in mutex locking, not the corresponding function. */ #define mutex_enter(M) mutex_enter_func((M), __FILE__, __LINE__) /**************************************************************//** NOTE! The following macro should be used in mutex locking, not the corresponding function. */ /* NOTE! currently same as mutex_enter! */ #define mutex_enter_fast(M) mutex_enter_func((M), __FILE__, __LINE__) /******************************************************************//** NOTE! Use the corresponding macro in the header file, not this function directly. Locks a mutex for the current thread. If the mutex is reserved the function spins a preset time (controlled by SYNC_SPIN_ROUNDS) waiting for the mutex before suspending the thread. */ UNIV_INLINE void mutex_enter_func( /*=============*/ mutex_t* mutex, /*!< in: pointer to mutex */ const char* file_name, /*!< in: file name where locked */ ulint line); /*!< in: line where locked */ /**************************************************************//** NOTE! The following macro should be used in mutex locking, not the corresponding function. */ #define mutex_enter_nowait(M) \ mutex_enter_nowait_func((M), __FILE__, __LINE__) /********************************************************************//** NOTE! Use the corresponding macro in the header file, not this function directly. Tries to lock the mutex for the current thread. If the lock is not acquired immediately, returns with return value 1. @return 0 if succeed, 1 if not */ UNIV_INTERN ulint mutex_enter_nowait_func( /*====================*/ mutex_t* mutex, /*!< in: pointer to mutex */ const char* file_name, /*!< in: file name where mutex requested */ ulint line); /*!< in: line where requested */ /******************************************************************//** Unlocks a mutex owned by the current thread. */ UNIV_INLINE void mutex_exit( /*=======*/ mutex_t* mutex); /*!< in: pointer to mutex */ #ifdef UNIV_SYNC_DEBUG /******************************************************************//** Returns TRUE if no mutex or rw-lock is currently locked. Works only in the debug version. @return TRUE if no mutexes and rw-locks reserved */ UNIV_INTERN ibool sync_all_freed(void); /*================*/ #endif /* UNIV_SYNC_DEBUG */ /*##################################################################### FUNCTION PROTOTYPES FOR DEBUGGING */ /*******************************************************************//** Prints wait info of the sync system. */ UNIV_INTERN void sync_print_wait_info( /*=================*/ ib_stream_t ib_stream); /*!< in: stream where to print */ /*******************************************************************//** Prints info of the sync system. */ UNIV_INTERN void sync_print( /*=======*/ ib_stream_t ib_stream); /*!< in: stream where to print */ #ifdef UNIV_DEBUG /******************************************************************//** Checks that the mutex has been initialized. @return TRUE */ UNIV_INTERN ibool mutex_validate( /*===========*/ const mutex_t* mutex); /*!< in: mutex */ /******************************************************************//** Checks that the current thread owns the mutex. Works only in the debug version. @return TRUE if owns */ UNIV_INTERN ibool mutex_own( /*======*/ const mutex_t* mutex) /*!< in: mutex */ __attribute__((warn_unused_result)); #endif /* UNIV_DEBUG */ #ifdef UNIV_SYNC_DEBUG /******************************************************************//** Adds a latch and its level in the thread level array. Allocates the memory for the array if called first time for this OS thread. Makes the checks against other latch levels stored in the array for this thread. */ UNIV_INTERN void sync_thread_add_level( /*==================*/ void* latch, /*!< in: pointer to a mutex or an rw-lock */ ulint level); /*!< in: level in the latching order; if SYNC_LEVEL_VARYING, nothing is done */ /******************************************************************//** Removes a latch from the thread level array if it is found there. @return TRUE if found in the array; it is no error if the latch is not found, as we presently are not able to determine the level for every latch reservation the program does */ UNIV_INTERN ibool sync_thread_reset_level( /*====================*/ void* latch); /*!< in: pointer to a mutex or an rw-lock */ /******************************************************************//** Checks that the level array for the current thread is empty. @return TRUE if empty */ UNIV_INTERN ibool sync_thread_levels_empty(void); /*==========================*/ /******************************************************************//** Checks if the level array for the current thread contains a mutex or rw-latch at the specified level. @return a matching latch, or NULL if not found */ UNIV_INTERN void* sync_thread_levels_contains( /*========================*/ ulint level); /*!< in: latching order level (SYNC_DICT, ...)*/ /******************************************************************//** Checks if the level array for the current thread is empty. @return a latch, or NULL if empty except the exceptions specified below */ UNIV_INTERN void* sync_thread_levels_nonempty_gen( /*============================*/ ibool dict_mutex_allowed); /*!< in: TRUE if dictionary mutex is allowed to be owned by the thread, also purge_is_running mutex is allowed */ #define sync_thread_levels_empty_gen(d) (!sync_thread_levels_nonempty_gen(d)) /******************************************************************//** Gets the debug information for a reserved mutex. */ UNIV_INTERN void mutex_get_debug_info( /*=================*/ mutex_t* mutex, /*!< in: mutex */ const char** file_name, /*!< out: file where requested */ ulint* line, /*!< out: line where requested */ os_thread_id_t* thread_id); /*!< out: id of the thread which owns the mutex */ /******************************************************************//** Counts currently reserved mutexes. Works only in the debug version. @return number of reserved mutexes */ UNIV_INTERN ulint mutex_n_reserved(void); /*==================*/ #endif /* UNIV_SYNC_DEBUG */ /******************************************************************//** NOT to be used outside this module except in debugging! Gets the value of the lock word. */ UNIV_INLINE lock_word_t mutex_get_lock_word( /*================*/ const mutex_t* mutex); /*!< in: mutex */ #ifdef UNIV_SYNC_DEBUG /******************************************************************//** NOT to be used outside this module except in debugging! Gets the waiters field in a mutex. @return value to set */ UNIV_INLINE ulint mutex_get_waiters( /*==============*/ const mutex_t* mutex); /*!< in: mutex */ #endif /* UNIV_SYNC_DEBUG */ /********************************************************************** Reset variables. */ UNIV_INTERN void sync_var_init(void); /*===============*/ /* LATCHING ORDER WITHIN THE DATABASE ================================== The mutex or latch in the central memory object, for instance, a rollback segment object, must be acquired before acquiring the latch or latches to the corresponding file data structure. In the latching order below, these file page object latches are placed immediately below the corresponding central memory object latch or mutex. Synchronization object Notes ---------------------- ----- Dictionary mutex If we have a pointer to a dictionary | object, e.g., a table, it can be | accessed without reserving the | dictionary mutex. We must have a | reservation, a memoryfix, to the | appropriate table object in this case, | and the table must be explicitly | released later. V Dictionary header | V Secondary index tree latch The tree latch protects also all | the B-tree non-leaf pages. These V can be read with the page only Secondary index non-leaf bufferfixed to save CPU time, | no s-latch is needed on the page. | Modification of a page requires an | x-latch on the page, however. If a | thread owns an x-latch to the tree, | it is allowed to latch non-leaf pages | even after it has acquired the fsp | latch. V Secondary index leaf The latch on the secondary index leaf | can be kept while accessing the | clustered index, to save CPU time. V Clustered index tree latch To increase concurrency, the tree | latch is usually released when the | leaf page latch has been acquired. V Clustered index non-leaf | V Clustered index leaf | V Transaction system header | V Transaction undo mutex The undo log entry must be written | before any index page is modified. | Transaction undo mutex is for the undo | logs the analogue of the tree latch | for a B-tree. If a thread has the | trx undo mutex reserved, it is allowed | to latch the undo log pages in any | order, and also after it has acquired | the fsp latch. V Rollback segment mutex The rollback segment mutex must be | reserved, if, e.g., a new page must | be added to an undo log. The rollback | segment and the undo logs in its | history list can be seen as an | analogue of a B-tree, and the latches | reserved similarly, using a version of | lock-coupling. If an undo log must be | extended by a page when inserting an | undo log record, this corresponds to | a pessimistic insert in a B-tree. V Rollback segment header | V Purge system latch | V Undo log pages If a thread owns the trx undo mutex, | or for a log in the history list, the | rseg mutex, it is allowed to latch | undo log pages in any order, and even | after it has acquired the fsp latch. | If a thread does not have the | appropriate mutex, it is allowed to | latch only a single undo log page in | a mini-transaction. V File space management latch If a mini-transaction must allocate | several file pages, it can do that, | because it keeps the x-latch to the | file space management in its memo. V File system pages | V Kernel mutex If a kernel operation needs a file | page allocation, it must reserve the | fsp x-latch before acquiring the kernel | mutex. V Search system mutex | V Buffer pool mutex | V Log mutex | Any other latch | V Memory pool mutex */ /* Latching order levels */ /* User transaction locks are higher than any of the latch levels below: no latches are allowed when a thread goes to wait for a normal table or row lock! */ #define SYNC_USER_TRX_LOCK 9999 #define SYNC_NO_ORDER_CHECK 3000 /* this can be used to suppress latching order checking */ #define SYNC_LEVEL_VARYING 2000 /* Level is varying. Only used with buffer pool page locks, which do not have a fixed level, but instead have their level set after the page is locked; see e.g. ibuf_bitmap_get_map_page(). */ #define SYNC_TRX_I_S_RWLOCK 1910 /* Used for trx_i_s_cache_t::rw_lock */ #define SYNC_TRX_I_S_LAST_READ 1900 /* Used for trx_i_s_cache_t::last_read_mutex */ #define SYNC_FILE_FORMAT_TAG 1200 /* Used to serialize access to the file format tag */ #define SYNC_DICT_OPERATION 1001 /* table create, drop, etc. reserve this in X-mode, implicit or backround operations purge, rollback, foreign key checks reserve this in S-mode */ #define SYNC_DICT 1000 #define SYNC_DICT_AUTOINC_MUTEX 999 #define SYNC_DICT_HEADER 995 #define SYNC_IBUF_HEADER 914 #define SYNC_IBUF_PESS_INSERT_MUTEX 912 #define SYNC_IBUF_MUTEX 910 /* ibuf mutex is really below SYNC_FSP_PAGE: we assign a value this high only to make the program to pass the debug checks */ /*-------------------------------*/ #define SYNC_INDEX_TREE 900 #define SYNC_TREE_NODE_NEW 892 #define SYNC_TREE_NODE_FROM_HASH 891 #define SYNC_TREE_NODE 890 #define SYNC_PURGE_SYS 810 #define SYNC_PURGE_LATCH 800 #define SYNC_TRX_UNDO 700 #define SYNC_RSEG 600 #define SYNC_RSEG_HEADER_NEW 591 #define SYNC_RSEG_HEADER 590 #define SYNC_TRX_UNDO_PAGE 570 #define SYNC_EXTERN_STORAGE 500 #define SYNC_FSP 400 #define SYNC_FSP_PAGE 395 /*------------------------------------- Insert buffer headers */ /*------------------------------------- ibuf_mutex */ /*------------------------------------- Insert buffer tree */ #define SYNC_IBUF_BITMAP_MUTEX 351 #define SYNC_IBUF_BITMAP 350 /*-------------------------------*/ #define SYNC_KERNEL 300 #define SYNC_REC_LOCK 299 #define SYNC_TRX_LOCK_HEAP 298 #define SYNC_TRX_SYS_HEADER 290 #define SYNC_LOG 170 #define SYNC_RECV 168 #define SYNC_WORK_QUEUE 162 #define SYNC_SEARCH_SYS_CONF 161 /* for assigning btr_search_enabled */ #define SYNC_SEARCH_SYS 160 /* NOTE that if we have a memory heap that can be extended to the buffer pool, its logical level is SYNC_SEARCH_SYS, as memory allocation can call routines there! Otherwise the level is SYNC_MEM_HASH. */ #define SYNC_BUF_POOL 150 #define SYNC_BUF_BLOCK 149 #define SYNC_DOUBLEWRITE 140 #define SYNC_ANY_LATCH 135 #define SYNC_THR_LOCAL 133 #define SYNC_MEM_HASH 131 #define SYNC_MEM_POOL 130 /* Codes used to designate lock operations */ #define RW_LOCK_NOT_LOCKED 350 #define RW_LOCK_EX 351 #define RW_LOCK_EXCLUSIVE 351 #define RW_LOCK_SHARED 352 #define RW_LOCK_WAIT_EX 353 #define SYNC_MUTEX 354 /* NOTE! The structure appears here only for the compiler to know its size. Do not use its fields directly! The structure used in the spin lock implementation of a mutual exclusion semaphore. */ /** InnoDB mutex */ struct mutex_struct { os_event_t event; /*!< Used by sync0arr.c for the wait queue */ volatile lock_word_t lock_word; /*!< lock_word is the target of the atomic test-and-set instruction when atomic operations are enabled. */ #if !defined(HAVE_ATOMIC_BUILTINS) os_fast_mutex_t os_fast_mutex; /*!< We use this OS mutex in place of lock_word when atomic operations are not enabled */ #endif ulint waiters; /*!< This ulint is set to 1 if there are (or may be) threads waiting in the global wait array for this mutex to be released. Otherwise, this is 0. */ UT_LIST_NODE_T(mutex_t) list; /*!< All allocated mutexes are put into a list. Pointers to the next and prev. */ #ifdef UNIV_SYNC_DEBUG const char* file_name; /*!< File where the mutex was locked */ ulint line; /*!< Line where the mutex was locked */ ulint level; /*!< Level in the global latching order */ #endif /* UNIV_SYNC_DEBUG */ const char* cfile_name;/*!< File name where mutex created */ ulint cline; /*!< Line where created */ #ifdef UNIV_DEBUG os_thread_id_t thread_id; /*!< The thread id of the thread which locked the mutex. */ ulint magic_n; /*!< MUTEX_MAGIC_N */ /** Value of mutex_struct::magic_n */ # define MUTEX_MAGIC_N (ulint)979585 #endif /* UNIV_DEBUG */ ulong count_os_wait; /*!< count of os_wait */ #ifdef UNIV_DEBUG ulong count_using; /*!< count of times mutex used */ ulong count_spin_loop; /*!< count of spin loops */ ulong count_spin_rounds;/*!< count of spin rounds */ ulong count_os_yield; /*!< count of os_wait */ ib_uint64_t lspent_time; /*!< mutex os_wait timer msec */ ib_uint64_t lmax_spent_time;/*!< mutex os_wait timer msec */ const char* cmutex_name; /*!< mutex name */ ulint mutex_type; /*!< 0=usual mutex, 1=rw_lock mutex */ #endif /* UNIV_DEBUG */ }; /** The global array of wait cells for implementation of the databases own mutexes and read-write locks. */ extern sync_array_t* sync_primary_wait_array;/* Appears here for debugging purposes only! */ /** Constant determining how long spin wait is continued before suspending the thread. A value 600 rounds on a 1995 100 MHz Pentium seems to correspond to 20 microseconds. */ #define SYNC_SPIN_ROUNDS srv_n_spin_wait_rounds /** The number of mutex_exit calls. Intended for performance monitoring. */ extern ib_int64_t mutex_exit_count; #ifdef UNIV_SYNC_DEBUG /** Latching order checks start when this is set TRUE */ extern ibool sync_order_checks_on; #endif /* UNIV_SYNC_DEBUG */ /** This variable is set to TRUE when sync_init is called */ extern ibool sync_initialized; /** Global list of database mutexes (not OS mutexes) created. */ typedef UT_LIST_BASE_NODE_T(mutex_t) ut_list_base_node_t; /** Global list of database mutexes (not OS mutexes) created. */ extern ut_list_base_node_t mutex_list; /** Mutex protecting the mutex_list variable */ extern mutex_t mutex_list_mutex; #ifndef UNIV_NONINL #include "sync0sync.ic" #endif #endif haildb-2.3.2/include/page0page.ic0000644000175000017500000007415411513177357017447 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1994, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/page0page.ic Index page routines Created 2/2/1994 Heikki Tuuri *******************************************************/ #include "mach0data.h" #ifdef UNIV_DEBUG # include "log0recv.h" #endif /* !UNIV_DEBUG */ #ifndef UNIV_HOTBACKUP # include "rem0cmp.h" #endif /* !UNIV_HOTBACKUP */ #include "mtr0log.h" #ifdef WITH_ZIP #include "page0zip.h" #endif /* WITH_ZIP */ #ifdef UNIV_MATERIALIZE #undef UNIV_INLINE #define UNIV_INLINE #endif /************************************************************//** Gets the start of a page. @return start of the page */ UNIV_INLINE page_t* page_align( /*=======*/ const void* ptr) /*!< in: pointer to page frame */ { return((page_t*) ut_align_down(ptr, UNIV_PAGE_SIZE)); } /************************************************************//** Gets the offset within a page. @return offset from the start of the page */ UNIV_INLINE ulint page_offset( /*========*/ const void* ptr) /*!< in: pointer to page frame */ { return(ut_align_offset(ptr, UNIV_PAGE_SIZE)); } /*************************************************************//** Returns the max trx id field value. */ UNIV_INLINE trx_id_t page_get_max_trx_id( /*================*/ const page_t* page) /*!< in: page */ { ut_ad(page); return(mach_read_from_8(page + PAGE_HEADER + PAGE_MAX_TRX_ID)); } /*************************************************************//** Sets the max trx id field value if trx_id is bigger than the previous value. */ UNIV_INLINE void page_update_max_trx_id( /*===================*/ buf_block_t* block, /*!< in/out: page */ page_zip_des_t* page_zip,/*!< in/out: compressed page whose uncompressed part will be updated, or NULL */ trx_id_t trx_id, /*!< in: transaction id */ mtr_t* mtr) /*!< in/out: mini-transaction */ { ut_ad(block); ut_ad(mtr_memo_contains(mtr, block, MTR_MEMO_PAGE_X_FIX)); /* During crash recovery, this function may be called on something else than a leaf page of a secondary index or the insert buffer index tree (dict_index_is_sec_or_ibuf() returns TRUE for the dummy indexes constructed during redo log application). In that case, PAGE_MAX_TRX_ID is unused, and trx_id is usually zero. */ ut_ad(!ut_dulint_is_zero(trx_id) || recv_recovery_is_on()); ut_ad(page_is_leaf(buf_block_get_frame(block))); if (ut_dulint_cmp(page_get_max_trx_id(buf_block_get_frame(block)), trx_id) < 0) { page_set_max_trx_id(block, page_zip, trx_id, mtr); } } /*************************************************************//** Reads the given header field. */ UNIV_INLINE ulint page_header_get_field( /*==================*/ const page_t* page, /*!< in: page */ ulint field) /*!< in: PAGE_LEVEL, ... */ { ut_ad(page); ut_ad(field <= PAGE_INDEX_ID); return(mach_read_from_2(page + PAGE_HEADER + field)); } /*************************************************************//** Sets the given header field. */ UNIV_INLINE void page_header_set_field( /*==================*/ page_t* page, /*!< in/out: page */ page_zip_des_t* page_zip,/*!< in/out: compressed page whose uncompressed part will be updated, or NULL */ ulint field, /*!< in: PAGE_N_DIR_SLOTS, ... */ ulint val) /*!< in: value */ { ut_ad(page); ut_ad(field <= PAGE_N_RECS); ut_ad(field == PAGE_N_HEAP || val < UNIV_PAGE_SIZE); ut_ad(field != PAGE_N_HEAP || (val & 0x7fff) < UNIV_PAGE_SIZE); mach_write_to_2(page + PAGE_HEADER + field, val); #ifdef WITH_ZIP if (UNIV_LIKELY_NULL(page_zip)) { page_zip_write_header(page_zip, page + PAGE_HEADER + field, 2, NULL); } #endif } /*************************************************************//** Returns the offset stored in the given header field. @return offset from the start of the page, or 0 */ UNIV_INLINE ulint page_header_get_offs( /*=================*/ const page_t* page, /*!< in: page */ ulint field) /*!< in: PAGE_FREE, ... */ { ulint offs; ut_ad(page); ut_ad((field == PAGE_FREE) || (field == PAGE_LAST_INSERT) || (field == PAGE_HEAP_TOP)); offs = page_header_get_field(page, field); ut_ad((field != PAGE_HEAP_TOP) || offs); return(offs); } /*************************************************************//** Sets the pointer stored in the given header field. */ UNIV_INLINE void page_header_set_ptr( /*================*/ page_t* page, /*!< in: page */ page_zip_des_t* page_zip,/*!< in/out: compressed page whose uncompressed part will be updated, or NULL */ ulint field, /*!< in: PAGE_FREE, ... */ const byte* ptr) /*!< in: pointer or NULL*/ { ulint offs; ut_ad(page); ut_ad((field == PAGE_FREE) || (field == PAGE_LAST_INSERT) || (field == PAGE_HEAP_TOP)); if (ptr == NULL) { offs = 0; } else { offs = ptr - page; } ut_ad((field != PAGE_HEAP_TOP) || offs); page_header_set_field(page, page_zip, field, offs); } #ifndef UNIV_HOTBACKUP /*************************************************************//** Resets the last insert info field in the page header. Writes to mlog about this operation. */ UNIV_INLINE void page_header_reset_last_insert( /*==========================*/ page_t* page, /*!< in/out: page */ page_zip_des_t* page_zip,/*!< in/out: compressed page whose uncompressed part will be updated, or NULL */ mtr_t* mtr) /*!< in: mtr */ { ut_ad(page && mtr); #ifdef WITH_ZIP if (UNIV_LIKELY_NULL(page_zip)) { mach_write_to_2(page + (PAGE_HEADER + PAGE_LAST_INSERT), 0); page_zip_write_header(page_zip, page + (PAGE_HEADER + PAGE_LAST_INSERT), 2, mtr); } else { #endif /* WITH_ZIP */ mlog_write_ulint(page + (PAGE_HEADER + PAGE_LAST_INSERT), 0, MLOG_2BYTES, mtr); #ifdef WITH_ZIP } #endif /* WITH_ZIP */ } #endif /* !UNIV_HOTBACKUP */ /************************************************************//** Determine whether the page is in new-style compact format. @return nonzero if the page is in compact format, zero if it is in old-style format */ UNIV_INLINE ulint page_is_comp( /*=========*/ const page_t* page) /*!< in: index page */ { return(UNIV_EXPECT(page_header_get_field(page, PAGE_N_HEAP) & 0x8000, 0x8000)); } /************************************************************//** TRUE if the record is on a page in compact format. @return nonzero if in compact format */ UNIV_INLINE ulint page_rec_is_comp( /*=============*/ const rec_t* rec) /*!< in: record */ { return(page_is_comp(page_align(rec))); } /***************************************************************//** Returns the heap number of a record. @return heap number */ UNIV_INLINE ulint page_rec_get_heap_no( /*=================*/ const rec_t* rec) /*!< in: the physical record */ { if (page_rec_is_comp(rec)) { return(rec_get_heap_no_new(rec)); } else { return(rec_get_heap_no_old(rec)); } } /************************************************************//** Determine whether the page is a B-tree leaf. @return TRUE if the page is a B-tree leaf */ UNIV_INLINE ibool page_is_leaf( /*=========*/ const page_t* page) /*!< in: page */ { return(!*(const ib_uint16_t*) (page + (PAGE_HEADER + PAGE_LEVEL))); } /************************************************************//** Gets the offset of the first record on the page. @return offset of the first record in record list, relative from page */ UNIV_INLINE ulint page_get_infimum_offset( /*====================*/ const page_t* page) /*!< in: page which must have record(s) */ { ut_ad(page); ut_ad(!page_offset(page)); if (page_is_comp(page)) { return(PAGE_NEW_INFIMUM); } else { return(PAGE_OLD_INFIMUM); } } /************************************************************//** Gets the offset of the last record on the page. @return offset of the last record in record list, relative from page */ UNIV_INLINE ulint page_get_supremum_offset( /*=====================*/ const page_t* page) /*!< in: page which must have record(s) */ { ut_ad(page); ut_ad(!page_offset(page)); if (page_is_comp(page)) { return(PAGE_NEW_SUPREMUM); } else { return(PAGE_OLD_SUPREMUM); } } /************************************************************//** TRUE if the record is a user record on the page. @return TRUE if a user record */ UNIV_INLINE ibool page_rec_is_user_rec_low( /*=====================*/ ulint offset) /*!< in: record offset on page */ { ut_ad(offset >= PAGE_NEW_INFIMUM); #if PAGE_OLD_INFIMUM < PAGE_NEW_INFIMUM # error "PAGE_OLD_INFIMUM < PAGE_NEW_INFIMUM" #endif #if PAGE_OLD_SUPREMUM < PAGE_NEW_SUPREMUM # error "PAGE_OLD_SUPREMUM < PAGE_NEW_SUPREMUM" #endif #if PAGE_NEW_INFIMUM > PAGE_OLD_SUPREMUM # error "PAGE_NEW_INFIMUM > PAGE_OLD_SUPREMUM" #endif #if PAGE_OLD_INFIMUM > PAGE_NEW_SUPREMUM # error "PAGE_OLD_INFIMUM > PAGE_NEW_SUPREMUM" #endif #if PAGE_NEW_SUPREMUM > PAGE_OLD_SUPREMUM_END # error "PAGE_NEW_SUPREMUM > PAGE_OLD_SUPREMUM_END" #endif #if PAGE_OLD_SUPREMUM > PAGE_NEW_SUPREMUM_END # error "PAGE_OLD_SUPREMUM > PAGE_NEW_SUPREMUM_END" #endif ut_ad(offset <= UNIV_PAGE_SIZE - PAGE_EMPTY_DIR_START); return(UNIV_LIKELY(offset != PAGE_NEW_SUPREMUM) && UNIV_LIKELY(offset != PAGE_NEW_INFIMUM) && UNIV_LIKELY(offset != PAGE_OLD_INFIMUM) && UNIV_LIKELY(offset != PAGE_OLD_SUPREMUM)); } /************************************************************//** TRUE if the record is the supremum record on a page. @return TRUE if the supremum record */ UNIV_INLINE ibool page_rec_is_supremum_low( /*=====================*/ ulint offset) /*!< in: record offset on page */ { ut_ad(offset >= PAGE_NEW_INFIMUM); ut_ad(offset <= UNIV_PAGE_SIZE - PAGE_EMPTY_DIR_START); return(UNIV_UNLIKELY(offset == PAGE_NEW_SUPREMUM) || UNIV_UNLIKELY(offset == PAGE_OLD_SUPREMUM)); } /************************************************************//** TRUE if the record is the infimum record on a page. @return TRUE if the infimum record */ UNIV_INLINE ibool page_rec_is_infimum_low( /*====================*/ ulint offset) /*!< in: record offset on page */ { ut_ad(offset >= PAGE_NEW_INFIMUM); ut_ad(offset <= UNIV_PAGE_SIZE - PAGE_EMPTY_DIR_START); return(UNIV_UNLIKELY(offset == PAGE_NEW_INFIMUM) || UNIV_UNLIKELY(offset == PAGE_OLD_INFIMUM)); } /************************************************************//** TRUE if the record is a user record on the page. @return TRUE if a user record */ UNIV_INLINE ibool page_rec_is_user_rec( /*=================*/ const rec_t* rec) /*!< in: record */ { return(page_rec_is_user_rec_low(page_offset(rec))); } /************************************************************//** TRUE if the record is the supremum record on a page. @return TRUE if the supremum record */ UNIV_INLINE ibool page_rec_is_supremum( /*=================*/ const rec_t* rec) /*!< in: record */ { return(page_rec_is_supremum_low(page_offset(rec))); } /************************************************************//** TRUE if the record is the infimum record on a page. @return TRUE if the infimum record */ UNIV_INLINE ibool page_rec_is_infimum( /*================*/ const rec_t* rec) /*!< in: record */ { return(page_rec_is_infimum_low(page_offset(rec))); } #ifndef UNIV_HOTBACKUP /*************************************************************//** Compares a data tuple to a physical record. Differs from the function cmp_dtuple_rec_with_match in the way that the record must reside on an index page, and also page infimum and supremum records can be given in the parameter rec. These are considered as the negative infinity and the positive infinity in the alphabetical order. @return 1, 0, -1, if dtuple is greater, equal, less than rec, respectively, when only the common first fields are compared */ UNIV_INLINE int page_cmp_dtuple_rec_with_match( /*===========================*/ void* cmp_ctx,/*!< in: client compare context */ const dtuple_t* dtuple, /*!< in: data tuple */ const rec_t* rec, /*!< in: physical record on a page; may also be page infimum or supremum, in which case matched-parameter values below are not affected */ const ulint* offsets,/*!< in: array returned by rec_get_offsets() */ ulint* matched_fields, /*!< in/out: number of already completely matched fields; when function returns contains the value for current comparison */ ulint* matched_bytes) /*!< in/out: number of already matched bytes within the first field not completely matched; when function returns contains the value for current comparison */ { ulint rec_offset; ut_ad(dtuple_check_typed(dtuple)); ut_ad(rec_offs_validate(rec, NULL, offsets)); ut_ad(!rec_offs_comp(offsets) == !page_rec_is_comp(rec)); rec_offset = page_offset(rec); if (UNIV_UNLIKELY(rec_offset == PAGE_NEW_INFIMUM) || UNIV_UNLIKELY(rec_offset == PAGE_OLD_INFIMUM)) { return(1); } if (UNIV_UNLIKELY(rec_offset == PAGE_NEW_SUPREMUM) || UNIV_UNLIKELY(rec_offset == PAGE_OLD_SUPREMUM)) { return(-1); } return(cmp_dtuple_rec_with_match( cmp_ctx, dtuple, rec, offsets, matched_fields, matched_bytes)); } #endif /* !UNIV_HOTBACKUP */ /*************************************************************//** Gets the page number. @return page number */ UNIV_INLINE ulint page_get_page_no( /*=============*/ const page_t* page) /*!< in: page */ { ut_ad(page == page_align((page_t*) page)); return(mach_read_from_4(page + FIL_PAGE_OFFSET)); } /*************************************************************//** Gets the tablespace identifier. @return space id */ UNIV_INLINE ulint page_get_space_id( /*==============*/ const page_t* page) /*!< in: page */ { ut_ad(page == page_align((page_t*) page)); return(mach_read_from_4(page + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID)); } /*************************************************************//** Gets the number of user records on page (infimum and supremum records are not user records). @return number of user records */ UNIV_INLINE ulint page_get_n_recs( /*============*/ const page_t* page) /*!< in: index page */ { return(page_header_get_field(page, PAGE_N_RECS)); } /*************************************************************//** Gets the number of dir slots in directory. @return number of slots */ UNIV_INLINE ulint page_dir_get_n_slots( /*=================*/ const page_t* page) /*!< in: index page */ { return(page_header_get_field(page, PAGE_N_DIR_SLOTS)); } /*************************************************************//** Sets the number of dir slots in directory. */ UNIV_INLINE void page_dir_set_n_slots( /*=================*/ page_t* page, /*!< in/out: page */ page_zip_des_t* page_zip,/*!< in/out: compressed page whose uncompressed part will be updated, or NULL */ ulint n_slots)/*!< in: number of slots */ { page_header_set_field(page, page_zip, PAGE_N_DIR_SLOTS, n_slots); } /*************************************************************//** Gets the number of records in the heap. @return number of user records */ UNIV_INLINE ulint page_dir_get_n_heap( /*================*/ const page_t* page) /*!< in: index page */ { return(page_header_get_field(page, PAGE_N_HEAP) & 0x7fff); } /*************************************************************//** Sets the number of records in the heap. */ UNIV_INLINE void page_dir_set_n_heap( /*================*/ page_t* page, /*!< in/out: index page */ page_zip_des_t* page_zip,/*!< in/out: compressed page whose uncompressed part will be updated, or NULL. Note that the size of the dense page directory in the compressed page trailer is n_heap * PAGE_ZIP_DIR_SLOT_SIZE. */ ulint n_heap) /*!< in: number of records */ { ut_ad(n_heap < 0x8000); ut_ad(!page_zip || n_heap == (page_header_get_field(page, PAGE_N_HEAP) & 0x7fff) + 1); page_header_set_field(page, page_zip, PAGE_N_HEAP, n_heap | (0x8000 & page_header_get_field(page, PAGE_N_HEAP))); } #ifdef UNIV_DEBUG /*************************************************************//** Gets pointer to nth directory slot. @return pointer to dir slot */ UNIV_INLINE page_dir_slot_t* page_dir_get_nth_slot( /*==================*/ const page_t* page, /*!< in: index page */ ulint n) /*!< in: position */ { ut_ad(page_dir_get_n_slots(page) > n); return((page_dir_slot_t*) page + UNIV_PAGE_SIZE - PAGE_DIR - (n + 1) * PAGE_DIR_SLOT_SIZE); } #endif /* UNIV_DEBUG */ /**************************************************************//** Used to check the consistency of a record on a page. @return TRUE if succeed */ UNIV_INLINE ibool page_rec_check( /*===========*/ const rec_t* rec) /*!< in: record */ { const page_t* page = page_align(rec); ut_a(rec); ut_a(page_offset(rec) <= page_header_get_field(page, PAGE_HEAP_TOP)); ut_a(page_offset(rec) >= PAGE_DATA); return(TRUE); } /***************************************************************//** Gets the record pointed to by a directory slot. @return pointer to record */ UNIV_INLINE const rec_t* page_dir_slot_get_rec( /*==================*/ const page_dir_slot_t* slot) /*!< in: directory slot */ { return(page_align(slot) + mach_read_from_2(slot)); } /***************************************************************//** This is used to set the record offset in a directory slot. */ UNIV_INLINE void page_dir_slot_set_rec( /*==================*/ page_dir_slot_t* slot, /*!< in: directory slot */ rec_t* rec) /*!< in: record on the page */ { ut_ad(page_rec_check(rec)); mach_write_to_2(slot, page_offset(rec)); } /***************************************************************//** Gets the number of records owned by a directory slot. @return number of records */ UNIV_INLINE ulint page_dir_slot_get_n_owned( /*======================*/ const page_dir_slot_t* slot) /*!< in: page directory slot */ { const rec_t* rec = page_dir_slot_get_rec(slot); if (page_rec_is_comp(slot)) { return(rec_get_n_owned_new(rec)); } else { return(rec_get_n_owned_old(rec)); } } /***************************************************************//** This is used to set the owned records field of a directory slot. */ UNIV_INLINE void page_dir_slot_set_n_owned( /*======================*/ page_dir_slot_t*slot, /*!< in/out: directory slot */ page_zip_des_t* page_zip,/*!< in/out: compressed page, or NULL */ ulint n) /*!< in: number of records owned by the slot */ { rec_t* rec = (rec_t*) page_dir_slot_get_rec(slot); if (page_rec_is_comp(slot)) { rec_set_n_owned_new(rec, page_zip, n); } else { ut_ad(!page_zip); rec_set_n_owned_old(rec, n); } } /************************************************************//** Calculates the space reserved for directory slots of a given number of records. The exact value is a fraction number n * PAGE_DIR_SLOT_SIZE / PAGE_DIR_SLOT_MIN_N_OWNED, and it is rounded upwards to an integer. */ UNIV_INLINE ulint page_dir_calc_reserved_space( /*=========================*/ ulint n_recs) /*!< in: number of records */ { return((PAGE_DIR_SLOT_SIZE * n_recs + PAGE_DIR_SLOT_MIN_N_OWNED - 1) / PAGE_DIR_SLOT_MIN_N_OWNED); } /************************************************************//** Gets the pointer to the next record on the page. @return pointer to next record */ UNIV_INLINE const rec_t* page_rec_get_next_low( /*==================*/ const rec_t* rec, /*!< in: pointer to record */ ulint comp) /*!< in: nonzero=compact page layout */ { ulint offs; const page_t* page; ut_ad(page_rec_check(rec)); page = page_align(rec); offs = rec_get_next_offs(rec, comp); if (UNIV_UNLIKELY(offs >= UNIV_PAGE_SIZE)) { ib_logger(ib_stream, "InnoDB: Next record offset is nonsensical %lu" " in record at offset %lu\n" "InnoDB: rec address %p, space id %lu, page %lu\n", (ulong)offs, (ulong) page_offset(rec), (void*) rec, (ulong) page_get_space_id(page), (ulong) page_get_page_no(page)); buf_page_print(page, 0); ut_error; } if (UNIV_UNLIKELY(offs == 0)) { return(NULL); } return(page + offs); } /************************************************************//** Gets the pointer to the next record on the page. @return pointer to next record */ UNIV_INLINE rec_t* page_rec_get_next( /*==============*/ rec_t* rec) /*!< in: pointer to record */ { return((rec_t*) page_rec_get_next_low(rec, page_rec_is_comp(rec))); } /************************************************************//** Gets the pointer to the next record on the page. @return pointer to next record */ UNIV_INLINE const rec_t* page_rec_get_next_const( /*====================*/ const rec_t* rec) /*!< in: pointer to record */ { return(page_rec_get_next_low(rec, page_rec_is_comp(rec))); } /************************************************************//** Sets the pointer to the next record on the page. */ UNIV_INLINE void page_rec_set_next( /*==============*/ rec_t* rec, /*!< in: pointer to record, must not be page supremum */ rec_t* next) /*!< in: pointer to next record, must not be page infimum */ { ulint offs; ut_ad(page_rec_check(rec)); ut_ad(!page_rec_is_supremum(rec)); ut_ad(rec != next); ut_ad(!next || !page_rec_is_infimum(next)); ut_ad(!next || page_align(rec) == page_align(next)); if (UNIV_LIKELY(next != NULL)) { offs = page_offset(next); } else { offs = 0; } if (page_rec_is_comp(rec)) { rec_set_next_offs_new(rec, offs); } else { rec_set_next_offs_old(rec, offs); } } /************************************************************//** Gets the pointer to the previous record. @return pointer to previous record */ UNIV_INLINE const rec_t* page_rec_get_prev_const( /*====================*/ const rec_t* rec) /*!< in: pointer to record, must not be page infimum */ { const page_dir_slot_t* slot; ulint slot_no; const rec_t* rec2; const rec_t* prev_rec = NULL; const page_t* page; ut_ad(page_rec_check(rec)); page = page_align(rec); ut_ad(!page_rec_is_infimum(rec)); slot_no = page_dir_find_owner_slot(rec); ut_a(slot_no != 0); slot = page_dir_get_nth_slot(page, slot_no - 1); rec2 = page_dir_slot_get_rec(slot); if (page_is_comp(page)) { while (rec != rec2) { prev_rec = rec2; rec2 = page_rec_get_next_low(rec2, TRUE); } } else { while (rec != rec2) { prev_rec = rec2; rec2 = page_rec_get_next_low(rec2, FALSE); } } ut_a(prev_rec); return(prev_rec); } /************************************************************//** Gets the pointer to the previous record. @return pointer to previous record */ UNIV_INLINE rec_t* page_rec_get_prev( /*==============*/ rec_t* rec) /*!< in: pointer to record, must not be page infimum */ { return((rec_t*) page_rec_get_prev_const(rec)); } /***************************************************************//** Looks for the record which owns the given record. @return the owner record */ UNIV_INLINE rec_t* page_rec_find_owner_rec( /*====================*/ rec_t* rec) /*!< in: the physical record */ { ut_ad(page_rec_check(rec)); if (page_rec_is_comp(rec)) { while (rec_get_n_owned_new(rec) == 0) { rec = page_rec_get_next(rec); } } else { while (rec_get_n_owned_old(rec) == 0) { rec = page_rec_get_next(rec); } } return(rec); } /**********************************************************//** Returns the base extra size of a physical record. This is the size of the fixed header, independent of the record size. @return REC_N_NEW_EXTRA_BYTES or REC_N_OLD_EXTRA_BYTES */ UNIV_INLINE ulint page_rec_get_base_extra_size( /*=========================*/ const rec_t* rec) /*!< in: physical record */ { #if REC_N_NEW_EXTRA_BYTES + 1 != REC_N_OLD_EXTRA_BYTES # error "REC_N_NEW_EXTRA_BYTES + 1 != REC_N_OLD_EXTRA_BYTES" #endif return(REC_N_NEW_EXTRA_BYTES + (ulint) !page_rec_is_comp(rec)); } /************************************************************//** Returns the sum of the sizes of the records in the record list, excluding the infimum and supremum records. @return data in bytes */ UNIV_INLINE ulint page_get_data_size( /*===============*/ const page_t* page) /*!< in: index page */ { ulint ret; ret = (ulint)(page_header_get_field(page, PAGE_HEAP_TOP) - (page_is_comp(page) ? PAGE_NEW_SUPREMUM_END : PAGE_OLD_SUPREMUM_END) - page_header_get_field(page, PAGE_GARBAGE)); ut_ad(ret < UNIV_PAGE_SIZE); return(ret); } /************************************************************//** Allocates a block of memory from the free list of an index page. */ UNIV_INLINE void page_mem_alloc_free( /*================*/ page_t* page, /*!< in/out: index page */ page_zip_des_t* page_zip,/*!< in/out: compressed page with enough space available for inserting the record, or NULL */ rec_t* next_rec,/*!< in: pointer to the new head of the free record list */ ulint need) /*!< in: number of bytes allocated */ { ulint garbage; #ifdef UNIV_DEBUG const rec_t* old_rec = page_header_get_ptr(page, PAGE_FREE); ulint next_offs; ut_ad(old_rec); next_offs = rec_get_next_offs(old_rec, page_is_comp(page)); ut_ad(next_rec == (next_offs ? page + next_offs : NULL)); #endif page_header_set_ptr(page, page_zip, PAGE_FREE, next_rec); garbage = page_header_get_field(page, PAGE_GARBAGE); ut_ad(garbage >= need); page_header_set_field(page, page_zip, PAGE_GARBAGE, garbage - need); } /*************************************************************//** Calculates free space if a page is emptied. @return free space */ UNIV_INLINE ulint page_get_free_space_of_empty( /*=========================*/ ulint comp) /*!< in: nonzero=compact page layout */ { if (UNIV_LIKELY(comp)) { return((ulint)(UNIV_PAGE_SIZE - PAGE_NEW_SUPREMUM_END - PAGE_DIR - 2 * PAGE_DIR_SLOT_SIZE)); } return((ulint)(UNIV_PAGE_SIZE - PAGE_OLD_SUPREMUM_END - PAGE_DIR - 2 * PAGE_DIR_SLOT_SIZE)); } /************************************************************//** Each user record on a page, and also the deleted user records in the heap takes its size plus the fraction of the dir cell size / PAGE_DIR_SLOT_MIN_N_OWNED bytes for it. If the sum of these exceeds the value of page_get_free_space_of_empty, the insert is impossible, otherwise it is allowed. This function returns the maximum combined size of records which can be inserted on top of the record heap. @return maximum combined size for inserted records */ UNIV_INLINE ulint page_get_max_insert_size( /*=====================*/ const page_t* page, /*!< in: index page */ ulint n_recs) /*!< in: number of records */ { ulint occupied; ulint free_space; if (page_is_comp(page)) { occupied = page_header_get_field(page, PAGE_HEAP_TOP) - PAGE_NEW_SUPREMUM_END + page_dir_calc_reserved_space( n_recs + page_dir_get_n_heap(page) - 2); free_space = page_get_free_space_of_empty(TRUE); } else { occupied = page_header_get_field(page, PAGE_HEAP_TOP) - PAGE_OLD_SUPREMUM_END + page_dir_calc_reserved_space( n_recs + page_dir_get_n_heap(page) - 2); free_space = page_get_free_space_of_empty(FALSE); } /* Above the 'n_recs +' part reserves directory space for the new inserted records; the '- 2' excludes page infimum and supremum records */ if (occupied > free_space) { return(0); } return(free_space - occupied); } /************************************************************//** Returns the maximum combined size of records which can be inserted on top of the record heap if a page is first reorganized. @return maximum combined size for inserted records */ UNIV_INLINE ulint page_get_max_insert_size_after_reorganize( /*======================================*/ const page_t* page, /*!< in: index page */ ulint n_recs) /*!< in: number of records */ { ulint occupied; ulint free_space; occupied = page_get_data_size(page) + page_dir_calc_reserved_space(n_recs + page_get_n_recs(page)); free_space = page_get_free_space_of_empty(page_is_comp(page)); if (occupied > free_space) { return(0); } return(free_space - occupied); } /************************************************************//** Puts a record to free list. */ UNIV_INLINE void page_mem_free( /*==========*/ page_t* page, /*!< in/out: index page */ page_zip_des_t* page_zip,/*!< in/out: compressed page, or NULL */ rec_t* rec, /*!< in: pointer to the (origin of) record */ dict_index_t* dict_index,/*!< in: index of rec */ const ulint* offsets)/*!< in: array returned by rec_get_offsets() */ { rec_t* free_rec; ulint garbage; ut_ad(rec_offs_validate(rec, dict_index, offsets)); free_rec = page_header_get_ptr(page, PAGE_FREE); page_rec_set_next(rec, free_rec); page_header_set_ptr(page, page_zip, PAGE_FREE, rec); garbage = page_header_get_field(page, PAGE_GARBAGE); page_header_set_field(page, page_zip, PAGE_GARBAGE, garbage + rec_offs_size(offsets)); #ifdef WITH_ZIP if (UNIV_LIKELY_NULL(page_zip)) { page_zip_dir_delete(page_zip, rec, dict_index, offsets, free_rec); } else #endif /* WITH_ZIP */ { page_header_set_field(page, page_zip, PAGE_N_RECS, page_get_n_recs(page) - 1); } } /**********************************************************************//** Determine if a record is so big that it needs to be stored externally. @return FALSE if the entire record can be stored locally on the page */ UNIV_INLINE ibool page_rec_needs_ext( /*===============*/ ulint rec_size, /*!< in: length of the record in bytes */ ulint comp, /*!< in: nonzero=compact format */ ulint n_fields, /*!< in: number of fields in the record; ignored if zip_size == 0 */ ulint zip_size) /*!< in: compressed page size in bytes, or 0 */ { ut_ad(rec_size > comp ? REC_N_NEW_EXTRA_BYTES : REC_N_OLD_EXTRA_BYTES); ut_ad(ut_is_2pow(zip_size)); ut_ad(comp || !zip_size); #if UNIV_PAGE_SIZE > REC_MAX_DATA_SIZE if (UNIV_UNLIKELY(rec_size >= REC_MAX_DATA_SIZE)) { return(TRUE); } #endif #ifdef WITH_ZIP if (UNIV_UNLIKELY(zip_size)) { return(page_zip_rec_needs_ext( rec_size, comp, n_fields, zip_size)); } #endif /* WITH_ZIP */ return(rec_size >= page_get_free_space_of_empty(comp) / 2); } #ifdef UNIV_MATERIALIZE #undef UNIV_INLINE #define UNIV_INLINE UNIV_INLINE_ORIGINAL #endif haildb-2.3.2/include/fut0lst.h0000644000175000017500000001637211513177357017051 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /******************************************************************//** @file include/fut0lst.h File-based list utilities Created 11/28/1995 Heikki Tuuri ***********************************************************************/ #ifndef fut0lst_h #define fut0lst_h #include "univ.i" #include "fil0fil.h" #include "mtr0mtr.h" /* The C 'types' of base node and list node: these should be used to write self-documenting code. Of course, the sizeof macro cannot be applied to these types! */ typedef byte flst_base_node_t; typedef byte flst_node_t; /* The physical size of a list base node in bytes */ #define FLST_BASE_NODE_SIZE (4 + 2 * FIL_ADDR_SIZE) /* The physical size of a list node in bytes */ #define FLST_NODE_SIZE (2 * FIL_ADDR_SIZE) #ifndef UNIV_HOTBACKUP /********************************************************************//** Initializes a list base node. */ UNIV_INLINE void flst_init( /*======*/ flst_base_node_t* base, /*!< in: pointer to base node */ mtr_t* mtr); /*!< in: mini-transaction handle */ /********************************************************************//** Adds a node as the last node in a list. */ UNIV_INTERN void flst_add_last( /*==========*/ flst_base_node_t* base, /*!< in: pointer to base node of list */ flst_node_t* node, /*!< in: node to add */ mtr_t* mtr); /*!< in: mini-transaction handle */ /********************************************************************//** Adds a node as the first node in a list. */ UNIV_INTERN void flst_add_first( /*===========*/ flst_base_node_t* base, /*!< in: pointer to base node of list */ flst_node_t* node, /*!< in: node to add */ mtr_t* mtr); /*!< in: mini-transaction handle */ /********************************************************************//** Inserts a node after another in a list. */ UNIV_INTERN void flst_insert_after( /*==============*/ flst_base_node_t* base, /*!< in: pointer to base node of list */ flst_node_t* node1, /*!< in: node to insert after */ flst_node_t* node2, /*!< in: node to add */ mtr_t* mtr); /*!< in: mini-transaction handle */ /********************************************************************//** Inserts a node before another in a list. */ UNIV_INTERN void flst_insert_before( /*===============*/ flst_base_node_t* base, /*!< in: pointer to base node of list */ flst_node_t* node2, /*!< in: node to insert */ flst_node_t* node3, /*!< in: node to insert before */ mtr_t* mtr); /*!< in: mini-transaction handle */ /********************************************************************//** Removes a node. */ UNIV_INTERN void flst_remove( /*========*/ flst_base_node_t* base, /*!< in: pointer to base node of list */ flst_node_t* node2, /*!< in: node to remove */ mtr_t* mtr); /*!< in: mini-transaction handle */ /********************************************************************//** Cuts off the tail of the list, including the node given. The number of nodes which will be removed must be provided by the caller, as this function does not measure the length of the tail. */ UNIV_INTERN void flst_cut_end( /*=========*/ flst_base_node_t* base, /*!< in: pointer to base node of list */ flst_node_t* node2, /*!< in: first node to remove */ ulint n_nodes,/*!< in: number of nodes to remove, must be >= 1 */ mtr_t* mtr); /*!< in: mini-transaction handle */ /********************************************************************//** Cuts off the tail of the list, not including the given node. The number of nodes which will be removed must be provided by the caller, as this function does not measure the length of the tail. */ UNIV_INTERN void flst_truncate_end( /*==============*/ flst_base_node_t* base, /*!< in: pointer to base node of list */ flst_node_t* node2, /*!< in: first node not to remove */ ulint n_nodes,/*!< in: number of nodes to remove */ mtr_t* mtr); /*!< in: mini-transaction handle */ /********************************************************************//** Gets list length. @return length */ UNIV_INLINE ulint flst_get_len( /*=========*/ const flst_base_node_t* base, /*!< in: pointer to base node */ mtr_t* mtr); /*!< in: mini-transaction handle */ /********************************************************************//** Gets list first node address. @return file address */ UNIV_INLINE fil_addr_t flst_get_first( /*===========*/ const flst_base_node_t* base, /*!< in: pointer to base node */ mtr_t* mtr); /*!< in: mini-transaction handle */ /********************************************************************//** Gets list last node address. @return file address */ UNIV_INLINE fil_addr_t flst_get_last( /*==========*/ const flst_base_node_t* base, /*!< in: pointer to base node */ mtr_t* mtr); /*!< in: mini-transaction handle */ /********************************************************************//** Gets list next node address. @return file address */ UNIV_INLINE fil_addr_t flst_get_next_addr( /*===============*/ const flst_node_t* node, /*!< in: pointer to node */ mtr_t* mtr); /*!< in: mini-transaction handle */ /********************************************************************//** Gets list prev node address. @return file address */ UNIV_INLINE fil_addr_t flst_get_prev_addr( /*===============*/ const flst_node_t* node, /*!< in: pointer to node */ mtr_t* mtr); /*!< in: mini-transaction handle */ /********************************************************************//** Writes a file address. */ UNIV_INLINE void flst_write_addr( /*============*/ fil_faddr_t* faddr, /*!< in: pointer to file faddress */ fil_addr_t addr, /*!< in: file address */ mtr_t* mtr); /*!< in: mini-transaction handle */ /********************************************************************//** Reads a file address. @return file address */ UNIV_INLINE fil_addr_t flst_read_addr( /*===========*/ const fil_faddr_t* faddr, /*!< in: pointer to file faddress */ mtr_t* mtr); /*!< in: mini-transaction handle */ /********************************************************************//** Validates a file-based list. @return TRUE if ok */ UNIV_INTERN ibool flst_validate( /*==========*/ const flst_base_node_t* base, /*!< in: pointer to base node of list */ mtr_t* mtr1); /*!< in: mtr */ /********************************************************************//** Prints info of a file-based list. */ UNIV_INTERN void flst_print( /*=======*/ const flst_base_node_t* base, /*!< in: pointer to base node of list */ mtr_t* mtr); /*!< in: mtr */ #ifndef UNIV_NONINL #include "fut0lst.ic" #endif #endif /* !UNIV_HOTBACKUP */ #endif haildb-2.3.2/include/eval0eval.h0000644000175000017500000000725011513177357017322 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1997, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/eval0eval.h SQL evaluator: evaluates simple data structures, like expressions, in a query graph Created 12/29/1997 Heikki Tuuri *******************************************************/ #ifndef eval0eval_h #define eval0eval_h #include "univ.i" #include "que0types.h" #include "pars0sym.h" #include "pars0pars.h" /*****************************************************************//** Free the buffer from global dynamic memory for a value of a que_node, if it has been allocated in the above function. The freeing for pushed column values is done in sel_col_prefetch_buf_free. */ UNIV_INTERN void eval_node_free_val_buf( /*===================*/ que_node_t* node); /*!< in: query graph node */ /*****************************************************************//** Evaluates a symbol table symbol. */ UNIV_INLINE void eval_sym( /*=====*/ sym_node_t* sym_node); /*!< in: symbol table node */ /*****************************************************************//** Evaluates an expression. */ UNIV_INLINE void eval_exp( /*=====*/ que_node_t* exp_node); /*!< in: expression */ /*****************************************************************//** Sets an integer value as the value of an expression node. */ UNIV_INLINE void eval_node_set_int_val( /*==================*/ que_node_t* node, /*!< in: expression node */ lint val); /*!< in: value to set */ /*****************************************************************//** Gets an integer value from an expression node. @return integer value */ UNIV_INLINE lint eval_node_get_int_val( /*==================*/ que_node_t* node); /*!< in: expression node */ /*****************************************************************//** Copies a binary string value as the value of a query graph node. Allocates a new buffer if necessary. */ UNIV_INLINE void eval_node_copy_and_alloc_val( /*=========================*/ que_node_t* node, /*!< in: query graph node */ const byte* str, /*!< in: binary string */ ulint len); /*!< in: string length or UNIV_SQL_NULL */ /*****************************************************************//** Copies a query node value to another node. */ UNIV_INLINE void eval_node_copy_val( /*===============*/ que_node_t* node1, /*!< in: node to copy to */ que_node_t* node2); /*!< in: node to copy from */ /*****************************************************************//** Gets a iboolean value from a query node. @return iboolean value */ UNIV_INLINE ibool eval_node_get_ibool_val( /*====================*/ que_node_t* node); /*!< in: query graph node */ /*****************************************************************//** Evaluates a comparison node. @return the result of the comparison */ UNIV_INTERN ibool eval_cmp( /*=====*/ func_node_t* cmp_node); /*!< in: comparison node */ #ifndef UNIV_NONINL #include "eval0eval.ic" #endif #endif haildb-2.3.2/include/fsp0types.h0000644000175000017500000000766711513177357017414 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /****************************************************** @file include/fsp0types.h File space management types Created May 26, 2009 Vasil Dimov *******************************************************/ #ifndef fsp0types_h #define fsp0types_h #include "univ.i" #include "fil0fil.h" /* for FIL_PAGE_DATA */ /** @name Flags for inserting records in order If records are inserted in order, there are the following flags to tell this (their type is made byte for the compiler to warn if direction and hint parameters are switched in fseg_alloc_free_page) */ /* @{ */ #define FSP_UP ((byte)111) /*!< alphabetically upwards */ #define FSP_DOWN ((byte)112) /*!< alphabetically downwards */ #define FSP_NO_DIR ((byte)113) /*!< no order */ /* @} */ /** File space extent size (one megabyte) in pages */ #define FSP_EXTENT_SIZE (1 << (20 - UNIV_PAGE_SIZE_SHIFT)) /** On a page of any file segment, data may be put starting from this offset */ #define FSEG_PAGE_DATA FIL_PAGE_DATA /** @name File segment header The file segment header points to the inode describing the file segment. */ /* @{ */ /** Data type for file segment header */ typedef byte fseg_header_t; #define FSEG_HDR_SPACE 0 /*!< space id of the inode */ #define FSEG_HDR_PAGE_NO 4 /*!< page number of the inode */ #define FSEG_HDR_OFFSET 8 /*!< byte offset of the inode */ #define FSEG_HEADER_SIZE 10 /*!< Length of the file system header, in bytes */ /* @} */ /** Flags for fsp_reserve_free_extents @{ */ #define FSP_NORMAL 1000000 #define FSP_UNDO 2000000 #define FSP_CLEANING 3000000 /* @} */ /* Number of pages described in a single descriptor page: currently each page description takes less than 1 byte; a descriptor page is repeated every this many file pages */ /* #define XDES_DESCRIBED_PER_PAGE UNIV_PAGE_SIZE */ /* This has been replaced with either UNIV_PAGE_SIZE or page_zip->size. */ /** @name The space low address page map The pages at FSP_XDES_OFFSET and FSP_IBUF_BITMAP_OFFSET are repeated every XDES_DESCRIBED_PER_PAGE pages in every tablespace. */ /* @{ */ /*--------------------------------------*/ #define FSP_XDES_OFFSET 0 /* !< extent descriptor */ #define FSP_IBUF_BITMAP_OFFSET 1 /* !< insert buffer bitmap */ /* The ibuf bitmap pages are the ones whose page number is the number above plus a multiple of XDES_DESCRIBED_PER_PAGE */ #define FSP_FIRST_INODE_PAGE_NO 2 /*!< in every tablespace */ /* The following pages exist in the system tablespace (space 0). */ #define FSP_IBUF_HEADER_PAGE_NO 3 /*!< insert buffer header page, in tablespace 0 */ #define FSP_IBUF_TREE_ROOT_PAGE_NO 4 /*!< insert buffer B-tree root page in tablespace 0 */ /* The ibuf tree root page number in tablespace 0; its fseg inode is on the page number FSP_FIRST_INODE_PAGE_NO */ #define FSP_TRX_SYS_PAGE_NO 5 /*!< transaction system header, in tablespace 0 */ #define FSP_FIRST_RSEG_PAGE_NO 6 /*!< first rollback segment page, in tablespace 0 */ #define FSP_DICT_HDR_PAGE_NO 7 /*!< data dictionary header page, in tablespace 0 */ /*--------------------------------------*/ /* @} */ #endif /* fsp0types_h */ haildb-2.3.2/include/buf0lru.ic0000644000175000017500000000204611513177357017164 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/buf0lru.ic The database buffer replacement algorithm Created 11/5/1995 Heikki Tuuri *******************************************************/ haildb-2.3.2/include/ut0vec.ic0000644000175000017500000000654511513177357017023 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 2006, 2010, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /*******************************************************************//** @file include/ut0vec.ic A vector of pointers to data items Created 4/6/2006 Osku Salerma ************************************************************************/ /****************************************************************//** Get number of elements in vector. @return number of elements in vector */ UNIV_INLINE ulint ib_vector_size( /*===========*/ const ib_vector_t* vec) /*!< in: vector */ { return(vec->used); } /****************************************************************//** Get n'th element. @return n'th element */ UNIV_INLINE void* ib_vector_get( /*==========*/ ib_vector_t* vec, /*!< in: vector */ ulint n) /*!< in: element index to get */ { ut_a(n < ib_vector_size(vec)); return(vec->data[n]); } /****************************************************************//** Get n'th element as a const pointer. @return n'th element */ UNIV_INLINE const void* ib_vector_get_const( /*================*/ const ib_vector_t* vec, /*!< in: vector */ ulint n) /*!< in: element index to get */ { ut_a(n < ib_vector_size(vec)); return(vec->data[n]); } /****************************************************************//** Set n'th element and return the previous value. @return n'th element */ UNIV_INLINE void* ib_vector_set( /*==========*/ ib_vector_t* vec, /*!< in: vector */ ulint n, /*!< in: element index to set */ void* p) /*!< in: new value to set */ { void* prev; ut_a(n < ib_vector_size(vec)); prev = vec->data[n]; vec->data[n] = p; return(prev); } /******************************************************************** Remove the last element from the vector. @return last vector element */ UNIV_INLINE void* ib_vector_pop( /*==========*/ ib_vector_t* vec) /*!< in/out: vector */ { void* elem; ut_a(vec->used > 0); --vec->used; elem = vec->data[vec->used]; ut_d(vec->data[vec->used] = NULL); UNIV_MEM_INVALID(&vec->data[vec->used], sizeof(*vec->data)); return(elem); } /****************************************************************//** Free the underlying heap of the vector. Note that vec is invalid after this call. */ UNIV_INLINE void ib_vector_free( /*===========*/ ib_vector_t* vec) /*!< in, own: vector */ { mem_heap_free(vec->heap); } /****************************************************************//** Test whether a vector is empty or not. @return TRUE if empty */ UNIV_INLINE ibool ib_vector_is_empty( /*===============*/ const ib_vector_t* vec) /*!< in: vector */ { return(ib_vector_size(vec) == 0); } haildb-2.3.2/include/row0purge.h0000644000175000017500000000643711513177357017403 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1997, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/row0purge.h Purge obsolete records Created 3/14/1997 Heikki Tuuri *******************************************************/ #ifndef row0purge_h #define row0purge_h #include "univ.i" #include "data0data.h" #include "btr0types.h" #include "btr0pcur.h" #include "dict0types.h" #include "trx0types.h" #include "que0types.h" #include "row0types.h" /********************************************************************//** Creates a purge node to a query graph. @return own: purge node */ UNIV_INTERN purge_node_t* row_purge_node_create( /*==================*/ que_thr_t* parent, /*!< in: parent node, i.e., a thr node */ mem_heap_t* heap); /*!< in: memory heap where created */ /***********************************************************//** Does the purge operation for a single undo log record. This is a high-level function used in an SQL execution graph. @return query thread to run next or NULL */ UNIV_INTERN que_thr_t* row_purge_step( /*===========*/ que_thr_t* thr); /*!< in: query thread */ /* Purge node structure */ struct purge_node_struct{ que_common_t common; /*!< node type: QUE_NODE_PURGE */ /*----------------------*/ /* Local storage for this graph node */ roll_ptr_t roll_ptr;/* roll pointer to undo log record */ trx_undo_rec_t* undo_rec;/* undo log record */ trx_undo_inf_t* reservation;/* reservation for the undo log record in the purge array */ undo_no_t undo_no;/* undo number of the record */ ulint rec_type;/* undo log record type: TRX_UNDO_INSERT_REC, ... */ btr_pcur_t pcur; /*!< persistent cursor used in searching the clustered index record */ ibool found_clust;/* TRUE if the clustered index record determined by ref was found in the clustered index, and we were able to position pcur on it */ dict_table_t* table; /*!< table where purge is done */ ulint cmpl_info;/* compiler analysis info of an update */ upd_t* update; /*!< update vector for a clustered index record */ dtuple_t* ref; /*!< NULL, or row reference to the next row to handle */ dtuple_t* row; /*!< NULL, or a copy (also fields copied to heap) of the indexed fields of the row to handle */ dict_index_t* index; /*!< NULL, or the next index whose record should be handled */ mem_heap_t* heap; /*!< memory heap used as auxiliary storage for row; this must be emptied after a successful purge of a row */ }; #ifndef UNIV_NONINL #include "row0purge.ic" #endif #endif haildb-2.3.2/include/usr0types.h0000644000175000017500000000216111513177357017415 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/usr0types.h Users and sessions global types Created 6/25/1996 Heikki Tuuri *******************************************************/ #ifndef usr0types_h #define usr0types_h typedef struct sess_struct sess_t; #endif haildb-2.3.2/include/api0api.h0000644000175000017500000000517711513177357016774 0ustar00pcrewspcrews00000000000000/*********************************************************************** Copyright (c) 2008, 2009 Innobase Oy. All rights reserved. Copyright (c) 2008, 2009 Oracle. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ************************************************************************/ #ifndef INNOBASE_API_H #define INNOBASE_API_H #include "db0err.h" #include "haildb.h" #include /*********************************************************************//** Declare private functions that should not be visible in the public API below, outside of API_BEGIN_INCLUDE/API_END_INCLUDE. *************************************************************************/ /*********************************************************************//** Execute arbitrary SQL using InnoDB's internal parser. The statement is executed in a new transaction. Table name parameters must be prefixed with a '$' symbol and variables with ':' @return DB_SUCCESS or error code */ ib_err_t ib_exec_sql( /*========*/ const char* sql, /*!< in: sql to execute */ ib_ulint_t n_args, /*!< in: no. of args */ ...); /*********************************************************************//** Execute arbitrary SQL using InnoDB's internal parser. The statement is executed in a background transaction. It will lock the data dictionary lock for the duration of the query. @return DB_SUCCESS or error code */ ib_err_t ib_exec_ddl_sql( /*============*/ const char* sql, /*!< in: sql to execute */ ib_ulint_t n_args, /*!< in: no. of args */ ...); /*********************************************************************//** Initialize the config system. @return DB_SUCCESS or error code */ ib_err_t ib_cfg_init(void); /*==============*/ /*********************************************************************//** Shutdown the config system. @return DB_SUCCESS or error code */ ib_err_t ib_cfg_shutdown(void); /*==================*/ extern int srv_panic_status; #define IB_CHECK_PANIC() do { if (srv_panic_status) return srv_panic_status; } while(0) #endif /* INNOBASE_API_H */ haildb-2.3.2/include/os0sync.ic0000644000175000017500000000316211513177357017203 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2010, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/os0sync.ic The interface to the operating system synchronization primitives. Created 9/6/1995 Heikki Tuuri *******************************************************/ #ifdef __WIN__ #include #else #include #endif /* __WIN__ */ /**********************************************************//** Acquires ownership of a fast mutex. Currently in Windows this is the same as os_fast_mutex_lock! @return 0 if success, != 0 if was reserved by another thread */ UNIV_INLINE ulint os_fast_mutex_trylock( /*==================*/ os_fast_mutex_t* fast_mutex) /*!< in: mutex to acquire */ { #ifdef __WIN__ EnterCriticalSection(fast_mutex); return(0); #else return((ulint) pthread_mutex_trylock(fast_mutex)); #endif } haildb-2.3.2/include/buf0buddy.ic0000644000175000017500000001015611513177357017472 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 2006, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/buf0buddy.ic Binary buddy allocator for compressed pages Created December 2006 by Marko Makela *******************************************************/ #ifdef UNIV_MATERIALIZE # undef UNIV_INLINE # define UNIV_INLINE #endif #include "buf0buf.h" #include "buf0buddy.h" #include "ut0ut.h" #include "sync0sync.h" /**********************************************************************//** Allocate a block. The thread calling this function must hold buf_pool_mutex and must not hold buf_pool_zip_mutex or any block->mutex. The buf_pool_mutex may only be released and reacquired if lru != NULL. @return allocated block, possibly NULL if lru==NULL */ UNIV_INTERN void* buf_buddy_alloc_low( /*================*/ ulint i, /*!< in: index of buf_pool->zip_free[], or BUF_BUDDY_SIZES */ ibool* lru) /*!< in: pointer to a variable that will be assigned TRUE if storage was allocated from the LRU list and buf_pool_mutex was temporarily released, or NULL if the LRU list should not be used */ __attribute__((malloc)); /**********************************************************************//** Deallocate a block. */ UNIV_INTERN void buf_buddy_free_low( /*===============*/ void* buf, /*!< in: block to be freed, must not be pointed to by the buffer pool */ ulint i) /*!< in: index of buf_pool->zip_free[], or BUF_BUDDY_SIZES */ __attribute__((nonnull)); /**********************************************************************//** Get the index of buf_pool->zip_free[] for a given block size. @return index of buf_pool->zip_free[], or BUF_BUDDY_SIZES */ UNIV_INLINE ulint buf_buddy_get_slot( /*===============*/ ulint size) /*!< in: block size */ { ulint i; ulint s; for (i = 0, s = BUF_BUDDY_LOW; s < size; i++, s <<= 1) { } ut_ad(i <= BUF_BUDDY_SIZES); return(i); } /**********************************************************************//** Allocate a block. The thread calling this function must hold buf_pool_mutex and must not hold buf_pool_zip_mutex or any block->mutex. The buf_pool_mutex may only be released and reacquired if lru != NULL. This function should only be used for allocating compressed page frames or control blocks (buf_page_t). Allocated control blocks must be properly initialized immediately after buf_buddy_alloc() has returned the memory, before releasing buf_pool_mutex. @return allocated block, possibly NULL if lru == NULL */ UNIV_INLINE void* buf_buddy_alloc( /*============*/ ulint size, /*!< in: block size, up to UNIV_PAGE_SIZE */ ibool* lru) /*!< in: pointer to a variable that will be assigned TRUE if storage was allocated from the LRU list and buf_pool_mutex was temporarily released, or NULL if the LRU list should not be used */ { ut_ad(buf_pool_mutex_own()); return(buf_buddy_alloc_low(buf_buddy_get_slot(size), lru)); } /**********************************************************************//** Deallocate a block. */ UNIV_INLINE void buf_buddy_free( /*===========*/ void* buf, /*!< in: block to be freed, must not be pointed to by the buffer pool */ ulint size) /*!< in: block size, up to UNIV_PAGE_SIZE */ { ut_ad(buf_pool_mutex_own()); buf_buddy_free_low(buf, buf_buddy_get_slot(size)); } #ifdef UNIV_MATERIALIZE # undef UNIV_INLINE # define UNIV_INLINE UNIV_INLINE_ORIGINAL #endif haildb-2.3.2/include/sync0sync.ic0000644000175000017500000001514411513177357017541 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2009, Innobase Oy. All Rights Reserved. Copyright (c) 2008, Google Inc. Portions of this file contain modifications contributed and copyrighted by Google, Inc. Those modifications are gratefully acknowledged and are described briefly in the InnoDB documentation. The contributions by Google are incorporated with their permission, and subject to the conditions contained in the file COPYING.Google. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/sync0sync.ic Mutex, the basic synchronization primitive Created 9/5/1995 Heikki Tuuri *******************************************************/ /******************************************************************//** Sets the waiters field in a mutex. */ UNIV_INTERN void mutex_set_waiters( /*==============*/ mutex_t* mutex, /*!< in: mutex */ ulint n); /*!< in: value to set */ /******************************************************************//** Reserves a mutex for the current thread. If the mutex is reserved, the function spins a preset time (controlled by SYNC_SPIN_ROUNDS) waiting for the mutex before suspending the thread. */ UNIV_INTERN void mutex_spin_wait( /*============*/ mutex_t* mutex, /*!< in: pointer to mutex */ const char* file_name, /*!< in: file name where mutex requested */ ulint line); /*!< in: line where requested */ #ifdef UNIV_SYNC_DEBUG /******************************************************************//** Sets the debug information for a reserved mutex. */ UNIV_INTERN void mutex_set_debug_info( /*=================*/ mutex_t* mutex, /*!< in: mutex */ const char* file_name, /*!< in: file where requested */ ulint line); /*!< in: line where requested */ #endif /* UNIV_SYNC_DEBUG */ /******************************************************************//** Releases the threads waiting in the primary wait array for this mutex. */ UNIV_INTERN void mutex_signal_object( /*================*/ mutex_t* mutex); /*!< in: mutex */ /******************************************************************//** Performs an atomic test-and-set instruction to the lock_word field of a mutex. @return the previous value of lock_word: 0 or 1 */ UNIV_INLINE byte mutex_test_and_set( /*===============*/ mutex_t* mutex) /*!< in: mutex */ { #if defined(HAVE_ATOMIC_BUILTINS) return(os_atomic_test_and_set_byte(&mutex->lock_word, 1)); #else ibool ret; ret = os_fast_mutex_trylock(&(mutex->os_fast_mutex)); if (ret == 0) { /* We check that os_fast_mutex_trylock does not leak and allow race conditions */ ut_a(mutex->lock_word == 0); mutex->lock_word = 1; } return((byte)ret); #endif } /******************************************************************//** Performs a reset instruction to the lock_word field of a mutex. This instruction also serializes memory operations to the program order. */ UNIV_INLINE void mutex_reset_lock_word( /*==================*/ mutex_t* mutex) /*!< in: mutex */ { #if defined(HAVE_ATOMIC_BUILTINS) /* In theory __sync_lock_release should be used to release the lock. Unfortunately, it does not work properly alone. The workaround is that more conservative __sync_lock_test_and_set is used instead. */ os_atomic_test_and_set_byte(&mutex->lock_word, 0); #else mutex->lock_word = 0; os_fast_mutex_unlock(&(mutex->os_fast_mutex)); #endif } /******************************************************************//** Gets the value of the lock word. */ UNIV_INLINE lock_word_t mutex_get_lock_word( /*================*/ const mutex_t* mutex) /*!< in: mutex */ { ut_ad(mutex); return(mutex->lock_word); } /******************************************************************//** Gets the waiters field in a mutex. @return value to set */ UNIV_INLINE ulint mutex_get_waiters( /*==============*/ const mutex_t* mutex) /*!< in: mutex */ { const volatile ulint* ptr; /*!< declared volatile to ensure that the value is read from memory */ ut_ad(mutex); ptr = &(mutex->waiters); return(*ptr); /* Here we assume that the read of a single word from memory is atomic */ } /******************************************************************//** Unlocks a mutex owned by the current thread. */ UNIV_INLINE void mutex_exit( /*=======*/ mutex_t* mutex) /*!< in: pointer to mutex */ { ut_ad(mutex_own(mutex)); ut_d(mutex->thread_id = (os_thread_id_t) ULINT_UNDEFINED); #ifdef UNIV_SYNC_DEBUG sync_thread_reset_level(mutex); #endif mutex_reset_lock_word(mutex); /* A problem: we assume that mutex_reset_lock word is a memory barrier, that is when we read the waiters field next, the read must be serialized in memory after the reset. A speculative processor might perform the read first, which could leave a waiting thread hanging indefinitely. Our current solution call every second sync_arr_wake_threads_if_sema_free() to wake up possible hanging threads if they are missed in mutex_signal_object. */ if (mutex_get_waiters(mutex) != 0) { mutex_signal_object(mutex); } #ifdef UNIV_SYNC_PERF_STAT mutex_exit_count++; #endif } /******************************************************************//** Locks a mutex for the current thread. If the mutex is reserved, the function spins a preset time (controlled by SYNC_SPIN_ROUNDS), waiting for the mutex before suspending the thread. */ UNIV_INLINE void mutex_enter_func( /*=============*/ mutex_t* mutex, /*!< in: pointer to mutex */ const char* file_name, /*!< in: file name where locked */ ulint line) /*!< in: line where locked */ { ut_ad(mutex_validate(mutex)); ut_ad(!mutex_own(mutex)); /* Note that we do not peek at the value of lock_word before trying the atomic test_and_set; we could peek, and possibly save time. */ ut_d(mutex->count_using++); if (!mutex_test_and_set(mutex)) { ut_d(mutex->thread_id = os_thread_get_curr_id()); #ifdef UNIV_SYNC_DEBUG mutex_set_debug_info(mutex, file_name, line); #endif return; /* Succeeded! */ } mutex_spin_wait(mutex, file_name, line); } haildb-2.3.2/include/row0undo.ic0000644000175000017500000000200411513177357017354 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1997, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/row0undo.ic Row undo Created 1/8/1997 Heikki Tuuri *******************************************************/ haildb-2.3.2/include/ut0ut.h0000644000175000017500000003303511513177357016524 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1994, 2009, Innobase Oy. All Rights Reserved. Copyright (c) 2009, Sun Microsystems, Inc. Portions of this file contain modifications contributed and copyrighted by Sun Microsystems, Inc. Those modifications are gratefully acknowledged and are described briefly in the InnoDB documentation. The contributions by Sun Microsystems are incorporated with their permission, and subject to the conditions contained in the file COPYING.Sun_Microsystems. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /******************************************************************//** @file include/ut0ut.h Various utilities Created 1/20/1994 Heikki Tuuri ***********************************************************************/ #ifndef ut0ut_h #define ut0ut_h #include "univ.i" #ifndef UNIV_HOTBACKUP # include "os0sync.h" /* for HAVE_ATOMIC_BUILTINS */ #endif /* UNIV_HOTBACKUP */ #include #include /** Index name prefix in fast index creation */ #define TEMP_INDEX_PREFIX '\377' /** Index name prefix in fast index creation, as a string constant */ #define TEMP_INDEX_PREFIX_STR "\377" /** Time stamp */ typedef time_t ib_time_t; /* The first argument to the InnoDB error logging function. */ typedef void* ib_stream_t; /************************************************************************//** All log messages are written to this function. It should have the same behavior as fprintf(). */ typedef int (*ib_logger_t)(ib_stream_t, const char*, ...); #ifndef UNIV_HOTBACKUP #if defined(HAVE_IB_PAUSE_INSTRUCTION) # ifdef WIN32 /* In the Win32 API, the x86 PAUSE instruction is executed by calling the YieldProcessor macro defined in WinNT.h. It is a CPU architecture- independent way by using YieldProcessor.*/ # define UT_RELAX_CPU() YieldProcessor() # else /* According to the gcc info page, asm volatile means that the instruction has important side-effects and must not be removed. Also asm volatile may trigger a memory barrier (spilling all registers to memory). */ # define UT_RELAX_CPU() __asm__ __volatile__ ("pause") # endif #elif defined(HAVE_SOLARIS_ATOMICS) # define UT_RELAX_CPU() do { \ volatile lint volatile_var; \ os_compare_and_swap_lint(&volatile_var, 0, 1); \ } while (0) #else # define UT_RELAX_CPU() ((void)0) /* avoid warning for an empty statement */ #endif /*********************************************************************//** Delays execution for at most max_wait_us microseconds or returns earlier if cond becomes true. @param cond in: condition to wait for; evaluated every 2 ms @param max_wait_us in: maximum delay to wait, in microseconds */ #define UT_WAIT_FOR(cond, max_wait_us) \ do { \ ib_uint64_t start_us; \ start_us = ut_time_us(NULL); \ while (!(cond) \ && ut_time_us(NULL) - start_us < (max_wait_us)) {\ \ os_thread_sleep(2000 /* 2 ms */); \ } \ } while (0) #endif /* !UNIV_HOTBACKUP */ /********************************************************//** Gets the high 32 bits in a ulint. That is makes a shift >> 32, but since there seem to be compiler bugs in both gcc and Visual C++, we do this by a special conversion. @return a >> 32 */ UNIV_INTERN ulint ut_get_high32( /*==========*/ ulint a); /*!< in: ulint */ /******************************************************//** Calculates the minimum of two ulints. @return minimum */ UNIV_INLINE ulint ut_min( /*===*/ ulint n1, /*!< in: first number */ ulint n2); /*!< in: second number */ /******************************************************//** Calculates the maximum of two ulints. @return maximum */ UNIV_INLINE ulint ut_max( /*===*/ ulint n1, /*!< in: first number */ ulint n2); /*!< in: second number */ /****************************************************************//** Calculates minimum of two ulint-pairs. */ UNIV_INLINE void ut_pair_min( /*========*/ ulint* a, /*!< out: more significant part of minimum */ ulint* b, /*!< out: less significant part of minimum */ ulint a1, /*!< in: more significant part of first pair */ ulint b1, /*!< in: less significant part of first pair */ ulint a2, /*!< in: more significant part of second pair */ ulint b2); /*!< in: less significant part of second pair */ /******************************************************//** Compares two ulints. @return 1 if a > b, 0 if a == b, -1 if a < b */ UNIV_INLINE int ut_ulint_cmp( /*=========*/ ulint a, /*!< in: ulint */ ulint b); /*!< in: ulint */ /*******************************************************//** Compares two pairs of ulints. @return -1 if a < b, 0 if a == b, 1 if a > b */ UNIV_INLINE int ut_pair_cmp( /*========*/ ulint a1, /*!< in: more significant part of first pair */ ulint a2, /*!< in: less significant part of first pair */ ulint b1, /*!< in: more significant part of second pair */ ulint b2); /*!< in: less significant part of second pair */ /*************************************************************//** Determines if a number is zero or a power of two. @param n in: number @return nonzero if n is zero or a power of two; zero otherwise */ #define ut_is_2pow(n) UNIV_LIKELY(!((n) & ((n) - 1))) /*************************************************************//** Calculates fast the remainder of n/m when m is a power of two. @param n in: numerator @param m in: denominator, must be a power of two @return the remainder of n/m */ #define ut_2pow_remainder(n, m) ((n) & ((m) - 1)) /*************************************************************//** Calculates the biggest multiple of m that is not bigger than n when m is a power of two. In other words, rounds n down to m * k. @param n in: number to round down @param m in: alignment, must be a power of two @return n rounded down to the biggest possible integer multiple of m */ #define ut_2pow_round(n, m) ((n) & ~((m) - 1)) /** Align a number down to a multiple of a power of two. @param n in: number to round down @param m in: alignment, must be a power of two @return n rounded down to the biggest possible integer multiple of m */ #define ut_calc_align_down(n, m) ut_2pow_round(n, m) /********************************************************//** Calculates the smallest multiple of m that is not smaller than n when m is a power of two. In other words, rounds n up to m * k. @param n in: number to round up @param m in: alignment, must be a power of two @return n rounded up to the smallest possible integer multiple of m */ #define ut_calc_align(n, m) (((n) + ((m) - 1)) & ~((m) - 1)) /*************************************************************//** Calculates fast the 2-logarithm of a number, rounded upward to an integer. @return logarithm in the base 2, rounded upward */ UNIV_INLINE ulint ut_2_log( /*=====*/ ulint n); /*!< in: number */ /*************************************************************//** Calculates 2 to power n. @return 2 to power n */ UNIV_INLINE ulint ut_2_exp( /*=====*/ ulint n); /*!< in: number */ /*************************************************************//** Calculates fast the number rounded up to the nearest power of 2. @return first power of 2 which is >= n */ UNIV_INTERN ulint ut_2_power_up( /*==========*/ ulint n) /*!< in: number != 0 */ __attribute__((const)); /** Determine how many bytes (groups of 8 bits) are needed to store the given number of bits. @param b in: bits @return number of bytes (octets) needed to represent b */ #define UT_BITS_IN_BYTES(b) (((b) + 7) / 8) /**********************************************************//** Returns system time. We do not specify the format of the time returned: the only way to manipulate it is to use the function ut_difftime. @return system time */ UNIV_INTERN ib_time_t ut_time(void); /*=========*/ #ifndef UNIV_HOTBACKUP /**********************************************************//** Returns system time. Upon successful completion, the value 0 is returned; otherwise the value -1 is returned and the global variable errno is set to indicate the error. @return 0 on success, -1 otherwise */ UNIV_INTERN int ut_usectime( /*========*/ ulint* sec, /*!< out: seconds since the Epoch */ ulint* ms); /*!< out: microseconds since the Epoch+*sec */ /**********************************************************//** Returns the number of microseconds since epoch. Similar to time(3), the return value is also stored in *tloc, provided that tloc is non-NULL. @return us since epoch */ UNIV_INTERN ib_uint64_t ut_time_us( /*=======*/ ib_uint64_t* tloc); /*!< out: us since epoch, if non-NULL */ /**********************************************************//** Returns the number of milliseconds since some epoch. The value may wrap around. It should only be used for heuristic purposes. @return ms since epoch */ UNIV_INTERN ulint ut_time_ms(void); /*============*/ #endif /* !UNIV_HOTBACKUP */ /**********************************************************//** Returns the difference of two times in seconds. @return time2 - time1 expressed in seconds */ UNIV_INTERN double ut_difftime( /*========*/ ib_time_t time2, /*!< in: time */ ib_time_t time1); /*!< in: time */ /**********************************************************//** Prints a timestamp to a file. */ UNIV_INTERN void ut_print_timestamp( /*===============*/ ib_stream_t ib_stream); /*!< in: file where to print */ /**********************************************************//** Sprintfs a timestamp to a buffer, 13..14 chars plus terminating NUL. */ UNIV_INTERN void ut_sprintf_timestamp( /*=================*/ char* buf); /*!< in: buffer where to sprintf */ #ifdef UNIV_HOTBACKUP /**********************************************************//** Sprintfs a timestamp to a buffer with no spaces and with ':' characters replaced by '_'. */ UNIV_INTERN void ut_sprintf_timestamp_without_extra_chars( /*=====================================*/ char* buf); /*!< in: buffer where to sprintf */ /**********************************************************//** Returns current year, month, day. */ UNIV_INTERN void ut_get_year_month_day( /*==================*/ ulint* year, /*!< out: current year */ ulint* month, /*!< out: month */ ulint* day); /*!< out: day */ #else /* UNIV_HOTBACKUP */ /*************************************************************//** Runs an idle loop on CPU. The argument gives the desired delay in microseconds on 100 MHz Pentium + Visual C++. @return dummy value */ UNIV_INTERN ulint ut_delay( /*=====*/ ulint delay); /*!< in: delay in microseconds on 100 MHz Pentium */ #endif /* UNIV_HOTBACKUP */ /*************************************************************//** Prints the contents of a memory buffer in hex and ascii. */ UNIV_INTERN void ut_print_buf( /*=========*/ ib_stream_t ib_stream, /*!< in: file where to print */ const void* buf, /*!< in: memory buffer */ ulint len); /*!< in: length of the buffer */ /**********************************************************************//** Outputs a NUL-terminated file name, quoted with apostrophes. */ UNIV_INTERN void ut_print_filename( /*==============*/ ib_stream_t ib_stream, /*!< in: output stream */ const char* name); /*!< in: name to print */ #ifndef UNIV_HOTBACKUP /* Forward declaration of transaction handle */ struct trx_struct; /**********************************************************************//** Outputs a fixed-length string, quoted as an SQL identifier. If the string contains a slash '/', the string will be output as two identifiers separated by a period (.), as in SQL database_name.identifier. */ UNIV_INTERN void ut_print_name( /*==========*/ ib_stream_t ib_stream, /*!< in: output stream */ struct trx_struct*trx, /*!< in: transaction */ ibool table_id, /*!< in: TRUE=print a table name, FALSE=print other identifier */ const char* name); /*!< in: name to print */ /**********************************************************************//** Outputs a fixed-length string, quoted as an SQL identifier. If the string contains a slash '/', the string will be output as two identifiers separated by a period (.), as in SQL database_name.identifier. */ UNIV_INTERN void ut_print_namel( /*===========*/ ib_stream_t ib_stream, /*!< in: output stream */ const char* name, /*!< in: name to print */ ulint namelen); /*!< in: length of name */ #endif /* !UNIV_HOTBACKUP */ #ifdef __WIN__ /**********************************************************************//** A substitute for snprintf(3), formatted output conversion into a limited buffer. @return number of characters that would have been printed if the size were unlimited, not including the terminating '\0'. */ UNIV_INTERN int ut_snprintf( /*========*/ char* str, /*!< out: string */ size_t size, /*!< in: str size */ const char* fmt, /*!< in: format */ ...); /*!< in: format values */ #else /**********************************************************************//** A wrapper for snprintf(3), formatted output conversion into a limited buffer. */ # define ut_snprintf snprintf #endif /* __WIN__ */ extern ib_logger_t ib_logger; extern ib_stream_t ib_stream; #ifndef UNIV_NONINL #include "ut0ut.ic" #endif #endif haildb-2.3.2/include/pars0sym.h0000644000175000017500000002010311513177357017211 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1997, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/pars0sym.h SQL parser symbol table Created 12/15/1997 Heikki Tuuri *******************************************************/ #ifndef pars0sym_h #define pars0sym_h #include "univ.i" #include "que0types.h" #include "usr0types.h" #include "dict0types.h" #include "pars0types.h" #include "row0types.h" /******************************************************************//** Creates a symbol table for a single stored procedure or query. @return own: symbol table */ UNIV_INTERN sym_tab_t* sym_tab_create( /*===========*/ mem_heap_t* heap); /*!< in: memory heap where to create */ /******************************************************************//** Frees the memory allocated dynamically AFTER parsing phase for variables etc. in the symbol table. Does not free the mem heap where the table was originally created. Frees also SQL explicit cursor definitions. */ UNIV_INTERN void sym_tab_free_private( /*=================*/ sym_tab_t* sym_tab); /*!< in, own: symbol table */ /******************************************************************//** Adds an integer literal to a symbol table. @return symbol table node */ UNIV_INTERN sym_node_t* sym_tab_add_int_lit( /*================*/ sym_tab_t* sym_tab, /*!< in: symbol table */ ulint val); /*!< in: integer value */ /******************************************************************//** Adds an string literal to a symbol table. @return symbol table node */ UNIV_INTERN sym_node_t* sym_tab_add_str_lit( /*================*/ sym_tab_t* sym_tab, /*!< in: symbol table */ byte* str, /*!< in: string with no quotes around it */ ulint len); /*!< in: string length */ /******************************************************************//** Add a bound literal to a symbol table. @return symbol table node */ UNIV_INTERN sym_node_t* sym_tab_add_bound_lit( /*==================*/ sym_tab_t* sym_tab, /*!< in: symbol table */ const char* name, /*!< in: name of bound literal */ ulint* lit_type); /*!< out: type of literal (PARS_*_LIT) */ /******************************************************************//** Adds an SQL null literal to a symbol table. @return symbol table node */ UNIV_INTERN sym_node_t* sym_tab_add_null_lit( /*=================*/ sym_tab_t* sym_tab); /*!< in: symbol table */ /******************************************************************//** Adds an identifier to a symbol table. @return symbol table node */ UNIV_INTERN sym_node_t* sym_tab_add_id( /*===========*/ sym_tab_t* sym_tab, /*!< in: symbol table */ byte* name, /*!< in: identifier name */ ulint len); /*!< in: identifier length */ /******************************************************************//** Add a bound identifier to a symbol table. @return symbol table node */ UNIV_INTERN sym_node_t* sym_tab_add_bound_id( /*===========*/ sym_tab_t* sym_tab, /*!< in: symbol table */ const char* name); /*!< in: name of bound id */ /** Index of sym_node_struct::field_nos corresponding to the clustered index */ #define SYM_CLUST_FIELD_NO 0 /** Index of sym_node_struct::field_nos corresponding to a secondary index */ #define SYM_SEC_FIELD_NO 1 /** Types of a symbol table node */ enum sym_tab_entry { SYM_VAR = 91, /*!< declared parameter or local variable of a procedure */ SYM_IMPLICIT_VAR, /*!< storage for a intermediate result of a calculation */ SYM_LIT, /*!< literal */ SYM_TABLE, /*!< database table name */ SYM_COLUMN, /*!< database table name */ SYM_CURSOR, /*!< named cursor */ SYM_PROCEDURE_NAME, /*!< stored procedure name */ SYM_INDEX, /*!< database index name */ SYM_FUNCTION /*!< user function name */ }; /** Symbol table node */ struct sym_node_struct{ que_common_t common; /*!< node type: QUE_NODE_SYMBOL */ /* NOTE: if the data field in 'common.val' is not NULL and the symbol table node is not for a temporary column, the memory for the value has been allocated from dynamic memory and it should be freed when the symbol table is discarded */ /* 'alias' and 'indirection' are almost the same, but not quite. 'alias' always points to the primary instance of the variable, while 'indirection' does the same only if we should use the primary instance's values for the node's data. This is usually the case, but when initializing a cursor (e.g., "DECLARE CURSOR c IS SELECT * FROM t WHERE id = x;"), we copy the values from the primary instance to the cursor's instance so that they are fixed for the duration of the cursor, and set 'indirection' to NULL. If we did not, the value of 'x' could change between fetches and things would break horribly. TODO: It would be cleaner to make 'indirection' a boolean field and always use 'alias' to refer to the primary node. */ sym_node_t* indirection; /*!< pointer to another symbol table node which contains the value for this node, NULL otherwise */ sym_node_t* alias; /*!< pointer to another symbol table node for which this node is an alias, NULL otherwise */ UT_LIST_NODE_T(sym_node_t) col_var_list; /*!< list of table columns or a list of input variables for an explicit cursor */ ibool copy_val; /*!< TRUE if a column and its value should be copied to dynamic memory when fetched */ ulint field_nos[2]; /*!< if a column, in the position SYM_CLUST_FIELD_NO is the field number in the clustered index; in the position SYM_SEC_FIELD_NO the field number in the non-clustered index to use first; if not found from the index, then ULINT_UNDEFINED */ ibool resolved; /*!< TRUE if the meaning of a variable or a column has been resolved; for literals this is always TRUE */ enum sym_tab_entry token_type; /*!< type of the parsed token */ const char* name; /*!< name of an id */ ulint name_len; /*!< id name length */ dict_table_t* table; /*!< table definition if a table id or a column id */ ulint col_no; /*!< column number if a column */ sel_buf_t* prefetch_buf; /*!< NULL, or a buffer for cached column values for prefetched rows */ sel_node_t* cursor_def; /*!< cursor definition select node if a named cursor */ ulint param_type; /*!< PARS_INPUT, PARS_OUTPUT, or PARS_NOT_PARAM if not a procedure parameter */ sym_tab_t* sym_table; /*!< back pointer to the symbol table */ UT_LIST_NODE_T(sym_node_t) sym_list; /*!< list of symbol nodes */ }; /** Symbol table */ struct sym_tab_struct{ que_t* query_graph; /*!< query graph generated by the parser */ const char* sql_string; /*!< SQL string to parse */ size_t string_len; /*!< SQL string length */ int next_char_pos; /*!< position of the next character in sql_string to give to the lexical analyzer */ pars_info_t* info; /*!< extra information, or NULL */ sym_node_list_t sym_list; /*!< list of symbol nodes in the symbol table */ UT_LIST_BASE_NODE_T(func_node_t) func_node_list; /*!< list of function nodes in the parsed query graph */ mem_heap_t* heap; /*!< memory heap from which we can allocate space */ }; #ifndef UNIV_NONINL #include "pars0sym.ic" #endif #endif haildb-2.3.2/include/log0recv.ic0000644000175000017500000000337611513177357017335 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1997, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/log0recv.ic Recovery Created 9/20/1997 Heikki Tuuri *******************************************************/ #include "univ.i" /*******************************************************************//** Returns TRUE if recovery is currently running. @return recv_recovery_on */ UNIV_INLINE ibool recv_recovery_is_on(void) /*=====================*/ { return(UNIV_UNLIKELY(recv_recovery_on)); } #ifdef UNIV_LOG_ARCHIVE /** TRUE when applying redo log records from an archived log file */ extern ibool recv_recovery_from_backup_on; /*******************************************************************//** Returns TRUE if recovery from backup is currently running. @return recv_recovery_from_backup_on */ UNIV_INLINE ibool recv_recovery_from_backup_is_on(void) /*=================================*/ { return(recv_recovery_from_backup_on); } #endif /* UNIV_LOG_ARCHIVE */ haildb-2.3.2/include/trx0trx.h0000644000175000017500000006676211513177357017113 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2010, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/trx0trx.h The transaction Created 3/26/1996 Heikki Tuuri *******************************************************/ #ifndef trx0trx_h #define trx0trx_h #include "univ.i" #include "trx0types.h" #include "dict0types.h" #ifndef UNIV_HOTBACKUP #include "lock0types.h" #include "usr0types.h" #include "que0types.h" #include "mem0mem.h" #include "read0types.h" #include "trx0xa.h" #include "ut0vec.h" #include "srv0srv.h" /** Dummy session used currently in MySQL interface */ extern sess_t* trx_dummy_sess; /** Number of transactions currently allocated for MySQL: protected by the kernel mutex */ extern ulint trx_n_transactions; /********************************************************************//** Releases the search latch if trx has reserved it. */ UNIV_INTERN void trx_search_latch_release_if_reserved( /*=================================*/ trx_t* trx); /*!< in: transaction */ /******************************************************************//** Set detailed error message for the transaction. */ UNIV_INTERN void trx_set_detailed_error( /*===================*/ trx_t* trx, /*!< in: transaction struct */ const char* msg); /*!< in: detailed error message */ /****************************************************************//** Retrieves the error_info field from a trx. @return the error info */ UNIV_INLINE const dict_index_t* trx_get_error_info( /*===============*/ const trx_t* trx); /*!< in: trx object */ /****************************************************************//** Creates and initializes a transaction object. @return own: the transaction */ UNIV_INTERN trx_t* trx_create( /*=======*/ sess_t* sess) /*!< in: session */ __attribute__((nonnull)); /********************************************************************//** Creates a transaction object. @return own: transaction object */ UNIV_INTERN trx_t* trx_allocate_(void); /*================*/ /********************************************************************//** Creates a transaction object for background operations by the master thread. @return own: transaction object */ UNIV_INTERN trx_t* trx_allocate_for_background(void); /*=============================*/ /********************************************************************//** Frees a transaction object. */ UNIV_INTERN void trx_free_( /*======*/ trx_t* trx); /*!< in, own: trx object */ /********************************************************************//** Frees a transaction object of a background operation of the master thread. */ UNIV_INTERN void trx_free_for_background( /*====================*/ trx_t* trx); /*!< in, own: trx object */ /****************************************************************//** Creates trx objects for transactions and initializes the trx list of trx_sys at database start. Rollback segment and undo log lists must already exist when this function is called, because the lists of transactions to be rolled back or cleaned up are built based on the undo log lists. */ UNIV_INTERN void trx_lists_init_at_db_start( /*=======================*/ ib_recovery_t recovery); /*!< in: recovery flag */ /****************************************************************//** Starts a new transaction. @return TRUE if success, FALSE if the rollback segment could not support this many transactions */ UNIV_INTERN ibool trx_start( /*======*/ trx_t* trx, /*!< in: transaction */ ulint rseg_id);/*!< in: rollback segment id; if ULINT_UNDEFINED is passed, the system chooses the rollback segment automatically in a round-robin fashion */ /****************************************************************//** Starts a new transaction. @return TRUE */ UNIV_INTERN ibool trx_start_low( /*==========*/ trx_t* trx, /*!< in: transaction */ ulint rseg_id);/*!< in: rollback segment id; if ULINT_UNDEFINED is passed, the system chooses the rollback segment automatically in a round-robin fashion */ /*************************************************************//** Starts the transaction if it is not yet started. */ UNIV_INLINE void trx_start_if_not_started( /*=====================*/ trx_t* trx); /*!< in: transaction */ /****************************************************************//** Commits a transaction. */ UNIV_INTERN void trx_commit_off_kernel( /*==================*/ trx_t* trx); /*!< in: transaction */ /****************************************************************//** Cleans up a transaction at database startup. The cleanup is needed if the transaction already got to the middle of a commit when the database crashed, and we cannot roll it back. */ UNIV_INTERN void trx_cleanup_at_db_startup( /*======================*/ trx_t* trx); /*!< in: transaction */ /**********************************************************************//** Does the transaction prepare. @return 0 or error number */ UNIV_INTERN ulint trx_prepare( /*========*/ trx_t* trx); /*!< in: trx handle */ /**********************************************************************//** This function is used to find number of prepared transactions and their transaction objects for a recovery. This function is used to recover any X/Open XA distributed transactions @return number of prepared transactions */ UNIV_INTERN int trx_recover( /*========*/ XID* xid_list, /*!< in/out: prepared transactions */ ulint len); /*!< in: number of slots in xid_list */ #ifdef WITH_XOPEN /*******************************************************************//** This function is used to find one X/Open XA distributed transaction which is in the prepared state @return trx or NULL */ UNIV_INTERN trx_t* trx_get_trx_by_xid( /*===============*/ XID* xid); /*!< in: X/Open XA transaction identification */ #endif /* WITH_XOPEN */ /**********************************************************************//** If required, flushes the log to disk if we called trx_commit_() with trx->flush_log_later == TRUE. @return 0 or error number */ UNIV_INTERN ulint trx_commit_complete_( /*=================*/ trx_t* trx); /*!< in: trx handle */ /**********************************************************************//** Marks the latest SQL statement ended. */ UNIV_INTERN void trx_mark_sql_stat_end( /*==================*/ trx_t* trx); /*!< in: trx handle */ /********************************************************************//** Assigns a read view for a consistent read query. All the consistent reads within the same transaction will get the same read view, which is created when this function is first called for a new started transaction. @return consistent read view */ UNIV_INTERN read_view_t* trx_assign_read_view( /*=================*/ trx_t* trx); /*!< in: active transaction */ /***********************************************************//** The transaction must be in the TRX_QUE_LOCK_WAIT state. Puts it to the TRX_QUE_RUNNING state and releases query threads which were waiting for a lock in the wait_thrs list. */ UNIV_INTERN void trx_end_lock_wait( /*==============*/ trx_t* trx); /*!< in: transaction */ /****************************************************************//** Sends a signal to a trx object. */ UNIV_INTERN void trx_sig_send( /*=========*/ trx_t* trx, /*!< in: trx handle */ ulint type, /*!< in: signal type */ ulint sender, /*!< in: TRX_SIG_SELF or TRX_SIG_OTHER_SESS */ que_thr_t* receiver_thr, /*!< in: query thread which wants the reply, or NULL; if type is TRX_SIG_END_WAIT, this must be NULL */ trx_savept_t* savept, /*!< in: possible rollback savepoint, or NULL */ que_thr_t** next_thr); /*!< in/out: next query thread to run; if the value which is passed in is a pointer to a NULL pointer, then the calling function can start running a new query thread; if the parameter is NULL, it is ignored */ /****************************************************************//** Send the reply message when a signal in the queue of the trx has been handled. */ UNIV_INTERN void trx_sig_reply( /*==========*/ trx_sig_t* sig, /*!< in: signal */ que_thr_t** next_thr); /*!< in/out: next query thread to run; if the value which is passed in is a pointer to a NULL pointer, then the calling function can start running a new query thread */ /****************************************************************//** Removes the signal object from a trx signal queue. */ UNIV_INTERN void trx_sig_remove( /*===========*/ trx_t* trx, /*!< in: trx handle */ trx_sig_t* sig); /*!< in, own: signal */ /****************************************************************//** Starts handling of a trx signal. */ UNIV_INTERN void trx_sig_start_handle( /*=================*/ trx_t* trx, /*!< in: trx handle */ que_thr_t** next_thr); /*!< in/out: next query thread to run; if the value which is passed in is a pointer to a NULL pointer, then the calling function can start running a new query thread */ /****************************************************************//** Ends signal handling. If the session is in the error state, and trx->graph_before_signal_handling != NULL, returns control to the error handling routine of the graph (currently only returns the control to the graph root which then sends an error message to the client). */ UNIV_INTERN void trx_end_signal_handling( /*====================*/ trx_t* trx); /*!< in: trx */ /*********************************************************************//** Creates a commit command node struct. @return own: commit node struct */ UNIV_INTERN commit_node_t* commit_node_create( /*===============*/ mem_heap_t* heap); /*!< in: mem heap where created */ /***********************************************************//** Performs an execution step for a commit type node in a query graph. @return query thread to run next, or NULL */ UNIV_INTERN que_thr_t* trx_commit_step( /*============*/ que_thr_t* thr); /*!< in: query thread */ /**********************************************************************//** Prints info about a transaction to the given file. The caller must own the kernel mutex. */ UNIV_INTERN void trx_print( /*======*/ ib_stream_t ib_stream, /*!< in: output stream */ trx_t* trx, /*!< in: transaction */ ulint max_query_len); /*!< in: max query length to print, or 0 to use the default max length */ /** Type of data dictionary operation */ typedef enum trx_dict_op { /** The transaction is not modifying the data dictionary. */ TRX_DICT_OP_NONE = 0, /** The transaction is creating a table or an index, or dropping a table. The table must be dropped in crash recovery. This and TRX_DICT_OP_NONE are the only possible operation modes in crash recovery. */ TRX_DICT_OP_TABLE = 1, /** The transaction is creating or dropping an index in an existing table. In crash recovery, the data dictionary must be locked, but the table must not be dropped. */ TRX_DICT_OP_INDEX = 2 } trx_dict_op_t; /**********************************************************************//** Determine if a transaction is a dictionary operation. @return dictionary operation mode */ UNIV_INLINE enum trx_dict_op trx_get_dict_operation( /*===================*/ const trx_t* trx) /*!< in: transaction */ __attribute__((pure)); /**********************************************************************//** Flag a transaction a dictionary operation. */ UNIV_INLINE void trx_set_dict_operation( /*===================*/ trx_t* trx, /*!< in/out: transaction */ enum trx_dict_op op); /*!< in: operation, not TRX_DICT_OP_NONE */ /**********************************************************************//** Determines if the currently running transaction has been interrupted. @return TRUE if interrupted */ UNIV_INTERN ibool trx_is_interrupted( /*===============*/ const trx_t* trx); /*!< in: transaction */ /*******************************************************************//** Calculates the "weight" of a transaction. The weight of one transaction is estimated as the number of altered rows + the number of locked rows. @param t transaction @return transaction weight */ #define TRX_WEIGHT(t) \ ut_dulint_add((t)->undo_no, UT_LIST_GET_LEN((t)->trx_locks)) /*******************************************************************//** Compares the "weight" (or size) of two transactions. Transactions that have edited non-transactional tables are considered heavier than ones that have not. @return <0, 0 or >0; similar to strcmp(3) */ UNIV_INTERN int trx_weight_cmp( /*===========*/ const trx_t* a, /*!< in: the first transaction to be compared */ const trx_t* b); /*!< in: the second transaction to be compared */ /*******************************************************************//** Retrieves transacion's id, represented as unsigned long long. @return transaction's id */ UNIV_INLINE ib_uint64_t trx_get_id( /*=======*/ const trx_t* trx); /*!< in: transaction */ /************************************************************************ Creates a transaction object for client. */ UNIV_INTERN trx_t* trx_allocate_for_client( /*====================*/ void* arg); /*!< in: pointer to client data */ /************************************************************************** Does the transaction commit for client. @return DB_SUCCESS or error number */ UNIV_INTERN ulint trx_commit( /*=======*/ trx_t* trx); /*!< in: trx handle */ /************************************************************************ Frees a transaction object for client. */ UNIV_INTERN void trx_free_for_client( /*================*/ trx_t* trx); /*!< in, own: trx object */ /* Maximum length of a string that can be returned by trx_get_que_state_str(). */ #define TRX_QUE_STATE_STR_MAX_LEN 12 /* "ROLLING BACK" */ /*******************************************************************//** Retrieves transaction's que state in a human readable string. The string should not be free()'d or modified. @return string in the data segment */ UNIV_INLINE const char* trx_get_que_state_str( /*==================*/ const trx_t* trx); /*!< in: transaction */ /************************************************************************** Reset global variables. */ UNIV_INTERN void trx_var_init(void); /*==============*/ /* Signal to a transaction */ struct trx_sig_struct{ unsigned type:3; /*!< signal type */ unsigned sender:1; /*!< TRX_SIG_SELF or TRX_SIG_OTHER_SESS */ que_thr_t* receiver; /*!< non-NULL if the sender of the signal wants reply after the operation induced by the signal is completed */ trx_savept_t savept; /*!< possible rollback savepoint */ UT_LIST_NODE_T(trx_sig_t) signals; /*!< queue of pending signals to the transaction */ UT_LIST_NODE_T(trx_sig_t) reply_signals; /*!< list of signals for which the sender transaction is waiting a reply */ }; #define TRX_MAGIC_N 91118598 /* The transaction handle; every session has a trx object which is freed only when the session is freed; in addition there may be session-less transactions rolling back after a database recovery */ struct trx_struct{ ulint magic_n; /* These fields are not protected by any mutex. */ const char* op_info; /*!< English text describing the current operation, or an empty string */ ulint conc_state; /*!< state of the trx from the point of view of concurrency control: TRX_ACTIVE, TRX_COMMITTED_IN_MEMORY, ... */ ulint isolation_level;/* TRX_ISO_REPEATABLE_READ, ... */ ulint check_foreigns; /* normally TRUE, but if the user wants to suppress foreign key checks, (in table imports, for example) we set this FALSE */ ulint check_unique_secondary; /*!< normally TRUE, but if the user wants to speed up inserts by suppressing unique key checks for secondary indexes when we decide if we can use the insert buffer for them, we set this FALSE */ #ifdef WITH_XOPEN ulint support_xa; /*!< normally we do the XA two-phase commit steps, but by setting this to FALSE, one can save CPU time and about 150 bytes in the undo log size as then we skip XA steps */ ulint flush_log_later;/* In 2PC, we hold the prepare_commit mutex across both phases. In that case, we defer flush of the logs to disk until after we release the mutex. */ ulint must_flush_log_later;/* this flag is set to TRUE in trx_commit_off_kernel() if flush_log_later was TRUE, and there were modifications by the transaction; in that case we must flush the log in trx_commit() */ #endif /* WITH_XOPEN */ ulint duplicates; /*!< TRX_DUP_IGNORE | TRX_DUP_REPLACE */ ulint has_search_latch; /*!< TRUE if this trx has latched the search system latch in S-mode */ ulint deadlock_mark; /*!< a mark field used in deadlock checking algorithm. */ trx_dict_op_t dict_operation; /**< @see enum trx_dict_op */ /* Fields protected by the srv_conc_mutex. */ ulint declared_to_be_inside_innodb; /* this is TRUE if we have declared this transaction in srv_conc_enter_innodb to be inside the InnoDB engine */ /* Fields protected by dict_operation_lock. The very latch it is used to track. */ ulint dict_operation_lock_mode; /*!< 0, RW_S_LATCH, or RW_X_LATCH: the latch mode trx currently holds on dict_operation_lock */ /* All the next fields are protected by the kernel mutex, except the undo logs which are protected by undo_mutex */ ulint is_purge; /*!< 0=user transaction, 1=purge */ ulint is_recovered; /*!< 0=normal transaction, 1=recovered, must be rolled back */ ulint que_state; /*!< valid when conc_state == TRX_ACTIVE: TRX_QUE_RUNNING, TRX_QUE_LOCK_WAIT, ... */ ulint handling_signals;/* this is TRUE as long as the trx is handling signals */ time_t start_time; /*!< time the trx object was created or the state last time became TRX_ACTIVE */ trx_id_t id; /*!< transaction id */ #ifdef WITH_XOPEN XID xid; /*!< X/Open XA transaction identification to identify a transaction branch */ #endif /* WITH_XOPEN */ trx_id_t no; /*!< transaction serialization number == max trx id when the transaction is moved to COMMITTED_IN_MEMORY state */ ib_uint64_t commit_lsn; /*!< lsn at the time of the commit */ trx_id_t table_id; /*!< Table to drop iff dict_operation is TRUE, or ut_dulint_zero. */ /*------------------------------*/ void* client_thd; /*!< Client thread handle corresponding to this trx, or NULL */ char** client_query_str;/*!< pointer to the SQL query string */ os_thread_id_t client_thread_id;/*!< id of the user thread associated with this transaction object */ ulint client_process_no;/*!< since in Linux, 'top' reports process id's and not thread id's, we store the process number too */ /*------------------------------*/ ulint n_client_tables_in_use; /*!< number of Innobase tables used in the processing of the current SQL statement. */ ulint client_n_tables_locked; /*!< how many tables the current SQL statement uses, except those in consistent read */ ulint search_latch_timeout; /*!< If we notice that someone is waiting for our S-lock on the search latch to be released, we wait in row0sel.c for BTR_SEA_TIMEOUT new searches until we try to keep the search latch again over calls from the client; this is intended to reduce contention on the search latch */ /*------------------------------*/ UT_LIST_NODE_T(trx_t) trx_list; /*!< list of transactions */ UT_LIST_NODE_T(trx_t) client_trx_list;/*!< list of transactions created for client */ /*------------------------------*/ enum db_err error_state; /*!< 0 if no error, otherwise error number; NOTE That ONLY the thread doing the transaction is allowed to set this field: this is NOT protected by the kernel mutex */ const dict_index_t*error_info; /*!< if the error number indicates a duplicate key error, a pointer to the problematic index is stored here */ ulint error_key_num; /*!< if the index creation fails to a duplicate key error, a key number of that index is stored here */ sess_t* sess; /*!< session of the trx, NULL if none */ que_t* graph; /*!< query currently run in the session, or NULL if none; NOTE that the query belongs to the session, and it can survive over a transaction commit, if it is a stored procedure with a COMMIT WORK statement, for instance */ ulint n_active_thrs; /*!< number of active query threads */ que_t* graph_before_signal_handling; /*!< value of graph when signal handling for this trx started: this is used to return control to the original query graph for error processing */ trx_sig_t sig; /*!< one signal object can be allocated in this space, avoiding mem_alloc */ UT_LIST_BASE_NODE_T(trx_sig_t) signals; /*!< queue of processed or pending signals to the trx */ UT_LIST_BASE_NODE_T(trx_sig_t) reply_signals; /*!< list of signals sent by the query threads of this trx for which a thread is waiting for a reply; if this trx is killed, the reply requests in the list must be canceled */ /*------------------------------*/ lock_t* wait_lock; /*!< if trx execution state is TRX_QUE_LOCK_WAIT, this points to the lock request, otherwise this is NULL */ ibool was_chosen_as_deadlock_victim; /*!< when the transaction decides to wait for a lock, it sets this to FALSE; if another transaction chooses this transaction as a victim in deadlock resolution, it sets this to TRUE */ time_t wait_started; /*!< lock wait started at this time */ UT_LIST_BASE_NODE_T(que_thr_t) wait_thrs; /*!< query threads belonging to this trx that are in the QUE_THR_LOCK_WAIT state */ /*------------------------------*/ mem_heap_t* lock_heap; /*!< memory heap for the locks of the transaction */ UT_LIST_BASE_NODE_T(lock_t) trx_locks; /*!< locks reserved by the transaction */ /*------------------------------*/ mem_heap_t* global_read_view_heap; /*!< memory heap for the global read view */ read_view_t* global_read_view; /*!< consistent read view associated to a transaction or NULL */ read_view_t* read_view; /*!< consistent read view used in the transaction or NULL, this read view if defined can be normal read view associated to a transaction (i.e. same as global_read_view) or read view associated to a cursor */ /*------------------------------*/ UT_LIST_BASE_NODE_T(trx_named_savept_t) trx_savepoints; /*!< savepoints set with SAVEPOINT ..., oldest first */ /*------------------------------*/ mutex_t undo_mutex; /*!< mutex protecting the fields in this section (down to undo_no_arr), EXCEPT last_sql_stat_start, which can be accessed only when we know that there cannot be any activity in the undo logs! */ undo_no_t undo_no; /*!< next undo log record number to assign; since the undo log is private for a transaction, this is a simple ascending sequence with no gaps; thus it represents the number of modified/inserted rows in a transaction */ trx_savept_t last_sql_stat_start; /*!< undo_no when the last sql statement was started: in case of an error, trx is rolled back down to this undo number; see note at undo_mutex! */ trx_rseg_t* rseg; /*!< rollback segment assigned to the transaction, or NULL if not assigned yet */ trx_undo_t* insert_undo; /*!< pointer to the insert undo log, or NULL if no inserts performed yet */ trx_undo_t* update_undo; /*!< pointer to the update undo log, or NULL if no update performed yet */ undo_no_t roll_limit; /*!< least undo number to undo during a rollback */ ulint pages_undone; /*!< number of undo log pages undone since the last undo log truncation */ trx_undo_arr_t* undo_no_arr; /*!< array of undo numbers of undo log records which are currently processed by a rollback operation */ /*------------------------------*/ char detailed_error[256]; /*!< detailed error message for last error, or empty. */ }; #define TRX_MAX_N_THREADS 32 /*!< maximum number of concurrent threads running a single operation of a transaction, e.g., a parallel query */ /* Transaction concurrency states (trx->conc_state) */ #define TRX_NOT_STARTED 0 #define TRX_ACTIVE 1 #define TRX_COMMITTED_IN_MEMORY 2 #define TRX_PREPARED 3 /*!< Support for 2PC/XA */ /* Transaction execution states when trx->conc_state == TRX_ACTIVE */ #define TRX_QUE_RUNNING 0 /*!< transaction is running */ #define TRX_QUE_LOCK_WAIT 1 /*!< transaction is waiting for a lock */ #define TRX_QUE_ROLLING_BACK 2 /*!< transaction is rolling back */ #define TRX_QUE_COMMITTING 3 /*!< transaction is committing */ /* Transaction isolation levels (trx->isolation_level) */ #define TRX_ISO_READ_UNCOMMITTED 0 /*!< dirty read: non-locking SELECTs are performed so that we do not look at a possible earlier version of a record; thus they are not 'consistent' reads under this isolation level; otherwise like level 2 */ #define TRX_ISO_READ_COMMITTED 1 /*!< somewhat Oracle-like isolation, except that in range UPDATE and DELETE we must block phantom rows with next-key locks; SELECT ... FOR UPDATE and ... LOCK IN SHARE MODE only lock the index records, NOT the gaps before them, and thus allow free inserting; each consistent read reads its own snapshot */ #define TRX_ISO_REPEATABLE_READ 2 /*!< this is the default; all consistent reads in the same trx read the same snapshot; full next-key locking used in locking reads to block insertions into gaps */ #define TRX_ISO_SERIALIZABLE 3 /*!< all plain SELECTs are converted to LOCK IN SHARE MODE reads */ /* Treatment of duplicate values (trx->duplicates; for example, in inserts). Multiple flags can be combined with bitwise OR. */ #define TRX_DUP_IGNORE 1 /*!< duplicate rows are to be updated */ #define TRX_DUP_REPLACE 2 /*!< duplicate rows are to be replaced */ /* Types of a trx signal */ #define TRX_SIG_NO_SIGNAL 0 #define TRX_SIG_TOTAL_ROLLBACK 1 #define TRX_SIG_ROLLBACK_TO_SAVEPT 2 #define TRX_SIG_COMMIT 3 #define TRX_SIG_ERROR_OCCURRED 4 #define TRX_SIG_BREAK_EXECUTION 5 /* Sender types of a signal */ #define TRX_SIG_SELF 0 /*!< sent by the session itself, or by an error occurring within this session */ #define TRX_SIG_OTHER_SESS 1 /*!< sent by another session (which must hold rights to this) */ /** Commit node states */ enum commit_node_state { COMMIT_NODE_SEND = 1, /*!< about to send a commit signal to the transaction */ COMMIT_NODE_WAIT /*!< commit signal sent to the transaction, waiting for completion */ }; /** Commit command node in a query graph */ struct commit_node_struct{ que_common_t common; /*!< node type: QUE_NODE_COMMIT */ enum commit_node_state state; /*!< node execution state */ }; #ifndef UNIV_NONINL #include "trx0trx.ic" #endif #endif /* !UNIV_HOTBACKUP */ #endif haildb-2.3.2/include/que0types.h0000644000175000017500000000376011513177357017404 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/que0types.h Query graph global types Created 5/27/1996 Heikki Tuuri *******************************************************/ #ifndef que0types_h #define que0types_h #include "data0data.h" #include "dict0types.h" /* Pseudotype for all graph nodes */ typedef void que_node_t; typedef struct que_fork_struct que_fork_t; /* Query graph root is a fork node */ typedef que_fork_t que_t; typedef struct que_thr_struct que_thr_t; typedef struct que_common_struct que_common_t; /* Common struct at the beginning of each query graph node; the name of this substruct must be 'common' */ struct que_common_struct{ ulint type; /*!< query node type */ que_node_t* parent; /*!< back pointer to parent node, or NULL */ que_node_t* brother;/* pointer to a possible brother node */ dfield_t val; /*!< evaluated value for an expression */ ulint val_buf_size; /* buffer size for the evaluated value data, if the buffer has been allocated dynamically: if this field is != 0, and the node is a symbol node or a function node, then we have to free the data field in val explicitly */ }; #endif haildb-2.3.2/include/ut0mem.ic0000644000175000017500000001675711513177357017032 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1994, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /*******************************************************************//** @file include/ut0mem.ic Memory primitives Created 5/30/1994 Heikki Tuuri ************************************************************************/ #include "ut0byte.h" #include "mach0data.h" /** Wrapper for memcpy(3). Copy memory area when the source and target are not overlapping. * @param dest in: copy to * @param sour in: copy from * @param n in: number of bytes to copy * @return dest */ UNIV_INLINE void* ut_memcpy(void* dest, const void* sour, ulint n) { return(memcpy(dest, sour, n)); } /** Wrapper for memmove(3). Copy memory area when the source and target are overlapping. * @param dest in: copy to * @param sour in: copy from * @param n in: number of bytes to copy * @return dest */ UNIV_INLINE void* ut_memmove(void* dest, const void* sour, ulint n) { return(memmove(dest, sour, n)); } /** Wrapper for memcmp(3). Compare memory areas. * @param str1 in: first memory block to compare * @param str2 in: second memory block to compare * @param n in: number of bytes to compare * @return negative, 0, or positive if str1 is smaller, equal, or greater than str2, respectively. */ UNIV_INLINE int ut_memcmp(const void* str1, const void* str2, ulint n) { return(memcmp(str1, str2, n)); } /** Wrapper for strcpy(3). Copy a NUL-terminated string. * @param dest in: copy to * @param sour in: copy from * @return dest */ UNIV_INLINE char* ut_strcpy(char* dest, const char* sour) { return(strcpy(dest, sour)); } /** Wrapper for strlen(3). Determine the length of a NUL-terminated string. * @param str in: string * @return length of the string in bytes, excluding the terminating NUL */ UNIV_INLINE ulint ut_strlen(const char* str) { return(strlen(str)); } /** Wrapper for strcmp(3). Compare NUL-terminated strings. * @param str1 in: first string to compare * @param str2 in: second string to compare * @return negative, 0, or positive if str1 is smaller, equal, or greater than str2, respectively. */ UNIV_INLINE int ut_strcmp(const char* str1, const char* str2) { return(strcmp(str1, str2)); } /**********************************************************************//** Compute strlen(ut_strcpyq(str, q)). @return length of the string when quoted */ UNIV_INLINE ulint ut_strlenq( /*=======*/ const char* str, /*!< in: null-terminated string */ char q) /*!< in: the quote character */ { ulint len; for (len = 0; *str; len++, str++) { if (*str == q) { len++; } } return(len); } /**********************************************************************//** Converts a raw binary data to a NUL-terminated hex string. The output is truncated if there is not enough space in "hex", make sure "hex_size" is at least (2 * raw_size + 1) if you do not want this to happen. Returns the actual number of characters written to "hex" (including the NUL). @return number of chars written */ UNIV_INLINE ulint ut_raw_to_hex( /*==========*/ const void* raw, /*!< in: raw data */ ulint raw_size, /*!< in: "raw" length in bytes */ char* hex, /*!< out: hex string */ ulint hex_size) /*!< in: "hex" size in bytes */ { #ifdef WORDS_BIGENDIAN #define MK_UINT16(a, b) (((ib_uint16_t) (a)) << 8 | (ib_uint16_t) (b)) #define UINT16_GET_A(u) ((unsigned char) ((u) >> 8)) #define UINT16_GET_B(u) ((unsigned char) ((u) & 0xFF)) #else /* WORDS_BIGENDIAN */ #define MK_UINT16(a, b) (((ib_uint16_t) (b)) << 8 | (ib_uint16_t) (a)) #define UINT16_GET_A(u) ((unsigned char) ((u) & 0xFF)) #define UINT16_GET_B(u) ((unsigned char) ((u) >> 8)) #endif /* WORDS_BIGENDIAN */ #define MK_ALL_UINT16_WITH_A(a) \ MK_UINT16(a, '0'), \ MK_UINT16(a, '1'), \ MK_UINT16(a, '2'), \ MK_UINT16(a, '3'), \ MK_UINT16(a, '4'), \ MK_UINT16(a, '5'), \ MK_UINT16(a, '6'), \ MK_UINT16(a, '7'), \ MK_UINT16(a, '8'), \ MK_UINT16(a, '9'), \ MK_UINT16(a, 'A'), \ MK_UINT16(a, 'B'), \ MK_UINT16(a, 'C'), \ MK_UINT16(a, 'D'), \ MK_UINT16(a, 'E'), \ MK_UINT16(a, 'F') static const ib_uint16_t hex_map[256] = { MK_ALL_UINT16_WITH_A('0'), MK_ALL_UINT16_WITH_A('1'), MK_ALL_UINT16_WITH_A('2'), MK_ALL_UINT16_WITH_A('3'), MK_ALL_UINT16_WITH_A('4'), MK_ALL_UINT16_WITH_A('5'), MK_ALL_UINT16_WITH_A('6'), MK_ALL_UINT16_WITH_A('7'), MK_ALL_UINT16_WITH_A('8'), MK_ALL_UINT16_WITH_A('9'), MK_ALL_UINT16_WITH_A('A'), MK_ALL_UINT16_WITH_A('B'), MK_ALL_UINT16_WITH_A('C'), MK_ALL_UINT16_WITH_A('D'), MK_ALL_UINT16_WITH_A('E'), MK_ALL_UINT16_WITH_A('F') }; const unsigned char* rawc; ulint read_bytes; ulint write_bytes; ulint i; rawc = (const unsigned char*) raw; if (hex_size == 0) { return(0); } if (hex_size <= 2 * raw_size) { read_bytes = hex_size / 2; write_bytes = hex_size; } else { read_bytes = raw_size; write_bytes = 2 * raw_size + 1; } #define LOOP_READ_BYTES(ASSIGN) \ for (i = 0; i < read_bytes; i++) { \ ASSIGN; \ hex += 2; \ rawc++; \ } if (ut_align_offset(hex, 2) == 0) { LOOP_READ_BYTES( *(ib_uint16_t*) hex = hex_map[*rawc] ); } else { LOOP_READ_BYTES( *hex = UINT16_GET_A(hex_map[*rawc]); *(hex + 1) = UINT16_GET_B(hex_map[*rawc]) ); } if (hex_size <= 2 * raw_size && hex_size % 2 == 0) { hex--; } *hex = '\0'; return(write_bytes); } /*******************************************************************//** Adds single quotes to the start and end of string and escapes any quotes by doubling them. Returns the number of bytes that were written to "buf" (including the terminating NUL). If buf_size is too small then the trailing bytes from "str" are discarded. @return number of bytes that were written */ UNIV_INLINE ulint ut_str_sql_format( /*==============*/ const char* str, /*!< in: string */ ulint str_len, /*!< in: string length in bytes */ char* buf, /*!< out: output buffer */ ulint buf_size) /*!< in: output buffer size in bytes */ { ulint str_i; ulint buf_i; buf_i = 0; switch (buf_size) { case 3: if (str_len == 0) { buf[buf_i] = '\''; buf_i++; buf[buf_i] = '\''; buf_i++; } /* FALLTHROUGH */ case 2: case 1: buf[buf_i] = '\0'; buf_i++; /* FALLTHROUGH */ case 0: return(buf_i); } /* buf_size >= 4 */ buf[0] = '\''; buf_i = 1; for (str_i = 0; str_i < str_len; str_i++) { char ch; if (buf_size - buf_i == 2) { break; } ch = str[str_i]; switch (ch) { case '\0': if (UNIV_UNLIKELY(buf_size - buf_i < 4)) { goto func_exit; } buf[buf_i] = '\\'; buf_i++; buf[buf_i] = '0'; buf_i++; break; case '\'': case '\\': if (UNIV_UNLIKELY(buf_size - buf_i < 4)) { goto func_exit; } buf[buf_i] = ch; buf_i++; /* FALLTHROUGH */ default: buf[buf_i] = ch; buf_i++; } } func_exit: buf[buf_i] = '\''; buf_i++; buf[buf_i] = '\0'; buf_i++; return(buf_i); } haildb-2.3.2/include/ut0sort.h0000644000175000017500000000666711513177357017076 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /******************************************************************//** @file include/ut0sort.h Sort utility Created 11/9/1995 Heikki Tuuri ***********************************************************************/ #ifndef ut0sort_h #define ut0sort_h #include "univ.i" /* This module gives a macro definition of the body of a standard sort function for an array of elements of any type. The comparison function is given as a parameter to the macro. The sort algorithm is mergesort which has logarithmic worst case. */ /*******************************************************************//** This macro expands to the body of a standard sort function. The sort function uses mergesort and must be defined separately for each type of array. Also the comparison function has to be defined individually for each array cell type. SORT_FUN is the sort function name. The function takes the array to be sorted (ARR), the array of auxiliary space (AUX_ARR) of same size, and the low (LOW), inclusive, and high (HIGH), noninclusive, limits for the sort interval as arguments. CMP_FUN is the comparison function name. It takes as arguments two elements from the array and returns 1, if the first is bigger, 0 if equal, and -1 if the second bigger. */ #define UT_SORT_FUNCTION_BODY(\ CMP_CTX, SORT_FUN, ARR, AUX_ARR, LOW, HIGH, CMP_FUN)\ {\ ulint ut_sort_mid77;\ ulint ut_sort_i77;\ ulint ut_sort_low77;\ ulint ut_sort_high77;\ \ ut_ad((LOW) < (HIGH));\ ut_ad(ARR);\ ut_ad(AUX_ARR);\ \ if ((LOW) == (HIGH) - 1) {\ return;\ } else if ((LOW) == (HIGH) - 2) {\ if (CMP_FUN((CMP_CTX),(ARR)[LOW], (ARR)[(HIGH) - 1]) > 0) {\ (AUX_ARR)[LOW] = (ARR)[LOW];\ (ARR)[LOW] = (ARR)[(HIGH) - 1];\ (ARR)[(HIGH) - 1] = (AUX_ARR)[LOW];\ }\ return;\ }\ \ ut_sort_mid77 = ((LOW) + (HIGH)) / 2;\ \ SORT_FUN((CMP_CTX), (ARR), (AUX_ARR), (LOW), ut_sort_mid77);\ SORT_FUN((CMP_CTX), (ARR), (AUX_ARR), ut_sort_mid77, (HIGH));\ \ ut_sort_low77 = (LOW);\ ut_sort_high77 = ut_sort_mid77;\ \ for (ut_sort_i77 = (LOW); ut_sort_i77 < (HIGH); ut_sort_i77++) {\ \ if (ut_sort_low77 >= ut_sort_mid77) {\ (AUX_ARR)[ut_sort_i77] = (ARR)[ut_sort_high77];\ ut_sort_high77++;\ } else if (ut_sort_high77 >= (HIGH)) {\ (AUX_ARR)[ut_sort_i77] = (ARR)[ut_sort_low77];\ ut_sort_low77++;\ } else if (CMP_FUN((CMP_CTX),(ARR)[ut_sort_low77],\ (ARR)[ut_sort_high77]) > 0) {\ (AUX_ARR)[ut_sort_i77] = (ARR)[ut_sort_high77];\ ut_sort_high77++;\ } else {\ (AUX_ARR)[ut_sort_i77] = (ARR)[ut_sort_low77];\ ut_sort_low77++;\ }\ }\ \ memcpy((void*) ((ARR) + (LOW)), (AUX_ARR) + (LOW),\ ((HIGH) - (LOW)) * sizeof *(ARR));\ }\ #endif haildb-2.3.2/include/sync0arr.h0000644000175000017500000001202111513177357017174 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/sync0arr.h The wait array used in synchronization primitives Created 9/5/1995 Heikki Tuuri *******************************************************/ #ifndef sync0arr_h #define sync0arr_h #include "univ.i" #include "ut0lst.h" #include "ut0mem.h" #include "os0thread.h" /** Synchronization wait array cell */ typedef struct sync_cell_struct sync_cell_t; /** Synchronization wait array */ typedef struct sync_array_struct sync_array_t; /** Parameters for sync_array_create() @{ */ #define SYNC_ARRAY_OS_MUTEX 1 /*!< protected by os_mutex_t */ #define SYNC_ARRAY_MUTEX 2 /*!< protected by mutex_t */ /* @} */ /*******************************************************************//** Creates a synchronization wait array. It is protected by a mutex which is automatically reserved when the functions operating on it are called. @return own: created wait array */ UNIV_INTERN sync_array_t* sync_array_create( /*==============*/ ulint n_cells, /*!< in: number of cells in the array to create */ ulint protection); /*!< in: either SYNC_ARRAY_OS_MUTEX or SYNC_ARRAY_MUTEX: determines the type of mutex protecting the data structure */ /******************************************************************//** Frees the resources in a wait array. */ UNIV_INTERN void sync_array_free( /*============*/ sync_array_t* arr); /*!< in, own: sync wait array */ /******************************************************************//** Reserves a wait array cell for waiting for an object. The event of the cell is reset to nonsignalled state. */ UNIV_INTERN void sync_array_reserve_cell( /*====================*/ sync_array_t* arr, /*!< in: wait array */ void* object, /*!< in: pointer to the object to wait for */ ulint type, /*!< in: lock request type */ const char* file, /*!< in: file where requested */ ulint line, /*!< in: line where requested */ ulint* index); /*!< out: index of the reserved cell */ /******************************************************************//** This function should be called when a thread starts to wait on a wait array cell. In the debug version this function checks if the wait for a semaphore will result in a deadlock, in which case prints info and asserts. */ UNIV_INTERN void sync_array_wait_event( /*==================*/ sync_array_t* arr, /*!< in: wait array */ ulint index); /*!< in: index of the reserved cell */ /******************************************************************//** Frees the cell. NOTE! sync_array_wait_event frees the cell automatically! */ UNIV_INTERN void sync_array_free_cell( /*=================*/ sync_array_t* arr, /*!< in: wait array */ ulint index); /*!< in: index of the cell in array */ /**********************************************************************//** Note that one of the wait objects was signalled. */ UNIV_INTERN void sync_array_object_signalled( /*========================*/ sync_array_t* arr); /*!< in: wait array */ /**********************************************************************//** If the wakeup algorithm does not work perfectly at semaphore relases, this function will do the waking (see the comment in mutex_exit). This function should be called about every 1 second in the server. */ UNIV_INTERN void sync_arr_wake_threads_if_sema_free(void); /*====================================*/ /**********************************************************************//** Prints warnings of long semaphore waits to stderr. @return TRUE if fatal semaphore wait threshold was exceeded */ UNIV_INTERN ibool sync_array_print_long_waits(void); /*=============================*/ /********************************************************************//** Validates the integrity of the wait array. Checks that the number of reserved cells equals the count variable. */ UNIV_INTERN void sync_array_validate( /*================*/ sync_array_t* arr); /*!< in: sync wait array */ /**********************************************************************//** Prints info of the wait array. */ UNIV_INTERN void sync_array_print_info( /*==================*/ ib_stream_t ib_stream, /*!< in: stream where to print */ sync_array_t* arr); /*!< in: wait array */ #ifndef UNIV_NONINL #include "sync0arr.ic" #endif #endif haildb-2.3.2/include/page0types.h0000644000175000017500000001227411513177357017526 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1994, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/page0types.h Index page routines Created 2/2/1994 Heikki Tuuri *******************************************************/ #ifndef page0types_h #define page0types_h #include "univ.i" #include "dict0types.h" #include "mtr0types.h" /** Eliminates a name collision on HP-UX */ #define page_t ib_page_t /** Type of the index page */ typedef byte page_t; /** Index page cursor */ typedef struct page_cur_struct page_cur_t; /** Compressed index page */ typedef byte page_zip_t; /** Compressed page descriptor */ typedef struct page_zip_des_struct page_zip_des_t; /* The following definitions would better belong to page0zip.h, but we cannot include page0zip.h from rem0rec.ic, because page0*.h includes rem0rec.h and may include rem0rec.ic. */ /** Number of bits needed for representing different compressed page sizes */ #define PAGE_ZIP_SSIZE_BITS 3 /** log2 of smallest compressed page size */ #define PAGE_ZIP_MIN_SIZE_SHIFT 10 /** Smallest compressed page size */ #define PAGE_ZIP_MIN_SIZE (1 << PAGE_ZIP_MIN_SIZE_SHIFT) /** Number of supported compressed page sizes */ #define PAGE_ZIP_NUM_SSIZE (UNIV_PAGE_SIZE_SHIFT - PAGE_ZIP_MIN_SIZE_SHIFT + 2) #if PAGE_ZIP_NUM_SSIZE > (1 << PAGE_ZIP_SSIZE_BITS) # error "PAGE_ZIP_NUM_SSIZE > (1 << PAGE_ZIP_SSIZE_BITS)" #endif /** Compressed page descriptor */ struct page_zip_des_struct { page_zip_t* data; /*!< compressed page data */ #ifdef UNIV_DEBUG unsigned m_start:16; /*!< start offset of modification log */ #endif /* UNIV_DEBUG */ unsigned m_end:16; /*!< end offset of modification log */ unsigned m_nonempty:1; /*!< TRUE if the modification log is not empty */ unsigned n_blobs:12; /*!< number of externally stored columns on the page; the maximum is 744 on a 16 KiB page */ unsigned ssize:PAGE_ZIP_SSIZE_BITS; /*!< 0 or compressed page size; the size in bytes is PAGE_ZIP_MIN_SIZE << (ssize - 1). */ }; /** Compression statistics for a given page size */ struct page_zip_stat_struct { /** Number of page compressions */ ulint compressed; /** Number of successful page compressions */ ulint compressed_ok; /** Number of page decompressions */ ulint decompressed; /** Duration of page compressions in microseconds */ ib_uint64_t compressed_usec; /** Duration of page decompressions in microseconds */ ib_uint64_t decompressed_usec; }; /** Compression statistics */ typedef struct page_zip_stat_struct page_zip_stat_t; /** Statistics on compression, indexed by page_zip_des_struct::ssize - 1 */ extern page_zip_stat_t page_zip_stat[PAGE_ZIP_NUM_SSIZE - 1]; /**********************************************************************//** Write the "deleted" flag of a record on a compressed page. The flag must already have been written on the uncompressed page. */ UNIV_INTERN void page_zip_rec_set_deleted( /*=====================*/ page_zip_des_t* page_zip,/*!< in/out: compressed page */ const byte* rec, /*!< in: record on the uncompressed page */ ulint flag) /*!< in: the deleted flag (nonzero=TRUE) */ __attribute__((nonnull)); /**********************************************************************//** Write the "owned" flag of a record on a compressed page. The n_owned field must already have been written on the uncompressed page. */ UNIV_INTERN void page_zip_rec_set_owned( /*===================*/ page_zip_des_t* page_zip,/*!< in/out: compressed page */ const byte* rec, /*!< in: record on the uncompressed page */ ulint flag) /*!< in: the owned flag (nonzero=TRUE) */ __attribute__((nonnull)); /**********************************************************************//** Shift the dense page directory when a record is deleted. */ UNIV_INTERN void page_zip_dir_delete( /*================*/ page_zip_des_t* page_zip,/*!< in/out: compressed page */ byte* rec, /*!< in: deleted record */ dict_index_t* index, /*!< in: index of rec */ const ulint* offsets,/*!< in: rec_get_offsets(rec) */ const byte* free) /*!< in: previous start of the free list */ __attribute__((nonnull(1,2,3,4))); /**********************************************************************//** Add a slot to the dense page directory. */ UNIV_INTERN void page_zip_dir_add_slot( /*==================*/ page_zip_des_t* page_zip, /*!< in/out: compressed page */ ulint is_clustered) /*!< in: nonzero for clustered index, zero for others */ __attribute__((nonnull)); #endif haildb-2.3.2/include/sync0types.h0000644000175000017500000000233711513177357017565 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/sync0types.h Global types for sync Created 9/5/1995 Heikki Tuuri *******************************************************/ #ifndef sync0types_h #define sync0types_h /** Rename mutex_t to avoid name space collision on some systems */ #define mutex_t ib_mutex_t /** InnoDB mutex */ typedef struct mutex_struct mutex_t; #endif haildb-2.3.2/include/eval0proc.h0000644000175000017500000000645311513177357017342 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1998, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/eval0proc.h Executes SQL stored procedures and their control structures Created 1/20/1998 Heikki Tuuri *******************************************************/ #ifndef eval0proc_h #define eval0proc_h #include "univ.i" #include "que0types.h" #include "pars0sym.h" #include "pars0pars.h" /**********************************************************************//** Performs an execution step of a procedure node. @return query thread to run next or NULL */ UNIV_INLINE que_thr_t* proc_step( /*======*/ que_thr_t* thr); /*!< in: query thread */ /**********************************************************************//** Performs an execution step of an if-statement node. @return query thread to run next or NULL */ UNIV_INTERN que_thr_t* if_step( /*====*/ que_thr_t* thr); /*!< in: query thread */ /**********************************************************************//** Performs an execution step of a while-statement node. @return query thread to run next or NULL */ UNIV_INTERN que_thr_t* while_step( /*=======*/ que_thr_t* thr); /*!< in: query thread */ /**********************************************************************//** Performs an execution step of a for-loop node. @return query thread to run next or NULL */ UNIV_INTERN que_thr_t* for_step( /*=====*/ que_thr_t* thr); /*!< in: query thread */ /**********************************************************************//** Performs an execution step of an assignment statement node. @return query thread to run next or NULL */ UNIV_INTERN que_thr_t* assign_step( /*========*/ que_thr_t* thr); /*!< in: query thread */ /**********************************************************************//** Performs an execution step of a procedure call node. @return query thread to run next or NULL */ UNIV_INLINE que_thr_t* proc_eval_step( /*===========*/ que_thr_t* thr); /*!< in: query thread */ /**********************************************************************//** Performs an execution step of an exit statement node. @return query thread to run next or NULL */ UNIV_INTERN que_thr_t* exit_step( /*======*/ que_thr_t* thr); /*!< in: query thread */ /**********************************************************************//** Performs an execution step of a return-statement node. @return query thread to run next or NULL */ UNIV_INTERN que_thr_t* return_step( /*========*/ que_thr_t* thr); /*!< in: query thread */ #ifndef UNIV_NONINL #include "eval0proc.ic" #endif #endif haildb-2.3.2/include/pars0opt.h0000644000175000017500000000520211513177357017206 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1997, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/pars0opt.h Simple SQL optimizer Created 12/21/1997 Heikki Tuuri *******************************************************/ #ifndef pars0opt_h #define pars0opt_h #include "univ.i" #include "que0types.h" #include "usr0types.h" #include "pars0sym.h" #include "dict0types.h" #include "row0sel.h" /*******************************************************************//** Optimizes a select. Decides which indexes to tables to use. The tables are accessed in the order that they were written to the FROM part in the select statement. */ UNIV_INTERN void opt_search_plan( /*============*/ sel_node_t* sel_node); /*!< in: parsed select node */ /*******************************************************************//** Looks for occurrences of the columns of the table in the query subgraph and adds them to the list of columns if an occurrence of the same column does not already exist in the list. If the column is already in the list, puts a value indirection to point to the occurrence in the column list, except if the column occurrence we are looking at is in the column list, in which case nothing is done. */ UNIV_INTERN void opt_find_all_cols( /*==============*/ ibool copy_val, /*!< in: if TRUE, new found columns are added as columns to copy */ dict_index_t* index, /*!< in: index to use */ sym_node_list_t* col_list, /*!< in: base node of a list where to add new found columns */ plan_t* plan, /*!< in: plan or NULL */ que_node_t* exp); /*!< in: expression or condition */ /********************************************************************//** Prints info of a query plan. */ UNIV_INTERN void opt_print_query_plan( /*=================*/ sel_node_t* sel_node); /*!< in: select node */ #ifndef UNIV_NONINL #include "pars0opt.ic" #endif #endif haildb-2.3.2/include/trx0sys.ic0000644000175000017500000002426311513177357017246 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/trx0sys.ic Transaction system Created 3/26/1996 Heikki Tuuri *******************************************************/ #include "trx0trx.h" #include "data0type.h" #ifndef UNIV_HOTBACKUP # include "srv0srv.h" # include "mtr0log.h" /* The typedef for rseg slot in the file copy */ typedef byte trx_sysf_rseg_t; /* Rollback segment specification slot offsets */ /*-------------------------------------------------------------*/ #define TRX_SYS_RSEG_SPACE 0 /* space where the segment header is placed; starting with InnoDB 5.1.7, this is ULINT_UNDEFINED for unused slots */ #define TRX_SYS_RSEG_PAGE_NO 4 /* page number where the segment header is placed; this is FIL_NULL if the slot is unused */ /*-------------------------------------------------------------*/ /* Size of a rollback segment specification slot */ #define TRX_SYS_RSEG_SLOT_SIZE 8 /*****************************************************************//** Writes the value of max_trx_id to the file based trx system header. */ UNIV_INTERN void trx_sys_flush_max_trx_id(void); /*==========================*/ /***************************************************************//** Checks if a page address is the trx sys header page. @return TRUE if trx sys header page */ UNIV_INLINE ibool trx_sys_hdr_page( /*=============*/ ulint space, /*!< in: space */ ulint page_no)/*!< in: page number */ { if ((space == TRX_SYS_SPACE) && (page_no == TRX_SYS_PAGE_NO)) { return(TRUE); } return(FALSE); } /***************************************************************//** Gets the pointer in the nth slot of the rseg array. @return pointer to rseg object, NULL if slot not in use */ UNIV_INLINE trx_rseg_t* trx_sys_get_nth_rseg( /*=================*/ trx_sys_t* sys, /*!< in: trx system */ ulint n) /*!< in: index of slot */ { ut_ad(mutex_own(&(kernel_mutex))); ut_ad(n < TRX_SYS_N_RSEGS); return(sys->rseg_array[n]); } /***************************************************************//** Sets the pointer in the nth slot of the rseg array. */ UNIV_INLINE void trx_sys_set_nth_rseg( /*=================*/ trx_sys_t* sys, /*!< in: trx system */ ulint n, /*!< in: index of slot */ trx_rseg_t* rseg) /*!< in: pointer to rseg object, NULL if slot not in use */ { ut_ad(n < TRX_SYS_N_RSEGS); sys->rseg_array[n] = rseg; } /**********************************************************************//** Gets a pointer to the transaction system header and x-latches its page. @return pointer to system header, page x-latched. */ UNIV_INLINE trx_sysf_t* trx_sysf_get( /*=========*/ mtr_t* mtr) /*!< in: mtr */ { buf_block_t* block; trx_sysf_t* header; ut_ad(mtr); block = buf_page_get(TRX_SYS_SPACE, 0, TRX_SYS_PAGE_NO, RW_X_LATCH, mtr); buf_block_dbg_add_level(block, SYNC_TRX_SYS_HEADER); header = TRX_SYS + buf_block_get_frame(block); return(header); } /*****************************************************************//** Gets the space of the nth rollback segment slot in the trx system file copy. @return space id */ UNIV_INLINE ulint trx_sysf_rseg_get_space( /*====================*/ trx_sysf_t* sys_header, /*!< in: trx sys header */ ulint i, /*!< in: slot index == rseg id */ mtr_t* mtr) /*!< in: mtr */ { ut_ad(mutex_own(&(kernel_mutex))); ut_ad(sys_header); ut_ad(i < TRX_SYS_N_RSEGS); return(mtr_read_ulint(sys_header + TRX_SYS_RSEGS + i * TRX_SYS_RSEG_SLOT_SIZE + TRX_SYS_RSEG_SPACE, MLOG_4BYTES, mtr)); } /*****************************************************************//** Gets the page number of the nth rollback segment slot in the trx system header. @return page number, FIL_NULL if slot unused */ UNIV_INLINE ulint trx_sysf_rseg_get_page_no( /*======================*/ trx_sysf_t* sys_header, /*!< in: trx system header */ ulint i, /*!< in: slot index == rseg id */ mtr_t* mtr) /*!< in: mtr */ { ut_ad(sys_header); ut_ad(mutex_own(&(kernel_mutex))); ut_ad(i < TRX_SYS_N_RSEGS); return(mtr_read_ulint(sys_header + TRX_SYS_RSEGS + i * TRX_SYS_RSEG_SLOT_SIZE + TRX_SYS_RSEG_PAGE_NO, MLOG_4BYTES, mtr)); } /*****************************************************************//** Sets the space id of the nth rollback segment slot in the trx system file copy. */ UNIV_INLINE void trx_sysf_rseg_set_space( /*====================*/ trx_sysf_t* sys_header, /*!< in: trx sys file copy */ ulint i, /*!< in: slot index == rseg id */ ulint space, /*!< in: space id */ mtr_t* mtr) /*!< in: mtr */ { ut_ad(mutex_own(&(kernel_mutex))); ut_ad(sys_header); ut_ad(i < TRX_SYS_N_RSEGS); mlog_write_ulint(sys_header + TRX_SYS_RSEGS + i * TRX_SYS_RSEG_SLOT_SIZE + TRX_SYS_RSEG_SPACE, space, MLOG_4BYTES, mtr); } /*****************************************************************//** Sets the page number of the nth rollback segment slot in the trx system header. */ UNIV_INLINE void trx_sysf_rseg_set_page_no( /*======================*/ trx_sysf_t* sys_header, /*!< in: trx sys header */ ulint i, /*!< in: slot index == rseg id */ ulint page_no, /*!< in: page number, FIL_NULL if the slot is reset to unused */ mtr_t* mtr) /*!< in: mtr */ { ut_ad(mutex_own(&(kernel_mutex))); ut_ad(sys_header); ut_ad(i < TRX_SYS_N_RSEGS); mlog_write_ulint(sys_header + TRX_SYS_RSEGS + i * TRX_SYS_RSEG_SLOT_SIZE + TRX_SYS_RSEG_PAGE_NO, page_no, MLOG_4BYTES, mtr); } #endif /* !UNIV_HOTBACKUP */ /*****************************************************************//** Writes a trx id to an index page. In case that the id size changes in some future version, this function should be used instead of mach_write_... */ UNIV_INLINE void trx_write_trx_id( /*=============*/ byte* ptr, /*!< in: pointer to memory where written */ trx_id_t id) /*!< in: id */ { #if DATA_TRX_ID_LEN != 6 # error "DATA_TRX_ID_LEN != 6" #endif mach_write_to_6(ptr, id); } #ifndef UNIV_HOTBACKUP /*****************************************************************//** Reads a trx id from an index page. In case that the id size changes in some future version, this function should be used instead of mach_read_... @return id */ UNIV_INLINE trx_id_t trx_read_trx_id( /*============*/ const byte* ptr) /*!< in: pointer to memory from where to read */ { #if DATA_TRX_ID_LEN != 6 # error "DATA_TRX_ID_LEN != 6" #endif return(mach_read_from_6(ptr)); } /****************************************************************//** Looks for the trx handle with the given id in trx_list. @return the trx handle or NULL if not found */ UNIV_INLINE trx_t* trx_get_on_id( /*==========*/ trx_id_t trx_id) /*!< in: trx id to search for */ { trx_t* trx; ut_ad(mutex_own(&(kernel_mutex))); trx = UT_LIST_GET_FIRST(trx_sys->trx_list); while (trx != NULL) { if (0 == ut_dulint_cmp(trx_id, trx->id)) { return(trx); } trx = UT_LIST_GET_NEXT(trx_list, trx); } return(NULL); } /****************************************************************//** Returns the minumum trx id in trx list. This is the smallest id for which the trx can possibly be active. (But, you must look at the trx->conc_state to find out if the minimum trx id transaction itself is active, or already committed.) @return the minimum trx id, or trx_sys->max_trx_id if the trx list is empty */ UNIV_INLINE trx_id_t trx_list_get_min_trx_id(void) /*=========================*/ { trx_t* trx; ut_ad(mutex_own(&(kernel_mutex))); trx = UT_LIST_GET_LAST(trx_sys->trx_list); if (trx == NULL) { return(trx_sys->max_trx_id); } return(trx->id); } /****************************************************************//** Checks if a transaction with the given id is active. @return TRUE if active */ UNIV_INLINE ibool trx_is_active( /*==========*/ trx_id_t trx_id) /*!< in: trx id of the transaction */ { trx_t* trx; ut_ad(mutex_own(&(kernel_mutex))); if (ut_dulint_cmp(trx_id, trx_list_get_min_trx_id()) < 0) { return(FALSE); } if (ut_dulint_cmp(trx_id, trx_sys->max_trx_id) >= 0) { /* There must be corruption: we return TRUE because this function is only called by lock_clust_rec_some_has_impl() and row_vers_impl_x_locked_off_kernel() and they have diagnostic prints in this case */ return(TRUE); } trx = trx_get_on_id(trx_id); if (trx && (trx->conc_state == TRX_ACTIVE || trx->conc_state == TRX_PREPARED)) { return(TRUE); } return(FALSE); } /*****************************************************************//** Allocates a new transaction id. @return new, allocated trx id */ UNIV_INLINE trx_id_t trx_sys_get_new_trx_id(void) /*========================*/ { trx_id_t id; ut_ad(mutex_own(&kernel_mutex)); /* VERY important: after the database is started, max_trx_id value is divisible by TRX_SYS_TRX_ID_WRITE_MARGIN, and the following if will evaluate to TRUE when this function is first time called, and the value for trx id will be written to disk-based header! Thus trx id values will not overlap when the database is repeatedly started! */ if (ut_dulint_get_low(trx_sys->max_trx_id) % TRX_SYS_TRX_ID_WRITE_MARGIN == 0) { trx_sys_flush_max_trx_id(); } id = trx_sys->max_trx_id; UT_DULINT_INC(trx_sys->max_trx_id); return(id); } /*****************************************************************//** Allocates a new transaction number. @return new, allocated trx number */ UNIV_INLINE trx_id_t trx_sys_get_new_trx_no(void) /*========================*/ { ut_ad(mutex_own(&kernel_mutex)); return(trx_sys_get_new_trx_id()); } #endif /* !UNIV_HOTBACKUP */ haildb-2.3.2/include/pars0sym.ic0000644000175000017500000000202511513177357017360 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1997, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/pars0sym.ic SQL parser symbol table Created 12/15/1997 Heikki Tuuri *******************************************************/ haildb-2.3.2/include/que0que.ic0000644000175000017500000001565211513177357017201 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2010, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/que0que.ic Query graph Created 5/27/1996 Heikki Tuuri *******************************************************/ #include "usr0sess.h" /***********************************************************************//** Gets the trx of a query thread. */ UNIV_INLINE trx_t* thr_get_trx( /*========*/ que_thr_t* thr) /*!< in: query thread */ { ut_ad(thr); return(thr->graph->trx); } /*******************************************************************//** Determines if this thread is rolling back an incomplete transaction in crash recovery. @return TRUE if thr is rolling back an incomplete transaction in crash recovery */ UNIV_INLINE ibool thr_is_recv( /*========*/ const que_thr_t* thr) /*!< in: query thread */ { return(trx_is_recv(thr->graph->trx)); } /***********************************************************************//** Gets the first thr in a fork. */ UNIV_INLINE que_thr_t* que_fork_get_first_thr( /*===================*/ que_fork_t* query_fork) /*!< in: query fork */ { return(UT_LIST_GET_FIRST(query_fork->thrs)); } /***********************************************************************//** Gets the child node of the first thr in a fork. */ UNIV_INLINE que_node_t* que_fork_get_child( /*===============*/ que_fork_t* query_fork) /*!< in: query fork */ { que_thr_t* thr; thr = UT_LIST_GET_FIRST(query_fork->thrs); return(thr->child); } /***********************************************************************//** Gets the type of a graph node. */ UNIV_INLINE ulint que_node_get_type( /*==============*/ que_node_t* node) /*!< in: graph node */ { ut_ad(node); return(((que_common_t*)node)->type); } /***********************************************************************//** Gets pointer to the value dfield of a graph node. */ UNIV_INLINE dfield_t* que_node_get_val( /*=============*/ que_node_t* node) /*!< in: graph node */ { ut_ad(node); return(&(((que_common_t*)node)->val)); } /***********************************************************************//** Gets the value buffer size of a graph node. @return val buffer size, not defined if val.data == NULL in node */ UNIV_INLINE ulint que_node_get_val_buf_size( /*======================*/ que_node_t* node) /*!< in: graph node */ { ut_ad(node); return(((que_common_t*)node)->val_buf_size); } /***********************************************************************//** Sets the value buffer size of a graph node. */ UNIV_INLINE void que_node_set_val_buf_size( /*======================*/ que_node_t* node, /*!< in: graph node */ ulint size) /*!< in: size */ { ut_ad(node); ((que_common_t*)node)->val_buf_size = size; } /***********************************************************************//** Sets the parent of a graph node. */ UNIV_INLINE void que_node_set_parent( /*================*/ que_node_t* node, /*!< in: graph node */ que_node_t* parent) /*!< in: parent */ { ut_ad(node); ((que_common_t*)node)->parent = parent; } /***********************************************************************//** Gets pointer to the value data type field of a graph node. */ UNIV_INLINE dtype_t* que_node_get_data_type( /*===================*/ que_node_t* node) /*!< in: graph node */ { ut_ad(node); return(dfield_get_type(&((que_common_t*) node)->val)); } /*********************************************************************//** Catenates a query graph node to a list of them, possible empty list. @return one-way list of nodes */ UNIV_INLINE que_node_t* que_node_list_add_last( /*===================*/ que_node_t* node_list, /*!< in: node list, or NULL */ que_node_t* node) /*!< in: node */ { que_common_t* cnode; que_common_t* cnode2; cnode = (que_common_t*) node; cnode->brother = NULL; if (node_list == NULL) { return(node); } cnode2 = (que_common_t*) node_list; while (cnode2->brother != NULL) { cnode2 = (que_common_t*) cnode2->brother; } cnode2->brother = node; return(node_list); } /*********************************************************************//** Gets the next list node in a list of query graph nodes. @return next node in a list of nodes */ UNIV_INLINE que_node_t* que_node_get_next( /*==============*/ que_node_t* node) /*!< in: node in a list */ { return(((que_common_t*)node)->brother); } /*********************************************************************//** Gets a query graph node list length. @return length, for NULL list 0 */ UNIV_INLINE ulint que_node_list_get_len( /*==================*/ que_node_t* node_list) /*!< in: node list, or NULL */ { const que_common_t* cnode; ulint len; cnode = (const que_common_t*) node_list; len = 0; while (cnode != NULL) { len++; cnode = (const que_common_t*) cnode->brother; } return(len); } /*********************************************************************//** Gets the parent node of a query graph node. @return parent node or NULL */ UNIV_INLINE que_node_t* que_node_get_parent( /*================*/ que_node_t* node) /*!< in: node */ { return(((que_common_t*)node)->parent); } /**********************************************************************//** Checks if graph, trx, or session is in a state where the query thread should be stopped. @return TRUE if should be stopped; NOTE that if the peek is made without reserving the kernel mutex, then another peek with the mutex reserved is necessary before deciding the actual stopping */ UNIV_INLINE ibool que_thr_peek_stop( /*==============*/ que_thr_t* thr) /*!< in: query thread */ { trx_t* trx; que_t* graph; graph = thr->graph; trx = graph->trx; if (graph->state != QUE_FORK_ACTIVE || trx->que_state == TRX_QUE_LOCK_WAIT || (UT_LIST_GET_LEN(trx->signals) > 0 && trx->que_state == TRX_QUE_RUNNING)) { return(TRUE); } return(FALSE); } /***********************************************************************//** Returns TRUE if the query graph is for a SELECT statement. @return TRUE if a select */ UNIV_INLINE ibool que_graph_is_select( /*================*/ que_t* graph) /*!< in: graph */ { if (graph->fork_type == QUE_FORK_SELECT_SCROLL || graph->fork_type == QUE_FORK_SELECT_NON_SCROLL) { return(TRUE); } return(FALSE); } haildb-2.3.2/include/page0page.h0000644000175000017500000011126111513177357017272 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1994, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/page0page.h Index page routines Created 2/2/1994 Heikki Tuuri *******************************************************/ #ifndef page0page_h #define page0page_h #include "univ.i" #include "page0types.h" #include "fil0fil.h" #include "buf0buf.h" #include "data0data.h" #include "dict0dict.h" #include "rem0rec.h" #include "fsp0fsp.h" #include "mtr0mtr.h" #ifdef UNIV_MATERIALIZE #undef UNIV_INLINE #define UNIV_INLINE #endif /* PAGE HEADER =========== Index page header starts at the first offset left free by the FIL-module */ typedef byte page_header_t; #define PAGE_HEADER FSEG_PAGE_DATA /* index page header starts at this offset */ /*-----------------------------*/ #define PAGE_N_DIR_SLOTS 0 /* number of slots in page directory */ #define PAGE_HEAP_TOP 2 /* pointer to record heap top */ #define PAGE_N_HEAP 4 /* number of records in the heap, bit 15=flag: new-style compact page format */ #define PAGE_FREE 6 /* pointer to start of page free record list */ #define PAGE_GARBAGE 8 /* number of bytes in deleted records */ #define PAGE_LAST_INSERT 10 /* pointer to the last inserted record, or NULL if this info has been reset by a delete, for example */ #define PAGE_DIRECTION 12 /* last insert direction: PAGE_LEFT, ... */ #define PAGE_N_DIRECTION 14 /* number of consecutive inserts to the same direction */ #define PAGE_N_RECS 16 /* number of user records on the page */ #define PAGE_MAX_TRX_ID 18 /* highest id of a trx which may have modified a record on the page; a dulint; defined only in secondary indexes and in the insert buffer tree; NOTE: this may be modified only when the thread has an x-latch to the page, and ALSO an x-latch to btr_search_latch if there is a hash index to the page! */ #define PAGE_HEADER_PRIV_END 26 /* end of private data structure of the page header which are set in a page create */ /*----*/ #define PAGE_LEVEL 26 /* level of the node in an index tree; the leaf level is the level 0. This field should not be written to after page creation. */ #define PAGE_INDEX_ID 28 /* index id where the page belongs. This field should not be written to after page creation. */ #define PAGE_BTR_SEG_LEAF 36 /* file segment header for the leaf pages in a B-tree: defined only on the root page of a B-tree, but not in the root of an ibuf tree */ #define PAGE_BTR_IBUF_FREE_LIST PAGE_BTR_SEG_LEAF #define PAGE_BTR_IBUF_FREE_LIST_NODE PAGE_BTR_SEG_LEAF /* in the place of PAGE_BTR_SEG_LEAF and _TOP there is a free list base node if the page is the root page of an ibuf tree, and at the same place is the free list node if the page is in a free list */ #define PAGE_BTR_SEG_TOP (36 + FSEG_HEADER_SIZE) /* file segment header for the non-leaf pages in a B-tree: defined only on the root page of a B-tree, but not in the root of an ibuf tree */ /*----*/ #define PAGE_DATA (PAGE_HEADER + 36 + 2 * FSEG_HEADER_SIZE) /* start of data on the page */ #define PAGE_OLD_INFIMUM (PAGE_DATA + 1 + REC_N_OLD_EXTRA_BYTES) /* offset of the page infimum record on an old-style page */ #define PAGE_OLD_SUPREMUM (PAGE_DATA + 2 + 2 * REC_N_OLD_EXTRA_BYTES + 8) /* offset of the page supremum record on an old-style page */ #define PAGE_OLD_SUPREMUM_END (PAGE_OLD_SUPREMUM + 9) /* offset of the page supremum record end on an old-style page */ #define PAGE_NEW_INFIMUM (PAGE_DATA + REC_N_NEW_EXTRA_BYTES) /* offset of the page infimum record on a new-style compact page */ #define PAGE_NEW_SUPREMUM (PAGE_DATA + 2 * REC_N_NEW_EXTRA_BYTES + 8) /* offset of the page supremum record on a new-style compact page */ #define PAGE_NEW_SUPREMUM_END (PAGE_NEW_SUPREMUM + 8) /* offset of the page supremum record end on a new-style compact page */ /*-----------------------------*/ /* Heap numbers */ #define PAGE_HEAP_NO_INFIMUM 0 /* page infimum */ #define PAGE_HEAP_NO_SUPREMUM 1 /* page supremum */ #define PAGE_HEAP_NO_USER_LOW 2 /* first user record in creation (insertion) order, not necessarily collation order; this record may have been deleted */ /* Directions of cursor movement */ #define PAGE_LEFT 1 #define PAGE_RIGHT 2 #define PAGE_SAME_REC 3 #define PAGE_SAME_PAGE 4 #define PAGE_NO_DIRECTION 5 /* PAGE DIRECTORY ============== */ typedef byte page_dir_slot_t; typedef page_dir_slot_t page_dir_t; /* Offset of the directory start down from the page end. We call the slot with the highest file address directory start, as it points to the first record in the list of records. */ #define PAGE_DIR FIL_PAGE_DATA_END /* We define a slot in the page directory as two bytes */ #define PAGE_DIR_SLOT_SIZE 2 /* The offset of the physically lower end of the directory, counted from page end, when the page is empty */ #define PAGE_EMPTY_DIR_START (PAGE_DIR + 2 * PAGE_DIR_SLOT_SIZE) /* The maximum and minimum number of records owned by a directory slot. The number may drop below the minimum in the first and the last slot in the directory. */ #define PAGE_DIR_SLOT_MAX_N_OWNED 8 #define PAGE_DIR_SLOT_MIN_N_OWNED 4 /************************************************************//** Gets the start of a page. @return start of the page */ UNIV_INLINE page_t* page_align( /*=======*/ const void* ptr) /*!< in: pointer to page frame */ __attribute__((const)); /************************************************************//** Gets the offset within a page. @return offset from the start of the page */ UNIV_INLINE ulint page_offset( /*========*/ const void* ptr) /*!< in: pointer to page frame */ __attribute__((const)); /*************************************************************//** Returns the max trx id field value. */ UNIV_INLINE trx_id_t page_get_max_trx_id( /*================*/ const page_t* page); /*!< in: page */ /*************************************************************//** Sets the max trx id field value. */ UNIV_INTERN void page_set_max_trx_id( /*================*/ buf_block_t* block, /*!< in/out: page */ page_zip_des_t* page_zip,/*!< in/out: compressed page, or NULL */ trx_id_t trx_id, /*!< in: transaction id */ mtr_t* mtr); /*!< in/out: mini-transaction, or NULL */ /*************************************************************//** Sets the max trx id field value if trx_id is bigger than the previous value. */ UNIV_INLINE void page_update_max_trx_id( /*===================*/ buf_block_t* block, /*!< in/out: page */ page_zip_des_t* page_zip,/*!< in/out: compressed page whose uncompressed part will be updated, or NULL */ trx_id_t trx_id, /*!< in: transaction id */ mtr_t* mtr); /*!< in/out: mini-transaction */ /*************************************************************//** Reads the given header field. */ UNIV_INLINE ulint page_header_get_field( /*==================*/ const page_t* page, /*!< in: page */ ulint field); /*!< in: PAGE_N_DIR_SLOTS, ... */ /*************************************************************//** Sets the given header field. */ UNIV_INLINE void page_header_set_field( /*==================*/ page_t* page, /*!< in/out: page */ page_zip_des_t* page_zip,/*!< in/out: compressed page whose uncompressed part will be updated, or NULL */ ulint field, /*!< in: PAGE_N_DIR_SLOTS, ... */ ulint val); /*!< in: value */ /*************************************************************//** Returns the offset stored in the given header field. @return offset from the start of the page, or 0 */ UNIV_INLINE ulint page_header_get_offs( /*=================*/ const page_t* page, /*!< in: page */ ulint field) /*!< in: PAGE_FREE, ... */ __attribute__((nonnull, pure)); /*************************************************************//** Returns the pointer stored in the given header field, or NULL. */ #define page_header_get_ptr(page, field) \ (page_header_get_offs(page, field) \ ? page + page_header_get_offs(page, field) : NULL) /*************************************************************//** Sets the pointer stored in the given header field. */ UNIV_INLINE void page_header_set_ptr( /*================*/ page_t* page, /*!< in/out: page */ page_zip_des_t* page_zip,/*!< in/out: compressed page whose uncompressed part will be updated, or NULL */ ulint field, /*!< in/out: PAGE_FREE, ... */ const byte* ptr); /*!< in: pointer or NULL*/ #ifndef UNIV_HOTBACKUP /*************************************************************//** Resets the last insert info field in the page header. Writes to mlog about this operation. */ UNIV_INLINE void page_header_reset_last_insert( /*==========================*/ page_t* page, /*!< in: page */ page_zip_des_t* page_zip,/*!< in/out: compressed page whose uncompressed part will be updated, or NULL */ mtr_t* mtr); /*!< in: mtr */ #endif /* !UNIV_HOTBACKUP */ /************************************************************//** Gets the offset of the first record on the page. @return offset of the first record in record list, relative from page */ UNIV_INLINE ulint page_get_infimum_offset( /*====================*/ const page_t* page); /*!< in: page which must have record(s) */ /************************************************************//** Gets the offset of the last record on the page. @return offset of the last record in record list, relative from page */ UNIV_INLINE ulint page_get_supremum_offset( /*=====================*/ const page_t* page); /*!< in: page which must have record(s) */ #define page_get_infimum_rec(page) ((page) + page_get_infimum_offset(page)) #define page_get_supremum_rec(page) ((page) + page_get_supremum_offset(page)) /************************************************************//** Returns the middle record of record list. If there are an even number of records in the list, returns the first record of upper half-list. @return middle record */ UNIV_INTERN rec_t* page_get_middle_rec( /*================*/ page_t* page); /*!< in: page */ #ifndef UNIV_HOTBACKUP /*************************************************************//** Compares a data tuple to a physical record. Differs from the function cmp_dtuple_rec_with_match in the way that the record must reside on an index page, and also page infimum and supremum records can be given in the parameter rec. These are considered as the negative infinity and the positive infinity in the alphabetical order. @return 1, 0, -1, if dtuple is greater, equal, less than rec, respectively, when only the common first fields are compared */ UNIV_INLINE int page_cmp_dtuple_rec_with_match( /*===========================*/ void* cmp_ctx,/*!< in: client compare context */ const dtuple_t* dtuple, /*!< in: data tuple */ const rec_t* rec, /*!< in: physical record on a page; may also be page infimum or supremum, in which case matched-parameter values below are not affected */ const ulint* offsets,/*!< in: array returned by rec_get_offsets() */ ulint* matched_fields, /*!< in/out: number of already completely matched fields; when function returns contains the value for current comparison */ ulint* matched_bytes); /*!< in/out: number of already matched bytes within the first field not completely matched; when function returns contains the value for current comparison */ #endif /* !UNIV_HOTBACKUP */ /*************************************************************//** Gets the page number. @return page number */ UNIV_INLINE ulint page_get_page_no( /*=============*/ const page_t* page); /*!< in: page */ /*************************************************************//** Gets the tablespace identifier. @return space id */ UNIV_INLINE ulint page_get_space_id( /*==============*/ const page_t* page); /*!< in: page */ /*************************************************************//** Gets the number of user records on page (the infimum and supremum records are not user records). @return number of user records */ UNIV_INLINE ulint page_get_n_recs( /*============*/ const page_t* page); /*!< in: index page */ /***************************************************************//** Returns the number of records before the given record in chain. The number includes infimum and supremum records. @return number of records */ UNIV_INTERN ulint page_rec_get_n_recs_before( /*=======================*/ const rec_t* rec); /*!< in: the physical record */ /*************************************************************//** Gets the number of records in the heap. @return number of user records */ UNIV_INLINE ulint page_dir_get_n_heap( /*================*/ const page_t* page); /*!< in: index page */ /*************************************************************//** Sets the number of records in the heap. */ UNIV_INLINE void page_dir_set_n_heap( /*================*/ page_t* page, /*!< in/out: index page */ page_zip_des_t* page_zip,/*!< in/out: compressed page whose uncompressed part will be updated, or NULL. Note that the size of the dense page directory in the compressed page trailer is n_heap * PAGE_ZIP_DIR_SLOT_SIZE. */ ulint n_heap);/*!< in: number of records */ /*************************************************************//** Gets the number of dir slots in directory. @return number of slots */ UNIV_INLINE ulint page_dir_get_n_slots( /*=================*/ const page_t* page); /*!< in: index page */ /*************************************************************//** Sets the number of dir slots in directory. */ UNIV_INLINE void page_dir_set_n_slots( /*=================*/ page_t* page, /*!< in/out: page */ page_zip_des_t* page_zip,/*!< in/out: compressed page whose uncompressed part will be updated, or NULL */ ulint n_slots);/*!< in: number of slots */ #ifdef UNIV_DEBUG /*************************************************************//** Gets pointer to nth directory slot. @return pointer to dir slot */ UNIV_INLINE page_dir_slot_t* page_dir_get_nth_slot( /*==================*/ const page_t* page, /*!< in: index page */ ulint n); /*!< in: position */ #else /* UNIV_DEBUG */ # define page_dir_get_nth_slot(page, n) \ ((page) + UNIV_PAGE_SIZE - PAGE_DIR \ - (n + 1) * PAGE_DIR_SLOT_SIZE) #endif /* UNIV_DEBUG */ /**************************************************************//** Used to check the consistency of a record on a page. @return TRUE if succeed */ UNIV_INLINE ibool page_rec_check( /*===========*/ const rec_t* rec); /*!< in: record */ /***************************************************************//** Gets the record pointed to by a directory slot. @return pointer to record */ UNIV_INLINE const rec_t* page_dir_slot_get_rec( /*==================*/ const page_dir_slot_t* slot); /*!< in: directory slot */ /***************************************************************//** This is used to set the record offset in a directory slot. */ UNIV_INLINE void page_dir_slot_set_rec( /*==================*/ page_dir_slot_t* slot, /*!< in: directory slot */ rec_t* rec); /*!< in: record on the page */ /***************************************************************//** Gets the number of records owned by a directory slot. @return number of records */ UNIV_INLINE ulint page_dir_slot_get_n_owned( /*======================*/ const page_dir_slot_t* slot); /*!< in: page directory slot */ /***************************************************************//** This is used to set the owned records field of a directory slot. */ UNIV_INLINE void page_dir_slot_set_n_owned( /*======================*/ page_dir_slot_t*slot, /*!< in/out: directory slot */ page_zip_des_t* page_zip,/*!< in/out: compressed page, or NULL */ ulint n); /*!< in: number of records owned by the slot */ /************************************************************//** Calculates the space reserved for directory slots of a given number of records. The exact value is a fraction number n * PAGE_DIR_SLOT_SIZE / PAGE_DIR_SLOT_MIN_N_OWNED, and it is rounded upwards to an integer. */ UNIV_INLINE ulint page_dir_calc_reserved_space( /*=========================*/ ulint n_recs); /*!< in: number of records */ /***************************************************************//** Looks for the directory slot which owns the given record. @return the directory slot number */ UNIV_INTERN ulint page_dir_find_owner_slot( /*=====================*/ const rec_t* rec); /*!< in: the physical record */ /************************************************************//** Determine whether the page is in new-style compact format. @return nonzero if the page is in compact format, zero if it is in old-style format */ UNIV_INLINE ulint page_is_comp( /*=========*/ const page_t* page); /*!< in: index page */ /************************************************************//** TRUE if the record is on a page in compact format. @return nonzero if in compact format */ UNIV_INLINE ulint page_rec_is_comp( /*=============*/ const rec_t* rec); /*!< in: record */ /***************************************************************//** Returns the heap number of a record. @return heap number */ UNIV_INLINE ulint page_rec_get_heap_no( /*=================*/ const rec_t* rec); /*!< in: the physical record */ /************************************************************//** Determine whether the page is a B-tree leaf. @return TRUE if the page is a B-tree leaf */ UNIV_INLINE ibool page_is_leaf( /*=========*/ const page_t* page) /*!< in: page */ __attribute__((nonnull, pure)); /************************************************************//** Gets the pointer to the next record on the page. @return pointer to next record */ UNIV_INLINE const rec_t* page_rec_get_next_low( /*==================*/ const rec_t* rec, /*!< in: pointer to record */ ulint comp); /*!< in: nonzero=compact page layout */ /************************************************************//** Gets the pointer to the next record on the page. @return pointer to next record */ UNIV_INLINE rec_t* page_rec_get_next( /*==============*/ rec_t* rec); /*!< in: pointer to record */ /************************************************************//** Gets the pointer to the next record on the page. @return pointer to next record */ UNIV_INLINE const rec_t* page_rec_get_next_const( /*====================*/ const rec_t* rec); /*!< in: pointer to record */ /************************************************************//** Sets the pointer to the next record on the page. */ UNIV_INLINE void page_rec_set_next( /*==============*/ rec_t* rec, /*!< in: pointer to record, must not be page supremum */ rec_t* next); /*!< in: pointer to next record, must not be page infimum */ /************************************************************//** Gets the pointer to the previous record. @return pointer to previous record */ UNIV_INLINE const rec_t* page_rec_get_prev_const( /*====================*/ const rec_t* rec); /*!< in: pointer to record, must not be page infimum */ /************************************************************//** Gets the pointer to the previous record. @return pointer to previous record */ UNIV_INLINE rec_t* page_rec_get_prev( /*==============*/ rec_t* rec); /*!< in: pointer to record, must not be page infimum */ /************************************************************//** TRUE if the record is a user record on the page. @return TRUE if a user record */ UNIV_INLINE ibool page_rec_is_user_rec_low( /*=====================*/ ulint offset) /*!< in: record offset on page */ __attribute__((const)); /************************************************************//** TRUE if the record is the supremum record on a page. @return TRUE if the supremum record */ UNIV_INLINE ibool page_rec_is_supremum_low( /*=====================*/ ulint offset) /*!< in: record offset on page */ __attribute__((const)); /************************************************************//** TRUE if the record is the infimum record on a page. @return TRUE if the infimum record */ UNIV_INLINE ibool page_rec_is_infimum_low( /*====================*/ ulint offset) /*!< in: record offset on page */ __attribute__((const)); /************************************************************//** TRUE if the record is a user record on the page. @return TRUE if a user record */ UNIV_INLINE ibool page_rec_is_user_rec( /*=================*/ const rec_t* rec) /*!< in: record */ __attribute__((const)); /************************************************************//** TRUE if the record is the supremum record on a page. @return TRUE if the supremum record */ UNIV_INLINE ibool page_rec_is_supremum( /*=================*/ const rec_t* rec) /*!< in: record */ __attribute__((const)); /************************************************************//** TRUE if the record is the infimum record on a page. @return TRUE if the infimum record */ UNIV_INLINE ibool page_rec_is_infimum( /*================*/ const rec_t* rec) /*!< in: record */ __attribute__((const)); /***************************************************************//** Looks for the record which owns the given record. @return the owner record */ UNIV_INLINE rec_t* page_rec_find_owner_rec( /*====================*/ rec_t* rec); /*!< in: the physical record */ /***********************************************************************//** This is a low-level operation which is used in a database index creation to update the page number of a created B-tree to a data dictionary record. */ UNIV_INTERN void page_rec_write_index_page_no( /*=========================*/ rec_t* rec, /*!< in: record to update */ ulint i, /*!< in: index of the field to update */ ulint page_no,/*!< in: value to write */ mtr_t* mtr); /*!< in: mtr */ /************************************************************//** Returns the maximum combined size of records which can be inserted on top of record heap. @return maximum combined size for inserted records */ UNIV_INLINE ulint page_get_max_insert_size( /*=====================*/ const page_t* page, /*!< in: index page */ ulint n_recs);/*!< in: number of records */ /************************************************************//** Returns the maximum combined size of records which can be inserted on top of record heap if page is first reorganized. @return maximum combined size for inserted records */ UNIV_INLINE ulint page_get_max_insert_size_after_reorganize( /*======================================*/ const page_t* page, /*!< in: index page */ ulint n_recs);/*!< in: number of records */ /*************************************************************//** Calculates free space if a page is emptied. @return free space */ UNIV_INLINE ulint page_get_free_space_of_empty( /*=========================*/ ulint comp) /*!< in: nonzero=compact page format */ __attribute__((const)); /**********************************************************//** Returns the base extra size of a physical record. This is the size of the fixed header, independent of the record size. @return REC_N_NEW_EXTRA_BYTES or REC_N_OLD_EXTRA_BYTES */ UNIV_INLINE ulint page_rec_get_base_extra_size( /*=========================*/ const rec_t* rec); /*!< in: physical record */ /************************************************************//** Returns the sum of the sizes of the records in the record list excluding the infimum and supremum records. @return data in bytes */ UNIV_INLINE ulint page_get_data_size( /*===============*/ const page_t* page); /*!< in: index page */ /************************************************************//** Allocates a block of memory from the head of the free list of an index page. */ UNIV_INLINE void page_mem_alloc_free( /*================*/ page_t* page, /*!< in/out: index page */ page_zip_des_t* page_zip,/*!< in/out: compressed page with enough space available for inserting the record, or NULL */ rec_t* next_rec,/*!< in: pointer to the new head of the free record list */ ulint need); /*!< in: number of bytes allocated */ /************************************************************//** Allocates a block of memory from the heap of an index page. @return pointer to start of allocated buffer, or NULL if allocation fails */ UNIV_INTERN byte* page_mem_alloc_heap( /*================*/ page_t* page, /*!< in/out: index page */ page_zip_des_t* page_zip,/*!< in/out: compressed page with enough space available for inserting the record, or NULL */ ulint need, /*!< in: total number of bytes needed */ ulint* heap_no);/*!< out: this contains the heap number of the allocated record if allocation succeeds */ /************************************************************//** Puts a record to free list. */ UNIV_INLINE void page_mem_free( /*==========*/ page_t* page, /*!< in/out: index page */ page_zip_des_t* page_zip,/*!< in/out: compressed page, or NULL */ rec_t* rec, /*!< in: pointer to the (origin of) record */ dict_index_t* index, /*!< in: index of rec */ const ulint* offsets);/*!< in: array returned by rec_get_offsets() */ /**********************************************************//** Create an uncompressed B-tree index page. @return pointer to the page */ UNIV_INTERN page_t* page_create( /*========*/ buf_block_t* block, /*!< in: a buffer block where the page is created */ mtr_t* mtr, /*!< in: mini-transaction handle */ ulint comp); /*!< in: nonzero=compact page format */ /**********************************************************//** Create a compressed B-tree index page. @return pointer to the page */ UNIV_INTERN page_t* page_create_zip( /*============*/ buf_block_t* block, /*!< in/out: a buffer frame where the page is created */ dict_index_t* index, /*!< in: the index of the page */ ulint level, /*!< in: the B-tree level of the page */ mtr_t* mtr); /*!< in: mini-transaction handle */ /*************************************************************//** Differs from page_copy_rec_list_end, because this function does not touch the lock table and max trx id on page or compress the page. */ UNIV_INTERN void page_copy_rec_list_end_no_locks( /*============================*/ buf_block_t* new_block, /*!< in: index page to copy to */ buf_block_t* block, /*!< in: index page of rec */ rec_t* rec, /*!< in: record on page */ dict_index_t* index, /*!< in: record descriptor */ mtr_t* mtr); /*!< in: mtr */ /*************************************************************//** Copies records from page to new_page, from the given record onward, including that record. Infimum and supremum records are not copied. The records are copied to the start of the record list on new_page. @return pointer to the original successor of the infimum record on new_page, or NULL on zip overflow (new_block will be decompressed) */ UNIV_INTERN rec_t* page_copy_rec_list_end( /*===================*/ buf_block_t* new_block, /*!< in/out: index page to copy to */ buf_block_t* block, /*!< in: index page containing rec */ rec_t* rec, /*!< in: record on page */ dict_index_t* index, /*!< in: record descriptor */ mtr_t* mtr) /*!< in: mtr */ __attribute__((nonnull)); /*************************************************************//** Copies records from page to new_page, up to the given record, NOT including that record. Infimum and supremum records are not copied. The records are copied to the end of the record list on new_page. @return pointer to the original predecessor of the supremum record on new_page, or NULL on zip overflow (new_block will be decompressed) */ UNIV_INTERN rec_t* page_copy_rec_list_start( /*=====================*/ buf_block_t* new_block, /*!< in/out: index page to copy to */ buf_block_t* block, /*!< in: index page containing rec */ rec_t* rec, /*!< in: record on page */ dict_index_t* index, /*!< in: record descriptor */ mtr_t* mtr) /*!< in: mtr */ __attribute__((nonnull)); /*************************************************************//** Deletes records from a page from a given record onward, including that record. The infimum and supremum records are not deleted. */ UNIV_INTERN void page_delete_rec_list_end( /*=====================*/ rec_t* rec, /*!< in: pointer to record on page */ buf_block_t* block, /*!< in: buffer block of the page */ dict_index_t* index, /*!< in: record descriptor */ ulint n_recs, /*!< in: number of records to delete, or ULINT_UNDEFINED if not known */ ulint size, /*!< in: the sum of the sizes of the records in the end of the chain to delete, or ULINT_UNDEFINED if not known */ mtr_t* mtr) /*!< in: mtr */ __attribute__((nonnull)); /*************************************************************//** Deletes records from page, up to the given record, NOT including that record. Infimum and supremum records are not deleted. */ UNIV_INTERN void page_delete_rec_list_start( /*=======================*/ rec_t* rec, /*!< in: record on page */ buf_block_t* block, /*!< in: buffer block of the page */ dict_index_t* index, /*!< in: record descriptor */ mtr_t* mtr) /*!< in: mtr */ __attribute__((nonnull)); /*************************************************************//** Moves record list end to another page. Moved records include split_rec. @return TRUE on success; FALSE on compression failure (new_block will be decompressed) */ UNIV_INTERN ibool page_move_rec_list_end( /*===================*/ buf_block_t* new_block, /*!< in/out: index page where to move */ buf_block_t* block, /*!< in: index page from where to move */ rec_t* split_rec, /*!< in: first record to move */ dict_index_t* index, /*!< in: record descriptor */ mtr_t* mtr) /*!< in: mtr */ __attribute__((nonnull(1, 2, 4, 5))); /*************************************************************//** Moves record list start to another page. Moved records do not include split_rec. @return TRUE on success; FALSE on compression failure */ UNIV_INTERN ibool page_move_rec_list_start( /*=====================*/ buf_block_t* new_block, /*!< in/out: index page where to move */ buf_block_t* block, /*!< in/out: page containing split_rec */ rec_t* split_rec, /*!< in: first record not to move */ dict_index_t* index, /*!< in: record descriptor */ mtr_t* mtr) /*!< in: mtr */ __attribute__((nonnull(1, 2, 4, 5))); /****************************************************************//** Splits a directory slot which owns too many records. */ UNIV_INTERN void page_dir_split_slot( /*================*/ page_t* page, /*!< in: index page */ page_zip_des_t* page_zip,/*!< in/out: compressed page whose uncompressed part will be written, or NULL */ ulint slot_no)/*!< in: the directory slot */ __attribute__((nonnull(1))); /*************************************************************//** Tries to balance the given directory slot with too few records with the upper neighbor, so that there are at least the minimum number of records owned by the slot; this may result in the merging of two slots. */ UNIV_INTERN void page_dir_balance_slot( /*==================*/ page_t* page, /*!< in/out: index page */ page_zip_des_t* page_zip,/*!< in/out: compressed page, or NULL */ ulint slot_no)/*!< in: the directory slot */ __attribute__((nonnull(1))); /**********************************************************//** Parses a log record of a record list end or start deletion. @return end of log record or NULL */ UNIV_INTERN byte* page_parse_delete_rec_list( /*=======================*/ byte type, /*!< in: MLOG_LIST_END_DELETE, MLOG_LIST_START_DELETE, MLOG_COMP_LIST_END_DELETE or MLOG_COMP_LIST_START_DELETE */ byte* ptr, /*!< in: buffer */ byte* end_ptr,/*!< in: buffer end */ buf_block_t* block, /*!< in/out: buffer block or NULL */ dict_index_t* index, /*!< in: record descriptor */ mtr_t* mtr); /*!< in: mtr or NULL */ /***********************************************************//** Parses a redo log record of creating a page. @return end of log record or NULL */ UNIV_INTERN byte* page_parse_create( /*==============*/ byte* ptr, /*!< in: buffer */ byte* end_ptr,/*!< in: buffer end */ ulint comp, /*!< in: nonzero=compact page format */ buf_block_t* block, /*!< in: block or NULL */ mtr_t* mtr); /*!< in: mtr or NULL */ /************************************************************//** Prints record contents including the data relevant only in the index page context. */ UNIV_INTERN void page_rec_print( /*===========*/ const rec_t* rec, /*!< in: physical record */ const ulint* offsets);/*!< in: record descriptor */ /***************************************************************//** This is used to print the contents of the directory for debugging purposes. */ UNIV_INTERN void page_dir_print( /*===========*/ page_t* page, /*!< in: index page */ ulint pr_n); /*!< in: print n first and n last entries */ /***************************************************************//** This is used to print the contents of the page record list for debugging purposes. */ UNIV_INTERN void page_print_list( /*============*/ buf_block_t* block, /*!< in: index page */ dict_index_t* index, /*!< in: dictionary index of the page */ ulint pr_n); /*!< in: print n first and n last entries */ /***************************************************************//** Prints the info in a page header. */ UNIV_INTERN void page_header_print( /*==============*/ const page_t* page); /*!< in: index page */ /***************************************************************//** This is used to print the contents of the page for debugging purposes. */ UNIV_INTERN void page_print( /*=======*/ buf_block_t* block, /*!< in: index page */ dict_index_t* index, /*!< in: dictionary index of the page */ ulint dn, /*!< in: print dn first and last entries in directory */ ulint rn); /*!< in: print rn first and last records in directory */ /***************************************************************//** The following is used to validate a record on a page. This function differs from rec_validate as it can also check the n_owned field and the heap_no field. @return TRUE if ok */ UNIV_INTERN ibool page_rec_validate( /*==============*/ rec_t* rec, /*!< in: physical record */ const ulint* offsets);/*!< in: array returned by rec_get_offsets() */ /***************************************************************//** Checks that the first directory slot points to the infimum record and the last to the supremum. This function is intended to track if the bug fixed in 4.0.14 has caused corruption to users' databases. */ UNIV_INTERN void page_check_dir( /*===========*/ const page_t* page); /*!< in: index page */ /***************************************************************//** This function checks the consistency of an index page when we do not know the index. This is also resilient so that this should never crash even if the page is total garbage. @return TRUE if ok */ UNIV_INTERN ibool page_simple_validate_old( /*=====================*/ page_t* page); /*!< in: old-style index page */ /***************************************************************//** This function checks the consistency of an index page when we do not know the index. This is also resilient so that this should never crash even if the page is total garbage. @return TRUE if ok */ UNIV_INTERN ibool page_simple_validate_new( /*=====================*/ page_t* block); /*!< in: new-style index page */ /***************************************************************//** This function checks the consistency of an index page. @return TRUE if ok */ UNIV_INTERN ibool page_validate( /*==========*/ page_t* page, /*!< in: index page */ dict_index_t* index); /*!< in: data dictionary index containing the page record type definition */ /***************************************************************//** Looks in the page record list for a record with the given heap number. @return record, NULL if not found */ const rec_t* page_find_rec_with_heap_no( /*=======================*/ const page_t* page, /*!< in: index page */ ulint heap_no);/*!< in: heap number */ /**********************************************************************//** Determine if a record is so big that it needs to be stored externally. @return FALSE if the entire record can be stored locally on the page */ UNIV_INLINE ibool page_rec_needs_ext( /*===============*/ ulint rec_size, /*!< in: length of the record in bytes */ ulint comp, /*!< in: nonzero=compact format */ ulint n_fields, /*!< in: number of fields in the record; ignored if zip_size == 0 */ ulint zip_size); /*!< in: compressed page size in bytes, or 0 */ #ifdef UNIV_MATERIALIZE #undef UNIV_INLINE #define UNIV_INLINE UNIV_INLINE_ORIGINAL #endif #ifndef UNIV_NONINL #include "page0page.ic" #endif #endif haildb-2.3.2/include/ut0ut.ic0000644000175000017500000000726211513177357016673 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1994, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************************//** @file include/ut0ut.ic Various utilities Created 5/30/1994 Heikki Tuuri *******************************************************************/ /******************************************************//** Calculates the minimum of two ulints. @return minimum */ UNIV_INLINE ulint ut_min( /*===*/ ulint n1, /*!< in: first number */ ulint n2) /*!< in: second number */ { return((n1 <= n2) ? n1 : n2); } /******************************************************//** Calculates the maximum of two ulints. @return maximum */ UNIV_INLINE ulint ut_max( /*===*/ ulint n1, /*!< in: first number */ ulint n2) /*!< in: second number */ { return((n1 <= n2) ? n2 : n1); } /****************************************************************//** Calculates minimum of two ulint-pairs. */ UNIV_INLINE void ut_pair_min( /*========*/ ulint* a, /*!< out: more significant part of minimum */ ulint* b, /*!< out: less significant part of minimum */ ulint a1, /*!< in: more significant part of first pair */ ulint b1, /*!< in: less significant part of first pair */ ulint a2, /*!< in: more significant part of second pair */ ulint b2) /*!< in: less significant part of second pair */ { if (a1 == a2) { *a = a1; *b = ut_min(b1, b2); } else if (a1 < a2) { *a = a1; *b = b1; } else { *a = a2; *b = b2; } } /******************************************************//** Compares two ulints. @return 1 if a > b, 0 if a == b, -1 if a < b */ UNIV_INLINE int ut_ulint_cmp( /*=========*/ ulint a, /*!< in: ulint */ ulint b) /*!< in: ulint */ { if (a < b) { return(-1); } else if (a == b) { return(0); } else { return(1); } } /*******************************************************//** Compares two pairs of ulints. @return -1 if a < b, 0 if a == b, 1 if a > b */ UNIV_INLINE int ut_pair_cmp( /*========*/ ulint a1, /*!< in: more significant part of first pair */ ulint a2, /*!< in: less significant part of first pair */ ulint b1, /*!< in: more significant part of second pair */ ulint b2) /*!< in: less significant part of second pair */ { if (a1 > b1) { return(1); } else if (a1 < b1) { return(-1); } else if (a2 > b2) { return(1); } else if (a2 < b2) { return(-1); } else { return(0); } } /*************************************************************//** Calculates fast the 2-logarithm of a number, rounded upward to an integer. @return logarithm in the base 2, rounded upward */ UNIV_INLINE ulint ut_2_log( /*=====*/ ulint n) /*!< in: number != 0 */ { ulint res; res = 0; ut_ad(n > 0); n = n - 1; for (;;) { n = n / 2; if (n == 0) { break; } res++; } return(res + 1); } /*************************************************************//** Calculates 2 to power n. @return 2 to power n */ UNIV_INLINE ulint ut_2_exp( /*=====*/ ulint n) /*!< in: number */ { return((ulint) 1 << n); } haildb-2.3.2/include/row0uins.h0000644000175000017500000000340011513177357017222 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1997, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/row0uins.h Fresh insert undo Created 2/25/1997 Heikki Tuuri *******************************************************/ #ifndef row0uins_h #define row0uins_h #include "univ.i" #include "data0data.h" #include "dict0types.h" #include "trx0types.h" #include "que0types.h" #include "row0types.h" #include "mtr0mtr.h" /***********************************************************//** Undoes a fresh insert of a row to a table. A fresh insert means that the same clustered index unique key did not have any record, even delete marked, at the time of the insert. InnoDB is eager in a rollback: if it figures out that an index record will be removed in the purge anyway, it will remove it in the rollback. @return DB_SUCCESS */ UNIV_INTERN ulint row_undo_ins( /*=========*/ undo_node_t* node); /*!< in: row undo node */ #ifndef UNIV_NONINL #include "row0uins.ic" #endif #endif haildb-2.3.2/include/lock0priv.ic0000644000175000017500000000323011513177357017512 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 2007, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/lock0priv.ic Lock module internal inline methods. Created July 16, 2007 Vasil Dimov *******************************************************/ /* This file contains only methods which are used in lock/lock0* files, other than lock/lock0lock.c. I.e. lock/lock0lock.c contains more internal inline methods but they are used only in that file. */ #ifndef LOCK_MODULE_IMPLEMENTATION #error Do not include lock0priv.ic outside of the lock/ module #endif /*********************************************************************//** Gets the type of a lock. @return LOCK_TABLE or LOCK_REC */ UNIV_INLINE ulint lock_get_type_low( /*==============*/ const lock_t* lock) /*!< in: lock */ { ut_ad(lock); return(lock->type_mode & LOCK_TYPE_MASK); } /* vim: set filetype=c: */ haildb-2.3.2/include/dict0dict.h0000644000175000017500000012613611513177357017317 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/dict0dict.h Data dictionary system Created 1/8/1996 Heikki Tuuri *******************************************************/ #ifndef dict0dict_h #define dict0dict_h #include "univ.i" #include "dict0types.h" #include "dict0mem.h" #include "data0type.h" #include "data0data.h" #include "mem0mem.h" #include "rem0types.h" #include "ut0mem.h" #include "ut0lst.h" #include "hash0hash.h" #include "ut0rnd.h" #include "ut0byte.h" #include "trx0types.h" #include "srv0srv.h" #ifndef UNIV_HOTBACKUP # include "sync0sync.h" # include "sync0rw.h" /******************************************************************//** Makes all characters in a NUL-terminated UTF-8 string lower case. */ UNIV_INTERN void dict_casedn_str( /*============*/ char* a); /*!< in/out: string to put in lower case */ /********************************************************************//** Get the database name length in a table name. @return database name length */ UNIV_INTERN ulint dict_get_db_name_len( /*=================*/ const char* name); /*!< in: table name in the form dbname '/' tablename */ /********************************************************************//** Return the end of table name where we have removed dbname and '/'. @return table name */ const char* dict_remove_db_name( /*================*/ const char* name); /*!< in: table name in the form dbname '/' tablename */ /**********************************************************************//** Returns a table object based on table id. @return table, NULL if does not exist */ UNIV_INTERN dict_table_t* dict_table_get_on_id( /*=================*/ ib_recovery_t recovery, /*!< in: recovery flag */ dulint table_id, /*!< in: table id */ trx_t* trx); /*!< in: transaction handle */ /********************************************************************//** Decrements the count of open handles to a table. */ UNIV_INTERN void dict_table_decrement_handle_count( /*==============================*/ dict_table_t* table, /*!< in/out: table */ ibool dict_locked); /*!< in: TRUE=data dictionary locked */ /**********************************************************************//** Increments the count of open client handles to a table. */ UNIV_INTERN void dict_table_increment_handle_count( /*==============================*/ dict_table_t* table, /*!< in/out: table */ ibool dict_locked); /*!< in: TRUE=data dictionary locked */ /**********************************************************************//** Inits the data dictionary module. */ UNIV_INTERN void dict_init(void); /*===========*/ /************************************************************************//** Closes the data dictionary module. */ UNIV_INTERN void dict_close(void); /*============*/ /*********************************************************************//** Gets the column data type. */ UNIV_INLINE void dict_col_copy_type( /*===============*/ const dict_col_t* col, /*!< in: column */ dtype_t* type); /*!< out: data type */ #endif /* !UNIV_HOTBACKUP */ #ifdef UNIV_DEBUG /*********************************************************************//** Assert that a column and a data type match. @return TRUE */ UNIV_INLINE ibool dict_col_type_assert_equal( /*=======================*/ const dict_col_t* col, /*!< in: column */ const dtype_t* type); /*!< in: data type */ #endif /* UNIV_DEBUG */ #ifndef UNIV_HOTBACKUP /***********************************************************************//** Returns the minimum size of the column. @return minimum size */ UNIV_INLINE ulint dict_col_get_min_size( /*==================*/ const dict_col_t* col); /*!< in: column */ /***********************************************************************//** Returns the maximum size of the column. @return maximum size */ UNIV_INLINE ulint dict_col_get_max_size( /*==================*/ const dict_col_t* col); /*!< in: column */ /***********************************************************************//** Returns the size of a fixed size column, 0 if not a fixed size column. @return fixed size, or 0 */ UNIV_INLINE ulint dict_col_get_fixed_size( /*====================*/ const dict_col_t* col, /*!< in: column */ ulint comp); /*!< in: nonzero=ROW_FORMAT=COMPACT */ /***********************************************************************//** Returns the ROW_FORMAT=REDUNDANT stored SQL NULL size of a column. For fixed length types it is the fixed length of the type, otherwise 0. @return SQL null storage size in ROW_FORMAT=REDUNDANT */ UNIV_INLINE ulint dict_col_get_sql_null_size( /*=======================*/ const dict_col_t* col, /*!< in: column */ ulint comp); /*!< in: nonzero=ROW_FORMAT=COMPACT */ /*********************************************************************//** Gets the column number. @return col->ind, table column position (starting from 0) */ UNIV_INLINE ulint dict_col_get_no( /*============*/ const dict_col_t* col); /*!< in: column */ /*********************************************************************//** Gets the column position in the clustered index. */ UNIV_INLINE ulint dict_col_get_clust_pos( /*===================*/ const dict_col_t* col, /*!< in: table column */ const dict_index_t* clust_index); /*!< in: clustered index */ /****************************************************************//** If the given column name is reserved for InnoDB system columns, return TRUE. @return TRUE if name is reserved */ UNIV_INTERN ibool dict_col_name_is_reserved( /*======================*/ const char* name); /*!< in: column name */ #endif /* !UNIV_HOTBACKUP */ /**********************************************************************//** Adds system columns to a table object. */ UNIV_INTERN void dict_table_add_system_columns( /*==========================*/ dict_table_t* table, /*!< in/out: table */ mem_heap_t* heap); /*!< in: temporary heap */ #ifndef UNIV_HOTBACKUP /**********************************************************************//** Adds a table object to the dictionary cache. */ UNIV_INTERN void dict_table_add_to_cache( /*====================*/ dict_table_t* table, /*!< in: table */ mem_heap_t* heap); /*!< in: temporary heap */ /**********************************************************************//** Removes a table object from the dictionary cache. */ UNIV_INTERN void dict_table_remove_from_cache( /*=========================*/ dict_table_t* table); /*!< in, own: table */ /**********************************************************************//** Renames a table object. @return TRUE if success */ UNIV_INTERN ibool dict_table_rename_in_cache( /*=======================*/ dict_table_t* table, /*!< in/out: table */ const char* new_name, /*!< in: new name */ ibool rename_also_foreigns);/*!< in: in ALTER TABLE we want to preserve the original table name in constraints which reference it */ /**********************************************************************//** Removes an index from the dictionary cache. */ UNIV_INTERN void dict_index_remove_from_cache( /*=========================*/ dict_table_t* table, /*!< in/out: table */ dict_index_t* index); /*!< in, own: index */ /**********************************************************************//** Change the id of a table object in the dictionary cache. This is used in DISCARD TABLESPACE. */ UNIV_INTERN void dict_table_change_id_in_cache( /*==========================*/ dict_table_t* table, /*!< in/out: table object already in cache */ dulint new_id);/*!< in: new id to set */ /**********************************************************************//** Adds a foreign key constraint object to the dictionary cache. May free the object if there already is an object with the same identifier in. At least one of foreign table or referenced table must already be in the dictionary cache! @return DB_SUCCESS or error code */ UNIV_INTERN ulint dict_foreign_add_to_cache( /*======================*/ dict_foreign_t* foreign, /*!< in, own: foreign key constraint */ ibool check_charsets);/*!< in: TRUE=check charset compatibility */ /*********************************************************************//** Check if the index is referenced by a foreign key, if TRUE return the matching instance NULL otherwise. @return pointer to foreign key struct if index is defined for foreign key, otherwise NULL */ UNIV_INTERN dict_foreign_t* dict_table_get_referenced_constraint( /*=================================*/ dict_table_t* table, /*!< in: InnoDB table */ dict_index_t* index); /*!< in: InnoDB index */ /*********************************************************************//** Checks if a table is referenced by foreign keys. @return TRUE if table is referenced by a foreign key */ UNIV_INTERN ibool dict_table_is_referenced_by_foreign_key( /*====================================*/ const dict_table_t* table); /*!< in: InnoDB table */ /**********************************************************************//** Replace the index in the foreign key list that matches this index's definition with an equivalent index. */ UNIV_INTERN void dict_table_replace_index_in_foreign_list( /*=====================================*/ dict_table_t* table, /*!< in/out: table */ dict_index_t* index); /*!< in: index to be replaced */ /*********************************************************************//** Checks if a index is defined for a foreign key constraint. Index is a part of a foreign key constraint if the index is referenced by foreign key or index is a foreign key index @return pointer to foreign key struct if index is defined for foreign key, otherwise NULL */ UNIV_INTERN dict_foreign_t* dict_table_get_foreign_constraint( /*==============================*/ dict_table_t* table, /*!< in: InnoDB table */ dict_index_t* index); /*!< in: InnoDB index */ /*********************************************************************//** Scans a table create SQL string and adds to the data dictionary the foreign key constraints declared in the string. This function should be called after the indexes for a table have been created. Each foreign key constraint must be accompanied with indexes in bot participating tables. The indexes are allowed to contain more fields than mentioned in the constraint. @return error code or DB_SUCCESS */ UNIV_INTERN ulint dict_create_foreign_constraints( /*============================*/ trx_t* trx, /*!< in: transaction */ const char* sql_string, /*!< in: table create statement where foreign keys are declared like: FOREIGN KEY (a, b) REFERENCES table2(c, d), table2 can be written also with the database name before it: test.table2; the default database id the database of parameter name */ const char* name, /*!< in: table full name in the normalized form database_name/table_name */ ibool reject_fks); /*!< in: if TRUE, fail with error code DB_CANNOT_ADD_CONSTRAINT if any foreign keys are found. */ /**********************************************************************//** Parses the CONSTRAINT id's to be dropped in an ALTER TABLE statement. @return DB_SUCCESS or DB_CANNOT_DROP_CONSTRAINT if syntax error or the constraint id does not match */ UNIV_INTERN ulint dict_foreign_parse_drop_constraints( /*================================*/ mem_heap_t* heap, /*!< in: heap from which we can allocate memory */ trx_t* trx, /*!< in: transaction */ dict_table_t* table, /*!< in: table */ ulint* n, /*!< out: number of constraints to drop */ const char*** constraints_to_drop); /*!< out: id's of the constraints to drop */ /**********************************************************************//** Returns a table object and optionally increment its open handle count. NOTE! This is a high-level function to be used mainly from outside the 'dict' directory. Inside this directory dict_table_get_low is usually the appropriate function. @return table, NULL if does not exist */ UNIV_INTERN dict_table_t* dict_table_get( /*===========*/ const char* table_name, /*!< in: table name */ ibool inc_count); /*!< in: whether to increment the open handle count on the table */ /**********************************************************************//** Returns a table instance based on table id. @return table, NULL if does not exist */ UNIV_INTERN dict_table_t* dict_table_get_using_id( /*====================*/ ib_recovery_t recovery,/*!< in: recovery flag */ dulint table_id, /*!< in: table id */ ibool ref_count); /*!< in: increment open handle count if TRUE */ /************************************************************************** Returns a index object, based on table and index id, and memoryfixes it. @return index, NULL if does not exist */ UNIV_INTERN dict_index_t* dict_index_get_on_id_low( /*=====================*/ dict_table_t* table, /*!< in: table */ dulint index_id); /*!< in: index id */ /**********************************************************************//** Checks if a table is in the dictionary cache. @return table, NULL if not found */ UNIV_INLINE dict_table_t* dict_table_check_if_in_cache_low( /*=============================*/ const char* table_name); /*!< in: table name */ /**********************************************************************//** Gets a table; loads it to the dictionary cache if necessary. A low-level function. @return table, NULL if not found */ UNIV_INLINE dict_table_t* dict_table_get_low( /*===============*/ const char* table_name); /*!< in: table name */ /**********************************************************************//** Returns a table object based on table id. @return table, NULL if does not exist */ UNIV_INLINE dict_table_t* dict_table_get_on_id_low( /*=====================*/ ib_recovery_t recovery,/*!< in: recovery flag */ dulint table_id); /*!< in: table id */ /**********************************************************************//** Find an index that is equivalent to the one passed in and is not marked for deletion. @return index equivalent to foreign->foreign_index, or NULL */ UNIV_INTERN dict_index_t* dict_foreign_find_equiv_index( /*==========================*/ dict_foreign_t* foreign);/*!< in: foreign key */ /**********************************************************************//** Returns an index object by matching on the name and column names and if more than one index matches return the index with the max id @return matching index, NULL if not found */ UNIV_INTERN dict_index_t* dict_table_get_index_by_max_id( /*===========================*/ dict_table_t* table, /*!< in: table */ const char* name, /*!< in: the index name to find */ const char** columns,/*!< in: array of column names */ ulint n_cols);/*!< in: number of columns */ /**********************************************************************//** Returns a column's name. @return column name. NOTE: not guaranteed to stay valid if table is modified in any way (columns added, etc.). */ UNIV_INTERN const char* dict_table_get_col_name( /*====================*/ const dict_table_t* table, /*!< in: table */ ulint col_nr);/*!< in: column number */ /**********************************************************************//** Returns a column's ordinal value. @return column pos. -1 if not found. NOTE: not guaranteed to stay valid if table is modified in any way (columns added, etc.). */ UNIV_INTERN int dict_table_get_col_no( /*==================*/ const dict_table_t* table, /*!< in: table */ const char* name); /*!< in: column name */ /************************************************************************** Prints a table definition. */ UNIV_INTERN void dict_table_print( /*=============*/ dict_table_t* table); /*!< in: table */ /**********************************************************************//** Prints a table data. */ UNIV_INTERN void dict_table_print_low( /*=================*/ dict_table_t* table); /*!< in: table */ /**********************************************************************//** Prints a table data when we know the table name. */ UNIV_INTERN void dict_table_print_by_name( /*=====================*/ const char* name); /*!< in: table name */ /**********************************************************************//** Outputs info on foreign keys of a table. */ UNIV_INTERN void dict_print_info_on_foreign_keys( /*============================*/ ibool create_table_format, /*!< in: if TRUE then print in a format suitable to be inserted into a CREATE TABLE, otherwise in the format of SHOW TABLE STATUS */ ib_stream_t ib_stream, /*!< in: stream where to print */ trx_t* trx, /*!< in: transaction */ dict_table_t* table); /*!< in: table */ /**********************************************************************//** Outputs info on a foreign key of a table in a format suitable for CREATE TABLE. */ UNIV_INTERN void dict_print_info_on_foreign_key_in_create_format( /*============================================*/ ib_stream_t stream, /*!< in: file where to print */ trx_t* trx, /*!< in: transaction */ dict_foreign_t* foreign, /*!< in: foreign key constraint */ ibool add_newline); /*!< in: whether to add a newline */ /********************************************************************//** Displays the names of the index and the table. */ UNIV_INTERN void dict_index_name_print( /*==================*/ ib_stream_t stream, /*!< in: output stream */ trx_t* trx, /*!< in: transaction */ const dict_index_t* index); /*!< in: index to print */ #ifdef UNIV_DEBUG /********************************************************************//** Gets the first index on the table (the clustered index). @return index, NULL if none exists */ UNIV_INLINE dict_index_t* dict_table_get_first_index( /*=======================*/ const dict_table_t* table); /*!< in: table */ /********************************************************************//** Gets the next index on the table. @return index, NULL if none left */ UNIV_INLINE dict_index_t* dict_table_get_next_index( /*======================*/ const dict_index_t* index); /*!< in: index */ #else /* UNIV_DEBUG */ # define dict_table_get_first_index(table) UT_LIST_GET_FIRST((table)->indexes) # define dict_table_get_next_index(index) UT_LIST_GET_NEXT(indexes, index) #endif /* UNIV_DEBUG */ #endif /* !UNIV_HOTBACKUP */ /********************************************************************//** Check whether the index is the clustered index. @return nonzero for clustered index, zero for other indexes */ UNIV_INLINE ulint dict_index_is_clust( /*================*/ const dict_index_t* index) /*!< in: index */ __attribute__((pure)); /********************************************************************//** Check whether the index is unique. @return nonzero for unique index, zero for other indexes */ UNIV_INLINE ulint dict_index_is_unique( /*=================*/ const dict_index_t* index) /*!< in: index */ __attribute__((pure)); /********************************************************************//** Check whether the index is the insert buffer tree. @return nonzero for insert buffer, zero for other indexes */ UNIV_INLINE ulint dict_index_is_ibuf( /*===============*/ const dict_index_t* index) /*!< in: index */ __attribute__((pure)); /********************************************************************//** Check whether the index is a secondary index or the insert buffer tree. @return nonzero for insert buffer, zero for other indexes */ UNIV_INLINE ulint dict_index_is_sec_or_ibuf( /*======================*/ const dict_index_t* index) /*!< in: index */ __attribute__((pure)); /********************************************************************//** Gets the number of user-defined columns in a table in the dictionary cache. @return number of user-defined (e.g., not ROW_ID) columns of a table */ UNIV_INLINE ulint dict_table_get_n_user_cols( /*=======================*/ const dict_table_t* table); /*!< in: table */ /********************************************************************//** Gets the number of system columns in a table in the dictionary cache. @return number of system (e.g., ROW_ID) columns of a table */ UNIV_INLINE ulint dict_table_get_n_sys_cols( /*======================*/ const dict_table_t* table); /*!< in: table */ /********************************************************************//** Gets the number of all columns (also system) in a table in the dictionary cache. @return number of columns of a table */ UNIV_INLINE ulint dict_table_get_n_cols( /*==================*/ const dict_table_t* table); /*!< in: table */ #ifdef UNIV_DEBUG /********************************************************************//** Gets the nth column of a table. @return pointer to column object */ UNIV_INLINE dict_col_t* dict_table_get_nth_col( /*===================*/ const dict_table_t* table, /*!< in: table */ ulint pos); /*!< in: position of column */ /********************************************************************//** Gets the given system column of a table. @return pointer to column object */ UNIV_INLINE dict_col_t* dict_table_get_sys_col( /*===================*/ const dict_table_t* table, /*!< in: table */ ulint sys); /*!< in: DATA_ROW_ID, ... */ #else /* UNIV_DEBUG */ #define dict_table_get_nth_col(table, pos) \ ((table)->cols + (pos)) #define dict_table_get_sys_col(table, sys) \ ((table)->cols + (table)->n_cols + (sys) - DATA_N_SYS_COLS) #endif /* UNIV_DEBUG */ /********************************************************************//** Gets the given system column number of a table. @return column number */ UNIV_INLINE ulint dict_table_get_sys_col_no( /*======================*/ const dict_table_t* table, /*!< in: table */ ulint sys); /*!< in: DATA_ROW_ID, ... */ #ifndef UNIV_HOTBACKUP /********************************************************************//** Returns the minimum data size of an index record. @return minimum data size in bytes */ UNIV_INLINE ulint dict_index_get_min_size( /*====================*/ const dict_index_t* index); /*!< in: index */ #endif /* !UNIV_HOTBACKUP */ /********************************************************************//** Check whether the table uses the compact page format. @return TRUE if table uses the compact page format */ UNIV_INLINE ibool dict_table_is_comp( /*===============*/ const dict_table_t* table); /*!< in: table */ /********************************************************************//** Determine the file format of a table. @return file format version */ UNIV_INLINE ulint dict_table_get_format( /*==================*/ const dict_table_t* table); /*!< in: table */ /********************************************************************//** Set the file format of a table. */ UNIV_INLINE void dict_table_set_format( /*==================*/ dict_table_t* table, /*!< in/out: table */ ulint format);/*!< in: file format version */ /********************************************************************//** Extract the compressed page size from table flags. @return compressed page size, or 0 if not compressed */ UNIV_INLINE ulint dict_table_flags_to_zip_size( /*=========================*/ ulint flags) /*!< in: flags */ __attribute__((const)); /********************************************************************//** Check whether the table uses the compressed compact page format. @return compressed page size, or 0 if not compressed */ UNIV_INLINE ulint dict_table_zip_size( /*================*/ const dict_table_t* table); /*!< in: table */ /********************************************************************//** Checks if a column is in the ordering columns of the clustered index of a table. Column prefixes are treated like whole columns. @return TRUE if the column, or its prefix, is in the clustered key */ UNIV_INTERN ibool dict_table_col_in_clustered_key( /*============================*/ const dict_table_t* table, /*!< in: table */ ulint n); /*!< in: column number */ #ifndef UNIV_HOTBACKUP /*******************************************************************//** Copies types of columns contained in table to tuple and sets all fields of the tuple to the SQL NULL value. This function should be called right after dtuple_create(). */ UNIV_INTERN void dict_table_copy_types( /*==================*/ dtuple_t* tuple, /*!< in/out: data tuple */ const dict_table_t* table); /*!< in: table */ /**********************************************************************//** Looks for an index with the given id. NOTE that we do not reserve the dictionary mutex: this function is for emergency purposes like printing info of a corrupt database page! @return index or NULL if not found from cache */ UNIV_INTERN dict_index_t* dict_index_find_on_id_low( /*======================*/ dulint id); /*!< in: index id */ /**********************************************************************//** Adds an index to the dictionary cache. @return DB_SUCCESS, DB_TOO_BIG_RECORD, or DB_CORRUPTION */ UNIV_INTERN ulint dict_index_add_to_cache( /*====================*/ dict_table_t* table, /*!< in: table on which the index is */ dict_index_t* index, /*!< in, own: index; NOTE! The index memory object is freed in this function! */ ulint page_no,/*!< in: root page number of the index */ ibool strict);/*!< in: TRUE=refuse to create the index if records could be too big to fit in an B-tree page */ /**********************************************************************//** Removes an index from the dictionary cache. */ UNIV_INTERN void dict_index_remove_from_cache( /*=========================*/ dict_table_t* table, /*!< in/out: table */ dict_index_t* index); /*!< in, own: index */ #endif /* !UNIV_HOTBACKUP */ /********************************************************************//** Gets the number of fields in the internal representation of an index, including fields added by the dictionary system. @return number of fields */ UNIV_INLINE ulint dict_index_get_n_fields( /*====================*/ const dict_index_t* index); /*!< in: an internal representation of index (in the dictionary cache) */ /********************************************************************//** Gets the number of fields in the internal representation of an index that uniquely determine the position of an index entry in the index, if we do not take multiversioning into account: in the B-tree use the value returned by dict_index_get_n_unique_in_tree. @return number of fields */ UNIV_INLINE ulint dict_index_get_n_unique( /*====================*/ const dict_index_t* index); /*!< in: an internal representation of index (in the dictionary cache) */ /********************************************************************//** Gets the number of fields in the internal representation of an index which uniquely determine the position of an index entry in the index, if we also take multiversioning into account. @return number of fields */ UNIV_INLINE ulint dict_index_get_n_unique_in_tree( /*============================*/ const dict_index_t* index); /*!< in: an internal representation of index (in the dictionary cache) */ /********************************************************************//** Gets the number of user-defined ordering fields in the index. In the internal representation we add the row id to the ordering fields to make all indexes unique, but this function returns the number of fields the user defined in the index as ordering fields. @return number of fields */ UNIV_INLINE ulint dict_index_get_n_ordering_defined_by_user( /*======================================*/ const dict_index_t* index); /*!< in: an internal representation of index (in the dictionary cache) */ #ifdef UNIV_DEBUG /********************************************************************//** Gets the nth field of an index. @return pointer to field object */ UNIV_INLINE dict_field_t* dict_index_get_nth_field( /*=====================*/ const dict_index_t* index, /*!< in: index */ ulint pos); /*!< in: position of field */ #else /* UNIV_DEBUG */ # define dict_index_get_nth_field(index, pos) ((index)->fields + (pos)) #endif /* UNIV_DEBUG */ /********************************************************************//** Gets pointer to the nth column in an index. @return column */ UNIV_INLINE const dict_col_t* dict_index_get_nth_col( /*===================*/ const dict_index_t* index, /*!< in: index */ ulint pos); /*!< in: position of the field */ /********************************************************************//** Gets the column number of the nth field in an index. @return column number */ UNIV_INLINE ulint dict_index_get_nth_col_no( /*======================*/ const dict_index_t* index, /*!< in: index */ ulint pos); /*!< in: position of the field */ /********************************************************************//** Looks for column n in an index. @return position in internal representation of the index; ULINT_UNDEFINED if not contained */ UNIV_INTERN ulint dict_index_get_nth_col_pos( /*=======================*/ const dict_index_t* index, /*!< in: index */ ulint n); /*!< in: column number */ /********************************************************************//** Returns TRUE if the index contains a column or a prefix of that column. @return TRUE if contains the column or its prefix */ UNIV_INTERN ibool dict_index_contains_col_or_prefix( /*==============================*/ const dict_index_t* index, /*!< in: index */ ulint n); /*!< in: column number */ /********************************************************************//** Looks for a matching field in an index. The column has to be the same. The column in index must be complete, or must contain a prefix longer than the column in index2. That is, we must be able to construct the prefix in index2 from the prefix in index. @return position in internal representation of the index; ULINT_UNDEFINED if not contained */ UNIV_INTERN ulint dict_index_get_nth_field_pos( /*=========================*/ const dict_index_t* index, /*!< in: index from which to search */ const dict_index_t* index2, /*!< in: index */ ulint n); /*!< in: field number in index2 */ /********************************************************************//** Looks for column n position in the clustered index. @return position in internal representation of the clustered index */ UNIV_INTERN ulint dict_table_get_nth_col_pos( /*=======================*/ const dict_table_t* table, /*!< in: table */ ulint n); /*!< in: column number */ /********************************************************************//** Returns the position of a system column in an index. @return position, ULINT_UNDEFINED if not contained */ UNIV_INLINE ulint dict_index_get_sys_col_pos( /*=======================*/ const dict_index_t* index, /*!< in: index */ ulint type); /*!< in: DATA_ROW_ID, ... */ /*******************************************************************//** Adds a column to index. */ UNIV_INTERN void dict_index_add_col( /*===============*/ dict_index_t* index, /*!< in/out: index */ const dict_table_t* table, /*!< in: table */ dict_col_t* col, /*!< in: column */ ulint prefix_len); /*!< in: column prefix length */ #ifndef UNIV_HOTBACKUP /*******************************************************************//** Copies types of fields contained in index to tuple. */ UNIV_INTERN void dict_index_copy_types( /*==================*/ dtuple_t* tuple, /*!< in/out: data tuple */ const dict_index_t* index, /*!< in: index */ ulint n_fields); /*!< in: number of field types to copy */ #endif /* !UNIV_HOTBACKUP */ /*********************************************************************//** Gets the field column. @return field->col, pointer to the table column */ UNIV_INLINE const dict_col_t* dict_field_get_col( /*===============*/ const dict_field_t* field); /*!< in: index field */ #ifndef UNIV_HOTBACKUP /**********************************************************************//** Returns an index object if it is found in the dictionary cache. Assumes that dict_sys->mutex is already being held. @return index, NULL if not found */ UNIV_INTERN dict_index_t* dict_index_get_if_in_cache_low( /*===========================*/ dulint index_id); /*!< in: index id */ #if defined UNIV_DEBUG || defined UNIV_BUF_DEBUG /**********************************************************************//** Returns an index object if it is found in the dictionary cache. @return index, NULL if not found */ UNIV_INTERN dict_index_t* dict_index_get_if_in_cache( /*=======================*/ dulint index_id); /*!< in: index id */ #endif /* UNIV_DEBUG || UNIV_BUF_DEBUG */ #ifdef UNIV_DEBUG /**********************************************************************//** Checks that a tuple has n_fields_cmp value in a sensible range, so that no comparison can occur with the page number field in a node pointer. @return TRUE if ok */ UNIV_INTERN ibool dict_index_check_search_tuple( /*==========================*/ const dict_index_t* index, /*!< in: index tree */ const dtuple_t* tuple); /*!< in: tuple used in a search */ #endif /* UNIV_DEBUG */ /**********************************************************************//** Builds a node pointer out of a physical record and a page number. @return own: node pointer */ UNIV_INTERN dtuple_t* dict_index_build_node_ptr( /*======================*/ const dict_index_t* index, /*!< in: index */ const rec_t* rec, /*!< in: record for which to build node pointer */ ulint page_no,/*!< in: page number to put in node pointer */ mem_heap_t* heap, /*!< in: memory heap where pointer created */ ulint level); /*!< in: level of rec in tree: 0 means leaf level */ /**********************************************************************//** Copies an initial segment of a physical record, long enough to specify an index entry uniquely. @return pointer to the prefix record */ UNIV_INTERN rec_t* dict_index_copy_rec_order_prefix( /*=============================*/ const dict_index_t* index, /*!< in: index */ const rec_t* rec, /*!< in: record for which to copy prefix */ ulint* n_fields,/*!< out: number of fields copied */ byte** buf, /*!< in/out: memory buffer for the copied prefix, or NULL */ ulint* buf_size);/*!< in/out: buffer size */ /**********************************************************************//** Builds a typed data tuple out of a physical record. @return own: data tuple */ UNIV_INTERN dtuple_t* dict_index_build_data_tuple( /*========================*/ dict_index_t* index, /*!< in: index */ rec_t* rec, /*!< in: record for which to build data tuple */ ulint n_fields,/*!< in: number of data fields */ mem_heap_t* heap); /*!< in: memory heap where tuple created */ /*********************************************************************//** Gets the space id of the root of the index tree. @return space id */ UNIV_INLINE ulint dict_index_get_space( /*=================*/ const dict_index_t* index); /*!< in: index */ /*********************************************************************//** Sets the space id of the root of the index tree. */ UNIV_INLINE void dict_index_set_space( /*=================*/ dict_index_t* index, /*!< in/out: index */ ulint space); /*!< in: space id */ /*********************************************************************//** Gets the page number of the root of the index tree. @return page number */ UNIV_INLINE ulint dict_index_get_page( /*================*/ const dict_index_t* tree); /*!< in: index */ /*********************************************************************//** Sets the page number of the root of index tree. */ UNIV_INLINE void dict_index_set_page( /*================*/ dict_index_t* index, /*!< in/out: index */ ulint page); /*!< in: page number */ /*********************************************************************//** Gets the read-write lock of the index tree. @return read-write lock */ UNIV_INLINE rw_lock_t* dict_index_get_lock( /*================*/ dict_index_t* index); /*!< in: index */ /********************************************************************//** Returns free space reserved for future updates of records. This is relevant only in the case of many consecutive inserts, as updates which make the records bigger might fragment the index. @return number of free bytes on page, reserved for updates */ UNIV_INLINE ulint dict_index_get_space_reserve(void); /*==============================*/ /*********************************************************************//** Calculates the minimum record length in an index. */ UNIV_INTERN ulint dict_index_calc_min_rec_len( /*========================*/ const dict_index_t* index); /*!< in: index */ /*********************************************************************//** Calculates new estimates for table and index statistics. The statistics are used in query optimization. */ UNIV_INTERN void dict_update_statistics_low( /*=======================*/ dict_table_t* table, /*!< in/out: table */ ibool has_dict_mutex);/*!< in: TRUE if the caller has the dictionary mutex */ /*********************************************************************//** Calculates new estimates for table and index statistics. The statistics are used in query optimization. */ UNIV_INTERN void dict_update_statistics( /*===================*/ dict_table_t* table); /*!< in/out: table */ /********************************************************************//** Reserves the dictionary system mutex. */ UNIV_INTERN void dict_mutex_enter(void); /*==================*/ /********************************************************************//** Releases the dictionary system mutex. */ UNIV_INTERN void dict_mutex_exit(void); /*=================*/ /**********************************************************************//** Lock the appropriate mutex to protect index->stat_n_diff_key_vals[]. index->id is used to pick the right mutex and it should not change before dict_index_stat_mutex_exit() is called on this index. */ UNIV_INTERN void dict_index_stat_mutex_enter( /*========================*/ const dict_index_t* index); /*!< in: index */ /**********************************************************************//** Unlock the appropriate mutex that protects index->stat_n_diff_key_vals[]. */ UNIV_INTERN void dict_index_stat_mutex_exit( /*=======================*/ const dict_index_t* index); /*!< in: index */ /********************************************************************//** Checks if the database name in two table names is the same. @return TRUE if same db name */ UNIV_INTERN ibool dict_tables_have_same_db( /*=====================*/ const char* name1, /*!< in: table name in the form dbname '/' tablename */ const char* name2); /*!< in: table name in the form dbname '/' tablename */ /*********************************************************************//** Removes an index from the cache */ UNIV_INTERN void dict_index_remove_from_cache( /*=========================*/ dict_table_t* table, /*!< in/out: table */ dict_index_t* index); /*!< in, own: index */ /**********************************************************************//** Get index by name @return index, NULL if does not exist */ UNIV_INTERN dict_index_t* dict_table_get_index_on_name( /*=========================*/ dict_table_t* table, /*!< in: table */ const char* name); /*!< in: name of the index to find */ /**********************************************************************//** In case there is more than one index with the same name return the index with the min(id). @return index, NULL if does not exist */ UNIV_INTERN dict_index_t* dict_table_get_index_on_name_and_min_id( /*====================================*/ dict_table_t* table, /*!< in: table */ const char* name); /*!< in: name of the index to find */ /************************************************************************* Locks the data dictionary exclusively for performing a table create or other data dictionary modification operation. */ UNIV_INTERN void dict_lock_data_dictionary( /*======================*/ trx_t* trx); /*!< in: transaction */ /************************************************************************* Unlocks the data dictionary exclusive lock. */ UNIV_INTERN void dict_unlock_data_dictionary( /*========================*/ trx_t* trx); /*!< in: transaction */ /************************************************************************* Locks the data dictionary in shared mode from modifications, for performing foreign key check, rollback, or other operation invisible to users. */ UNIV_INTERN void dict_freeze_data_dictionary( /*========================*/ trx_t* trx); /*!< in: transaction */ /************************************************************************* Unlocks the data dictionary shared lock. */ UNIV_INTERN void dict_unfreeze_data_dictionary( /*==========================*/ trx_t* trx); /*!< in: transaction */ /********************************************************************** Reset dict variables. */ UNIV_INTERN void dict_var_init(void); /*===============*/ /* Buffers for storing detailed information about the latest foreign key and unique key errors */ extern ib_stream_t dict_foreign_err_file; extern mutex_t dict_foreign_err_mutex; /* mutex protecting the buffers */ /** the dictionary system */ extern dict_sys_t* dict_sys; /** the data dictionary rw-latch protecting dict_sys */ extern rw_lock_t dict_operation_lock; /* Dictionary system struct */ struct dict_sys_struct{ mutex_t mutex; /*!< mutex protecting the data dictionary; protects also the disk-based dictionary system tables; this mutex serializes CREATE TABLE and DROP TABLE, as well as reading the dictionary data for a table from system tables */ dulint row_id; /*!< the next row id to assign; NOTE that at a checkpoint this must be written to the dict system header and flushed to a file; in recovery this must be derived from the log records */ hash_table_t* table_hash; /*!< hash table of the tables, based on name */ hash_table_t* table_id_hash; /*!< hash table of the tables, based on id */ UT_LIST_BASE_NODE_T(dict_table_t) table_LRU; /*!< LRU list of tables */ ulint size; /*!< varying space in bytes occupied by the data dictionary table and index objects */ dict_table_t* sys_tables; /*!< SYS_TABLES table */ dict_table_t* sys_columns; /*!< SYS_COLUMNS table */ dict_table_t* sys_indexes; /*!< SYS_INDEXES table */ dict_table_t* sys_fields; /*!< SYS_FIELDS table */ }; #endif /* !UNIV_HOTBACKUP */ /** dummy index for ROW_FORMAT=REDUNDANT supremum and infimum records */ extern dict_index_t* dict_ind_redundant; /** dummy index for ROW_FORMAT=COMPACT supremum and infimum records */ extern dict_index_t* dict_ind_compact; /**********************************************************************//** Inits dict_ind_redundant and dict_ind_compact. */ UNIV_INTERN void dict_ind_init(void); /*===============*/ /**********************************************************************//** Closes the data dictionary module. */ UNIV_INTERN void dict_close(void); /*============*/ #ifndef UNIV_NONINL #include "dict0dict.ic" #endif #endif haildb-2.3.2/include/trx0purge.h0000644000175000017500000001623511513177357017406 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/trx0purge.h Purge old versions Created 3/26/1996 Heikki Tuuri *******************************************************/ #ifndef trx0purge_h #define trx0purge_h #include "univ.i" #include "trx0types.h" #include "mtr0mtr.h" #include "trx0sys.h" #include "que0types.h" #include "page0page.h" #include "usr0sess.h" #include "fil0fil.h" /** The global data structure coordinating a purge */ extern trx_purge_t* purge_sys; /** A dummy undo record used as a return value when we have a whole undo log which needs no purge */ extern trx_undo_rec_t trx_purge_dummy_rec; /********************************************************************//** Calculates the file address of an undo log header when we have the file address of its history list node. @return file address of the log */ UNIV_INLINE fil_addr_t trx_purge_get_log_from_hist( /*========================*/ fil_addr_t node_addr); /*!< in: file address of the history list node of the log */ /*****************************************************************//** Checks if trx_id is >= purge_view: then it is guaranteed that its update undo log still exists in the system. @return TRUE if is sure that it is preserved, also if the function returns FALSE, it is possible that the undo log still exists in the system */ UNIV_INTERN ibool trx_purge_update_undo_must_exist( /*=============================*/ trx_id_t trx_id);/*!< in: transaction id */ /********************************************************************//** Creates the global purge system control structure and inits the history mutex. */ UNIV_INTERN void trx_purge_sys_create(void); /*======================*/ /********************************************************************//** Frees the global purge system control structure. */ UNIV_INTERN void trx_purge_sys_close(void); /*======================*/ /************************************************************************ Adds the update undo log as the first log in the history list. Removes the update undo log segment from the rseg slot if it is too big for reuse. */ UNIV_INTERN void trx_purge_add_update_undo_to_history( /*=================================*/ trx_t* trx, /*!< in: transaction */ page_t* undo_page, /*!< in: update undo log header page, x-latched */ mtr_t* mtr); /*!< in: mtr */ /********************************************************************//** Fetches the next undo log record from the history list to purge. It must be released with the corresponding release function. @return copy of an undo log record or pointer to trx_purge_dummy_rec, if the whole undo log can skipped in purge; NULL if none left */ UNIV_INTERN trx_undo_rec_t* trx_purge_fetch_next_rec( /*=====================*/ roll_ptr_t* roll_ptr,/*!< out: roll pointer to undo record */ trx_undo_inf_t** cell, /*!< out: storage cell for the record in the purge array */ mem_heap_t* heap); /*!< in: memory heap where copied */ /*******************************************************************//** Releases a reserved purge undo record. */ UNIV_INTERN void trx_purge_rec_release( /*==================*/ trx_undo_inf_t* cell); /*!< in: storage cell */ /*******************************************************************//** This function runs a purge batch. @return number of undo log pages handled in the batch */ UNIV_INTERN ulint trx_purge(void); /*===========*/ /******************************************************************//** Prints information of the purge system to stderr. */ UNIV_INTERN void trx_purge_sys_print(void); /*======================*/ /********************************************************************* Reset the variables. */ UNIV_INTERN void trx_purge_var_init(void); /*====================*/ /** The control structure used in the purge operation */ struct trx_purge_struct{ ulint state; /*!< Purge system state */ sess_t* sess; /*!< System session running the purge query */ trx_t* trx; /*!< System transaction running the purge query: this trx is not in the trx list of the trx system and it never ends */ que_t* query; /*!< The query graph which will do the parallelized purge operation */ rw_lock_t latch; /*!< The latch protecting the purge view. A purge operation must acquire an x-latch here for the instant at which it changes the purge view: an undo log operation can prevent this by obtaining an s-latch here. */ read_view_t* view; /*!< The purge will not remove undo logs which are >= this view (purge view) */ mutex_t mutex; /*!< Mutex protecting the fields below */ ulint n_pages_handled;/*!< Approximate number of undo log pages processed in purge */ ulint handle_limit; /*!< Target of how many pages to get processed in the current purge */ /*------------------------------*/ /* The following two fields form the 'purge pointer' which advances during a purge, and which is used in history list truncation */ trx_id_t purge_trx_no; /*!< Purge has advanced past all transactions whose number is less than this */ undo_no_t purge_undo_no; /*!< Purge has advanced past all records whose undo number is less than this */ /*-----------------------------*/ ibool next_stored; /*!< TRUE if the info of the next record to purge is stored below: if yes, then the transaction number and the undo number of the record are stored in purge_trx_no and purge_undo_no above */ trx_rseg_t* rseg; /*!< Rollback segment for the next undo record to purge */ ulint page_no; /*!< Page number for the next undo record to purge, page number of the log header, if dummy record */ ulint offset; /*!< Page offset for the next undo record to purge, 0 if the dummy record */ ulint hdr_page_no; /*!< Header page of the undo log where the next record to purge belongs */ ulint hdr_offset; /*!< Header byte offset on the page */ /*-----------------------------*/ trx_undo_arr_t* arr; /*!< Array of transaction numbers and undo numbers of the undo records currently under processing in purge */ mem_heap_t* heap; /*!< Temporary storage used during a purge: can be emptied after purge completes */ }; #define TRX_PURGE_ON 1 /* purge operation is running */ #define TRX_STOP_PURGE 2 /* purge operation is stopped, or it should be stopped */ #ifndef UNIV_NONINL #include "trx0purge.ic" #endif #endif haildb-2.3.2/include/btr0btr.h0000644000175000017500000004514511513177357017027 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1994, 2010, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/btr0btr.h The B-tree Created 6/2/1994 Heikki Tuuri *******************************************************/ #ifndef btr0btr_h #define btr0btr_h #include "univ.i" #include "dict0dict.h" #include "data0data.h" #include "page0cur.h" #include "mtr0mtr.h" #include "btr0types.h" #ifndef UNIV_HOTBACKUP /** Maximum record size which can be stored on a page, without using the special big record storage structure */ #define BTR_PAGE_MAX_REC_SIZE (UNIV_PAGE_SIZE / 2 - 200) /** @brief Maximum depth of a B-tree in InnoDB. Note that this isn't a maximum as such; none of the tree operations avoid producing trees bigger than this. It is instead a "max depth that other code must work with", useful for e.g. fixed-size arrays that must store some information about each level in a tree. In other words: if a B-tree with bigger depth than this is encountered, it is not acceptable for it to lead to mysterious memory corruption, but it is acceptable for the program to die with a clear assert failure. */ #define BTR_MAX_LEVELS 100 /** Latching modes for btr_cur_search_to_nth_level(). */ enum btr_latch_mode { /** Search a record on a leaf page and S-latch it. */ BTR_SEARCH_LEAF = RW_S_LATCH, /** (Prepare to) modify a record on a leaf page and X-latch it. */ BTR_MODIFY_LEAF = RW_X_LATCH, /** Obtain no latches. */ BTR_NO_LATCHES = RW_NO_LATCH, /** Start modifying the entire B-tree. */ BTR_MODIFY_TREE = 33, /** Continue modifying the entire B-tree. */ BTR_CONT_MODIFY_TREE = 34, /** Search the previous record. */ BTR_SEARCH_PREV = 35, /** Modify the previous record. */ BTR_MODIFY_PREV = 36 }; /** If this is ORed to btr_latch_mode, it means that the search tuple will be inserted to the index, at the searched position */ #define BTR_INSERT 512 /** This flag ORed to btr_latch_mode says that we do the search in query optimization */ #define BTR_ESTIMATE 1024 /** This flag ORed to btr_latch_mode says that we can ignore possible UNIQUE definition on secondary indexes when we decide if we can use the insert buffer to speed up inserts */ #define BTR_IGNORE_SEC_UNIQUE 2048 /**************************************************************//** Gets the root node of a tree and x-latches it. @return root page, x-latched */ UNIV_INTERN page_t* btr_root_get( /*=========*/ dict_index_t* index, /*!< in: index tree */ mtr_t* mtr); /*!< in: mtr */ /**************************************************************//** Gets a buffer page and declares its latching order level. */ UNIV_INLINE buf_block_t* btr_block_get( /*==========*/ ulint space, /*!< in: space id */ ulint zip_size, /*!< in: compressed page size in bytes or 0 for uncompressed pages */ ulint page_no, /*!< in: page number */ ulint mode, /*!< in: latch mode */ mtr_t* mtr); /*!< in: mtr */ /**************************************************************//** Gets a buffer page and declares its latching order level. */ UNIV_INLINE page_t* btr_page_get( /*=========*/ ulint space, /*!< in: space id */ ulint zip_size, /*!< in: compressed page size in bytes or 0 for uncompressed pages */ ulint page_no, /*!< in: page number */ ulint mode, /*!< in: latch mode */ mtr_t* mtr); /*!< in: mtr */ #endif /* !UNIV_HOTBACKUP */ /**************************************************************//** Gets the index id field of a page. @return index id */ UNIV_INLINE dulint btr_page_get_index_id( /*==================*/ const page_t* page); /*!< in: index page */ #ifndef UNIV_HOTBACKUP /********************************************************//** Gets the node level field in an index page. @return level, leaf level == 0 */ UNIV_INLINE ulint btr_page_get_level_low( /*===================*/ const page_t* page); /*!< in: index page */ /********************************************************//** Gets the node level field in an index page. @return level, leaf level == 0 */ UNIV_INLINE ulint btr_page_get_level( /*===============*/ const page_t* page, /*!< in: index page */ mtr_t* mtr); /*!< in: mini-transaction handle */ /********************************************************//** Gets the next index page number. @return next page number */ UNIV_INLINE ulint btr_page_get_next( /*==============*/ const page_t* page, /*!< in: index page */ mtr_t* mtr); /*!< in: mini-transaction handle */ /********************************************************//** Gets the previous index page number. @return prev page number */ UNIV_INLINE ulint btr_page_get_prev( /*==============*/ const page_t* page, /*!< in: index page */ mtr_t* mtr); /*!< in: mini-transaction handle */ /*************************************************************//** Gets pointer to the previous user record in the tree. It is assumed that the caller has appropriate latches on the page and its neighbor. @return previous user record, NULL if there is none */ UNIV_INTERN rec_t* btr_get_prev_user_rec( /*==================*/ rec_t* rec, /*!< in: record on leaf level */ mtr_t* mtr); /*!< in: mtr holding a latch on the page, and if needed, also to the previous page */ /*************************************************************//** Gets pointer to the next user record in the tree. It is assumed that the caller has appropriate latches on the page and its neighbor. @return next user record, NULL if there is none */ UNIV_INTERN rec_t* btr_get_next_user_rec( /*==================*/ rec_t* rec, /*!< in: record on leaf level */ mtr_t* mtr); /*!< in: mtr holding a latch on the page, and if needed, also to the next page */ /**************************************************************//** Releases the latch on a leaf page and bufferunfixes it. */ UNIV_INLINE void btr_leaf_page_release( /*==================*/ buf_block_t* block, /*!< in: buffer block */ ulint latch_mode, /*!< in: BTR_SEARCH_LEAF or BTR_MODIFY_LEAF */ mtr_t* mtr); /*!< in: mtr */ /**************************************************************//** Gets the child node file address in a node pointer. NOTE: the offsets array must contain all offsets for the record since we read the last field according to offsets and assume that it contains the child page number. In other words offsets must have been retrieved with rec_get_offsets(n_fields=ULINT_UNDEFINED). @return child node address */ UNIV_INLINE ulint btr_node_ptr_get_child_page_no( /*===========================*/ const rec_t* rec, /*!< in: node pointer record */ const ulint* offsets);/*!< in: array returned by rec_get_offsets() */ /************************************************************//** Creates the root node for a new index tree. @return page number of the created root, FIL_NULL if did not succeed */ UNIV_INTERN ulint btr_create( /*=======*/ ulint type, /*!< in: type of the index */ ulint space, /*!< in: space where created */ ulint zip_size,/*!< in: compressed page size in bytes or 0 for uncompressed pages */ dulint index_id,/*!< in: index id */ dict_index_t* index, /*!< in: index */ mtr_t* mtr); /*!< in: mini-transaction handle */ /************************************************************//** Frees a B-tree except the root page, which MUST be freed after this by calling btr_free_root. */ UNIV_INTERN void btr_free_but_not_root( /*==================*/ ulint space, /*!< in: space where created */ ulint zip_size, /*!< in: compressed page size in bytes or 0 for uncompressed pages */ ulint root_page_no); /*!< in: root page number */ /************************************************************//** Frees the B-tree root page. Other tree MUST already have been freed. */ UNIV_INTERN void btr_free_root( /*==========*/ ulint space, /*!< in: space where created */ ulint zip_size, /*!< in: compressed page size in bytes or 0 for uncompressed pages */ ulint root_page_no, /*!< in: root page number */ mtr_t* mtr); /*!< in: a mini-transaction which has already been started */ /*************************************************************//** Makes tree one level higher by splitting the root, and inserts the tuple. It is assumed that mtr contains an x-latch on the tree. NOTE that the operation of this function must always succeed, we cannot reverse it: therefore enough free disk space must be guaranteed to be available before this function is called. @return inserted record */ UNIV_INTERN rec_t* btr_root_raise_and_insert( /*======================*/ btr_cur_t* cursor, /*!< in: cursor at which to insert: must be on the root page; when the function returns, the cursor is positioned on the predecessor of the inserted record */ const dtuple_t* tuple, /*!< in: tuple to insert */ ulint n_ext, /*!< in: number of externally stored columns */ mtr_t* mtr); /*!< in: mtr */ /*************************************************************//** Reorganizes an index page. IMPORTANT: if btr_page_reorganize() is invoked on a compressed leaf page of a non-clustered index, the caller must update the insert buffer free bits in the same mini-transaction in such a way that the modification will be redo-logged. @return TRUE on success, FALSE on failure */ UNIV_INTERN ibool btr_page_reorganize( /*================*/ buf_block_t* block, /*!< in: page to be reorganized */ dict_index_t* index, /*!< in: record descriptor */ mtr_t* mtr); /*!< in: mtr */ /*************************************************************//** Decides if the page should be split at the convergence point of inserts converging to left. @return TRUE if split recommended */ UNIV_INTERN ibool btr_page_get_split_rec_to_left( /*===========================*/ btr_cur_t* cursor, /*!< in: cursor at which to insert */ rec_t** split_rec);/*!< out: if split recommended, the first record on upper half page, or NULL if tuple should be first */ /*************************************************************//** Decides if the page should be split at the convergence point of inserts converging to right. @return TRUE if split recommended */ UNIV_INTERN ibool btr_page_get_split_rec_to_right( /*============================*/ btr_cur_t* cursor, /*!< in: cursor at which to insert */ rec_t** split_rec);/*!< out: if split recommended, the first record on upper half page, or NULL if tuple should be first */ /*************************************************************//** Splits an index page to halves and inserts the tuple. It is assumed that mtr holds an x-latch to the index tree. NOTE: the tree x-latch is released within this function! NOTE that the operation of this function must always succeed, we cannot reverse it: therefore enough free disk space (2 pages) must be guaranteed to be available before this function is called. @return inserted record */ UNIV_INTERN rec_t* btr_page_split_and_insert( /*======================*/ btr_cur_t* cursor, /*!< in: cursor at which to insert; when the function returns, the cursor is positioned on the predecessor of the inserted record */ const dtuple_t* tuple, /*!< in: tuple to insert */ ulint n_ext, /*!< in: number of externally stored columns */ mtr_t* mtr); /*!< in: mtr */ /*******************************************************//** Inserts a data tuple to a tree on a non-leaf level. It is assumed that mtr holds an x-latch on the tree. */ UNIV_INTERN void btr_insert_on_non_leaf_level_func( /*==============================*/ dict_index_t* index, /*!< in: index */ ulint level, /*!< in: level, must be > 0 */ dtuple_t* tuple, /*!< in: the record to be inserted */ const char* file, /*!< in: file name */ ulint line, /*!< in: line where called */ mtr_t* mtr); /*!< in: mtr */ # define btr_insert_on_non_leaf_level(i,l,t,m) \ btr_insert_on_non_leaf_level_func(i,l,t,__FILE__,__LINE__,m) #endif /* !UNIV_HOTBACKUP */ /****************************************************************//** Sets a record as the predefined minimum record. */ UNIV_INTERN void btr_set_min_rec_mark( /*=================*/ rec_t* rec, /*!< in/out: record */ mtr_t* mtr); /*!< in: mtr */ #ifndef UNIV_HOTBACKUP /*************************************************************//** Deletes on the upper level the node pointer to a page. */ UNIV_INTERN void btr_node_ptr_delete( /*================*/ dict_index_t* index, /*!< in: index tree */ buf_block_t* block, /*!< in: page whose node pointer is deleted */ mtr_t* mtr); /*!< in: mtr */ #ifdef UNIV_DEBUG /************************************************************//** Checks that the node pointer to a page is appropriate. @return TRUE */ UNIV_INTERN ibool btr_check_node_ptr( /*===============*/ dict_index_t* index, /*!< in: index tree */ buf_block_t* block, /*!< in: index page */ mtr_t* mtr); /*!< in: mtr */ #endif /* UNIV_DEBUG */ /*************************************************************//** Tries to merge the page first to the left immediate brother if such a brother exists, and the node pointers to the current page and to the brother reside on the same page. If the left brother does not satisfy these conditions, looks at the right brother. If the page is the only one on that level lifts the records of the page to the father page, thus reducing the tree height. It is assumed that mtr holds an x-latch on the tree and on the page. If cursor is on the leaf level, mtr must also hold x-latches to the brothers, if they exist. @return TRUE on success */ UNIV_INTERN ibool btr_compress( /*=========*/ btr_cur_t* cursor, /*!< in: cursor on the page to merge or lift; the page must not be empty: in record delete use btr_discard_page if the page would become empty */ mtr_t* mtr); /*!< in: mtr */ /*************************************************************//** Discards a page from a B-tree. This is used to remove the last record from a B-tree page: the whole page must be removed at the same time. This cannot be used for the root page, which is allowed to be empty. */ UNIV_INTERN void btr_discard_page( /*=============*/ btr_cur_t* cursor, /*!< in: cursor on the page to discard: not on the root page */ mtr_t* mtr); /*!< in: mtr */ #endif /* !UNIV_HOTBACKUP */ /****************************************************************//** Parses the redo log record for setting an index record as the predefined minimum record. @return end of log record or NULL */ UNIV_INTERN byte* btr_parse_set_min_rec_mark( /*=======================*/ byte* ptr, /*!< in: buffer */ byte* end_ptr,/*!< in: buffer end */ ulint comp, /*!< in: nonzero=compact page format */ page_t* page, /*!< in: page or NULL */ mtr_t* mtr); /*!< in: mtr or NULL */ /***********************************************************//** Parses a redo log record of reorganizing a page. @return end of log record or NULL */ UNIV_INTERN byte* btr_parse_page_reorganize( /*======================*/ byte* ptr, /*!< in: buffer */ byte* end_ptr,/*!< in: buffer end */ dict_index_t* index, /*!< in: record descriptor */ buf_block_t* block, /*!< in: page to be reorganized, or NULL */ mtr_t* mtr); /*!< in: mtr or NULL */ #ifndef UNIV_HOTBACKUP /**************************************************************//** Gets the number of pages in a B-tree. @return number of pages */ UNIV_INTERN ulint btr_get_size( /*=========*/ dict_index_t* index, /*!< in: index */ ulint flag); /*!< in: BTR_N_LEAF_PAGES or BTR_TOTAL_SIZE */ /**************************************************************//** Allocates a new file page to be used in an index tree. NOTE: we assume that the caller has made the reservation for free extents! @return new allocated block, x-latched; NULL if out of space */ UNIV_INTERN buf_block_t* btr_page_alloc( /*===========*/ dict_index_t* index, /*!< in: index tree */ ulint hint_page_no, /*!< in: hint of a good page */ byte file_direction, /*!< in: direction where a possible page split is made */ ulint level, /*!< in: level where the page is placed in the tree */ mtr_t* mtr); /*!< in: mtr */ /**************************************************************//** Frees a file page used in an index tree. NOTE: cannot free field external storage pages because the page must contain info on its level. */ UNIV_INTERN void btr_page_free( /*==========*/ dict_index_t* index, /*!< in: index tree */ buf_block_t* block, /*!< in: block to be freed, x-latched */ mtr_t* mtr); /*!< in: mtr */ /**************************************************************//** Frees a file page used in an index tree. Can be used also to BLOB external storage pages, because the page level 0 can be given as an argument. */ UNIV_INTERN void btr_page_free_low( /*==============*/ dict_index_t* index, /*!< in: index tree */ buf_block_t* block, /*!< in: block to be freed, x-latched */ ulint level, /*!< in: page level */ mtr_t* mtr); /*!< in: mtr */ #ifdef UNIV_BTR_PRINT /*************************************************************//** Prints size info of a B-tree. */ UNIV_INTERN void btr_print_size( /*===========*/ dict_index_t* index); /*!< in: index tree */ /**************************************************************//** Prints directories and other info of all nodes in the index. */ UNIV_INTERN void btr_print_index( /*============*/ dict_index_t* index, /*!< in: index */ ulint width); /*!< in: print this many entries from start and end */ #endif /* UNIV_BTR_PRINT */ /************************************************************//** Checks the size and number of fields in a record based on the definition of the index. @return TRUE if ok */ UNIV_INTERN ibool btr_index_rec_validate( /*===================*/ const rec_t* rec, /*!< in: index record */ const dict_index_t* index, /*!< in: index */ ibool dump_on_error); /*!< in: TRUE if the function should print hex dump of record and page on error */ /**************************************************************//** Checks the consistency of an index tree. @return TRUE if ok */ UNIV_INTERN ibool btr_validate_index( /*===============*/ dict_index_t* index, /*!< in: index */ trx_t* trx); /*!< in: transaction or NULL */ #define BTR_N_LEAF_PAGES 1 #define BTR_TOTAL_SIZE 2 #endif /* !UNIV_HOTBACKUP */ #ifndef UNIV_NONINL #include "btr0btr.ic" #endif #endif haildb-2.3.2/include/dict0dict.ic0000644000175000017500000005343111513177357017460 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /******************************************************************//** @file include/dict0dict.ic Data dictionary system Created 1/8/1996 Heikki Tuuri ***********************************************************************/ #include "data0type.h" #ifndef UNIV_HOTBACKUP #include "dict0load.h" #include "rem0types.h" /*********************************************************************//** Gets the column data type. */ UNIV_INLINE void dict_col_copy_type( /*===============*/ const dict_col_t* col, /*!< in: column */ dtype_t* type) /*!< out: data type */ { ut_ad(col && type); type->mtype = col->mtype; type->prtype = col->prtype; type->len = col->len; type->mbminlen = col->mbminlen; type->mbmaxlen = col->mbmaxlen; } #endif /* !UNIV_HOTBACKUP */ #ifdef UNIV_DEBUG /*********************************************************************//** Assert that a column and a data type match. @return TRUE */ UNIV_INLINE ibool dict_col_type_assert_equal( /*=======================*/ const dict_col_t* col, /*!< in: column */ const dtype_t* type) /*!< in: data type */ { ut_ad(col); ut_ad(type); ut_ad(col->mtype == type->mtype); ut_ad(col->prtype == type->prtype); ut_ad(col->len == type->len); # ifndef UNIV_HOTBACKUP ut_ad(col->mbminlen == type->mbminlen); ut_ad(col->mbmaxlen == type->mbmaxlen); # endif /* !UNIV_HOTBACKUP */ return(TRUE); } #endif /* UNIV_DEBUG */ #ifndef UNIV_HOTBACKUP /***********************************************************************//** Returns the minimum size of the column. @return minimum size */ UNIV_INLINE ulint dict_col_get_min_size( /*==================*/ const dict_col_t* col) /*!< in: column */ { return(dtype_get_min_size_low(col->mtype, col->prtype, col->len, col->mbminlen, col->mbmaxlen)); } /***********************************************************************//** Returns the maximum size of the column. @return maximum size */ UNIV_INLINE ulint dict_col_get_max_size( /*==================*/ const dict_col_t* col) /*!< in: column */ { return(dtype_get_max_size_low(col->mtype, col->len)); } #endif /* !UNIV_HOTBACKUP */ /***********************************************************************//** Returns the size of a fixed size column, 0 if not a fixed size column. @return fixed size, or 0 */ UNIV_INLINE ulint dict_col_get_fixed_size( /*====================*/ const dict_col_t* col, /*!< in: column */ ulint comp) /*!< in: nonzero=ROW_FORMAT=COMPACT */ { return(dtype_get_fixed_size_low(col->mtype, col->prtype, col->len, col->mbminlen, col->mbmaxlen, comp)); } /***********************************************************************//** Returns the ROW_FORMAT=REDUNDANT stored SQL NULL size of a column. For fixed length types it is the fixed length of the type, otherwise 0. @return SQL null storage size in ROW_FORMAT=REDUNDANT */ UNIV_INLINE ulint dict_col_get_sql_null_size( /*=======================*/ const dict_col_t* col, /*!< in: column */ ulint comp) /*!< in: nonzero=ROW_FORMAT=COMPACT */ { return(dict_col_get_fixed_size(col, comp)); } /*********************************************************************//** Gets the column number. @return col->ind, table column position (starting from 0) */ UNIV_INLINE ulint dict_col_get_no( /*============*/ const dict_col_t* col) /*!< in: column */ { ut_ad(col); return(col->ind); } /*********************************************************************//** Gets the column position in the clustered index. */ UNIV_INLINE ulint dict_col_get_clust_pos( /*===================*/ const dict_col_t* col, /*!< in: table column */ const dict_index_t* clust_index) /*!< in: clustered index */ { ulint i; ut_ad(col); ut_ad(clust_index); ut_ad(dict_index_is_clust(clust_index)); for (i = 0; i < clust_index->n_def; i++) { const dict_field_t* field = &clust_index->fields[i]; if (!field->prefix_len && field->col == col) { return(i); } } return(ULINT_UNDEFINED); } #ifndef UNIV_HOTBACKUP #ifdef UNIV_DEBUG /********************************************************************//** Gets the first index on the table (the clustered index). @return index, NULL if none exists */ UNIV_INLINE dict_index_t* dict_table_get_first_index( /*=======================*/ const dict_table_t* table) /*!< in: table */ { ut_ad(table); ut_ad(table->magic_n == DICT_TABLE_MAGIC_N); return(UT_LIST_GET_FIRST(((dict_table_t*) table)->indexes)); } /********************************************************************//** Gets the next index on the table. @return index, NULL if none left */ UNIV_INLINE dict_index_t* dict_table_get_next_index( /*======================*/ const dict_index_t* index) /*!< in: index */ { ut_ad(index); ut_ad(index->magic_n == DICT_INDEX_MAGIC_N); return(UT_LIST_GET_NEXT(indexes, (dict_index_t*) index)); } #endif /* UNIV_DEBUG */ #endif /* !UNIV_HOTBACKUP */ /********************************************************************//** Check whether the index is the clustered index. @return nonzero for clustered index, zero for other indexes */ UNIV_INLINE ulint dict_index_is_clust( /*================*/ const dict_index_t* dict_index) /*!< in: dict_index */ { ut_ad(dict_index); ut_ad(dict_index->magic_n == DICT_INDEX_MAGIC_N); return(UNIV_UNLIKELY(dict_index->type & DICT_CLUSTERED)); } /********************************************************************//** Check whether the index is unique. @return nonzero for unique index, zero for other indexes */ UNIV_INLINE ulint dict_index_is_unique( /*=================*/ const dict_index_t* dict_index) /*!< in: dict_index */ { ut_ad(dict_index); ut_ad(dict_index->magic_n == DICT_INDEX_MAGIC_N); return(UNIV_UNLIKELY(dict_index->type & DICT_UNIQUE)); } /********************************************************************//** Check whether the index is the insert buffer tree. @return nonzero for insert buffer, zero for other indexes */ UNIV_INLINE ulint dict_index_is_ibuf( /*===============*/ const dict_index_t* dict_index) /*!< in: dict_index */ { ut_ad(dict_index); ut_ad(dict_index->magic_n == DICT_INDEX_MAGIC_N); return(UNIV_UNLIKELY(dict_index->type & DICT_IBUF)); } /********************************************************************//** Check whether the index is a secondary index or the insert buffer tree. @return nonzero for insert buffer, zero for other indexes */ UNIV_INLINE ulint dict_index_is_sec_or_ibuf( /*======================*/ const dict_index_t* dict_index) /*!< in: dict_index */ { ulint type; ut_ad(dict_index); ut_ad(dict_index->magic_n == DICT_INDEX_MAGIC_N); type = dict_index->type; return(UNIV_LIKELY(!(type & DICT_CLUSTERED) || (type & DICT_IBUF))); } /********************************************************************//** Gets the number of user-defined columns in a table in the dictionary cache. @return number of user-defined (e.g., not ROW_ID) columns of a table */ UNIV_INLINE ulint dict_table_get_n_user_cols( /*=======================*/ const dict_table_t* table) /*!< in: table */ { ut_ad(table); ut_ad(table->magic_n == DICT_TABLE_MAGIC_N); return(table->n_cols - DATA_N_SYS_COLS); } /********************************************************************//** Gets the number of system columns in a table in the dictionary cache. @return number of system (e.g., ROW_ID) columns of a table */ UNIV_INLINE ulint dict_table_get_n_sys_cols( /*======================*/ const dict_table_t* table __attribute__((unused))) /*!< in: table */ { ut_ad(table); ut_ad(table->magic_n == DICT_TABLE_MAGIC_N); ut_ad(table->cached); return(DATA_N_SYS_COLS); } /********************************************************************//** Gets the number of all columns (also system) in a table in the dictionary cache. @return number of columns of a table */ UNIV_INLINE ulint dict_table_get_n_cols( /*==================*/ const dict_table_t* table) /*!< in: table */ { ut_ad(table); ut_ad(table->magic_n == DICT_TABLE_MAGIC_N); return(table->n_cols); } #ifdef UNIV_DEBUG /********************************************************************//** Gets the nth column of a table. @return pointer to column object */ UNIV_INLINE dict_col_t* dict_table_get_nth_col( /*===================*/ const dict_table_t* table, /*!< in: table */ ulint pos) /*!< in: position of column */ { ut_ad(table); ut_ad(pos < table->n_def); ut_ad(table->magic_n == DICT_TABLE_MAGIC_N); return((dict_col_t*) (table->cols) + pos); } /********************************************************************//** Gets the given system column of a table. @return pointer to column object */ UNIV_INLINE dict_col_t* dict_table_get_sys_col( /*===================*/ const dict_table_t* table, /*!< in: table */ ulint sys) /*!< in: DATA_ROW_ID, ... */ { dict_col_t* col; ut_ad(table); ut_ad(sys < DATA_N_SYS_COLS); ut_ad(table->magic_n == DICT_TABLE_MAGIC_N); col = dict_table_get_nth_col(table, table->n_cols - DATA_N_SYS_COLS + sys); ut_ad(col->mtype == DATA_SYS); ut_ad(col->prtype == (sys | DATA_NOT_NULL)); return(col); } #endif /* UNIV_DEBUG */ /********************************************************************//** Gets the given system column number of a table. @return column number */ UNIV_INLINE ulint dict_table_get_sys_col_no( /*======================*/ const dict_table_t* table, /*!< in: table */ ulint sys) /*!< in: DATA_ROW_ID, ... */ { ut_ad(table); ut_ad(sys < DATA_N_SYS_COLS); ut_ad(table->magic_n == DICT_TABLE_MAGIC_N); return(table->n_cols - DATA_N_SYS_COLS + sys); } /********************************************************************//** Check whether the table uses the compact page format. @return TRUE if table uses the compact page format */ UNIV_INLINE ibool dict_table_is_comp( /*===============*/ const dict_table_t* table) /*!< in: table */ { ut_ad(table); #if DICT_TF_COMPACT != TRUE #error #endif return(UNIV_LIKELY(table->flags & DICT_TF_COMPACT)); } /********************************************************************//** Determine the file format of a table. @return file format version */ UNIV_INLINE ulint dict_table_get_format( /*==================*/ const dict_table_t* table) /*!< in: table */ { ut_ad(table); return((table->flags & DICT_TF_FORMAT_MASK) >> DICT_TF_FORMAT_SHIFT); } /********************************************************************//** Determine the file format of a table. */ UNIV_INLINE void dict_table_set_format( /*==================*/ dict_table_t* table, /*!< in/out: table */ ulint format) /*!< in: file format version */ { ut_ad(table); table->flags = (table->flags & ~DICT_TF_FORMAT_MASK) | (format << DICT_TF_FORMAT_SHIFT); } /********************************************************************//** Extract the compressed page size from table flags. @return compressed page size, or 0 if not compressed */ UNIV_INLINE ulint dict_table_flags_to_zip_size( /*=========================*/ ulint flags) /*!< in: flags */ { ulint zip_size = flags & DICT_TF_ZSSIZE_MASK; if (UNIV_UNLIKELY(zip_size)) { zip_size = ((PAGE_ZIP_MIN_SIZE >> 1) << (zip_size >> DICT_TF_ZSSIZE_SHIFT)); ut_ad(zip_size <= UNIV_PAGE_SIZE); } return(zip_size); } /********************************************************************//** Check whether the table uses the compressed compact page format. @return compressed page size, or 0 if not compressed */ UNIV_INLINE ulint dict_table_zip_size( /*================*/ const dict_table_t* table) /*!< in: table */ { ut_ad(table); return(dict_table_flags_to_zip_size(table->flags)); } /********************************************************************//** Gets the number of fields in the internal representation of an index, including fields added by the dictionary system. @return number of fields */ UNIV_INLINE ulint dict_index_get_n_fields( /*====================*/ const dict_index_t* dict_index) /*!< in: an internal representation of index (in the dictionary cache) */ { ut_ad(dict_index); ut_ad(dict_index->magic_n == DICT_INDEX_MAGIC_N); return(dict_index->n_fields); } /********************************************************************//** Gets the number of fields in the internal representation of an index that uniquely determine the position of an index entry in the index, if we do not take multiversioning into account: in the B-tree use the value returned by dict_index_get_n_unique_in_tree. @return number of fields */ UNIV_INLINE ulint dict_index_get_n_unique( /*====================*/ const dict_index_t* dict_index) /*!< in: an internal representation of index (in the dictionary cache) */ { ut_ad(dict_index); ut_ad(dict_index->magic_n == DICT_INDEX_MAGIC_N); ut_ad(index->cached); return(dict_index->n_uniq); } /********************************************************************//** Gets the number of fields in the internal representation of an index which uniquely determine the position of an index entry in the index, if we also take multiversioning into account. @return number of fields */ UNIV_INLINE ulint dict_index_get_n_unique_in_tree( /*============================*/ const dict_index_t* dict_index) /*!< in: an internal representation of index (in the dictionary cache) */ { ut_ad(dict_index); ut_ad(dict_index->magic_n == DICT_INDEX_MAGIC_N); ut_ad(dict_index->cached); if (dict_index_is_clust(dict_index)) { return(dict_index_get_n_unique(dict_index)); } return(dict_index_get_n_fields(dict_index)); } /********************************************************************//** Gets the number of user-defined ordering fields in the index. In the internal representation of clustered indexes we add the row id to the ordering fields to make a clustered index unique, but this function returns the number of fields the user defined in the index as ordering fields. @return number of fields */ UNIV_INLINE ulint dict_index_get_n_ordering_defined_by_user( /*======================================*/ const dict_index_t* dict_index) /*!< in: an internal representation of index (in the dictionary cache) */ { return(dict_index->n_user_defined_cols); } #ifdef UNIV_DEBUG /********************************************************************//** Gets the nth field of an index. @return pointer to field object */ UNIV_INLINE dict_field_t* dict_index_get_nth_field( /*=====================*/ const dict_index_t* index, /*!< in: index */ ulint pos) /*!< in: position of field */ { ut_ad(index); ut_ad(pos < index->n_def); ut_ad(index->magic_n == DICT_INDEX_MAGIC_N); return((dict_field_t*) (index->fields) + pos); } #endif /* UNIV_DEBUG */ /********************************************************************//** Returns the position of a system column in an index. @return position, ULINT_UNDEFINED if not contained */ UNIV_INLINE ulint dict_index_get_sys_col_pos( /*=======================*/ const dict_index_t* dict_index, /*!< in: index */ ulint type) /*!< in: DATA_ROW_ID, ... */ { ut_ad(dict_index); ut_ad(dict_index->magic_n == DICT_INDEX_MAGIC_N); ut_ad(!(dict_index->type & DICT_UNIVERSAL)); if (dict_index_is_clust(dict_index)) { return(dict_col_get_clust_pos( dict_table_get_sys_col(dict_index->table, type), dict_index)); } return(dict_index_get_nth_col_pos( dict_index, dict_table_get_sys_col_no(dict_index->table, type))); } /*********************************************************************//** Gets the field column. @return field->col, pointer to the table column */ UNIV_INLINE const dict_col_t* dict_field_get_col( /*===============*/ const dict_field_t* field) /*!< in: index field */ { ut_ad(field); return(field->col); } /********************************************************************//** Gets pointer to the nth column in an index. @return column */ UNIV_INLINE const dict_col_t* dict_index_get_nth_col( /*===================*/ const dict_index_t* dict_index, /*!< in: index */ ulint pos) /*!< in: position of the field */ { return(dict_field_get_col(dict_index_get_nth_field(dict_index, pos))); } /********************************************************************//** Gets the column number the nth field in an index. @return column number */ UNIV_INLINE ulint dict_index_get_nth_col_no( /*======================*/ const dict_index_t* dict_index, /*!< in: index */ ulint pos) /*!< in: position of the field */ { return(dict_col_get_no(dict_index_get_nth_col(dict_index, pos))); } #ifndef UNIV_HOTBACKUP /********************************************************************//** Returns the minimum data size of an index record. @return minimum data size in bytes */ UNIV_INLINE ulint dict_index_get_min_size( /*====================*/ const dict_index_t* dict_index) /*!< in: dict_index */ { ulint n = dict_index_get_n_fields(dict_index); ulint size = 0; while (n--) { size += dict_col_get_min_size(dict_index_get_nth_col(dict_index, n)); } return(size); } /*********************************************************************//** Gets the space id of the root of the index tree. @return space id */ UNIV_INLINE ulint dict_index_get_space( /*=================*/ const dict_index_t* dict_index) /*!< in: dict_index */ { ut_ad(dict_index); ut_ad(dict_index->magic_n == DICT_INDEX_MAGIC_N); return(dict_index->space); } /*********************************************************************//** Sets the space id of the root of the index tree. */ UNIV_INLINE void dict_index_set_space( /*=================*/ dict_index_t* dict_index, /*!< in/out: dict_index */ ulint space) /*!< in: space id */ { ut_ad(dict_index); ut_ad(dict_index->magic_n == DICT_INDEX_MAGIC_N); dict_index->space = space; } /*********************************************************************//** Gets the page number of the root of the index tree. @return page number */ UNIV_INLINE ulint dict_index_get_page( /*================*/ const dict_index_t* dict_index) /*!< in: dict_index */ { ut_ad(dict_index); ut_ad(dict_index->magic_n == DICT_INDEX_MAGIC_N); return(dict_index->page); } /*********************************************************************//** Sets the page number of the root of index tree. */ UNIV_INLINE void dict_index_set_page( /*================*/ dict_index_t* dict_index, /*!< in/out: dict_index */ ulint page) /*!< in: page number */ { ut_ad(dict_index); ut_ad(dict_index->magic_n == DICT_INDEX_MAGIC_N); dict_index->page = page; } /*********************************************************************//** Gets the read-write lock of the index tree. @return read-write lock */ UNIV_INLINE rw_lock_t* dict_index_get_lock( /*================*/ dict_index_t* dict_index) /*!< in: dict_index */ { ut_ad(dict_index); ut_ad(dict_index->magic_n == DICT_INDEX_MAGIC_N); return(&(dict_index->lock)); } /********************************************************************//** Returns free space reserved for future updates of records. This is relevant only in the case of many consecutive inserts, as updates which make the records bigger might fragment the index. @return number of free bytes on page, reserved for updates */ UNIV_INLINE ulint dict_index_get_space_reserve(void) /*==============================*/ { return(UNIV_PAGE_SIZE / 16); } /**********************************************************************//** Checks if a table is in the dictionary cache. @return table, NULL if not found */ UNIV_INLINE dict_table_t* dict_table_check_if_in_cache_low( /*=============================*/ const char* table_name) /*!< in: table name */ { dict_table_t* table; ulint table_fold; ut_ad(table_name); ut_ad(mutex_own(&(dict_sys->mutex))); /* Look for the table name in the hash table */ table_fold = ut_fold_string(table_name); HASH_SEARCH(name_hash, dict_sys->table_hash, table_fold, dict_table_t*, table, ut_ad(table->cached), !strcmp(table->name, table_name)); return(table); } /**********************************************************************//** Gets a table; loads it to the dictionary cache if necessary. A low-level function. @return table, NULL if not found */ UNIV_INLINE dict_table_t* dict_table_get_low( /*===============*/ const char* table_name) /*!< in: table name */ { dict_table_t* table; ut_ad(table_name); ut_ad(mutex_own(&(dict_sys->mutex))); table = dict_table_check_if_in_cache_low(table_name); if (table == NULL) { // FIXME: srv_force_recovery should be passed in as an arg table = dict_load_table(srv_force_recovery, table_name); } ut_ad(!table || table->cached); return(table); } /**********************************************************************//** Returns a table object based on table id. @return table, NULL if does not exist */ UNIV_INLINE dict_table_t* dict_table_get_on_id_low( /*=====================*/ ib_recovery_t recovery,/*!< in: recovery flag */ dulint table_id) /*!< in: table id */ { dict_table_t* table; ulint fold; ut_ad(mutex_own(&(dict_sys->mutex))); /* Look for the table name in the hash table */ fold = ut_fold_dulint(table_id); HASH_SEARCH(id_hash, dict_sys->table_id_hash, fold, dict_table_t*, table, ut_ad(table->cached), !ut_dulint_cmp(table->id, table_id)); if (table == NULL) { table = dict_load_table_on_id(recovery, table_id); } ut_ad(!table || table->cached); return(table); } #endif /* !UNIV_HOTBACKUP */ haildb-2.3.2/include/btr0pcur.ic0000644000175000017500000004664711513177357017365 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2010, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/btr0pcur.ic The index tree persistent cursor Created 2/23/1996 Heikki Tuuri *******************************************************/ /*********************************************************//** Gets the rel_pos field for a cursor whose position has been stored. @return BTR_PCUR_ON, ... */ UNIV_INLINE ulint btr_pcur_get_rel_pos( /*=================*/ const btr_pcur_t* cursor) /*!< in: persistent cursor */ { ut_ad(cursor); ut_ad(cursor->old_rec); ut_ad(cursor->old_stored == BTR_PCUR_OLD_STORED); ut_ad(cursor->pos_state == BTR_PCUR_WAS_POSITIONED || cursor->pos_state == BTR_PCUR_IS_POSITIONED); return(cursor->rel_pos); } /*********************************************************//** Sets the mtr field for a pcur. */ UNIV_INLINE void btr_pcur_set_mtr( /*=============*/ btr_pcur_t* cursor, /*!< in: persistent cursor */ mtr_t* mtr) /*!< in, own: mtr */ { ut_ad(cursor); cursor->mtr = mtr; } /*********************************************************//** Gets the mtr field for a pcur. @return mtr */ UNIV_INLINE mtr_t* btr_pcur_get_mtr( /*=============*/ btr_pcur_t* cursor) /*!< in: persistent cursor */ { ut_ad(cursor); return(cursor->mtr); } #ifdef UNIV_DEBUG /*********************************************************//** Returns the btr cursor component of a persistent cursor. @return pointer to btr cursor component */ UNIV_INLINE btr_cur_t* btr_pcur_get_btr_cur( /*=================*/ const btr_pcur_t* cursor) /*!< in: persistent cursor */ { const btr_cur_t* btr_cur = &cursor->btr_cur; return((btr_cur_t*) btr_cur); } /*********************************************************//** Returns the page cursor component of a persistent cursor. @return pointer to page cursor component */ UNIV_INLINE page_cur_t* btr_pcur_get_page_cur( /*==================*/ const btr_pcur_t* cursor) /*!< in: persistent cursor */ { return(btr_cur_get_page_cur(btr_pcur_get_btr_cur(cursor))); } #endif /* UNIV_DEBUG */ /*********************************************************//** Returns the page of a persistent cursor. @return pointer to the page */ UNIV_INLINE page_t* btr_pcur_get_page( /*==============*/ btr_pcur_t* cursor) /*!< in: persistent cursor */ { ut_ad(cursor->pos_state == BTR_PCUR_IS_POSITIONED); return(btr_cur_get_page(btr_pcur_get_btr_cur(cursor))); } /*********************************************************//** Returns the buffer block of a persistent cursor. @return pointer to the block */ UNIV_INLINE buf_block_t* btr_pcur_get_block( /*===============*/ btr_pcur_t* cursor) /*!< in: persistent cursor */ { ut_ad(cursor->pos_state == BTR_PCUR_IS_POSITIONED); return(btr_cur_get_block(btr_pcur_get_btr_cur(cursor))); } /*********************************************************//** Returns the record of a persistent cursor. @return pointer to the record */ UNIV_INLINE rec_t* btr_pcur_get_rec( /*=============*/ btr_pcur_t* cursor) /*!< in: persistent cursor */ { ut_ad(cursor->pos_state == BTR_PCUR_IS_POSITIONED); ut_ad(cursor->latch_mode != BTR_NO_LATCHES); return(btr_cur_get_rec(btr_pcur_get_btr_cur(cursor))); } /**************************************************************//** Gets the up_match value for a pcur after a search. @return number of matched fields at the cursor or to the right if search mode was PAGE_CUR_GE, otherwise undefined */ UNIV_INLINE ulint btr_pcur_get_up_match( /*==================*/ btr_pcur_t* cursor) /*!< in: memory buffer for persistent cursor */ { btr_cur_t* btr_cursor; ut_ad((cursor->pos_state == BTR_PCUR_WAS_POSITIONED) || (cursor->pos_state == BTR_PCUR_IS_POSITIONED)); btr_cursor = btr_pcur_get_btr_cur(cursor); ut_ad(btr_cursor->up_match != ULINT_UNDEFINED); return(btr_cursor->up_match); } /**************************************************************//** Gets the low_match value for a pcur after a search. @return number of matched fields at the cursor or to the right if search mode was PAGE_CUR_LE, otherwise undefined */ UNIV_INLINE ulint btr_pcur_get_low_match( /*===================*/ btr_pcur_t* cursor) /*!< in: memory buffer for persistent cursor */ { btr_cur_t* btr_cursor; ut_ad((cursor->pos_state == BTR_PCUR_WAS_POSITIONED) || (cursor->pos_state == BTR_PCUR_IS_POSITIONED)); btr_cursor = btr_pcur_get_btr_cur(cursor); ut_ad(btr_cursor->low_match != ULINT_UNDEFINED); return(btr_cursor->low_match); } /*********************************************************//** Checks if the persistent cursor is after the last user record on a page. */ UNIV_INLINE ibool btr_pcur_is_after_last_on_page( /*===========================*/ const btr_pcur_t* cursor) /*!< in: persistent cursor */ { ut_ad(cursor->pos_state == BTR_PCUR_IS_POSITIONED); ut_ad(cursor->latch_mode != BTR_NO_LATCHES); return(page_cur_is_after_last(btr_pcur_get_page_cur(cursor))); } /*********************************************************//** Checks if the persistent cursor is before the first user record on a page. */ UNIV_INLINE ibool btr_pcur_is_before_first_on_page( /*=============================*/ const btr_pcur_t* cursor) /*!< in: persistent cursor */ { ut_ad(cursor->pos_state == BTR_PCUR_IS_POSITIONED); ut_ad(cursor->latch_mode != BTR_NO_LATCHES); return(page_cur_is_before_first(btr_pcur_get_page_cur(cursor))); } /*********************************************************//** Checks if the persistent cursor is on a user record. */ UNIV_INLINE ibool btr_pcur_is_on_user_rec( /*====================*/ const btr_pcur_t* cursor) /*!< in: persistent cursor */ { ut_ad(cursor->pos_state == BTR_PCUR_IS_POSITIONED); ut_ad(cursor->latch_mode != BTR_NO_LATCHES); if (btr_pcur_is_before_first_on_page(cursor) || btr_pcur_is_after_last_on_page(cursor)) { return(FALSE); } return(TRUE); } /*********************************************************//** Checks if the persistent cursor is before the first user record in the index tree. */ UNIV_INLINE ibool btr_pcur_is_before_first_in_tree( /*=============================*/ btr_pcur_t* cursor, /*!< in: persistent cursor */ mtr_t* mtr) /*!< in: mtr */ { ut_ad(cursor->pos_state == BTR_PCUR_IS_POSITIONED); ut_ad(cursor->latch_mode != BTR_NO_LATCHES); if (btr_page_get_prev(btr_pcur_get_page(cursor), mtr) != FIL_NULL) { return(FALSE); } return(page_cur_is_before_first(btr_pcur_get_page_cur(cursor))); } /*********************************************************//** Checks if the persistent cursor is after the last user record in the index tree. */ UNIV_INLINE ibool btr_pcur_is_after_last_in_tree( /*===========================*/ btr_pcur_t* cursor, /*!< in: persistent cursor */ mtr_t* mtr) /*!< in: mtr */ { ut_ad(cursor->pos_state == BTR_PCUR_IS_POSITIONED); ut_ad(cursor->latch_mode != BTR_NO_LATCHES); if (btr_page_get_next(btr_pcur_get_page(cursor), mtr) != FIL_NULL) { return(FALSE); } return(page_cur_is_after_last(btr_pcur_get_page_cur(cursor))); } /*********************************************************//** Moves the persistent cursor to the next record on the same page. */ UNIV_INLINE void btr_pcur_move_to_next_on_page( /*==========================*/ btr_pcur_t* cursor) /*!< in/out: persistent cursor */ { ut_ad(cursor->pos_state == BTR_PCUR_IS_POSITIONED); ut_ad(cursor->latch_mode != BTR_NO_LATCHES); page_cur_move_to_next(btr_pcur_get_page_cur(cursor)); cursor->old_stored = BTR_PCUR_OLD_NOT_STORED; } /*********************************************************//** Moves the persistent cursor to the previous record on the same page. */ UNIV_INLINE void btr_pcur_move_to_prev_on_page( /*==========================*/ btr_pcur_t* cursor) /*!< in/out: persistent cursor */ { ut_ad(cursor->pos_state == BTR_PCUR_IS_POSITIONED); ut_ad(cursor->latch_mode != BTR_NO_LATCHES); page_cur_move_to_prev(btr_pcur_get_page_cur(cursor)); cursor->old_stored = BTR_PCUR_OLD_NOT_STORED; } /*********************************************************//** Moves the persistent cursor to the last record on the same page. */ UNIV_INLINE void btr_pcur_move_to_last_on_page( /*==========================*/ btr_pcur_t* cursor, /*!< in: persistent cursor */ mtr_t* mtr) /*!< in: mtr */ { UT_NOT_USED(mtr); ut_ad(cursor->latch_mode != BTR_NO_LATCHES); page_cur_set_after_last(btr_pcur_get_block(cursor), btr_pcur_get_page_cur(cursor)); cursor->old_stored = BTR_PCUR_OLD_NOT_STORED; } /*********************************************************//** Moves the persistent cursor to the next user record in the tree. If no user records are left, the cursor ends up 'after last in tree'. @return TRUE if the cursor moved forward, ending on a user record */ UNIV_INLINE ibool btr_pcur_move_to_next_user_rec( /*===========================*/ btr_pcur_t* cursor, /*!< in: persistent cursor; NOTE that the function may release the page latch */ mtr_t* mtr) /*!< in: mtr */ { ut_ad(cursor->pos_state == BTR_PCUR_IS_POSITIONED); ut_ad(cursor->latch_mode != BTR_NO_LATCHES); cursor->old_stored = BTR_PCUR_OLD_NOT_STORED; loop: if (btr_pcur_is_after_last_on_page(cursor)) { if (btr_pcur_is_after_last_in_tree(cursor, mtr)) { return(FALSE); } btr_pcur_move_to_next_page(cursor, mtr); } else { btr_pcur_move_to_next_on_page(cursor); } if (btr_pcur_is_on_user_rec(cursor)) { return(TRUE); } goto loop; } /*********************************************************//** Moves the persistent cursor to the prev user record in the tree. If no user records are left, the cursor ends up 'before first in tree'. @return TRUE if the cursor moved backward, ending on a user record */ UNIV_INLINE ibool btr_pcur_move_to_prev_user_rec( /*===========================*/ btr_pcur_t* cursor, /*!< in: persistent cursor; NOTE that the function may release the page latch */ mtr_t* mtr) /*!< in: mtr */ { ut_ad(cursor->pos_state == BTR_PCUR_IS_POSITIONED); ut_ad(cursor->latch_mode != BTR_NO_LATCHES); cursor->old_stored = BTR_PCUR_OLD_NOT_STORED; loop: if (btr_pcur_is_before_first_on_page(cursor)) { if (btr_pcur_is_before_first_in_tree(cursor, mtr)) { return(FALSE); } btr_pcur_move_backward_from_page(cursor, mtr); } else { btr_pcur_move_to_prev_on_page(cursor); } if (btr_pcur_is_on_user_rec(cursor)) { return(TRUE); } goto loop; } /************************************************************* Moves the persistent cursor to the next record in the tree. If no records are left, the cursor stays 'after last in tree'. @return TRUE if the cursor was not after last in tree */ UNIV_INLINE ibool btr_pcur_move_to_next( /*==================*/ btr_pcur_t* cursor, /*!< in: persistent cursor; NOTE that the function may release the page latch */ mtr_t* mtr) /*!< in: mtr */ { ut_ad(cursor->pos_state == BTR_PCUR_IS_POSITIONED); ut_ad(cursor->latch_mode != BTR_NO_LATCHES); cursor->old_stored = BTR_PCUR_OLD_NOT_STORED; if (btr_pcur_is_after_last_on_page(cursor)) { if (btr_pcur_is_after_last_in_tree(cursor, mtr)) { return(FALSE); } btr_pcur_move_to_next_page(cursor, mtr); return(TRUE); } btr_pcur_move_to_next_on_page(cursor); return(TRUE); } /***********************************************************//** Moves the persistent cursor to the previous record in the tree. If no records are left, the cursor stays 'before first in tree'. @return TRUE if the cursor was not before first in tree */ UNIV_INLINE ibool btr_pcur_move_to_prev( /*==================*/ btr_pcur_t* cursor, /*!< in: persistent cursor; NOTE that the function may release the page latch */ mtr_t* mtr) /*!< in: mtr */ { ut_ad(cursor->pos_state == BTR_PCUR_IS_POSITIONED); ut_ad(cursor->latch_mode != BTR_NO_LATCHES); cursor->old_stored = BTR_PCUR_OLD_NOT_STORED; if (btr_pcur_is_before_first_on_page(cursor)) { if (btr_pcur_is_before_first_in_tree(cursor, mtr)) { return(FALSE); } btr_pcur_move_backward_from_page(cursor, mtr); return(TRUE); } btr_pcur_move_to_prev_on_page(cursor); return(TRUE); } /**************************************************************//** Commits the mtr and sets the pcur latch mode to BTR_NO_LATCHES, that is, the cursor becomes detached. If there have been modifications to the page where pcur is positioned, this can be used instead of btr_pcur_release_leaf. Function btr_pcur_store_position should be used before calling this, if restoration of cursor is wanted later. */ UNIV_INLINE void btr_pcur_commit_specify_mtr( /*========================*/ btr_pcur_t* pcur, /*!< in: persistent cursor */ mtr_t* mtr) /*!< in: mtr to commit */ { ut_a(pcur->pos_state == BTR_PCUR_IS_POSITIONED); pcur->latch_mode = BTR_NO_LATCHES; mtr_commit(mtr); pcur->pos_state = BTR_PCUR_WAS_POSITIONED; } /**************************************************************//** Sets the pcur latch mode to BTR_NO_LATCHES. */ UNIV_INLINE void btr_pcur_detach( /*============*/ btr_pcur_t* pcur) /*!< in: persistent cursor */ { ut_a(pcur->pos_state == BTR_PCUR_IS_POSITIONED); pcur->latch_mode = BTR_NO_LATCHES; pcur->pos_state = BTR_PCUR_WAS_POSITIONED; } /**************************************************************//** Tests if a cursor is detached: that is the latch mode is BTR_NO_LATCHES. @return TRUE if detached */ UNIV_INLINE ibool btr_pcur_is_detached( /*=================*/ btr_pcur_t* pcur) /*!< in: persistent cursor */ { if (pcur->latch_mode == BTR_NO_LATCHES) { return(TRUE); } return(FALSE); } /**************************************************************//** Sets the old_rec_buf field to NULL. */ UNIV_INLINE void btr_pcur_init( /*==========*/ btr_pcur_t* pcur) /*!< in: persistent cursor */ { pcur->old_stored = BTR_PCUR_OLD_NOT_STORED; pcur->old_rec_buf = NULL; pcur->old_rec = NULL; } /**************************************************************//** Initializes and opens a persistent cursor to an index tree. It should be closed with btr_pcur_close. */ UNIV_INLINE void btr_pcur_open_func( /*===============*/ dict_index_t* dict_index, /*!< in: dict_index */ const dtuple_t* tuple, /*!< in: tuple on which search done */ ulint mode, /*!< in: PAGE_CUR_L, ...; NOTE that if the search is made using a unique prefix of a record, mode should be PAGE_CUR_LE, not PAGE_CUR_GE, as the latter may end up on the previous page from the record! */ ulint latch_mode,/*!< in: BTR_SEARCH_LEAF, ... */ btr_pcur_t* cursor, /*!< in: memory buffer for persistent cursor */ const char* file, /*!< in: file name */ ulint line, /*!< in: line where called */ mtr_t* mtr) /*!< in: mtr */ { btr_cur_t* btr_cursor; /* Initialize the cursor */ btr_pcur_init(cursor); cursor->latch_mode = latch_mode; cursor->search_mode = mode; /* Search with the tree cursor */ btr_cursor = btr_pcur_get_btr_cur(cursor); btr_cur_search_to_nth_level(dict_index, 0, tuple, mode, latch_mode, btr_cursor, 0, file, line, mtr); cursor->pos_state = BTR_PCUR_IS_POSITIONED; cursor->trx_if_known = NULL; } /**************************************************************//** Opens an persistent cursor to an index tree without initializing the cursor. */ UNIV_INLINE void btr_pcur_open_with_no_init_func( /*============================*/ dict_index_t* dict_index, /*!< in: dict_index */ const dtuple_t* tuple, /*!< in: tuple on which search done */ ulint mode, /*!< in: PAGE_CUR_L, ...; NOTE that if the search is made using a unique prefix of a record, mode should be PAGE_CUR_LE, not PAGE_CUR_GE, as the latter may end up on the previous page of the record! */ ulint latch_mode,/*!< in: BTR_SEARCH_LEAF, ...; NOTE that if has_search_latch != 0 then we maybe do not acquire a latch on the cursor page, but assume that the caller uses his btr search latch to protect the record! */ btr_pcur_t* cursor, /*!< in: memory buffer for persistent cursor */ ulint has_search_latch,/*!< in: latch mode the caller currently has on btr_search_latch: RW_S_LATCH, or 0 */ const char* file, /*!< in: file name */ ulint line, /*!< in: line where called */ mtr_t* mtr) /*!< in: mtr */ { btr_cur_t* btr_cursor; cursor->latch_mode = latch_mode; cursor->search_mode = mode; /* Search with the tree cursor */ btr_cursor = btr_pcur_get_btr_cur(cursor); btr_cur_search_to_nth_level(dict_index, 0, tuple, mode, latch_mode, btr_cursor, has_search_latch, file, line, mtr); cursor->pos_state = BTR_PCUR_IS_POSITIONED; cursor->old_stored = BTR_PCUR_OLD_NOT_STORED; cursor->trx_if_known = NULL; } /*****************************************************************//** Opens a persistent cursor at either end of an index. */ UNIV_INLINE void btr_pcur_open_at_index_side( /*========================*/ ibool from_left, /*!< in: TRUE if open to the low end, FALSE if to the high end */ dict_index_t* dict_index, /*!< in: dict_index */ ulint latch_mode, /*!< in: latch mode */ btr_pcur_t* pcur, /*!< in: cursor */ ibool do_init, /*!< in: TRUE if should be initialized */ mtr_t* mtr) /*!< in: mtr */ { pcur->latch_mode = latch_mode; if (from_left) { pcur->search_mode = PAGE_CUR_G; } else { pcur->search_mode = PAGE_CUR_L; } if (do_init) { btr_pcur_init(pcur); } btr_cur_open_at_index_side(from_left, dict_index, latch_mode, btr_pcur_get_btr_cur(pcur), mtr); pcur->pos_state = BTR_PCUR_IS_POSITIONED; pcur->old_stored = BTR_PCUR_OLD_NOT_STORED; pcur->trx_if_known = NULL; } /**********************************************************************//** Positions a cursor at a randomly chosen position within a B-tree. */ UNIV_INLINE void btr_pcur_open_at_rnd_pos_func( /*==========================*/ dict_index_t* dict_index, /*!< in: dict_index */ ulint latch_mode, /*!< in: BTR_SEARCH_LEAF, ... */ btr_pcur_t* cursor, /*!< in/out: B-tree pcur */ const char* file, /*!< in: file name */ ulint line, /*!< in: line where called */ mtr_t* mtr) /*!< in: mtr */ { /* Initialize the cursor */ cursor->latch_mode = latch_mode; cursor->search_mode = PAGE_CUR_G; btr_pcur_init(cursor); btr_cur_open_at_rnd_pos_func(dict_index, latch_mode, btr_pcur_get_btr_cur(cursor), file, line, mtr); cursor->pos_state = BTR_PCUR_IS_POSITIONED; cursor->old_stored = BTR_PCUR_OLD_NOT_STORED; cursor->trx_if_known = NULL; } /**************************************************************//** Frees the possible memory heap of a persistent cursor and sets the latch mode of the persistent cursor to BTR_NO_LATCHES. */ UNIV_INLINE void btr_pcur_close( /*===========*/ btr_pcur_t* cursor) /*!< in: persistent cursor */ { if (cursor->old_rec_buf != NULL) { mem_free(cursor->old_rec_buf); cursor->old_rec = NULL; cursor->old_rec_buf = NULL; } cursor->btr_cur.page_cur.rec = NULL; cursor->btr_cur.page_cur.block = NULL; cursor->old_rec = NULL; cursor->old_stored = BTR_PCUR_OLD_NOT_STORED; cursor->latch_mode = BTR_NO_LATCHES; cursor->pos_state = BTR_PCUR_NOT_POSITIONED; cursor->trx_if_known = NULL; } haildb-2.3.2/include/buf0flu.ic0000644000175000017500000000772311513177357017157 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/buf0flu.ic The database buffer pool flush algorithm Created 11/5/1995 Heikki Tuuri *******************************************************/ #ifndef UNIV_HOTBACKUP #include "buf0buf.h" #include "mtr0mtr.h" /********************************************************************//** Inserts a modified block into the flush list. */ UNIV_INTERN void buf_flush_insert_into_flush_list( /*=============================*/ buf_block_t* block); /*!< in/out: block which is modified */ /********************************************************************//** Inserts a modified block into the flush list in the right sorted position. This function is used by recovery, because there the modifications do not necessarily come in the order of lsn's. */ UNIV_INTERN void buf_flush_insert_sorted_into_flush_list( /*====================================*/ buf_block_t* block); /*!< in/out: block which is modified */ /********************************************************************//** This function should be called at a mini-transaction commit, if a page was modified in it. Puts the block to the list of modified blocks, if it is not already in it. */ UNIV_INLINE void buf_flush_note_modification( /*========================*/ buf_block_t* block, /*!< in: block which is modified */ mtr_t* mtr) /*!< in: mtr */ { ut_ad(block); ut_ad(buf_block_get_state(block) == BUF_BLOCK_FILE_PAGE); ut_ad(block->page.buf_fix_count > 0); #ifdef UNIV_SYNC_DEBUG ut_ad(rw_lock_own(&(block->lock), RW_LOCK_EX)); #endif /* UNIV_SYNC_DEBUG */ ut_ad(buf_pool_mutex_own()); ut_ad(mtr->start_lsn != 0); ut_ad(mtr->modifications); ut_ad(block->page.newest_modification <= mtr->end_lsn); block->page.newest_modification = mtr->end_lsn; if (!block->page.oldest_modification) { block->page.oldest_modification = mtr->start_lsn; ut_ad(block->page.oldest_modification != 0); buf_flush_insert_into_flush_list(block); } else { ut_ad(block->page.oldest_modification <= mtr->start_lsn); } ++srv_buf_pool_write_requests; } /********************************************************************//** This function should be called when recovery has modified a buffer page. */ UNIV_INLINE void buf_flush_recv_note_modification( /*=============================*/ buf_block_t* block, /*!< in: block which is modified */ ib_uint64_t start_lsn, /*!< in: start lsn of the first mtr in a set of mtr's */ ib_uint64_t end_lsn) /*!< in: end lsn of the last mtr in the set of mtr's */ { ut_ad(block); ut_ad(buf_block_get_state(block) == BUF_BLOCK_FILE_PAGE); ut_ad(block->page.buf_fix_count > 0); #ifdef UNIV_SYNC_DEBUG ut_ad(rw_lock_own(&(block->lock), RW_LOCK_EX)); #endif /* UNIV_SYNC_DEBUG */ buf_pool_mutex_enter(); ut_ad(block->page.newest_modification <= end_lsn); block->page.newest_modification = end_lsn; if (!block->page.oldest_modification) { block->page.oldest_modification = start_lsn; ut_ad(block->page.oldest_modification != 0); buf_flush_insert_sorted_into_flush_list(block); } else { ut_ad(block->page.oldest_modification <= start_lsn); } buf_pool_mutex_exit(); } #endif /* !UNIV_HOTBACKUP */ haildb-2.3.2/include/eval0proc.ic0000644000175000017500000000452611513177357017505 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1998, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/eval0proc.ic Executes SQL stored procedures and their control structures Created 1/20/1998 Heikki Tuuri *******************************************************/ #include "pars0pars.h" #include "que0que.h" #include "eval0eval.h" /**********************************************************************//** Performs an execution step of a procedure node. @return query thread to run next or NULL */ UNIV_INLINE que_thr_t* proc_step( /*======*/ que_thr_t* thr) /*!< in: query thread */ { proc_node_t* node; ut_ad(thr); node = thr->run_node; ut_ad(que_node_get_type(node) == QUE_NODE_PROC); if (thr->prev_node == que_node_get_parent(node)) { /* Start execution from the first statement in the statement list */ thr->run_node = node->stat_list; } else { /* Move to the next statement */ ut_ad(que_node_get_next(thr->prev_node) == NULL); thr->run_node = NULL; } if (thr->run_node == NULL) { thr->run_node = que_node_get_parent(node); } return(thr); } /**********************************************************************//** Performs an execution step of a procedure call node. @return query thread to run next or NULL */ UNIV_INLINE que_thr_t* proc_eval_step( /*===========*/ que_thr_t* thr) /*!< in: query thread */ { func_node_t* node; ut_ad(thr); node = thr->run_node; ut_ad(que_node_get_type(node) == QUE_NODE_FUNC); /* Evaluate the procedure */ eval_exp(node); thr->run_node = que_node_get_parent(node); return(thr); } haildb-2.3.2/include/trx0purge.ic0000644000175000017500000000276011513177357017550 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/trx0purge.ic Purge old versions Created 3/26/1996 Heikki Tuuri *******************************************************/ #include "trx0undo.h" /********************************************************************//** Calculates the file address of an undo log header when we have the file address of its history list node. @return file address of the log */ UNIV_INLINE fil_addr_t trx_purge_get_log_from_hist( /*========================*/ fil_addr_t node_addr) /*!< in: file address of the history list node of the log */ { node_addr.boffset -= TRX_UNDO_HISTORY_NODE; return(node_addr); } haildb-2.3.2/include/srv0srv.h0000644000175000017500000005123211513177357017067 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2010, Innobase Oy. All Rights Reserved. Copyright (c) 2008, 2009, Google Inc. Copyright (c) 2009, Percona Inc. Portions of this file contain modifications contributed and copyrighted by Google, Inc. Those modifications are gratefully acknowledged and are described briefly in the InnoDB documentation. The contributions by Google are incorporated with their permission, and subject to the conditions contained in the file COPYING.Google. Portions of this file contain modifications contributed and copyrighted by Percona Inc.. Those modifications are gratefully acknowledged and are described briefly in the InnoDB documentation. The contributions by Percona Inc. are incorporated with their permission, and subject to the conditions contained in the file COPYING.Percona. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/srv0srv.h The server main program Created 10/10/1995 Heikki Tuuri *******************************************************/ #ifndef srv0srv_h #define srv0srv_h #include "univ.i" #ifndef UNIV_HOTBACKUP #include "sync0sync.h" #include "os0sync.h" #include "que0types.h" #include "trx0types.h" #include "api0api.h" extern const char* srv_main_thread_op_info; /* When this event is set the lock timeout and InnoDB monitor thread starts running */ extern os_event_t srv_lock_timeout_thread_event; /* If the last data file is auto-extended, we add this many pages to it at a time */ #define SRV_AUTO_EXTEND_INCREMENT \ (srv_auto_extend_increment * ((1024 * 1024) / UNIV_PAGE_SIZE)) /* FIXME: This is set to TRUE if the user has requested it. */ extern ibool srv_lower_case_table_names; /* FIXME: This is a session variable. */ extern ulint ses_lock_wait_timeout; /* FIXME: This is a session variable. */ extern ibool ses_rollback_on_timeout; /* Server parameters which are read from the initfile */ extern char* srv_data_home; extern char* srv_log_group_home_dir; #ifdef UNIV_LOG_ARCHIVE extern char* srv_arch_dir; #endif /* UNIV_LOG_ARCHIVE */ /** store to its own file each table created by an user; data dictionary tables are in the system tablespace 0 */ extern ibool srv_file_per_table; /** The file format to use on new *.ibd files. */ extern ulint srv_file_format; /** Whether to check file format during startup. A value of DICT_TF_FORMAT_MAX + 1 means no checking ie. FALSE. The default is to set it to the highest format we support. */ extern ulint srv_check_file_format_at_startup; #endif /* !UNIV_HOTBACKUP */ extern ulint srv_n_data_files; extern ulint* srv_data_file_sizes; extern ulint* srv_data_file_is_raw_partition; extern ibool srv_auto_extend_last_data_file; extern ulint srv_last_file_size_max; #ifndef UNIV_HOTBACKUP extern ulong srv_auto_extend_increment; extern ibool srv_created_new_raw; extern ulint srv_n_log_files; extern ulint srv_log_file_size; extern ulint srv_log_file_curr_size; extern ulint srv_log_buffer_size; extern ulint srv_log_buffer_curr_size; extern ulong srv_flush_log_at_trx_commit; extern ibool srv_adaptive_flushing; extern ibool srv_use_sys_malloc; extern ulint srv_buf_pool_size; /*!< requested size in bytes */ extern ulint srv_buf_pool_old_size; /*!< previously requested size */ extern ulint srv_buf_pool_curr_size; /*!< current size in bytes */ extern ulint srv_mem_pool_size; extern ulint srv_lock_table_size; extern ulint srv_n_file_io_threads; extern ulong srv_read_ahead_threshold; extern ulint srv_n_read_io_threads; extern ulint srv_n_write_io_threads; /* Number of IO operations per second the server can do */ extern ulong srv_io_capacity; /* Returns the number of IO operations that is X percent of the capacity. PCT_IO(5) -> returns the number of IO operations that is 5% of the max where max is srv_io_capacity. */ #define PCT_IO(p) ((ulong) (srv_io_capacity * ((double) p / 100.0))) #ifdef UNIV_LOG_ARCHIVE extern ibool srv_log_archive_on; extern ibool srv_archive_recovery; extern ib_uint64_t srv_archive_recovery_limit_lsn; #endif /* UNIV_LOG_ARCHIVE */ extern ulint srv_unix_file_flush_method; extern ulint srv_win_file_flush_method; extern ulint srv_max_n_open_files; extern ulint srv_max_dirty_pages_pct; extern ulint srv_force_recovery; extern ulong srv_thread_concurrency; extern ulint srv_max_n_threads; extern ulint srv_conc_n_waiting_threads; extern ib_shutdown_t srv_fast_shutdown; extern ibool srv_innodb_status; extern unsigned long long srv_stats_sample_pages; extern ibool srv_use_doublewrite_buf; extern ibool srv_use_checksums; extern ibool srv_set_thread_priorities; extern int srv_query_thread_priority; extern ulong srv_max_buf_pool_modified_pct; extern ulong srv_max_purge_lag; /*-------------------------------------------*/ extern ulint srv_n_rows_inserted; extern ulint srv_n_rows_updated; extern ulint srv_n_rows_deleted; extern ulint srv_n_rows_read; extern ibool srv_print_innodb_monitor; extern ibool srv_print_innodb_lock_monitor; extern ibool srv_print_innodb_tablespace_monitor; extern ibool srv_print_verbose_log; extern ibool srv_print_innodb_table_monitor; extern ibool srv_lock_timeout_active; extern ibool srv_monitor_active; extern ibool srv_error_monitor_active; extern ulong srv_n_spin_wait_rounds; extern ulong srv_spin_wait_delay; #ifdef UNIV_DEBUG extern ibool srv_print_thread_releases; extern ibool srv_print_lock_waits; extern ibool srv_print_buf_io; extern ibool srv_print_log_io; extern ibool srv_print_latch_waits; #else /* UNIV_DEBUG */ # define srv_print_thread_releases FALSE # define srv_print_lock_waits FALSE # define srv_print_buf_io FALSE # define srv_print_log_io FALSE # define srv_print_latch_waits FALSE #endif /* UNIV_DEBUG */ extern ulint srv_activity_count; extern ulint srv_fatal_semaphore_wait_threshold; extern ulint srv_dml_needed_delay; extern mutex_t* kernel_mutex_temp;/* mutex protecting the server, trx structs, query threads, and lock table: we allocate it from dynamic memory to get it to the same DRAM page as other hotspot semaphores */ #define kernel_mutex (*kernel_mutex_temp) #define SRV_MAX_N_IO_THREADS 130 /* the number of the log write requests done */ extern ulint srv_log_write_requests; /* the number of physical writes to the log performed */ extern ulint srv_log_writes; /* amount of data written to the log files in bytes */ extern ulint srv_os_log_written; /* amount of writes being done to the log files */ extern ulint srv_os_log_pending_writes; /* we increase this counter, when there we don't have enough space in the log buffer and have to flush it */ extern ulint srv_log_waits; /* variable that counts amount of data read in total (in bytes) */ extern ulint srv_data_read; /* here we count the amount of data written in total (in bytes) */ extern ulint srv_data_written; /* this variable counts the amount of times, when the doublewrite buffer was flushed */ extern ulint srv_dblwr_writes; /* here we store the number of pages that have been flushed to the doublewrite buffer */ extern ulint srv_dblwr_pages_written; /* in this variable we store the number of write requests issued */ extern ulint srv_buf_pool_write_requests; /* here we store the number of times when we had to wait for a free page in the buffer pool. It happens when the buffer pool is full and we need to make a flush, in order to be able to read or create a page. */ extern ulint srv_buf_pool_wait_free; /* variable to count the number of pages that were written from the buffer pool to disk */ extern ulint srv_buf_pool_flushed; /** Number of buffer pool reads that led to the reading of a disk page */ extern ulint srv_buf_pool_reads; /* In this structure we store status variables to be passed to the client. */ typedef struct export_var_struct export_struc; /** Status variables to be passed to MySQL */ extern export_struc export_vars; #endif /* !UNIV_HOTBACKUP */ /** Types of raw partitions in innodb_data_file_path */ enum { SRV_NOT_RAW = 0, /*!< Not a raw partition */ SRV_NEW_RAW, /*!< A 'newraw' partition, only to be initialized */ SRV_OLD_RAW /*!< An initialized raw partition */ }; /** Alternatives for the file flush option in Unix; see the InnoDB manual about what these mean */ enum { SRV_UNIX_FSYNC = 1, /*!< fsync, the default */ SRV_UNIX_O_DSYNC, /*!< open log files in O_SYNC mode */ SRV_UNIX_LITTLESYNC, /*!< do not call os_file_flush() when writing data files, but do flush after writing to log files */ SRV_UNIX_NOSYNC, /*!< do not flush after writing */ SRV_UNIX_O_DIRECT /*!< invoke os_file_set_nocache() on data files */ }; /** Alternatives for file i/o in Windows */ enum { SRV_WIN_IO_NORMAL = 1, /*!< buffered I/O */ SRV_WIN_IO_UNBUFFERED /*!< unbuffered I/O; this is the default */ }; /** Alternatives for srv_force_recovery. Non-zero values are intended to help the user get a damaged database up so that he can dump intact tables and rows with SELECT INTO OUTFILE. The database must not otherwise be used with these options! A bigger number below means that all precautions of lower numbers are included. NOTE: The order below is important, the code uses <= and >= to check for recovery levels. */ typedef enum ib_recovery_enum { IB_RECOVERY_DEFAULT, /*!< stop on all the errors that are listed in the other options below */ IB_RECOVERY_IGNORE_CORRUPT, /*!< let the server run even if it detects a corrupt page */ IB_RECOVERY_NO_BACKGROUND, /*!< prevent the main thread from running: if a crash would occur in purge, this prevents it */ IB_RECOVERY_NO_TRX_UNDO, /*!< do not run trx rollback after recovery */ IB_RECOVERY_NO_IBUF_MERGE, /*!< prevent also ibuf operations: if they would cause a crash, better not do them */ IB_RECOVERY_NO_UNDO_LOG_SCAN, /*!< do not look at undo logs when starting the database: InnoDB will treat even incomplete transactions as committed */ IB_RECOVERY_NO_LOG_REDO /*!< do not do the log roll-forward in connection with recovery */ } ib_recovery_t; #ifndef UNIV_HOTBACKUP /** Types of threads existing in the system. */ enum srv_thread_type { SRV_COM = 1, /**< threads serving communication and queries */ SRV_CONSOLE, /**< thread serving console */ SRV_WORKER, /**< threads serving parallelized queries and queries released from lock wait */ #if 0 /* Utility threads */ SRV_BUFFER, /**< thread flushing dirty buffer blocks */ SRV_RECOVERY, /**< threads finishing a recovery */ SRV_INSERT, /**< thread flushing the insert buffer to disk */ #endif SRV_MASTER /**< the master thread, (whose type number must be biggest) */ }; /*********************************************************************//** Boots Innobase server. @return DB_SUCCESS or error code */ UNIV_INTERN ulint srv_boot(void); /*==========*/ /*********************************************************************//** Frees the data structures created in srv_init(). */ UNIV_INTERN void srv_free(void); /*==========*/ /*********************************************************************//** Initializes the synchronization primitives, memory system, and the thread local storage. */ UNIV_INTERN void srv_general_init(void); /*==================*/ /*********************************************************************//** Gets the number of threads in the system. @return sum of srv_n_threads[] */ UNIV_INTERN ulint srv_get_n_threads(void); /*===================*/ /*********************************************************************//** Returns the calling thread type. @return SRV_COM, ... */ enum srv_thread_type srv_get_thread_type(void); /*=====================*/ /*********************************************************************//** Releases threads of the type given from suspension in the thread table. NOTE! The server mutex has to be reserved by the caller! @return number of threads released: this may be less than n if not enough threads were suspended at the moment */ UNIV_INTERN ulint srv_release_threads( /*================*/ enum srv_thread_type type, /*!< in: thread type */ ulint n); /*!< in: number of threads to release */ /*********************************************************************//** The master thread controlling the server. @return a dummy parameter */ UNIV_INTERN os_thread_ret_t srv_master_thread( /*==============*/ void* arg); /*!< in: a dummy parameter required by os_thread_create */ /*******************************************************************//** Tells the Innobase server that there has been activity in the database and wakes up the master thread if it is suspended (not sleeping). Used in the client interface. Note that there is a small chance that the master thread stays suspended (we do not protect our operation with the kernel mutex, for performace reasons). */ UNIV_INTERN void srv_active_wake_master_thread(void); /*===============================*/ /*******************************************************************//** Wakes up the master thread if it is suspended or being suspended. */ UNIV_INTERN void srv_wake_master_thread(void); /*========================*/ /*********************************************************************//** Puts an OS thread to wait if there are too many concurrent threads (>= srv_thread_concurrency) inside InnoDB. The threads wait in a FIFO queue. */ UNIV_INTERN void srv_conc_enter_innodb( /*==================*/ trx_t* trx); /*!< in: transaction object associated with the thread */ /*********************************************************************//** This must be called when a thread exits InnoDB in a lock wait or at the end of an SQL statement. */ UNIV_INTERN void srv_conc_force_exit_innodb( /*=======================*/ trx_t* trx); /*!< in: transaction object associated with the thread */ /***************************************************************//** Puts a user OS thread to wait for a lock to be released. If an error occurs during the wait trx->error_state associated with thr is != DB_SUCCESS when we return. DB_LOCK_WAIT_TIMEOUT and DB_DEADLOCK are possible errors. DB_DEADLOCK is returned if selective deadlock resolution chose this transaction as a victim. */ UNIV_INTERN void srv_suspend_user_thread( /*====================*/ que_thr_t* thr); /*!< in: query thread associated with the client OS thread */ /********************************************************************//** Releases a user OS thread waiting for a lock to be released, if the thread is already suspended. */ UNIV_INTERN void srv_release_user_thread_if_suspended( /*=================================*/ que_thr_t* thr); /*!< in: query thread associated with the client OS thread */ /*********************************************************************//** A thread which wakes up threads whose lock wait may have lasted too long. @return a dummy parameter */ UNIV_INTERN os_thread_ret_t srv_lock_timeout_thread( /*====================*/ void* arg); /*!< in: a dummy parameter required by os_thread_create */ /*********************************************************************//** A thread which prints the info output by various InnoDB monitors. @return a dummy parameter */ UNIV_INTERN os_thread_ret_t srv_monitor_thread( /*===============*/ void* arg); /*!< in: a dummy parameter required by os_thread_create */ /************************************************************************* A thread which prints warnings about semaphore waits which have lasted too long. These can be used to track bugs which cause hangs. @return a dummy parameter */ UNIV_INTERN os_thread_ret_t srv_error_monitor_thread( /*=====================*/ void* arg); /*!< in: a dummy parameter required by os_thread_create */ /******************************************************************//** Outputs to a file the output of the InnoDB Monitor. @return FALSE if not all information printed due to failure to obtain necessary mutex */ UNIV_INTERN ibool srv_printf_innodb_monitor( /*======================*/ ib_stream_t ib_stream, /*!< in: output stream */ ibool nowait, /*!< in: whether to wait for kernel mutex */ ulint* trx_start, /*!< out: file position of the start of the list of active transactions */ ulint* trx_end); /*!< out: file position of the end of the list of active transactions */ /******************************************************************//** Function to pass InnoDB status variables to client */ UNIV_INTERN void srv_export_innodb_status(void); /*==========================*/ /***********************************************************************//** Reset variables. */ UNIV_INTERN void srv_var_init(void); /*===============*/ /***********************************************************************//** Resets the variables of all the InnoDB modules. */ UNIV_INTERN void srv_modules_var_init(void); /*======================*/ /* In this structure we store status variables to be passed to the client. */ struct export_var_struct{ ulint innodb_data_pending_reads; /*!< Pending reads */ ulint innodb_data_pending_writes; /*!< Pending writes */ ulint innodb_data_pending_fsyncs; /*!< Pending fsyncs */ ulint innodb_data_fsyncs; /*!< Number of fsyncs so far */ ulint innodb_data_read; /*!< Data bytes read */ ulint innodb_data_writes; /*!< I/O write requests */ ulint innodb_data_written; /*!< Data bytes written */ ulint innodb_data_reads; /*!< I/O read requests */ ulint innodb_buffer_pool_pages_total; /*!< Buffer pool size */ ulint innodb_buffer_pool_pages_data; /*!< Data pages */ ulint innodb_buffer_pool_pages_dirty; /*!< Dirty data pages */ ulint innodb_buffer_pool_pages_misc; /*!< Miscellanous pages */ ulint innodb_buffer_pool_pages_free; /*!< Free pages */ #ifdef UNIV_DEBUG ulint innodb_buffer_pool_pages_latched; /*!< Latched pages */ #endif /* UNIV_DEBUG */ ulint innodb_buffer_pool_read_requests; /*!< buf_pool->stat.n_page_gets */ ulint innodb_buffer_pool_reads; /*!< srv_buf_pool_reads */ ulint innodb_buffer_pool_wait_free; /*!< srv_buf_pool_wait_free */ ulint innodb_buffer_pool_pages_flushed; /*!< srv_buf_pool_flushed */ ulint innodb_buffer_pool_write_requests;/*!< srv_buf_pool_write_requests */ ulint innodb_buffer_pool_read_ahead; /*!< srv_read_ahead */ ulint innodb_buffer_pool_read_ahead_evicted;/*!< srv_read_ahead evicted*/ ulint innodb_dblwr_pages_written; /*!< srv_dblwr_pages_written */ ulint innodb_dblwr_writes; /*!< srv_dblwr_writes */ ibool innodb_have_atomic_builtins; /*!< HAVE_ATOMIC_BUILTINS */ ulint innodb_log_waits; /*!< srv_log_waits */ ulint innodb_log_write_requests; /*!< srv_log_write_requests */ ulint innodb_log_writes; /*!< srv_log_writes */ ulint innodb_os_log_written; /*!< srv_os_log_written */ ulint innodb_os_log_fsyncs; /*!< fil_n_log_flushes */ ulint innodb_os_log_pending_writes; /*!< srv_os_log_pending_writes */ ulint innodb_os_log_pending_fsyncs; /*!< fil_n_pending_log_flushes */ ulint innodb_page_size; /*!< UNIV_PAGE_SIZE */ ulint innodb_pages_created; /*!< buf_pool->stat.n_pages_created */ ulint innodb_pages_read; /*!< buf_pool->stat.n_pages_read */ ulint innodb_pages_written; /*!< buf_pool->stat.n_pages_written */ ulint innodb_row_lock_waits; /*!< srv_n_lock_wait_count */ ulint innodb_row_lock_current_waits; /*!< srv_n_lock_wait_current_count */ ib_int64_t innodb_row_lock_time; /*!< srv_n_lock_wait_time / 1000 */ ulint innodb_row_lock_time_avg; /*!< srv_n_lock_wait_time / 1000 / srv_n_lock_wait_count */ ulint innodb_row_lock_time_max; /*!< srv_n_lock_max_wait_time / 1000 */ ulint innodb_rows_read; /*!< srv_n_rows_read */ ulint innodb_rows_inserted; /*!< srv_n_rows_inserted */ ulint innodb_rows_updated; /*!< srv_n_rows_updated */ ulint innodb_rows_deleted; /*!< srv_n_rows_deleted */ }; extern ulint srv_n_threads_active[]; #else /* !UNIV_HOTBACKUP */ # define srv_use_checksums TRUE # define srv_use_adaptive_hash_indexes FALSE # define srv_force_recovery 0UL # define srv_is_being_started 0 # define srv_win_file_flush_method SRV_WIN_IO_UNBUFFERED # define srv_unix_file_flush_method SRV_UNIX_O_DSYNC # define srv_start_raw_disk_in_use 0 # define srv_file_per_table 1 #endif /* !UNIV_HOTBACKUP */ extern void* ib_panic_data; typedef void (*ib_panic_function_t)(void*, int, char*, ...); UNIV_INTERN void srv_panic(int panic_ib_error, char* fmt, ...); #endif haildb-2.3.2/include/buf0buf.ic0000644000175000017500000007154511513177357017150 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2010, Innobase Oy. All Rights Reserved. Copyright (c) 2008, Google Inc. Portions of this file contain modifications contributed and copyrighted by Google, Inc. Those modifications are gratefully acknowledged and are described briefly in the InnoDB documentation. The contributions by Google are incorporated with their permission, and subject to the conditions contained in the file COPYING.Google. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/buf0buf.ic The database buffer buf_pool Created 11/5/1995 Heikki Tuuri *******************************************************/ #include "mtr0mtr.h" #ifndef UNIV_HOTBACKUP #include "buf0flu.h" #include "buf0lru.h" #include "buf0rea.h" /********************************************************************//** Reads the freed_page_clock of a buffer block. @return freed_page_clock */ UNIV_INLINE ulint buf_page_get_freed_page_clock( /*==========================*/ const buf_page_t* bpage) /*!< in: block */ { /* This is sometimes read without holding buf_pool_mutex. */ return(bpage->freed_page_clock); } /********************************************************************//** Reads the freed_page_clock of a buffer block. @return freed_page_clock */ UNIV_INLINE ulint buf_block_get_freed_page_clock( /*===========================*/ const buf_block_t* block) /*!< in: block */ { return(buf_page_get_freed_page_clock(&block->page)); } /********************************************************************//** Recommends a move of a block to the start of the LRU list if there is danger of dropping from the buffer pool. NOTE: does not reserve the buffer pool mutex. @return TRUE if should be made younger */ UNIV_INLINE ibool buf_page_peek_if_too_old( /*=====================*/ const buf_page_t* bpage) /*!< in: block to make younger */ { if (UNIV_UNLIKELY(buf_pool->freed_page_clock == 0)) { /* If eviction has not started yet, do not update the statistics or move blocks in the LRU list. This is either the warm-up phase or an in-memory workload. */ return(FALSE); } else if (buf_LRU_old_threshold_ms && bpage->old) { unsigned access_time = buf_page_is_accessed(bpage); if (access_time > 0 && ((ib_uint32_t) (ut_time_ms() - access_time)) >= buf_LRU_old_threshold_ms) { return(TRUE); } buf_pool->stat.n_pages_not_made_young++; return(FALSE); } else { /* FIXME: bpage->freed_page_clock is 31 bits */ return((buf_pool->freed_page_clock & ((1UL << 31) - 1)) > ((ulint) bpage->freed_page_clock + (buf_pool->curr_size * (BUF_LRU_OLD_RATIO_DIV - buf_LRU_old_ratio) / (BUF_LRU_OLD_RATIO_DIV * 4)))); } } /*********************************************************************//** Gets the current size of buffer buf_pool in bytes. @return size in bytes */ UNIV_INLINE ulint buf_pool_get_curr_size(void) /*========================*/ { return(buf_pool->curr_size * UNIV_PAGE_SIZE); } /********************************************************************//** Gets the smallest oldest_modification lsn for any page in the pool. Returns zero if all modified pages have been flushed to disk. @return oldest modification in pool, zero if none */ UNIV_INLINE ib_uint64_t buf_pool_get_oldest_modification(void) /*==================================*/ { buf_page_t* bpage; ib_uint64_t lsn; buf_pool_mutex_enter(); bpage = UT_LIST_GET_LAST(buf_pool->flush_list); if (bpage == NULL) { lsn = 0; } else { ut_ad(bpage->in_flush_list); lsn = bpage->oldest_modification; } buf_pool_mutex_exit(); /* The returned answer may be out of date: the flush_list can change after the mutex has been released. */ return(lsn); } #endif /* !UNIV_HOTBACKUP */ /*********************************************************************//** Gets the state of a block. @return state */ UNIV_INLINE enum buf_page_state buf_page_get_state( /*===============*/ const buf_page_t* bpage) /*!< in: pointer to the control block */ { enum buf_page_state state = (enum buf_page_state) bpage->state; #ifdef UNIV_DEBUG switch (state) { #ifdef WITH_ZIP case BUF_BLOCK_ZIP_FREE: case BUF_BLOCK_ZIP_PAGE: case BUF_BLOCK_ZIP_DIRTY: #endif /* WITH_ZIP */ case BUF_BLOCK_NOT_USED: case BUF_BLOCK_READY_FOR_USE: case BUF_BLOCK_FILE_PAGE: case BUF_BLOCK_MEMORY: case BUF_BLOCK_REMOVE_HASH: break; default: ut_error; } #endif /* UNIV_DEBUG */ return(state); } /*********************************************************************//** Gets the state of a block. @return state */ UNIV_INLINE enum buf_page_state buf_block_get_state( /*================*/ const buf_block_t* block) /*!< in: pointer to the control block */ { return(buf_page_get_state(&block->page)); } /*********************************************************************//** Sets the state of a block. */ UNIV_INLINE void buf_page_set_state( /*===============*/ buf_page_t* bpage, /*!< in/out: pointer to control block */ enum buf_page_state state) /*!< in: state */ { #ifdef UNIV_DEBUG enum buf_page_state old_state = buf_page_get_state(bpage); switch (old_state) { #ifdef WITH_ZIP case BUF_BLOCK_ZIP_FREE: ut_error; break; case BUF_BLOCK_ZIP_PAGE: ut_a(state == BUF_BLOCK_ZIP_DIRTY); break; case BUF_BLOCK_ZIP_DIRTY: ut_a(state == BUF_BLOCK_ZIP_PAGE); break; #endif /* WITH_ZIP */ case BUF_BLOCK_NOT_USED: ut_a(state == BUF_BLOCK_READY_FOR_USE); break; case BUF_BLOCK_READY_FOR_USE: ut_a(state == BUF_BLOCK_MEMORY || state == BUF_BLOCK_FILE_PAGE || state == BUF_BLOCK_NOT_USED); break; case BUF_BLOCK_MEMORY: ut_a(state == BUF_BLOCK_NOT_USED); break; case BUF_BLOCK_FILE_PAGE: ut_a(state == BUF_BLOCK_NOT_USED || state == BUF_BLOCK_REMOVE_HASH); break; case BUF_BLOCK_REMOVE_HASH: ut_a(state == BUF_BLOCK_MEMORY); break; } #endif /* UNIV_DEBUG */ bpage->state = state; ut_ad(buf_page_get_state(bpage) == state); } /*********************************************************************//** Sets the state of a block. */ UNIV_INLINE void buf_block_set_state( /*================*/ buf_block_t* block, /*!< in/out: pointer to control block */ enum buf_page_state state) /*!< in: state */ { buf_page_set_state(&block->page, state); } /*********************************************************************//** Determines if a block is mapped to a tablespace. @return TRUE if mapped */ UNIV_INLINE ibool buf_page_in_file( /*=============*/ const buf_page_t* bpage) /*!< in: pointer to control block */ { switch (buf_page_get_state(bpage)) { #ifdef WITH_ZIP case BUF_BLOCK_ZIP_FREE: /* This is a free page in buf_pool->zip_free[]. Such pages should only be accessed by the buddy allocator. */ ut_error; break; case BUF_BLOCK_ZIP_PAGE: case BUF_BLOCK_ZIP_DIRTY: #endif /* WITH_ZIP */ case BUF_BLOCK_FILE_PAGE: return(TRUE); case BUF_BLOCK_NOT_USED: case BUF_BLOCK_READY_FOR_USE: case BUF_BLOCK_MEMORY: case BUF_BLOCK_REMOVE_HASH: break; } return(FALSE); } #ifndef UNIV_HOTBACKUP #ifdef WITH_ZIP /*********************************************************************//** Determines if a block should be on unzip_LRU list. @return TRUE if block belongs to unzip_LRU */ UNIV_INLINE ibool buf_page_belongs_to_unzip_LRU( /*==========================*/ const buf_page_t* bpage) /*!< in: pointer to control block */ { ut_ad(buf_page_in_file(bpage)); return(bpage->zip.data && buf_page_get_state(bpage) == BUF_BLOCK_FILE_PAGE); } #endif /* WITH_ZIP */ /*********************************************************************//** Gets the mutex of a block. @return pointer to mutex protecting bpage */ UNIV_INLINE mutex_t* buf_page_get_mutex( /*===============*/ const buf_page_t* bpage) /*!< in: pointer to control block */ { #ifdef WITH_ZIP switch (buf_page_get_state(bpage)) { case BUF_BLOCK_ZIP_FREE: ut_error; return(NULL); case BUF_BLOCK_ZIP_PAGE: case BUF_BLOCK_ZIP_DIRTY: return(&buf_pool_zip_mutex); default: #endif /* WITH_ZIP */ return(&((buf_block_t*) bpage)->mutex); #ifdef WITH_ZIP } #endif /* WITH_ZIP */ } /*********************************************************************//** Get the flush type of a page. @return flush type */ UNIV_INLINE enum buf_flush buf_page_get_flush_type( /*====================*/ const buf_page_t* bpage) /*!< in: buffer page */ { enum buf_flush flush_type = (enum buf_flush) bpage->flush_type; #ifdef UNIV_DEBUG switch (flush_type) { case BUF_FLUSH_LRU: case BUF_FLUSH_SINGLE_PAGE: case BUF_FLUSH_LIST: return(flush_type); case BUF_FLUSH_N_TYPES: break; } ut_error; #endif /* UNIV_DEBUG */ return(flush_type); } /*********************************************************************//** Set the flush type of a page. */ UNIV_INLINE void buf_page_set_flush_type( /*====================*/ buf_page_t* bpage, /*!< in: buffer page */ enum buf_flush flush_type) /*!< in: flush type */ { bpage->flush_type = flush_type; ut_ad(buf_page_get_flush_type(bpage) == flush_type); } /*********************************************************************//** Map a block to a file page. */ UNIV_INLINE void buf_block_set_file_page( /*====================*/ buf_block_t* block, /*!< in/out: pointer to control block */ ulint space, /*!< in: tablespace id */ ulint page_no)/*!< in: page number */ { buf_block_set_state(block, BUF_BLOCK_FILE_PAGE); block->page.space = space; block->page.offset = page_no; } /*********************************************************************//** Gets the io_fix state of a block. @return io_fix state */ UNIV_INLINE enum buf_io_fix buf_page_get_io_fix( /*================*/ const buf_page_t* bpage) /*!< in: pointer to the control block */ { enum buf_io_fix io_fix = (enum buf_io_fix) bpage->io_fix; #ifdef UNIV_DEBUG switch (io_fix) { case BUF_IO_NONE: case BUF_IO_READ: case BUF_IO_WRITE: return(io_fix); } ut_error; #endif /* UNIV_DEBUG */ return(io_fix); } /*********************************************************************//** Gets the io_fix state of a block. @return io_fix state */ UNIV_INLINE enum buf_io_fix buf_block_get_io_fix( /*================*/ const buf_block_t* block) /*!< in: pointer to the control block */ { return(buf_page_get_io_fix(&block->page)); } /*********************************************************************//** Sets the io_fix state of a block. */ UNIV_INLINE void buf_page_set_io_fix( /*================*/ buf_page_t* bpage, /*!< in/out: control block */ enum buf_io_fix io_fix) /*!< in: io_fix state */ { ut_ad(buf_pool_mutex_own()); ut_ad(mutex_own(buf_page_get_mutex(bpage))); bpage->io_fix = io_fix; ut_ad(buf_page_get_io_fix(bpage) == io_fix); } /*********************************************************************//** Sets the io_fix state of a block. */ UNIV_INLINE void buf_block_set_io_fix( /*=================*/ buf_block_t* block, /*!< in/out: control block */ enum buf_io_fix io_fix) /*!< in: io_fix state */ { buf_page_set_io_fix(&block->page, io_fix); } /********************************************************************//** Determine if a buffer block can be relocated in memory. The block can be dirty, but it must not be I/O-fixed or bufferfixed. */ UNIV_INLINE ibool buf_page_can_relocate( /*==================*/ const buf_page_t* bpage) /*!< control block being relocated */ { ut_ad(buf_pool_mutex_own()); ut_ad(mutex_own(buf_page_get_mutex(bpage))); ut_ad(buf_page_in_file(bpage)); ut_ad(bpage->in_LRU_list); return(buf_page_get_io_fix(bpage) == BUF_IO_NONE && bpage->buf_fix_count == 0); } /*********************************************************************//** Determine if a block has been flagged old. @return TRUE if old */ UNIV_INLINE ibool buf_page_is_old( /*============*/ const buf_page_t* bpage) /*!< in: control block */ { ut_ad(buf_page_in_file(bpage)); ut_ad(buf_pool_mutex_own()); return(bpage->old); } /*********************************************************************//** Flag a block old. */ UNIV_INLINE void buf_page_set_old( /*=============*/ buf_page_t* bpage, /*!< in/out: control block */ ibool old) /*!< in: old */ { ut_a(buf_page_in_file(bpage)); ut_ad(buf_pool_mutex_own()); ut_ad(bpage->in_LRU_list); #ifdef UNIV_LRU_DEBUG ut_a((buf_pool->LRU_old_len == 0) == (buf_pool->LRU_old == NULL)); /* If a block is flagged "old", the LRU_old list must exist. */ ut_a(!old || buf_pool->LRU_old); if (UT_LIST_GET_PREV(LRU, bpage) && UT_LIST_GET_NEXT(LRU, bpage)) { const buf_page_t* prev = UT_LIST_GET_PREV(LRU, bpage); const buf_page_t* next = UT_LIST_GET_NEXT(LRU, bpage); if (prev->old == next->old) { ut_a(prev->old == old); } else { ut_a(!prev->old); ut_a(buf_pool->LRU_old == (old ? bpage : next)); } } #endif /* UNIV_LRU_DEBUG */ bpage->old = old; } /*********************************************************************//** Determine the time of first access of a block in the buffer pool. @return ut_time_ms() at the time of first access, 0 if not accessed */ UNIV_INLINE unsigned buf_page_is_accessed( /*=================*/ const buf_page_t* bpage) /*!< in: control block */ { ut_ad(buf_page_in_file(bpage)); return(bpage->access_time); } /*********************************************************************//** Flag a block accessed. */ UNIV_INLINE void buf_page_set_accessed( /*==================*/ buf_page_t* bpage, /*!< in/out: control block */ ulint time_ms) /*!< in: ut_time_ms() */ { ut_a(buf_page_in_file(bpage)); ut_ad(buf_pool_mutex_own()); if (!bpage->access_time) { /* Make this the time of the first access. */ bpage->access_time = time_ms; } } /*********************************************************************//** Gets the buf_block_t handle of a buffered file block if an uncompressed page frame exists, or NULL. @return control block, or NULL */ UNIV_INLINE buf_block_t* buf_page_get_block( /*===============*/ buf_page_t* bpage) /*!< in: control block, or NULL */ { if (UNIV_LIKELY(bpage != NULL)) { ut_ad(buf_page_in_file(bpage)); if (buf_page_get_state(bpage) == BUF_BLOCK_FILE_PAGE) { return((buf_block_t*) bpage); } } return(NULL); } #endif /* !UNIV_HOTBACKUP */ #ifdef UNIV_DEBUG /*********************************************************************//** Gets a pointer to the memory frame of a block. @return pointer to the frame */ UNIV_INLINE buf_frame_t* buf_block_get_frame( /*================*/ const buf_block_t* block) /*!< in: pointer to the control block */ { ut_ad(block); switch (buf_block_get_state(block)) { #ifdef WITH_ZIP case BUF_BLOCK_ZIP_FREE: case BUF_BLOCK_ZIP_PAGE: case BUF_BLOCK_ZIP_DIRTY: #endif /* WITH_ZIP */ case BUF_BLOCK_NOT_USED: ut_error; break; case BUF_BLOCK_FILE_PAGE: # ifndef UNIV_HOTBACKUP ut_a(block->page.buf_fix_count > 0); # endif /* !UNIV_HOTBACKUP */ /* fall through */ case BUF_BLOCK_READY_FOR_USE: case BUF_BLOCK_MEMORY: case BUF_BLOCK_REMOVE_HASH: goto ok; } ut_error; ok: return((buf_frame_t*) block->frame); } #endif /* UNIV_DEBUG */ /*********************************************************************//** Gets the space id of a block. @return space id */ UNIV_INLINE ulint buf_page_get_space( /*===============*/ const buf_page_t* bpage) /*!< in: pointer to the control block */ { ut_ad(bpage); ut_a(buf_page_in_file(bpage)); return(bpage->space); } /*********************************************************************//** Gets the space id of a block. @return space id */ UNIV_INLINE ulint buf_block_get_space( /*================*/ const buf_block_t* block) /*!< in: pointer to the control block */ { ut_ad(block); ut_a(buf_block_get_state(block) == BUF_BLOCK_FILE_PAGE); return(block->page.space); } /*********************************************************************//** Gets the page number of a block. @return page number */ UNIV_INLINE ulint buf_page_get_page_no( /*=================*/ const buf_page_t* bpage) /*!< in: pointer to the control block */ { ut_ad(bpage); ut_a(buf_page_in_file(bpage)); return(bpage->offset); } /*********************************************************************//** Gets the page number of a block. @return page number */ UNIV_INLINE ulint buf_block_get_page_no( /*==================*/ const buf_block_t* block) /*!< in: pointer to the control block */ { ut_ad(block); ut_a(buf_block_get_state(block) == BUF_BLOCK_FILE_PAGE); return(block->page.offset); } /*********************************************************************//** Gets the compressed page size of a block. @return compressed page size, or 0 */ UNIV_INLINE ulint buf_page_get_zip_size( /*==================*/ const buf_page_t* bpage) /*!< in: pointer to the control block */ { #ifdef WITH_ZIP return(bpage->zip.ssize ? 512 << bpage->zip.ssize : 0); #else return(0); #endif /* WITH_ZIP */ } /*********************************************************************//** Gets the compressed page size of a block. @return compressed page size, or 0 */ UNIV_INLINE ulint buf_block_get_zip_size( /*===================*/ const buf_block_t* block) /*!< in: pointer to the control block */ { #ifdef WITH_ZIP return(block->page.zip.ssize ? 512 << block->page.zip.ssize : 0); #else return(0); #endif /* WITH_ZIP */ } #ifndef UNIV_HOTBACKUP #ifdef WITH_ZIP #if defined UNIV_DEBUG || defined UNIV_ZIP_DEBUG /*********************************************************************//** Gets the compressed page descriptor corresponding to an uncompressed page if applicable. @return compressed page descriptor, or NULL */ UNIV_INLINE const page_zip_des_t* buf_frame_get_page_zip( /*===================*/ const byte* ptr) /*!< in: pointer to the page */ { return(buf_block_get_page_zip(buf_block_align(ptr))); } #endif /* UNIV_DEBUG || UNIV_ZIP_DEBUG */ #endif /* WITH_ZIP */ #endif /* !UNIV_HOTBACKUP */ /**********************************************************************//** Gets the space id, page offset, and byte offset within page of a pointer pointing to a buffer frame containing a file page. */ UNIV_INLINE void buf_ptr_get_fsp_addr( /*=================*/ const void* ptr, /*!< in: pointer to a buffer frame */ ulint* space, /*!< out: space id */ fil_addr_t* addr) /*!< out: page offset and byte offset */ { const page_t* page = (const page_t*) ut_align_down(ptr, UNIV_PAGE_SIZE); *space = mach_read_from_4(page + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID); addr->page = mach_read_from_4(page + FIL_PAGE_OFFSET); addr->boffset = ut_align_offset(ptr, UNIV_PAGE_SIZE); } #ifndef UNIV_HOTBACKUP /**********************************************************************//** Gets the hash value of the page the pointer is pointing to. This can be used in searches in the lock hash table. @return lock hash value */ UNIV_INLINE ulint buf_block_get_lock_hash_val( /*========================*/ const buf_block_t* block) /*!< in: block */ { ut_ad(block); ut_ad(buf_page_in_file(&block->page)); #ifdef UNIV_SYNC_DEBUG ut_ad(rw_lock_own(&(((buf_block_t*) block)->lock), RW_LOCK_EXCLUSIVE) || rw_lock_own(&(((buf_block_t*) block)->lock), RW_LOCK_SHARED)); #endif /* UNIV_SYNC_DEBUG */ return(block->lock_hash_val); } /********************************************************************//** Allocates a buffer block. @return own: the allocated block, in state BUF_BLOCK_MEMORY */ UNIV_INLINE buf_block_t* buf_block_alloc( /*============*/ ulint zip_size) /*!< in: compressed page size in bytes, or 0 if uncompressed tablespace */ { buf_block_t* block; block = buf_LRU_get_free_block(zip_size); buf_block_set_state(block, BUF_BLOCK_MEMORY); return(block); } /********************************************************************//** Frees a buffer block which does not contain a file page. */ UNIV_INLINE void buf_block_free( /*===========*/ buf_block_t* block) /*!< in, own: block to be freed */ { buf_pool_mutex_enter(); mutex_enter(&block->mutex); ut_a(buf_block_get_state(block) != BUF_BLOCK_FILE_PAGE); buf_LRU_block_free_non_file_page(block); mutex_exit(&block->mutex); buf_pool_mutex_exit(); } #endif /* !UNIV_HOTBACKUP */ /*********************************************************************//** Copies contents of a buffer frame to a given buffer. @return buf */ UNIV_INLINE byte* buf_frame_copy( /*===========*/ byte* buf, /*!< in: buffer to copy to */ const buf_frame_t* frame) /*!< in: buffer frame */ { ut_ad(buf && frame); ut_memcpy(buf, frame, UNIV_PAGE_SIZE); return(buf); } #ifndef UNIV_HOTBACKUP /********************************************************************//** Calculates a folded value of a file page address to use in the page hash table. @return the folded value */ UNIV_INLINE ulint buf_page_address_fold( /*==================*/ ulint space, /*!< in: space id */ ulint offset) /*!< in: offset of the page within space */ { return((space << 20) + space + offset); } /********************************************************************//** Gets the youngest modification log sequence number for a frame. Returns zero if not file page or no modification occurred yet. @return newest modification to page */ UNIV_INLINE ib_uint64_t buf_page_get_newest_modification( /*=============================*/ const buf_page_t* bpage) /*!< in: block containing the page frame */ { ib_uint64_t lsn; mutex_t* block_mutex = buf_page_get_mutex(bpage); mutex_enter(block_mutex); if (buf_page_in_file(bpage)) { lsn = bpage->newest_modification; } else { lsn = 0; } mutex_exit(block_mutex); return(lsn); } /********************************************************************//** Increments the modify clock of a frame by 1. The caller must (1) own the buf_pool mutex and block bufferfix count has to be zero, (2) or own an x-lock on the block. */ UNIV_INLINE void buf_block_modify_clock_inc( /*=======================*/ buf_block_t* block) /*!< in: block */ { #ifdef UNIV_SYNC_DEBUG ut_ad((buf_pool_mutex_own() && (block->page.buf_fix_count == 0)) || rw_lock_own(&(block->lock), RW_LOCK_EXCLUSIVE)); #endif /* UNIV_SYNC_DEBUG */ block->modify_clock++; } /********************************************************************//** Returns the value of the modify clock. The caller must have an s-lock or x-lock on the block. @return value */ UNIV_INLINE ib_uint64_t buf_block_get_modify_clock( /*=======================*/ buf_block_t* block) /*!< in: block */ { #ifdef UNIV_SYNC_DEBUG ut_ad(rw_lock_own(&(block->lock), RW_LOCK_SHARED) || rw_lock_own(&(block->lock), RW_LOCK_EXCLUSIVE)); #endif /* UNIV_SYNC_DEBUG */ return(block->modify_clock); } /*******************************************************************//** Increments the bufferfix count. */ UNIV_INLINE void buf_block_buf_fix_inc_func( /*=======================*/ #ifdef UNIV_SYNC_DEBUG const char* file, /*!< in: file name */ ulint line, /*!< in: line */ #endif /* UNIV_SYNC_DEBUG */ buf_block_t* block) /*!< in/out: block to bufferfix */ { #ifdef UNIV_SYNC_DEBUG ibool ret; ret = rw_lock_s_lock_nowait(&(block->debug_latch), file, line); ut_a(ret); #endif /* UNIV_SYNC_DEBUG */ ut_ad(mutex_own(&block->mutex)); block->page.buf_fix_count++; } #ifdef UNIV_SYNC_DEBUG /** Increments the bufferfix count. @param b in/out: block to bufferfix @param f in: file name where requested @param l in: line number where requested */ # define buf_block_buf_fix_inc(b,f,l) buf_block_buf_fix_inc_func(f,l,b) #else /* UNIV_SYNC_DEBUG */ /** Increments the bufferfix count. @param b in/out: block to bufferfix @param f in: file name where requested @param l in: line number where requested */ # define buf_block_buf_fix_inc(b,f,l) buf_block_buf_fix_inc_func(b) #endif /* UNIV_SYNC_DEBUG */ /*******************************************************************//** Decrements the bufferfix count. */ UNIV_INLINE void buf_block_buf_fix_dec( /*==================*/ buf_block_t* block) /*!< in/out: block to bufferunfix */ { ut_ad(mutex_own(&block->mutex)); block->page.buf_fix_count--; #ifdef UNIV_SYNC_DEBUG rw_lock_s_unlock(&block->debug_latch); #endif } /******************************************************************//** Returns the control block of a file page, NULL if not found. @return block, NULL if not found */ UNIV_INLINE buf_page_t* buf_page_hash_get( /*==============*/ ulint space, /*!< in: space id */ ulint offset) /*!< in: offset of the page within space */ { buf_page_t* bpage; ulint fold; ut_ad(buf_pool); ut_ad(buf_pool_mutex_own()); /* Look for the page in the hash table */ fold = buf_page_address_fold(space, offset); #ifdef WITH_ZIP HASH_SEARCH(hash, buf_pool->page_hash, fold, buf_page_t*, bpage, ut_ad(bpage->in_page_hash && !bpage->in_zip_hash && buf_page_in_file(bpage)), bpage->space == space && bpage->offset == offset); #else HASH_SEARCH(hash, buf_pool->page_hash, fold, buf_page_t*, bpage, ut_ad(bpage->in_page_hash && buf_page_in_file(bpage)), bpage->space == space && bpage->offset == offset); #endif /* WITH_ZIP */ if (bpage) { ut_a(buf_page_in_file(bpage)); ut_ad(bpage->in_page_hash); #ifdef WITH_ZIP ut_ad(!bpage->in_zip_hash); #endif /* WITH_ZIP */ UNIV_MEM_ASSERT_RW(bpage, sizeof *bpage); } return(bpage); } /******************************************************************//** Returns the control block of a file page, NULL if not found or an uncompressed page frame does not exist. @return block, NULL if not found */ UNIV_INLINE buf_block_t* buf_block_hash_get( /*===============*/ ulint space, /*!< in: space id */ ulint offset) /*!< in: offset of the page within space */ { return(buf_page_get_block(buf_page_hash_get(space, offset))); } /********************************************************************//** Returns TRUE if the page can be found in the buffer pool hash table. NOTE that it is possible that the page is not yet read from disk, though. @return TRUE if found in the page hash table */ UNIV_INLINE ibool buf_page_peek( /*==========*/ ulint space, /*!< in: space id */ ulint offset) /*!< in: page number */ { const buf_page_t* bpage; buf_pool_mutex_enter(); bpage = buf_page_hash_get(space, offset); buf_pool_mutex_exit(); return(bpage != NULL); } #ifdef WITH_ZIP /********************************************************************//** Releases a compressed-only page acquired with buf_page_get_zip(). */ UNIV_INLINE void buf_page_release_zip( /*=================*/ buf_page_t* bpage) /*!< in: buffer block */ { buf_block_t* block; ut_ad(bpage); ut_a(bpage->buf_fix_count > 0); switch (buf_page_get_state(bpage)) { #ifdef WITH_ZIP case BUF_BLOCK_ZIP_PAGE: case BUF_BLOCK_ZIP_DIRTY: mutex_enter(&buf_pool_zip_mutex); bpage->buf_fix_count--; mutex_exit(&buf_pool_zip_mutex); return; #endif /* WITH_ZIP */ case BUF_BLOCK_FILE_PAGE: block = (buf_block_t*) bpage; mutex_enter(&block->mutex); #ifdef UNIV_SYNC_DEBUG rw_lock_s_unlock(&block->debug_latch); #endif bpage->buf_fix_count--; mutex_exit(&block->mutex); return; #ifdef WITH_ZIP case BUF_BLOCK_ZIP_FREE: #endif /* WITH_ZIP */ case BUF_BLOCK_NOT_USED: case BUF_BLOCK_READY_FOR_USE: case BUF_BLOCK_MEMORY: case BUF_BLOCK_REMOVE_HASH: break; } ut_error; } #endif /* WITH_ZIP */ /********************************************************************//** Decrements the bufferfix count of a buffer control block and releases a latch, if specified. */ UNIV_INLINE void buf_page_release( /*=============*/ buf_block_t* block, /*!< in: buffer block */ ulint rw_latch, /*!< in: RW_S_LATCH, RW_X_LATCH, RW_NO_LATCH */ mtr_t* mtr) /*!< in: mtr */ { ut_ad(block); ut_a(buf_block_get_state(block) == BUF_BLOCK_FILE_PAGE); ut_a(block->page.buf_fix_count > 0); if (rw_latch == RW_X_LATCH && mtr->modifications) { buf_pool_mutex_enter(); buf_flush_note_modification(block, mtr); buf_pool_mutex_exit(); } mutex_enter(&block->mutex); #ifdef UNIV_SYNC_DEBUG rw_lock_s_unlock(&(block->debug_latch)); #endif block->page.buf_fix_count--; mutex_exit(&block->mutex); if (rw_latch == RW_S_LATCH) { rw_lock_s_unlock(&(block->lock)); } else if (rw_latch == RW_X_LATCH) { rw_lock_x_unlock(&(block->lock)); } } #ifdef UNIV_SYNC_DEBUG /*********************************************************************//** Adds latch level info for the rw-lock protecting the buffer frame. This should be called in the debug version after a successful latching of a page if we know the latching order level of the acquired latch. */ UNIV_INLINE void buf_block_dbg_add_level( /*====================*/ buf_block_t* block, /*!< in: buffer page where we have acquired latch */ ulint level) /*!< in: latching order level */ { sync_thread_add_level(&block->lock, level); } #endif /* UNIV_SYNC_DEBUG */ #endif /* !UNIV_HOTBACKUP */ haildb-2.3.2/include/row0vers.ic0000644000175000017500000000217111513177357017373 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1997, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/row0vers.ic Row versions Created 2/6/1997 Heikki Tuuri *******************************************************/ #include "row0row.h" #include "dict0dict.h" #include "read0read.h" #include "page0page.h" #include "log0recv.h" haildb-2.3.2/include/mach0data.h0000644000175000017500000003636711513177357017300 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /******************************************************************//** @file include/mach0data.h Utilities for converting data from the database file to the machine format. Created 11/28/1995 Heikki Tuuri ***********************************************************************/ #ifndef mach0data_h #define mach0data_h #include "univ.i" #include "ut0byte.h" /* The data and all fields are always stored in a database file in the same format: ascii, big-endian, ... . All data in the files MUST be accessed using the functions in this module. */ /*******************************************************//** The following function is used to store data in one byte. */ UNIV_INLINE void mach_write_to_1( /*============*/ byte* b, /*!< in: pointer to byte where to store */ ulint n); /*!< in: ulint integer to be stored, >= 0, < 256 */ /********************************************************//** The following function is used to fetch data from one byte. @return ulint integer, >= 0, < 256 */ UNIV_INLINE ulint mach_read_from_1( /*=============*/ const byte* b) /*!< in: pointer to byte */ __attribute__((nonnull, pure)); /*******************************************************//** The following function is used to store data in two consecutive bytes. We store the most significant byte to the lower address. */ UNIV_INLINE void mach_write_to_2( /*============*/ byte* b, /*!< in: pointer to two bytes where to store */ ulint n); /*!< in: ulint integer to be stored, >= 0, < 64k */ /********************************************************//** The following function is used to fetch data from two consecutive bytes. The most significant byte is at the lowest address. @return ulint integer, >= 0, < 64k */ UNIV_INLINE ulint mach_read_from_2( /*=============*/ const byte* b) /*!< in: pointer to two bytes */ __attribute__((nonnull, pure)); /********************************************************//** The following function is used to convert a 16-bit data item to the canonical format, for fast bytewise equality test against memory. @return 16-bit integer in canonical format */ UNIV_INLINE ib_uint16_t mach_encode_2( /*==========*/ ulint n) /*!< in: integer in machine-dependent format */ __attribute__((const)); /********************************************************//** The following function is used to convert a 16-bit data item from the canonical format, for fast bytewise equality test against memory. @return integer in machine-dependent format */ UNIV_INLINE ulint mach_decode_2( /*==========*/ ib_uint16_t n) /*!< in: 16-bit integer in canonical format */ __attribute__((const)); /*******************************************************//** The following function is used to store data in 3 consecutive bytes. We store the most significant byte to the lowest address. */ UNIV_INLINE void mach_write_to_3( /*============*/ byte* b, /*!< in: pointer to 3 bytes where to store */ ulint n); /*!< in: ulint integer to be stored */ /********************************************************//** The following function is used to fetch data from 3 consecutive bytes. The most significant byte is at the lowest address. @return ulint integer */ UNIV_INLINE ulint mach_read_from_3( /*=============*/ const byte* b) /*!< in: pointer to 3 bytes */ __attribute__((nonnull, pure)); /*******************************************************//** The following function is used to store data in four consecutive bytes. We store the most significant byte to the lowest address. */ UNIV_INLINE void mach_write_to_4( /*============*/ byte* b, /*!< in: pointer to four bytes where to store */ ulint n); /*!< in: ulint integer to be stored */ /********************************************************//** The following function is used to fetch data from 4 consecutive bytes. The most significant byte is at the lowest address. @return ulint integer */ UNIV_INLINE ulint mach_read_from_4( /*=============*/ const byte* b) /*!< in: pointer to four bytes */ __attribute__((nonnull, pure)); /*********************************************************//** Writes a ulint in a compressed form (1..5 bytes). @return stored size in bytes */ UNIV_INLINE ulint mach_write_compressed( /*==================*/ byte* b, /*!< in: pointer to memory where to store */ ulint n); /*!< in: ulint integer to be stored */ /*********************************************************//** Returns the size of an ulint when written in the compressed form. @return compressed size in bytes */ UNIV_INLINE ulint mach_get_compressed_size( /*=====================*/ ulint n) /*!< in: ulint integer to be stored */ __attribute__((const)); /*********************************************************//** Reads a ulint in a compressed form. @return read integer */ UNIV_INLINE ulint mach_read_compressed( /*=================*/ const byte* b) /*!< in: pointer to memory from where to read */ __attribute__((nonnull, pure)); /*******************************************************//** The following function is used to store data in 6 consecutive bytes. We store the most significant byte to the lowest address. */ UNIV_INLINE void mach_write_to_6( /*============*/ byte* b, /*!< in: pointer to 6 bytes where to store */ dulint n); /*!< in: dulint integer to be stored */ /********************************************************//** The following function is used to fetch data from 6 consecutive bytes. The most significant byte is at the lowest address. @return dulint integer */ UNIV_INLINE dulint mach_read_from_6( /*=============*/ const byte* b) /*!< in: pointer to 6 bytes */ __attribute__((nonnull, pure)); /*******************************************************//** The following function is used to store data in 7 consecutive bytes. We store the most significant byte to the lowest address. */ UNIV_INLINE void mach_write_to_7( /*============*/ byte* b, /*!< in: pointer to 7 bytes where to store */ dulint n); /*!< in: dulint integer to be stored */ /********************************************************//** The following function is used to fetch data from 7 consecutive bytes. The most significant byte is at the lowest address. @return dulint integer */ UNIV_INLINE dulint mach_read_from_7( /*=============*/ const byte* b) /*!< in: pointer to 7 bytes */ __attribute__((nonnull, pure)); /*******************************************************//** The following function is used to store data in 8 consecutive bytes. We store the most significant byte to the lowest address. */ UNIV_INLINE void mach_write_to_8( /*============*/ byte* b, /*!< in: pointer to 8 bytes where to store */ dulint n); /*!< in: dulint integer to be stored */ /*******************************************************//** The following function is used to store data in 8 consecutive bytes. We store the most significant byte to the lowest address. */ UNIV_INLINE void mach_write_ull( /*===========*/ byte* b, /*!< in: pointer to 8 bytes where to store */ ib_uint64_t n); /*!< in: 64-bit integer to be stored */ /********************************************************//** The following function is used to fetch data from 8 consecutive bytes. The most significant byte is at the lowest address. @return dulint integer */ UNIV_INLINE dulint mach_read_from_8( /*=============*/ const byte* b) /*!< in: pointer to 8 bytes */ __attribute__((nonnull, pure)); /********************************************************//** The following function is used to fetch data from 8 consecutive bytes. The most significant byte is at the lowest address. @return 64-bit integer */ UNIV_INLINE ib_uint64_t mach_read_ull( /*==========*/ const byte* b) /*!< in: pointer to 8 bytes */ __attribute__((nonnull, pure)); /*********************************************************//** Writes a dulint in a compressed form (5..9 bytes). @return size in bytes */ UNIV_INLINE ulint mach_dulint_write_compressed( /*=========================*/ byte* b, /*!< in: pointer to memory where to store */ dulint n); /*!< in: dulint integer to be stored */ /*********************************************************//** Returns the size of a dulint when written in the compressed form. @return compressed size in bytes */ UNIV_INLINE ulint mach_dulint_get_compressed_size( /*============================*/ dulint n); /*!< in: dulint integer to be stored */ /*********************************************************//** Reads a dulint in a compressed form. @return read dulint */ UNIV_INLINE dulint mach_dulint_read_compressed( /*========================*/ const byte* b) /*!< in: pointer to memory from where to read */ __attribute__((nonnull, pure)); /*********************************************************//** Writes a dulint in a compressed form (1..11 bytes). @return size in bytes */ UNIV_INLINE ulint mach_dulint_write_much_compressed( /*==============================*/ byte* b, /*!< in: pointer to memory where to store */ dulint n); /*!< in: dulint integer to be stored */ /*********************************************************//** Returns the size of a dulint when written in the compressed form. @return compressed size in bytes */ UNIV_INLINE ulint mach_dulint_get_much_compressed_size( /*=================================*/ dulint n) /*!< in: dulint integer to be stored */ __attribute__((const)); /*********************************************************//** Reads a dulint in a compressed form. @return read dulint */ UNIV_INLINE dulint mach_dulint_read_much_compressed( /*=============================*/ const byte* b) /*!< in: pointer to memory from where to read */ __attribute__((nonnull, pure)); /*********************************************************//** Reads a ulint in a compressed form if the log record fully contains it. @return pointer to end of the stored field, NULL if not complete */ UNIV_INTERN byte* mach_parse_compressed( /*==================*/ byte* ptr, /*!< in: pointer to buffer from where to read */ byte* end_ptr,/*!< in: pointer to end of the buffer */ ulint* val); /*!< out: read value */ /*********************************************************//** Reads a dulint in a compressed form if the log record fully contains it. @return pointer to end of the stored field, NULL if not complete */ UNIV_INTERN byte* mach_dulint_parse_compressed( /*=========================*/ byte* ptr, /*!< in: pointer to buffer from where to read */ byte* end_ptr,/*!< in: pointer to end of the buffer */ dulint* val); /*!< out: read value */ #ifndef UNIV_HOTBACKUP /*********************************************************//** Reads a double. It is stored in a little-endian format. @return double read */ UNIV_INLINE double mach_double_read( /*=============*/ const byte* b) /*!< in: pointer to memory from where to read */ __attribute__((nonnull, pure)); /*********************************************************//** Writes a pointer to a double. It is stored in a little-endian format. */ UNIV_INLINE void mach_double_ptr_write( /*==================*/ byte* b, /*!< in: pointer to memory where to write */ const byte* ptr); /*!< in: pointer to a double */ /***********************************************************//** Writes a double. It is stored in a little-endian format. */ UNIV_INLINE void mach_double_write( /*==============*/ byte* b, /*!< in: pointer to memory where to write */ double d); /*!< in: double */ /*********************************************************//** Reads a float. It is stored in a little-endian format. @return float read */ UNIV_INLINE float mach_float_read( /*============*/ const byte* b) /*!< in: pointer to memory from where to read */ __attribute__((nonnull, pure)); /*********************************************************//** Writes a pointer to float. It is stored in a little-endian format. */ UNIV_INLINE void mach_float_ptr_write( /*=================*/ byte* b, /*!< in: pointer to memory where to write */ const byte* p); /*!< in: pointer to float */ /***********************************************************//** Writes a float. It is stored in a little-endian format. */ UNIV_INLINE void mach_float_write( /*=============*/ byte* b, /*!< in: pointer to memory where to write */ float d); /*!< in: float */ /*********************************************************//** Reads a ulint stored in the little-endian format. @return unsigned long int */ UNIV_INLINE ulint mach_read_from_n_little_endian( /*===========================*/ const byte* buf, /*!< in: from where to read */ ulint buf_size) /*!< in: from how many bytes to read */ __attribute__((nonnull, pure)); /*********************************************************//** Writes a ulint in the little-endian format. */ UNIV_INLINE void mach_write_to_n_little_endian( /*==========================*/ byte* dest, /*!< in: where to write */ ulint dest_size, /*!< in: into how many bytes to write */ ulint n); /*!< in: unsigned long int to write */ /*********************************************************//** Reads a ulint stored in the little-endian format. @return unsigned long int */ UNIV_INLINE ulint mach_read_from_2_little_endian( /*===========================*/ const byte* buf) /*!< in: from where to read */ __attribute__((nonnull, pure)); /*********************************************************//** Writes a ulint in the little-endian format. */ UNIV_INLINE void mach_write_to_2_little_endian( /*==========================*/ byte* dest, /*!< in: where to write */ ulint n); /*!< in: unsigned long int to write */ /*********************************************************//** Convert integral type from storage byte order (big endian) to host byte order. @return integer value */ UNIV_INLINE void mach_read_int_type( /*===============*/ void* dst, /*!< out: where to write */ const byte* src, /*!< in: where to read from */ ulint len, /*!< in: length of src */ ibool unsigned_type); /*!< in: signed or unsigned flag */ /***********************************************************//** Convert integral type from host byte order to (big-endian) storage byte order. */ UNIV_INLINE void mach_write_int_type( /*================*/ byte* dest, /*!< in: where to write*/ const byte* src, /*!< in: where to read from */ ulint len, /*!< in: length of src */ ibool unsigned_type); /*!< in: signed or unsigned flag */ /***********************************************************//** Convert a 64 bit unsigned integral type to big endian from host byte order. */ UNIV_INLINE void mach_write_uint64( /*==============*/ byte* dest, /*!< out: where to write */ ib_uint64_t n); /*!< in: where to read from */ #endif /* !UNIV_HOTBACKUP */ #ifndef UNIV_NONINL #include "mach0data.ic" #endif #endif haildb-2.3.2/include/ibuf0types.h0000644000175000017500000000215711513177357017536 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1997, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/ibuf0types.h Insert buffer global types Created 7/29/1997 Heikki Tuuri *******************************************************/ #ifndef ibuf0types_h #define ibuf0types_h typedef struct ibuf_struct ibuf_t; #endif haildb-2.3.2/include/row0ext.h0000644000175000017500000000700711513177357017053 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 2006, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/row0ext.h Caching of externally stored column prefixes Created September 2006 Marko Makela *******************************************************/ #ifndef row0ext_h #define row0ext_h #include "univ.i" #include "row0types.h" #include "data0types.h" #include "mem0mem.h" /********************************************************************//** Creates a cache of column prefixes of externally stored columns. @return own: column prefix cache */ UNIV_INTERN row_ext_t* row_ext_create( /*===========*/ ulint n_ext, /*!< in: number of externally stored columns */ const ulint* ext, /*!< in: col_no's of externally stored columns in the InnoDB table object, as reported by dict_col_get_no(); NOT relative to the records in the clustered index */ const dtuple_t* tuple, /*!< in: data tuple containing the field references of the externally stored columns; must be indexed by col_no; the clustered index record must be covered by a lock or a page latch to prevent deletion (rollback or purge). */ ulint zip_size,/*!< compressed page size in bytes, or 0 */ mem_heap_t* heap); /*!< in: heap where created */ /********************************************************************//** Looks up a column prefix of an externally stored column. @return column prefix, or NULL if the column is not stored externally, or pointer to field_ref_zero if the BLOB pointer is unset */ UNIV_INLINE const byte* row_ext_lookup_ith( /*===============*/ const row_ext_t* ext, /*!< in/out: column prefix cache */ ulint i, /*!< in: index of ext->ext[] */ ulint* len); /*!< out: length of prefix, in bytes, at most REC_MAX_INDEX_COL_LEN */ /********************************************************************//** Looks up a column prefix of an externally stored column. @return column prefix, or NULL if the column is not stored externally, or pointer to field_ref_zero if the BLOB pointer is unset */ UNIV_INLINE const byte* row_ext_lookup( /*===========*/ const row_ext_t* ext, /*!< in: column prefix cache */ ulint col, /*!< in: column number in the InnoDB table object, as reported by dict_col_get_no(); NOT relative to the records in the clustered index */ ulint* len); /*!< out: length of prefix, in bytes, at most REC_MAX_INDEX_COL_LEN */ /** Prefixes of externally stored columns */ struct row_ext_struct{ ulint n_ext; /*!< number of externally stored columns */ const ulint* ext; /*!< col_no's of externally stored columns */ byte* buf; /*!< backing store of the column prefix cache */ ulint len[1]; /*!< prefix lengths; 0 if not cached */ }; #ifndef UNIV_NONINL #include "row0ext.ic" #endif #endif haildb-2.3.2/include/data0type.ic0000644000175000017500000003255211513177357017505 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2010, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/data0type.ic Data types Created 1/16/1996 Heikki Tuuri *******************************************************/ #include "api0ucode.h" #include "mach0data.h" #include "api0ucode.h" #ifndef UNIV_HOTBACKUP /************************************************************************* Gets the client charset-collation code for user string types. */ UNIV_INLINE ulint dtype_get_charset_coll( /*===================*/ ulint prtype) /*!< in: precise data type */ { return((prtype >> 16) & 0xFFUL); } /*********************************************************************//** Gets the user type code from a dtype. @return User type code; this is NOT an InnoDB type code! */ UNIV_INLINE ulint dtype_get_attrib( /*=============*/ const dtype_t* type) /*!< in: type struct */ { return(type->prtype & 0xFFUL); } /*********************************************************************//** Compute the mbminlen and mbmaxlen members of a data type structure. */ UNIV_INLINE void dtype_get_mblen( /*============*/ ulint mtype, /*!< in: main type */ ulint prtype, /*!< in: precise type (and collation) */ ulint* mbminlen, /*!< out: minimum length of a multi-byte character */ ulint* mbmaxlen) /*!< out: maximum length of a multi-byte character */ { if (dtype_is_string_type(mtype)) { const charset_t* cs; cs = ib_ucode_get_charset(dtype_get_charset_coll(prtype)); ib_ucode_get_charset_width(cs, mbminlen, mbmaxlen); ut_ad(*mbminlen <= *mbmaxlen); ut_ad(*mbminlen <= 2); /* mbminlen in dtype_t is 0..3 */ ut_ad(*mbmaxlen < 1 << 3); /* mbmaxlen in dtype_t is 0..7 */ } else { *mbminlen = *mbmaxlen = 0; } } /*********************************************************************//** Compute the mbminlen and mbmaxlen members of a data type structure. */ UNIV_INLINE void dtype_set_mblen( /*============*/ dtype_t* type) /*!< in/out: type */ { ulint mbminlen; ulint mbmaxlen; dtype_get_mblen(type->mtype, type->prtype, &mbminlen, &mbmaxlen); type->mbminlen = mbminlen; type->mbmaxlen = mbmaxlen; ut_ad(dtype_validate(type)); } #else /* !UNIV_HOTBACKUP */ # define dtype_set_mblen(type) (void) 0 #endif /* !UNIV_HOTBACKUP */ /*********************************************************************//** Sets a data type structure. */ UNIV_INLINE void dtype_set( /*======*/ dtype_t* type, /*!< in: type struct to init */ ulint mtype, /*!< in: main data type */ ulint prtype, /*!< in: precise type */ ulint len) /*!< in: precision of type */ { ut_ad(type); ut_ad(mtype <= DATA_MTYPE_MAX); type->mtype = mtype; type->prtype = prtype; type->len = len; dtype_set_mblen(type); } /*********************************************************************//** Copies a data type structure. */ UNIV_INLINE void dtype_copy( /*=======*/ dtype_t* type1, /*!< in: type struct to copy to */ const dtype_t* type2) /*!< in: type struct to copy from */ { *type1 = *type2; ut_ad(dtype_validate(type1)); } /*********************************************************************//** Gets the SQL main data type. @return SQL main data type */ UNIV_INLINE ulint dtype_get_mtype( /*============*/ const dtype_t* type) /*!< in: data type */ { ut_ad(type); return(type->mtype); } /*********************************************************************//** Gets the precise data type. @return precise data type */ UNIV_INLINE ulint dtype_get_prtype( /*=============*/ const dtype_t* type) /*!< in: data type */ { ut_ad(type); return(type->prtype); } /*********************************************************************//** Gets the type length. @return fixed length of the type, in bytes, or 0 if variable-length */ UNIV_INLINE ulint dtype_get_len( /*==========*/ const dtype_t* type) /*!< in: data type */ { ut_ad(type); return(type->len); } #ifndef UNIV_HOTBACKUP /*********************************************************************//** Gets the minimum length of a character, in bytes. @return minimum length of a char, in bytes, or 0 if this is not a character type */ UNIV_INLINE ulint dtype_get_mbminlen( /*===============*/ const dtype_t* type) /*!< in: type */ { ut_ad(type); return(type->mbminlen); } /*********************************************************************//** Gets the maximum length of a character, in bytes. @return maximum length of a char, in bytes, or 0 if this is not a character type */ UNIV_INLINE ulint dtype_get_mbmaxlen( /*===============*/ const dtype_t* type) /*!< in: type */ { ut_ad(type); return(type->mbmaxlen); } /*********************************************************************//** Gets the padding character code for a type. @return padding character code, or ULINT_UNDEFINED if no padding specified */ UNIV_INLINE ulint dtype_get_pad_char( /*===============*/ ulint mtype, /*!< in: main type */ ulint prtype) /*!< in: precise type */ { switch (mtype) { case DATA_FIXBINARY: case DATA_BINARY: if (UNIV_UNLIKELY(dtype_get_charset_coll(prtype) == DATA_CLIENT_BINARY_CHARSET_COLL)) { /* Starting from 5.0.18, do not pad VARBINARY or BINARY columns. */ return(ULINT_UNDEFINED); } /* Fall through */ case DATA_CHAR: case DATA_VARCHAR: case DATA_CLIENT: case DATA_VARCLIENT: /* Space is the padding character for all char and binary strings, and starting from 5.0.3, also for TEXT strings. */ return(0x20); case DATA_BLOB: if (!(prtype & DATA_BINARY_TYPE)) { return(0x20); } /* Fall through */ default: /* No padding specified */ return(ULINT_UNDEFINED); } } /**********************************************************************//** Stores for a type the information which determines its alphabetical ordering and the storage size of an SQL NULL value. This is the >= 4.1.x storage format. */ UNIV_INLINE void dtype_new_store_for_order_and_null_size( /*====================================*/ byte* buf, /*!< in: buffer for DATA_NEW_ORDER_NULL_TYPE_BUF_SIZE bytes where we store the info */ const dtype_t* type, /*!< in: type struct */ ulint prefix_len)/*!< in: prefix length to replace type->len, or 0 */ { #if 6 != DATA_NEW_ORDER_NULL_TYPE_BUF_SIZE #error "6 != DATA_NEW_ORDER_NULL_TYPE_BUF_SIZE" #endif ulint len; ut_ad(type); ut_ad(type->mtype >= DATA_VARCHAR); ut_ad(type->mtype <= DATA_MYSQL); buf[0] = (byte)(type->mtype & 0xFFUL); if (type->prtype & DATA_BINARY_TYPE) { buf[0] = buf[0] | 128; } buf[1] = (byte)(type->prtype & 0xFFUL); len = prefix_len ? prefix_len : type->len; mach_write_to_2(buf + 2, len & 0xFFFFUL); ut_ad(dtype_get_charset_coll(type->prtype) < 256); mach_write_to_2(buf + 4, dtype_get_charset_coll(type->prtype)); if (type->prtype & DATA_NOT_NULL) { buf[4] |= 128; } } /**********************************************************************//** Reads to a type the stored information which determines its alphabetical ordering and the storage size of an SQL NULL value. This is the < 4.1.x storage format. */ UNIV_INLINE void dtype_read_for_order_and_null_size( /*===============================*/ dtype_t* type, /*!< in: type struct */ const byte* buf) /*!< in: buffer for stored type order info */ { #if 4 != DATA_ORDER_NULL_TYPE_BUF_SIZE # error "4 != DATA_ORDER_NULL_TYPE_BUF_SIZE" #endif type->mtype = buf[0] & 63; type->prtype = buf[1]; if (buf[0] & 128) { type->prtype = type->prtype | DATA_BINARY_TYPE; } type->len = mach_read_from_2(buf + 2); type->prtype = dtype_form_prtype(type->prtype, data_client_default_charset_coll); dtype_set_mblen(type); } /**********************************************************************//** Reads to a type the stored information which determines its alphabetical ordering and the storage size of an SQL NULL value. This is the >= 4.1.x storage format. */ UNIV_INLINE void dtype_new_read_for_order_and_null_size( /*===================================*/ dtype_t* type, /*!< in: type struct */ const byte* buf) /*!< in: buffer for stored type order info */ { ulint charset_coll; #if 6 != DATA_NEW_ORDER_NULL_TYPE_BUF_SIZE #error "6 != DATA_NEW_ORDER_NULL_TYPE_BUF_SIZE" #endif type->mtype = buf[0] & 63; type->prtype = buf[1]; if (buf[0] & 128) { type->prtype |= DATA_BINARY_TYPE; } if (buf[4] & 128) { type->prtype |= DATA_NOT_NULL; } type->len = mach_read_from_2(buf + 2); charset_coll = mach_read_from_2(buf + 4) & 0x7fff; if (dtype_is_string_type(type->mtype)) { #if 0 /* FIXME: This is probably MySQL specific too. */ ut_a(charset_coll > 0); ut_a(charset_coll < 256); type->prtype = dtype_form_prtype(type->prtype, charset_coll); #else type->prtype = 1; /* FIXME: Hack for testing. */ #endif } dtype_set_mblen(type); } #endif /* !UNIV_HOTBACKUP */ /***********************************************************************//** Returns the size of a fixed size data type, 0 if not a fixed size type. @return fixed size, or 0 */ UNIV_INLINE ulint dtype_get_fixed_size_low( /*=====================*/ ulint mtype, /*!< in: main type */ ulint prtype, /*!< in: precise type */ ulint len, /*!< in: length */ ulint mbminlen, /*!< in: minimum length of a multibyte char */ ulint mbmaxlen, /*!< in: maximum length of a multibyte char */ ulint comp) /*!< in: nonzero=ROW_FORMAT=COMPACT */ { switch (mtype) { case DATA_SYS: #ifdef UNIV_DEBUG switch (prtype & DATA_CLIENT_TYPE_MASK) { case DATA_ROW_ID: ut_ad(len == DATA_ROW_ID_LEN); break; case DATA_TRX_ID: ut_ad(len == DATA_TRX_ID_LEN); break; case DATA_ROLL_PTR: ut_ad(len == DATA_ROLL_PTR_LEN); break; default: ut_ad(0); return(0); } #endif /* UNIV_DEBUG */ case DATA_CHAR: case DATA_FIXBINARY: case DATA_INT: case DATA_FLOAT: case DATA_DOUBLE: return(len); case DATA_CLIENT: if ((prtype & DATA_BINARY_TYPE) || mbminlen == mbmaxlen) { return(len); } /* fall through for variable-length charsets */ case DATA_VARCHAR: case DATA_BINARY: case DATA_DECIMAL: case DATA_VARCLIENT: case DATA_BLOB: return(0); default: ut_error; } return(0); } #ifndef UNIV_HOTBACKUP /***********************************************************************//** Returns the minimum size of a data type. @return minimum size */ UNIV_INLINE ulint dtype_get_min_size_low( /*===================*/ ulint mtype, /*!< in: main type */ ulint prtype, /*!< in: precise type */ ulint len, /*!< in: length */ ulint mbminlen, /*!< in: minimum length of a multibyte char */ ulint mbmaxlen) /*!< in: maximum length of a multibyte char */ { switch (mtype) { case DATA_SYS: #ifdef UNIV_DEBUG switch (prtype & DATA_CLIENT_TYPE_MASK) { case DATA_ROW_ID: ut_ad(len == DATA_ROW_ID_LEN); break; case DATA_TRX_ID: ut_ad(len == DATA_TRX_ID_LEN); break; case DATA_ROLL_PTR: ut_ad(len == DATA_ROLL_PTR_LEN); break; default: ut_ad(0); return(0); } #endif /* UNIV_DEBUG */ case DATA_CHAR: case DATA_FIXBINARY: case DATA_INT: case DATA_FLOAT: case DATA_DOUBLE: return(len); case DATA_CLIENT: if ((prtype & DATA_BINARY_TYPE) || mbminlen == mbmaxlen) { return(len); } /* this is a variable-length character set */ ut_a(mbminlen > 0); ut_a(mbmaxlen > mbminlen); ut_a(len % mbmaxlen == 0); return(len * mbminlen / mbmaxlen); case DATA_VARCHAR: case DATA_BINARY: case DATA_DECIMAL: case DATA_VARCLIENT: case DATA_BLOB: return(0); default: ut_error; } return(0); } /***********************************************************************//** Returns the maximum size of a data type. Note: types in system tables may be incomplete and return incorrect information. @return maximum size */ UNIV_INLINE ulint dtype_get_max_size_low( /*===================*/ ulint mtype, /*!< in: main type */ ulint len) /*!< in: length */ { switch (mtype) { case DATA_SYS: case DATA_CHAR: case DATA_FIXBINARY: case DATA_INT: case DATA_FLOAT: case DATA_DOUBLE: case DATA_CLIENT: case DATA_VARCHAR: case DATA_BINARY: case DATA_DECIMAL: case DATA_VARCLIENT: return(len); case DATA_BLOB: break; default: ut_error; } return(ULINT_MAX); } #endif /* !UNIV_HOTBACKUP */ /***********************************************************************//** Returns the ROW_FORMAT=REDUNDANT stored SQL NULL size of a type. For fixed length types it is the fixed length of the type, otherwise 0. @return SQL null storage size in ROW_FORMAT=REDUNDANT */ UNIV_INLINE ulint dtype_get_sql_null_size( /*====================*/ const dtype_t* type, /*!< in: type */ ulint comp) /*!< in: nonzero=ROW_FORMAT=COMPACT */ { #ifndef UNIV_HOTBACKUP return(dtype_get_fixed_size_low(type->mtype, type->prtype, type->len, type->mbminlen, type->mbmaxlen, comp)); #else /* !UNIV_HOTBACKUP */ return(dtype_get_fixed_size_low(type->mtype, type->prtype, type->len, 0, 0, 0)); #endif /* !UNIV_HOTBACKUP */ } haildb-2.3.2/include/lock0priv.h0000644000175000017500000000640211513177357017352 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 2007, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/lock0priv.h Lock module internal structures and methods. Created July 12, 2007 Vasil Dimov *******************************************************/ #ifndef lock0priv_h #define lock0priv_h #ifndef LOCK_MODULE_IMPLEMENTATION /* If you need to access members of the structures defined in this file, please write appropriate functions that retrieve them and put those functions in lock/ */ #error Do not include lock0priv.h outside of the lock/ module #endif #include "univ.i" #include "dict0types.h" #include "hash0hash.h" #include "trx0types.h" #include "ut0lst.h" /** A table lock */ typedef struct lock_table_struct lock_table_t; /** A table lock */ struct lock_table_struct { dict_table_t* table; /*!< database table in dictionary cache */ UT_LIST_NODE_T(lock_t) locks; /*!< list of locks on the same table */ }; /** Record lock for a page */ typedef struct lock_rec_struct lock_rec_t; /** Record lock for a page */ struct lock_rec_struct { ulint space; /*!< space id */ ulint page_no; /*!< page number */ ulint n_bits; /*!< number of bits in the lock bitmap; NOTE: the lock bitmap is placed immediately after the lock struct */ }; /** Lock struct */ struct lock_struct { trx_t* trx; /*!< transaction owning the lock */ UT_LIST_NODE_T(lock_t) trx_locks; /*!< list of the locks of the transaction */ ulint type_mode; /*!< lock type, mode, LOCK_GAP or LOCK_REC_NOT_GAP, LOCK_INSERT_INTENTION, wait flag, ORed */ hash_node_t hash; /*!< hash chain node for a record lock */ dict_index_t* index; /*!< index for a record lock */ union { lock_table_t tab_lock;/*!< table lock */ lock_rec_t rec_lock;/*!< record lock */ } un_member; /*!< lock details */ }; /*********************************************************************//** Gets the type of a lock. @return LOCK_TABLE or LOCK_REC */ UNIV_INLINE ulint lock_get_type_low( /*==============*/ const lock_t* lock); /*!< in: lock */ /*********************************************************************//** Gets the previous record lock set on a record. @return previous lock on the same record, NULL if none exists */ UNIV_INTERN const lock_t* lock_rec_get_prev( /*==============*/ const lock_t* in_lock,/*!< in: record lock */ ulint heap_no);/*!< in: heap number of the record */ #ifndef UNIV_NONINL #include "lock0priv.ic" #endif #endif /* lock0priv_h */ haildb-2.3.2/include/buf0rea.h0000644000175000017500000001275611513177357016776 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/buf0rea.h The database buffer read Created 11/5/1995 Heikki Tuuri *******************************************************/ #ifndef buf0rea_h #define buf0rea_h #include "univ.i" #include "buf0types.h" /********************************************************************//** High-level function which reads a page asynchronously from a file to the buffer buf_pool if it is not already there. Sets the io_fix flag and sets an exclusive lock on the buffer frame. The flag is cleared and the x-lock released by the i/o-handler thread. @return TRUE if page has been read in, FALSE in case of failure */ UNIV_INTERN ibool buf_read_page( /*==========*/ ulint space, /*!< in: space id */ ulint zip_size,/*!< in: compressed page size in bytes, or 0 */ ulint offset);/*!< in: page number */ /********************************************************************//** Applies linear read-ahead if in the buf_pool the page is a border page of a linear read-ahead area and all the pages in the area have been accessed. Does not read any page if the read-ahead mechanism is not activated. Note that the algorithm looks at the 'natural' adjacent successor and predecessor of the page, which on the leaf level of a B-tree are the next and previous page in the chain of leaves. To know these, the page specified in (space, offset) must already be present in the buf_pool. Thus, the natural way to use this function is to call it when a page in the buf_pool is accessed the first time, calling this function just after it has been bufferfixed. NOTE 1: as this function looks at the natural predecessor and successor fields on the page, what happens, if these are not initialized to any sensible value? No problem, before applying read-ahead we check that the area to read is within the span of the space, if not, read-ahead is not applied. An uninitialized value may result in a useless read operation, but only very improbably. NOTE 2: the calling thread may own latches on pages: to avoid deadlocks this function must be written such that it cannot end up waiting for these latches! NOTE 3: the calling thread must want access to the page given: this rule is set to prevent unintended read-aheads performed by ibuf routines, a situation which could result in a deadlock if the OS does not support asynchronous io. @return number of page read requests issued */ UNIV_INTERN ulint buf_read_ahead_linear( /*==================*/ ulint space, /*!< in: space id */ ulint zip_size,/*!< in: compressed page size in bytes, or 0 */ ulint offset);/*!< in: page number of a page; NOTE: the current thread must want access to this page (see NOTE 3 above) */ /********************************************************************//** Issues read requests for pages which the ibuf module wants to read in, in order to contract the insert buffer tree. Technically, this function is like a read-ahead function. */ UNIV_INTERN void buf_read_ibuf_merge_pages( /*======================*/ ibool sync, /*!< in: TRUE if the caller wants this function to wait for the highest address page to get read in, before this function returns */ const ulint* space_ids, /*!< in: array of space ids */ const ib_int64_t* space_versions,/*!< in: the spaces must have this version number (timestamp), otherwise we discard the read; we use this to cancel reads if DISCARD + IMPORT may have changed the tablespace size */ const ulint* page_nos, /*!< in: array of page numbers to read, with the highest page number the last in the array */ ulint n_stored); /*!< in: number of elements in the arrays */ /********************************************************************//** Issues read requests for pages which recovery wants to read in. */ UNIV_INTERN void buf_read_recv_pages( /*================*/ ibool sync, /*!< in: TRUE if the caller wants this function to wait for the highest address page to get read in, before this function returns */ ulint space, /*!< in: space id */ ulint zip_size, /*!< in: compressed page size in bytes, or 0 */ const ulint* page_nos, /*!< in: array of page numbers to read, with the highest page number the last in the array */ ulint n_stored); /*!< in: number of page numbers in the array */ /** The size in pages of the area which the read-ahead algorithms read if invoked */ #define BUF_READ_AHEAD_AREA \ ut_min(64, ut_2_power_up(buf_pool->curr_size / 32)) /** @name Modes used in read-ahead @{ */ /** read only pages belonging to the insert buffer tree */ #define BUF_READ_IBUF_PAGES_ONLY 131 /** read any page */ #define BUF_READ_ANY_PAGE 132 /* @} */ #endif haildb-2.3.2/include/eval0eval.ic0000644000175000017500000001444611513177357017473 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1997, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/eval0eval.ic SQL evaluator: evaluates simple data structures, like expressions, in a query graph Created 12/29/1997 Heikki Tuuri *******************************************************/ #include "que0que.h" #include "rem0cmp.h" #include "pars0grm.h" /*****************************************************************//** Evaluates a function node. */ UNIV_INTERN void eval_func( /*======*/ func_node_t* func_node); /*!< in: function node */ /*****************************************************************//** Allocate a buffer from global dynamic memory for a value of a que_node. NOTE that this memory must be explicitly freed when the query graph is freed. If the node already has allocated buffer, that buffer is freed here. NOTE that this is the only function where dynamic memory should be allocated for a query node val field. @return pointer to allocated buffer */ UNIV_INTERN byte* eval_node_alloc_val_buf( /*====================*/ que_node_t* node, /*!< in: query graph node; sets the val field data field to point to the new buffer, and len field equal to size */ ulint size); /*!< in: buffer size */ /*****************************************************************//** Allocates a new buffer if needed. @return pointer to buffer */ UNIV_INLINE byte* eval_node_ensure_val_buf( /*=====================*/ que_node_t* node, /*!< in: query graph node; sets the val field data field to point to the new buffer, and len field equal to size */ ulint size) /*!< in: buffer size */ { dfield_t* dfield; byte* data; dfield = que_node_get_val(node); dfield_set_len(dfield, size); data = dfield_get_data(dfield); if (!data || que_node_get_val_buf_size(node) < size) { data = eval_node_alloc_val_buf(node, size); } return(data); } /*****************************************************************//** Evaluates a symbol table symbol. */ UNIV_INLINE void eval_sym( /*=====*/ sym_node_t* sym_node) /*!< in: symbol table node */ { ut_ad(que_node_get_type(sym_node) == QUE_NODE_SYMBOL); if (sym_node->indirection) { /* The symbol table node is an alias for a variable or a column */ dfield_copy_data(que_node_get_val(sym_node), que_node_get_val(sym_node->indirection)); } } /*****************************************************************//** Evaluates an expression. */ UNIV_INLINE void eval_exp( /*=====*/ que_node_t* exp_node) /*!< in: expression */ { if (que_node_get_type(exp_node) == QUE_NODE_SYMBOL) { eval_sym((sym_node_t*)exp_node); return; } eval_func(exp_node); } /*****************************************************************//** Sets an integer value as the value of an expression node. */ UNIV_INLINE void eval_node_set_int_val( /*==================*/ que_node_t* node, /*!< in: expression node */ lint val) /*!< in: value to set */ { dfield_t* dfield; byte* data; dfield = que_node_get_val(node); data = dfield_get_data(dfield); if (data == NULL) { data = eval_node_alloc_val_buf(node, 4); } ut_ad(dfield_get_len(dfield) == 4); mach_write_to_4(data, (ulint)val); } /*****************************************************************//** Gets an integer non-SQL null value from an expression node. @return integer value */ UNIV_INLINE lint eval_node_get_int_val( /*==================*/ que_node_t* node) /*!< in: expression node */ { dfield_t* dfield; dfield = que_node_get_val(node); ut_ad(dfield_get_len(dfield) == 4); return((int)mach_read_from_4(dfield_get_data(dfield))); } /*****************************************************************//** Gets a iboolean value from a query node. @return iboolean value */ UNIV_INLINE ibool eval_node_get_ibool_val( /*====================*/ que_node_t* node) /*!< in: query graph node */ { dfield_t* dfield; byte* data; dfield = que_node_get_val(node); data = dfield_get_data(dfield); ut_ad(data != NULL); return(mach_read_from_1(data)); } /*****************************************************************//** Sets a iboolean value as the value of a function node. */ UNIV_INLINE void eval_node_set_ibool_val( /*====================*/ func_node_t* func_node, /*!< in: function node */ ibool val) /*!< in: value to set */ { dfield_t* dfield; byte* data; dfield = que_node_get_val(func_node); data = dfield_get_data(dfield); if (data == NULL) { /* Allocate 1 byte to hold the value */ data = eval_node_alloc_val_buf(func_node, 1); } ut_ad(dfield_get_len(dfield) == 1); mach_write_to_1(data, val); } /*****************************************************************//** Copies a binary string value as the value of a query graph node. Allocates a new buffer if necessary. */ UNIV_INLINE void eval_node_copy_and_alloc_val( /*=========================*/ que_node_t* node, /*!< in: query graph node */ const byte* str, /*!< in: binary string */ ulint len) /*!< in: string length or UNIV_SQL_NULL */ { byte* data; if (len == UNIV_SQL_NULL) { dfield_set_len(que_node_get_val(node), len); return; } data = eval_node_ensure_val_buf(node, len); ut_memcpy(data, str, len); } /*****************************************************************//** Copies a query node value to another node. */ UNIV_INLINE void eval_node_copy_val( /*===============*/ que_node_t* node1, /*!< in: node to copy to */ que_node_t* node2) /*!< in: node to copy from */ { dfield_t* dfield2; dfield2 = que_node_get_val(node2); eval_node_copy_and_alloc_val(node1, dfield_get_data(dfield2), dfield_get_len(dfield2)); } haildb-2.3.2/include/dyn0dyn.h0000644000175000017500000001420511513177357017026 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/dyn0dyn.h The dynamically allocated array Created 2/5/1996 Heikki Tuuri *******************************************************/ #ifndef dyn0dyn_h #define dyn0dyn_h #include "univ.i" #include "ut0lst.h" #include "mem0mem.h" /** A block in a dynamically allocated array */ typedef struct dyn_block_struct dyn_block_t; /** Dynamically allocated array */ typedef dyn_block_t dyn_array_t; /** This is the initial 'payload' size of a dynamic array; this must be > MLOG_BUF_MARGIN + 30! */ #define DYN_ARRAY_DATA_SIZE 512 /*********************************************************************//** Initializes a dynamic array. @return initialized dyn array */ UNIV_INLINE dyn_array_t* dyn_array_create( /*=============*/ dyn_array_t* arr); /*!< in: pointer to a memory buffer of size sizeof(dyn_array_t) */ /************************************************************//** Frees a dynamic array. */ UNIV_INLINE void dyn_array_free( /*===========*/ dyn_array_t* arr); /*!< in: dyn array */ /*********************************************************************//** Makes room on top of a dyn array and returns a pointer to a buffer in it. After copying the elements, the caller must close the buffer using dyn_array_close. @return pointer to the buffer */ UNIV_INLINE byte* dyn_array_open( /*===========*/ dyn_array_t* arr, /*!< in: dynamic array */ ulint size); /*!< in: size in bytes of the buffer; MUST be smaller than DYN_ARRAY_DATA_SIZE! */ /*********************************************************************//** Closes the buffer returned by dyn_array_open. */ UNIV_INLINE void dyn_array_close( /*============*/ dyn_array_t* arr, /*!< in: dynamic array */ byte* ptr); /*!< in: buffer space from ptr up was not used */ /*********************************************************************//** Makes room on top of a dyn array and returns a pointer to the added element. The caller must copy the element to the pointer returned. @return pointer to the element */ UNIV_INLINE void* dyn_array_push( /*===========*/ dyn_array_t* arr, /*!< in: dynamic array */ ulint size); /*!< in: size in bytes of the element */ /************************************************************//** Returns pointer to an element in dyn array. @return pointer to element */ UNIV_INLINE void* dyn_array_get_element( /*==================*/ dyn_array_t* arr, /*!< in: dyn array */ ulint pos); /*!< in: position of element as bytes from array start */ /************************************************************//** Returns the size of stored data in a dyn array. @return data size in bytes */ UNIV_INLINE ulint dyn_array_get_data_size( /*====================*/ dyn_array_t* arr); /*!< in: dyn array */ /************************************************************//** Gets the first block in a dyn array. */ UNIV_INLINE dyn_block_t* dyn_array_get_first_block( /*======================*/ dyn_array_t* arr); /*!< in: dyn array */ /************************************************************//** Gets the last block in a dyn array. */ UNIV_INLINE dyn_block_t* dyn_array_get_last_block( /*=====================*/ dyn_array_t* arr); /*!< in: dyn array */ /********************************************************************//** Gets the next block in a dyn array. @return pointer to next, NULL if end of list */ UNIV_INLINE dyn_block_t* dyn_array_get_next_block( /*=====================*/ dyn_array_t* arr, /*!< in: dyn array */ dyn_block_t* block); /*!< in: dyn array block */ /********************************************************************//** Gets the number of used bytes in a dyn array block. @return number of bytes used */ UNIV_INLINE ulint dyn_block_get_used( /*===============*/ dyn_block_t* block); /*!< in: dyn array block */ /********************************************************************//** Gets pointer to the start of data in a dyn array block. @return pointer to data */ UNIV_INLINE byte* dyn_block_get_data( /*===============*/ dyn_block_t* block); /*!< in: dyn array block */ /********************************************************//** Pushes n bytes to a dyn array. */ UNIV_INLINE void dyn_push_string( /*============*/ dyn_array_t* arr, /*!< in: dyn array */ const byte* str, /*!< in: string to write */ ulint len); /*!< in: string length */ /*#################################################################*/ /** @brief A block in a dynamically allocated array. NOTE! Do not access the fields of the struct directly: the definition appears here only for the compiler to know its size! */ struct dyn_block_struct{ mem_heap_t* heap; /*!< in the first block this is != NULL if dynamic allocation has been needed */ ulint used; /*!< number of data bytes used in this block; DYN_BLOCK_FULL_FLAG is set when the block becomes full */ byte data[DYN_ARRAY_DATA_SIZE]; /*!< storage for array elements */ UT_LIST_BASE_NODE_T(dyn_block_t) base; /*!< linear list of dyn blocks: this node is used only in the first block */ UT_LIST_NODE_T(dyn_block_t) list; /*!< linear list node: used in all blocks */ #ifdef UNIV_DEBUG ulint buf_end;/*!< only in the debug version: if dyn array is opened, this is the buffer end offset, else this is 0 */ ulint magic_n;/*!< magic number (DYN_BLOCK_MAGIC_N) */ #endif }; #ifndef UNIV_NONINL #include "dyn0dyn.ic" #endif #endif haildb-2.3.2/include/row0row.h0000644000175000017500000002644111513177357017065 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/row0row.h General row routines Created 4/20/1996 Heikki Tuuri *******************************************************/ #ifndef row0row_h #define row0row_h #include "univ.i" #include "data0data.h" #include "dict0types.h" #include "trx0types.h" #include "que0types.h" #include "mtr0mtr.h" #include "rem0types.h" #include "read0types.h" #include "row0types.h" #include "btr0types.h" /*********************************************************************//** Gets the offset of the trx id field, in bytes relative to the origin of a clustered index record. @return offset of DATA_TRX_ID */ UNIV_INTERN ulint row_get_trx_id_offset( /*==================*/ const rec_t* rec, /*!< in: record */ dict_index_t* index, /*!< in: clustered index */ const ulint* offsets);/*!< in: rec_get_offsets(rec, index) */ /*********************************************************************//** Reads the trx id field from a clustered index record. @return value of the field */ UNIV_INLINE trx_id_t row_get_rec_trx_id( /*===============*/ const rec_t* rec, /*!< in: record */ dict_index_t* index, /*!< in: clustered index */ const ulint* offsets);/*!< in: rec_get_offsets(rec, index) */ /*********************************************************************//** Reads the roll pointer field from a clustered index record. @return value of the field */ UNIV_INLINE roll_ptr_t row_get_rec_roll_ptr( /*=================*/ const rec_t* rec, /*!< in: record */ dict_index_t* index, /*!< in: clustered index */ const ulint* offsets);/*!< in: rec_get_offsets(rec, index) */ /*****************************************************************//** When an insert or purge to a table is performed, this function builds the entry to be inserted into or purged from an index on the table. @return index entry which should be inserted or purged, or NULL if the externally stored columns in the clustered index record are unavailable and ext != NULL */ UNIV_INTERN dtuple_t* row_build_index_entry( /*==================*/ const dtuple_t* row, /*!< in: row which should be inserted or purged */ row_ext_t* ext, /*!< in: externally stored column prefixes, or NULL */ dict_index_t* index, /*!< in: index on the table */ mem_heap_t* heap); /*!< in: memory heap from which the memory for the index entry is allocated */ /*******************************************************************//** An inverse function to row_build_index_entry. Builds a row from a record in a clustered index. @return own: row built; see the NOTE below! */ UNIV_INTERN dtuple_t* row_build( /*======*/ ulint type, /*!< in: ROW_COPY_POINTERS or ROW_COPY_DATA; the latter copies also the data fields to heap while the first only places pointers to data fields on the index page, and thus is more efficient */ const dict_index_t* index, /*!< in: clustered index */ const rec_t* rec, /*!< in: record in the clustered index; NOTE: in the case ROW_COPY_POINTERS the data fields in the row will point directly into this record, therefore, the buffer page of this record must be at least s-latched and the latch held as long as the row dtuple is used! */ const ulint* offsets,/*!< in: rec_get_offsets(rec,index) or NULL, in which case this function will invoke rec_get_offsets() */ const dict_table_t* col_table, /*!< in: table, to check which externally stored columns occur in the ordering columns of an index, or NULL if index->table should be consulted instead; the user columns in this table should be the same columns as in index->table */ row_ext_t** ext, /*!< out, own: cache of externally stored column prefixes, or NULL */ mem_heap_t* heap); /*!< in: memory heap from which the memory needed is allocated */ /*******************************************************************//** Converts an index record to a typed data tuple. @return index entry built; does not set info_bits, and the data fields in the entry will point directly to rec */ UNIV_INTERN dtuple_t* row_rec_to_index_entry_low( /*=======================*/ const rec_t* rec, /*!< in: record in the index */ const dict_index_t* index, /*!< in: index */ const ulint* offsets,/*!< in: rec_get_offsets(rec, index) */ ulint* n_ext, /*!< out: number of externally stored columns */ mem_heap_t* heap); /*!< in: memory heap from which the memory needed is allocated */ /*******************************************************************//** Converts an index record to a typed data tuple. NOTE that externally stored (often big) fields are NOT copied to heap. @return own: index entry built; see the NOTE below! */ UNIV_INTERN dtuple_t* row_rec_to_index_entry( /*===================*/ ulint type, /*!< in: ROW_COPY_DATA, or ROW_COPY_POINTERS: the former copies also the data fields to heap as the latter only places pointers to data fields on the index page */ const rec_t* rec, /*!< in: record in the index; NOTE: in the case ROW_COPY_POINTERS the data fields in the row will point directly into this record, therefore, the buffer page of this record must be at least s-latched and the latch held as long as the dtuple is used! */ const dict_index_t* index, /*!< in: index */ ulint* offsets,/*!< in/out: rec_get_offsets(rec) */ ulint* n_ext, /*!< out: number of externally stored columns */ mem_heap_t* heap); /*!< in: memory heap from which the memory needed is allocated */ /*******************************************************************//** Builds from a secondary index record a row reference with which we can search the clustered index record. @return own: row reference built; see the NOTE below! */ UNIV_INTERN dtuple_t* row_build_row_ref( /*==============*/ ulint type, /*!< in: ROW_COPY_DATA, or ROW_COPY_POINTERS: the former copies also the data fields to heap, whereas the latter only places pointers to data fields on the index page */ dict_index_t* index, /*!< in: secondary index */ const rec_t* rec, /*!< in: record in the index; NOTE: in the case ROW_COPY_POINTERS the data fields in the row will point directly into this record, therefore, the buffer page of this record must be at least s-latched and the latch held as long as the row reference is used! */ mem_heap_t* heap); /*!< in: memory heap from which the memory needed is allocated */ /*******************************************************************//** Builds from a secondary index record a row reference with which we can search the clustered index record. */ UNIV_INTERN void row_build_row_ref_in_tuple( /*=======================*/ dtuple_t* ref, /*!< in/out: row reference built; see the NOTE below! */ const rec_t* rec, /*!< in: record in the index; NOTE: the data fields in ref will point directly into this record, therefore, the buffer page of this record must be at least s-latched and the latch held as long as the row reference is used! */ const dict_index_t* index, /*!< in: secondary index */ ulint* offsets,/*!< in: rec_get_offsets(rec, index) or NULL */ trx_t* trx); /*!< in: transaction */ /*******************************************************************//** Builds from a secondary index record a row reference with which we can search the clustered index record. */ UNIV_INLINE void row_build_row_ref_fast( /*===================*/ dtuple_t* ref, /*!< in/out: typed data tuple where the reference is built */ const ulint* map, /*!< in: array of field numbers in rec telling how ref should be built from the fields of rec */ const rec_t* rec, /*!< in: record in the index; must be preserved while ref is used, as we do not copy field values to heap */ const ulint* offsets);/*!< in: array returned by rec_get_offsets() */ /***************************************************************//** Searches the clustered index record for a row, if we have the row reference. @return TRUE if found */ UNIV_INTERN ibool row_search_on_row_ref( /*==================*/ btr_pcur_t* pcur, /*!< out: persistent cursor, which must be closed by the caller */ ulint mode, /*!< in: BTR_MODIFY_LEAF, ... */ const dict_table_t* table, /*!< in: table */ const dtuple_t* ref, /*!< in: row reference */ mtr_t* mtr); /*!< in/out: mtr */ /*********************************************************************//** Fetches the clustered index record for a secondary index record. The latches on the secondary index record are preserved. @return record or NULL, if no record found */ UNIV_INTERN rec_t* row_get_clust_rec( /*==============*/ ulint mode, /*!< in: BTR_MODIFY_LEAF, ... */ const rec_t* rec, /*!< in: record in a secondary index */ dict_index_t* index, /*!< in: secondary index */ dict_index_t** clust_index,/*!< out: clustered index */ mtr_t* mtr); /*!< in: mtr */ /***************************************************************//** Searches an index record. @return TRUE if found */ UNIV_INTERN ibool row_search_index_entry( /*===================*/ dict_index_t* index, /*!< in: index */ const dtuple_t* entry, /*!< in: index entry */ ulint mode, /*!< in: BTR_MODIFY_LEAF, ... */ btr_pcur_t* pcur, /*!< in/out: persistent cursor, which must be closed by the caller */ mtr_t* mtr); /*!< in: mtr */ #define ROW_COPY_DATA 1 #define ROW_COPY_POINTERS 2 /* The allowed latching order of index records is the following: (1) a secondary index record -> (2) the clustered index record -> (3) rollback segment data for the clustered index record. No new latches may be obtained while the kernel mutex is reserved. However, the kernel mutex can be reserved while latches are owned. */ /*******************************************************************//** Formats the raw data in "data" (in InnoDB on-disk format) using "dict_field" and writes the result to "buf". Not more than "buf_size" bytes are written to "buf". The result is always NUL-terminated (provided buf_size is positive) and the number of bytes that were written to "buf" is returned (including the terminating NUL). @return number of bytes that were written */ UNIV_INTERN ulint row_raw_format( /*===========*/ const char* data, /*!< in: raw data */ ulint data_len, /*!< in: raw data length in bytes */ const dict_field_t* dict_field, /*!< in: index field */ char* buf, /*!< out: output buffer */ ulint buf_size); /*!< in: output buffer size in bytes */ #ifndef UNIV_NONINL #include "row0row.ic" #endif #endif haildb-2.3.2/include/btr0cur.h0000644000175000017500000007545111513177357017034 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1994, 2010, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/btr0cur.h The index tree cursor Created 10/16/1994 Heikki Tuuri *******************************************************/ #ifndef btr0cur_h #define btr0cur_h #include "univ.i" #include "dict0dict.h" #include "page0cur.h" #include "btr0types.h" /* Mode flags for btr_cur operations; these can be ORed */ #define BTR_NO_UNDO_LOG_FLAG 1 /* do no undo logging */ #define BTR_NO_LOCKING_FLAG 2 /* do no record lock checking */ #define BTR_KEEP_SYS_FLAG 4 /* sys fields will be found from the update vector or inserted entry */ #ifndef UNIV_HOTBACKUP #include "que0types.h" #include "row0types.h" #include "ha0ha.h" #define BTR_CUR_ADAPT #define BTR_CUR_HASH_ADAPT #ifdef UNIV_DEBUG /*********************************************************//** Returns the page cursor component of a tree cursor. @return pointer to page cursor component */ UNIV_INLINE page_cur_t* btr_cur_get_page_cur( /*=================*/ const btr_cur_t* cursor);/*!< in: tree cursor */ #else /* UNIV_DEBUG */ # define btr_cur_get_page_cur(cursor) (&(cursor)->page_cur) #endif /* UNIV_DEBUG */ /*********************************************************//** Returns the buffer block on which the tree cursor is positioned. @return pointer to buffer block */ UNIV_INLINE buf_block_t* btr_cur_get_block( /*==============*/ btr_cur_t* cursor);/*!< in: tree cursor */ /*********************************************************//** Returns the record pointer of a tree cursor. @return pointer to record */ UNIV_INLINE rec_t* btr_cur_get_rec( /*============*/ btr_cur_t* cursor);/*!< in: tree cursor */ /*********************************************************//** Returns the compressed page on which the tree cursor is positioned. @return pointer to compressed page, or NULL if the page is not compressed */ UNIV_INLINE page_zip_des_t* btr_cur_get_page_zip( /*=================*/ btr_cur_t* cursor);/*!< in: tree cursor */ /*********************************************************//** Invalidates a tree cursor by setting record pointer to NULL. */ UNIV_INLINE void btr_cur_invalidate( /*===============*/ btr_cur_t* cursor);/*!< in: tree cursor */ /*********************************************************//** Returns the page of a tree cursor. @return pointer to page */ UNIV_INLINE page_t* btr_cur_get_page( /*=============*/ btr_cur_t* cursor);/*!< in: tree cursor */ /*********************************************************//** Returns the index of a cursor. @return index */ UNIV_INLINE dict_index_t* btr_cur_get_index( /*==============*/ btr_cur_t* cursor);/*!< in: B-tree cursor */ /*********************************************************//** Positions a tree cursor at a given record. */ UNIV_INLINE void btr_cur_position( /*=============*/ dict_index_t* index, /*!< in: index */ rec_t* rec, /*!< in: record in tree */ buf_block_t* block, /*!< in: buffer block of rec */ btr_cur_t* cursor);/*!< in: cursor */ /********************************************************************//** Searches an index tree and positions a tree cursor on a given level. NOTE: n_fields_cmp in tuple must be set so that it cannot be compared to node pointer page number fields on the upper levels of the tree! Note that if mode is PAGE_CUR_LE, which is used in inserts, then cursor->up_match and cursor->low_match both will have sensible values. If mode is PAGE_CUR_GE, then up_match will a have a sensible value. */ UNIV_INTERN void btr_cur_search_to_nth_level( /*========================*/ dict_index_t* index, /*!< in: index */ ulint level, /*!< in: the tree level of search */ const dtuple_t* tuple, /*!< in: data tuple; NOTE: n_fields_cmp in tuple must be set so that it cannot get compared to the node ptr page number field! */ ulint mode, /*!< in: PAGE_CUR_L, ...; NOTE that if the search is made using a unique prefix of a record, mode should be PAGE_CUR_LE, not PAGE_CUR_GE, as the latter may end up on the previous page of the record! Inserts should always be made using PAGE_CUR_LE to search the position! */ ulint latch_mode, /*!< in: BTR_SEARCH_LEAF, ..., ORed with BTR_INSERT and BTR_ESTIMATE; cursor->left_block is used to store a pointer to the left neighbor page, in the cases BTR_SEARCH_PREV and BTR_MODIFY_PREV; NOTE that if has_search_latch is != 0, we maybe do not have a latch set on the cursor page, we assume the caller uses his search latch to protect the record! */ btr_cur_t* cursor, /*!< in/out: tree cursor; the cursor page is s- or x-latched, but see also above! */ ulint has_search_latch,/*!< in: latch mode the caller currently has on btr_search_latch: RW_S_LATCH, or 0 */ const char* file, /*!< in: file name */ ulint line, /*!< in: line where called */ mtr_t* mtr); /*!< in: mtr */ /*****************************************************************//** Opens a cursor at either end of an index. */ UNIV_INTERN void btr_cur_open_at_index_side_func( /*============================*/ ibool from_left, /*!< in: TRUE if open to the low end, FALSE if to the high end */ dict_index_t* index, /*!< in: index */ ulint latch_mode, /*!< in: latch mode */ btr_cur_t* cursor, /*!< in: cursor */ const char* file, /*!< in: file name */ ulint line, /*!< in: line where called */ mtr_t* mtr); /*!< in: mtr */ #define btr_cur_open_at_index_side(f,i,l,c,m) \ btr_cur_open_at_index_side_func(f,i,l,c,__FILE__,__LINE__,m) /**********************************************************************//** Positions a cursor at a randomly chosen position within a B-tree. */ UNIV_INTERN void btr_cur_open_at_rnd_pos_func( /*=========================*/ dict_index_t* index, /*!< in: index */ ulint latch_mode, /*!< in: BTR_SEARCH_LEAF, ... */ btr_cur_t* cursor, /*!< in/out: B-tree cursor */ const char* file, /*!< in: file name */ ulint line, /*!< in: line where called */ mtr_t* mtr); /*!< in: mtr */ #define btr_cur_open_at_rnd_pos(i,l,c,m) \ btr_cur_open_at_rnd_pos_func(i,l,c,__FILE__,__LINE__,m) /*************************************************************//** Tries to perform an insert to a page in an index tree, next to cursor. It is assumed that mtr holds an x-latch on the page. The operation does not succeed if there is too little space on the page. If there is just one record on the page, the insert will always succeed; this is to prevent trying to split a page with just one record. @return DB_SUCCESS, DB_WAIT_LOCK, DB_FAIL, or error number */ UNIV_INTERN ulint btr_cur_optimistic_insert( /*======================*/ ulint flags, /*!< in: undo logging and locking flags: if not zero, the parameters index and thr should be specified */ btr_cur_t* cursor, /*!< in: cursor on page after which to insert; cursor stays valid */ dtuple_t* entry, /*!< in/out: entry to insert */ rec_t** rec, /*!< out: pointer to inserted record if succeed */ big_rec_t** big_rec,/*!< out: big rec vector whose fields have to be stored externally by the caller, or NULL */ ulint n_ext, /*!< in: number of externally stored columns */ que_thr_t* thr, /*!< in: query thread or NULL */ mtr_t* mtr); /*!< in: mtr; if this function returns DB_SUCCESS on a leaf page of a secondary index in a compressed tablespace, the mtr must be committed before latching any further pages */ /*************************************************************//** Performs an insert on a page of an index tree. It is assumed that mtr holds an x-latch on the tree and on the cursor page. If the insert is made on the leaf level, to avoid deadlocks, mtr must also own x-latches to brothers of page, if those brothers exist. @return DB_SUCCESS or error number */ UNIV_INTERN ulint btr_cur_pessimistic_insert( /*=======================*/ ulint flags, /*!< in: undo logging and locking flags: if not zero, the parameter thr should be specified; if no undo logging is specified, then the caller must have reserved enough free extents in the file space so that the insertion will certainly succeed */ btr_cur_t* cursor, /*!< in: cursor after which to insert; cursor stays valid */ dtuple_t* entry, /*!< in/out: entry to insert */ rec_t** rec, /*!< out: pointer to inserted record if succeed */ big_rec_t** big_rec,/*!< out: big rec vector whose fields have to be stored externally by the caller, or NULL */ ulint n_ext, /*!< in: number of externally stored columns */ que_thr_t* thr, /*!< in: query thread or NULL */ mtr_t* mtr); /*!< in: mtr */ /*************************************************************//** Updates a record when the update causes no size changes in its fields. @return DB_SUCCESS or error number */ UNIV_INTERN ulint btr_cur_update_in_place( /*====================*/ ulint flags, /*!< in: undo logging and locking flags */ btr_cur_t* cursor, /*!< in: cursor on the record to update; cursor stays valid and positioned on the same record */ const upd_t* update, /*!< in: update vector */ ulint cmpl_info,/*!< in: compiler info on secondary index updates */ que_thr_t* thr, /*!< in: query thread */ mtr_t* mtr); /*!< in: mtr; must be committed before latching any further pages */ /*************************************************************//** Tries to update a record on a page in an index tree. It is assumed that mtr holds an x-latch on the page. The operation does not succeed if there is too little space on the page or if the update would result in too empty a page, so that tree compression is recommended. @return DB_SUCCESS, or DB_OVERFLOW if the updated record does not fit, DB_UNDERFLOW if the page would become too empty, or DB_ZIP_OVERFLOW if there is not enough space left on the compressed page */ UNIV_INTERN ulint btr_cur_optimistic_update( /*======================*/ ulint flags, /*!< in: undo logging and locking flags */ btr_cur_t* cursor, /*!< in: cursor on the record to update; cursor stays valid and positioned on the same record */ const upd_t* update, /*!< in: update vector; this must also contain trx id and roll ptr fields */ ulint cmpl_info,/*!< in: compiler info on secondary index updates */ que_thr_t* thr, /*!< in: query thread */ mtr_t* mtr); /*!< in: mtr; must be committed before latching any further pages */ /*************************************************************//** Performs an update of a record on a page of a tree. It is assumed that mtr holds an x-latch on the tree and on the cursor page. If the update is made on the leaf level, to avoid deadlocks, mtr must also own x-latches to brothers of page, if those brothers exist. @return DB_SUCCESS or error code */ UNIV_INTERN ulint btr_cur_pessimistic_update( /*=======================*/ ulint flags, /*!< in: undo logging, locking, and rollback flags */ btr_cur_t* cursor, /*!< in: cursor on the record to update */ mem_heap_t** heap, /*!< in/out: pointer to memory heap, or NULL */ big_rec_t** big_rec,/*!< out: big rec vector whose fields have to be stored externally by the caller, or NULL */ const upd_t* update, /*!< in: update vector; this is allowed also contain trx id and roll ptr fields, but the values in update vector have no effect */ ulint cmpl_info,/*!< in: compiler info on secondary index updates */ que_thr_t* thr, /*!< in: query thread */ mtr_t* mtr); /*!< in: mtr; must be committed before latching any further pages */ /***********************************************************//** Marks a clustered index record deleted. Writes an undo log record to undo log on this delete marking. Writes in the trx id field the id of the deleting transaction, and in the roll ptr field pointer to the undo log record created. @return DB_SUCCESS, DB_LOCK_WAIT, or error number */ UNIV_INTERN ulint btr_cur_del_mark_set_clust_rec( /*===========================*/ ulint flags, /*!< in: undo logging and locking flags */ btr_cur_t* cursor, /*!< in: cursor */ ibool val, /*!< in: value to set */ que_thr_t* thr, /*!< in: query thread */ mtr_t* mtr); /*!< in: mtr */ /***********************************************************//** Sets a secondary index record delete mark to TRUE or FALSE. @return DB_SUCCESS, DB_LOCK_WAIT, or error number */ UNIV_INTERN ulint btr_cur_del_mark_set_sec_rec( /*=========================*/ ulint flags, /*!< in: locking flag */ btr_cur_t* cursor, /*!< in: cursor */ ibool val, /*!< in: value to set */ que_thr_t* thr, /*!< in: query thread */ mtr_t* mtr); /*!< in: mtr */ /***********************************************************//** Clear a secondary index record's delete mark. This function is only used by the insert buffer insert merge mechanism. */ UNIV_INTERN void btr_cur_del_unmark_for_ibuf( /*========================*/ rec_t* rec, /*!< in/out: record to delete unmark */ page_zip_des_t* page_zip, /*!< in/out: compressed page corresponding to rec, or NULL when the tablespace is uncompressed */ mtr_t* mtr); /*!< in: mtr */ /*************************************************************//** Tries to compress a page of the tree if it seems useful. It is assumed that mtr holds an x-latch on the tree and on the cursor page. To avoid deadlocks, mtr must also own x-latches to brothers of page, if those brothers exist. NOTE: it is assumed that the caller has reserved enough free extents so that the compression will always succeed if done! @return TRUE if compression occurred */ UNIV_INTERN ibool btr_cur_compress_if_useful( /*=======================*/ btr_cur_t* cursor, /*!< in: cursor on the page to compress; cursor does not stay valid if compression occurs */ mtr_t* mtr); /*!< in: mtr */ /*******************************************************//** Removes the record on which the tree cursor is positioned. It is assumed that the mtr has an x-latch on the page where the cursor is positioned, but no latch on the whole tree. @return TRUE if success, i.e., the page did not become too empty */ UNIV_INTERN ibool btr_cur_optimistic_delete( /*======================*/ btr_cur_t* cursor, /*!< in: cursor on the record to delete; cursor stays valid: if deletion succeeds, on function exit it points to the successor of the deleted record */ mtr_t* mtr); /*!< in: mtr; if this function returns TRUE on a leaf page of a secondary index, the mtr must be committed before latching any further pages */ /*************************************************************//** Removes the record on which the tree cursor is positioned. Tries to compress the page if its fillfactor drops below a threshold or if it is the only page on the level. It is assumed that mtr holds an x-latch on the tree and on the cursor page. To avoid deadlocks, mtr must also own x-latches to brothers of page, if those brothers exist. @return TRUE if compression occurred */ UNIV_INTERN ibool btr_cur_pessimistic_delete( /*=======================*/ ulint* err, /*!< out: DB_SUCCESS or DB_OUT_OF_FILE_SPACE; the latter may occur because we may have to update node pointers on upper levels, and in the case of variable length keys these may actually grow in size */ ibool has_reserved_extents, /*!< in: TRUE if the caller has already reserved enough free extents so that he knows that the operation will succeed */ btr_cur_t* cursor, /*!< in: cursor on the record to delete; if compression does not occur, the cursor stays valid: it points to successor of deleted record on function exit */ enum trx_rb_ctx rb_ctx, /*!< in: rollback context */ mtr_t* mtr); /*!< in: mtr */ #endif /* !UNIV_HOTBACKUP */ /***********************************************************//** Parses a redo log record of updating a record in-place. @return end of log record or NULL */ UNIV_INTERN byte* btr_cur_parse_update_in_place( /*==========================*/ byte* ptr, /*!< in: buffer */ byte* end_ptr,/*!< in: buffer end */ page_t* page, /*!< in/out: page or NULL */ page_zip_des_t* page_zip,/*!< in/out: compressed page, or NULL */ dict_index_t* index); /*!< in: index corresponding to page */ /****************************************************************//** Parses the redo log record for delete marking or unmarking of a clustered index record. @return end of log record or NULL */ UNIV_INTERN byte* btr_cur_parse_del_mark_set_clust_rec( /*=================================*/ byte* ptr, /*!< in: buffer */ byte* end_ptr,/*!< in: buffer end */ page_t* page, /*!< in/out: page or NULL */ page_zip_des_t* page_zip,/*!< in/out: compressed page, or NULL */ dict_index_t* index); /*!< in: index corresponding to page */ /****************************************************************//** Parses the redo log record for delete marking or unmarking of a secondary index record. @return end of log record or NULL */ UNIV_INTERN byte* btr_cur_parse_del_mark_set_sec_rec( /*===============================*/ byte* ptr, /*!< in: buffer */ byte* end_ptr,/*!< in: buffer end */ page_t* page, /*!< in/out: page or NULL */ page_zip_des_t* page_zip);/*!< in/out: compressed page, or NULL */ #ifndef UNIV_HOTBACKUP /*******************************************************************//** Estimates the number of rows in a given index range. @return estimated number of rows */ UNIV_INTERN ib_int64_t btr_estimate_n_rows_in_range( /*=========================*/ dict_index_t* index, /*!< in: index */ const dtuple_t* tuple1, /*!< in: range start, may also be empty tuple */ ulint mode1, /*!< in: search mode for range start */ const dtuple_t* tuple2, /*!< in: range end, may also be empty tuple */ ulint mode2); /*!< in: search mode for range end */ /*******************************************************************//** Estimates the number of different key values in a given index, for each n-column prefix of the index where n <= dict_index_get_n_unique(index). The estimates are stored in the array index->stat_n_diff_key_vals. */ UNIV_INTERN void btr_estimate_number_of_different_key_vals( /*======================================*/ dict_index_t* index); /*!< in: index */ /*******************************************************************//** Marks not updated extern fields as not-owned by this record. The ownership is transferred to the updated record which is inserted elsewhere in the index tree. In purge only the owner of externally stored field is allowed to free the field. */ UNIV_INTERN void btr_cur_mark_extern_inherited_fields( /*=================================*/ page_zip_des_t* page_zip,/*!< in/out: compressed page whose uncompressed part will be updated, or NULL */ rec_t* rec, /*!< in/out: record in a clustered index */ dict_index_t* index, /*!< in: index of the page */ const ulint* offsets,/*!< in: array returned by rec_get_offsets() */ const upd_t* update, /*!< in: update vector */ mtr_t* mtr); /*!< in: mtr, or NULL if not logged */ /*******************************************************************//** The complement of the previous function: in an update entry may inherit some externally stored fields from a record. We must mark them as inherited in entry, so that they are not freed in a rollback. */ UNIV_INTERN void btr_cur_mark_dtuple_inherited_extern( /*=================================*/ dtuple_t* entry, /*!< in/out: updated entry to be inserted to clustered index */ const upd_t* update); /*!< in: update vector */ /*******************************************************************//** Marks all extern fields in a dtuple as owned by the record. */ UNIV_INTERN void btr_cur_unmark_dtuple_extern_fields( /*================================*/ dtuple_t* entry); /*!< in/out: clustered index entry */ /*******************************************************************//** Stores the fields in big_rec_vec to the tablespace and puts pointers to them in rec. The extern flags in rec will have to be set beforehand. The fields are stored on pages allocated from leaf node file segment of the index tree. @return DB_SUCCESS or error */ UNIV_INTERN ulint btr_store_big_rec_extern_fields( /*============================*/ dict_index_t* index, /*!< in: index of rec; the index tree MUST be X-latched */ buf_block_t* rec_block, /*!< in/out: block containing rec */ rec_t* rec, /*!< in: record */ const ulint* offsets, /*!< in: rec_get_offsets(rec, index); the "external storage" flags in offsets will not correspond to rec when this function returns */ big_rec_t* big_rec_vec, /*!< in: vector containing fields to be stored externally */ mtr_t* local_mtr); /*!< in: mtr containing the latch to rec and to the tree */ /*******************************************************************//** Frees the space in an externally stored field to the file space management if the field in data is owned the externally stored field, in a rollback we may have the additional condition that the field must not be inherited. */ UNIV_INTERN void btr_free_externally_stored_field( /*=============================*/ dict_index_t* index, /*!< in: index of the data, the index tree MUST be X-latched; if the tree height is 1, then also the root page must be X-latched! (this is relevant in the case this function is called from purge where 'data' is located on an undo log page, not an index page) */ byte* field_ref, /*!< in/out: field reference */ const rec_t* rec, /*!< in: record containing field_ref, for page_zip_write_blob_ptr(), or NULL */ const ulint* offsets, /*!< in: rec_get_offsets(rec, index), or NULL */ page_zip_des_t* page_zip, /*!< in: compressed page corresponding to rec, or NULL if rec == NULL */ ulint i, /*!< in: field number of field_ref; ignored if rec == NULL */ enum trx_rb_ctx rb_ctx, /*!< in: rollback context */ mtr_t* local_mtr); /*!< in: mtr containing the latch to data an an X-latch to the index tree */ /*******************************************************************//** Copies the prefix of an externally stored field of a record. The clustered index record must be protected by a lock or a page latch. @return the length of the copied field, or 0 if the column was being or has been deleted */ UNIV_INTERN ulint btr_copy_externally_stored_field_prefix( /*====================================*/ byte* buf, /*!< out: the field, or a prefix of it */ ulint len, /*!< in: length of buf, in bytes */ ulint zip_size,/*!< in: nonzero=compressed BLOB page size, zero for uncompressed BLOBs */ const byte* data, /*!< in: 'internally' stored part of the field containing also the reference to the external part; must be protected by a lock or a page latch */ ulint local_len);/*!< in: length of data, in bytes */ /*******************************************************************//** Copies an externally stored field of a record to mem heap. @return the field copied to heap */ UNIV_INTERN byte* btr_rec_copy_externally_stored_field( /*=================================*/ const rec_t* rec, /*!< in: record in a clustered index; must be protected by a lock or a page latch */ const ulint* offsets,/*!< in: array returned by rec_get_offsets() */ ulint zip_size,/*!< in: nonzero=compressed BLOB page size, zero for uncompressed BLOBs */ ulint no, /*!< in: field number */ ulint* len, /*!< out: length of the field */ mem_heap_t* heap); /*!< in: mem heap */ /*******************************************************************//** Flags the data tuple fields that are marked as extern storage in the update vector. We use this function to remember which fields we must mark as extern storage in a record inserted for an update. @return number of flagged external columns */ UNIV_INTERN ulint btr_push_update_extern_fields( /*==========================*/ dtuple_t* tuple, /*!< in/out: data tuple */ const upd_t* update, /*!< in: update vector */ mem_heap_t* heap) /*!< in: memory heap */ __attribute__((nonnull)); /************************************************************************ Reset global configuration variables. */ UNIV_INTERN void btr_cur_var_init(void); /*==================*/ /*######################################################################*/ /** In the pessimistic delete, if the page data size drops below this limit, merging it to a neighbor is tried */ #define BTR_CUR_PAGE_COMPRESS_LIMIT (UNIV_PAGE_SIZE / 2) /** A slot in the path array. We store here info on a search path down the tree. Each slot contains data on a single level of the tree. */ typedef struct btr_path_struct btr_path_t; struct btr_path_struct{ ulint nth_rec; /*!< index of the record where the page cursor stopped on this level (index in alphabetical order); value ULINT_UNDEFINED denotes array end */ ulint n_recs; /*!< number of records on the page */ }; #define BTR_PATH_ARRAY_N_SLOTS 250 /*!< size of path array (in slots) */ /** Values for the flag documenting the used search method */ enum btr_cur_method { BTR_CUR_HASH = 1, /*!< successful shortcut using the hash index */ BTR_CUR_HASH_FAIL, /*!< failure using hash, success using binary search: the misleading hash reference is stored in the field hash_node, and might be necessary to update */ BTR_CUR_BINARY, /*!< success using the binary search */ BTR_CUR_INSERT_TO_IBUF /*!< performed the intended insert to the insert buffer */ }; /** The tree cursor: the definition appears here only for the compiler to know struct size! */ struct btr_cur_struct { dict_index_t* index; /*!< index where positioned */ page_cur_t page_cur; /*!< page cursor */ buf_block_t* left_block; /*!< this field is used to store a pointer to the left neighbor page, in the cases BTR_SEARCH_PREV and BTR_MODIFY_PREV */ /*------------------------------*/ que_thr_t* thr; /*!< this field is only used when btr_cur_search_to_nth_level is called for an index entry insertion: the calling query thread is passed here to be used in the insert buffer */ /*------------------------------*/ /** The following fields are used in btr_cur_search_to_nth_level to pass information: */ /* @{ */ enum btr_cur_method flag; /*!< Search method used */ ulint tree_height; /*!< Tree height if the search is done for a pessimistic insert or update operation */ ulint up_match; /*!< If the search mode was PAGE_CUR_LE, the number of matched fields to the the first user record to the right of the cursor record after btr_cur_search_to_nth_level; for the mode PAGE_CUR_GE, the matched fields to the first user record AT THE CURSOR or to the right of it; NOTE that the up_match and low_match values may exceed the correct values for comparison to the adjacent user record if that record is on a different leaf page! (See the note in row_ins_duplicate_key.) */ ulint up_bytes; /*!< number of matched bytes to the right at the time cursor positioned; only used internally in searches: not defined after the search */ ulint low_match; /*!< if search mode was PAGE_CUR_LE, the number of matched fields to the first user record AT THE CURSOR or to the left of it after btr_cur_search_to_nth_level; NOT defined for PAGE_CUR_GE or any other search modes; see also the NOTE in up_match! */ ulint low_bytes; /*!< number of matched bytes to the right at the time cursor positioned; only used internally in searches: not defined after the search */ ulint n_fields; /*!< prefix length used in a hash search if hash_node != NULL */ ulint n_bytes; /*!< hash prefix bytes if hash_node != NULL */ ulint fold; /*!< fold value used in the search if flag is BTR_CUR_HASH */ /*------------------------------*/ /* @} */ btr_path_t* path_arr; /*!< in estimating the number of rows in range, we store in this array information of the path through the tree */ }; /** If pessimistic delete fails because of lack of file space, there is still a good change of success a little later. Try this many times. */ #define BTR_CUR_RETRY_DELETE_N_TIMES 100 /** If pessimistic delete fails because of lack of file space, there is still a good change of success a little later. Sleep this many microseconds between retries. */ #define BTR_CUR_RETRY_SLEEP_TIME 50000 /** The reference in a field for which data is stored on a different page. The reference is at the end of the 'locally' stored part of the field. 'Locally' means storage in the index record. We store locally a long enough prefix of each column so that we can determine the ordering parts of each index record without looking into the externally stored part. */ /*-------------------------------------- @{ */ #define BTR_EXTERN_SPACE_ID 0 /*!< space id where stored */ #define BTR_EXTERN_PAGE_NO 4 /*!< page no where stored */ #define BTR_EXTERN_OFFSET 8 /*!< offset of BLOB header on that page */ #define BTR_EXTERN_LEN 12 /*!< 8 bytes containing the length of the externally stored part of the BLOB. The 2 highest bits are reserved to the flags below. */ /*-------------------------------------- @} */ /* #define BTR_EXTERN_FIELD_REF_SIZE 20 // moved to btr0types.h */ /** The most significant bit of BTR_EXTERN_LEN (i.e., the most significant bit of the byte at smallest address) is set to 1 if this field does not 'own' the externally stored field; only the owner field is allowed to free the field in purge! */ #define BTR_EXTERN_OWNER_FLAG 128 /** If the second most significant bit of BTR_EXTERN_LEN (i.e., the second most significant bit of the byte at smallest address) is 1 then it means that the externally stored field was inherited from an earlier version of the row. In rollback we are not allowed to free an inherited external field. */ #define BTR_EXTERN_INHERITED_FLAG 64 /** Number of searches down the B-tree in btr_cur_search_to_nth_level(). */ extern ulint btr_cur_n_non_sea; /** Number of successful adaptive hash index lookups in btr_cur_search_to_nth_level(). */ extern ulint btr_cur_n_sea; /** Old value of btr_cur_n_non_sea. Copied by srv_refresh_innodb_monitor_stats(). Referenced by srv_printf_innodb_monitor(). */ extern ulint btr_cur_n_non_sea_old; /** Old value of btr_cur_n_sea. Copied by srv_refresh_innodb_monitor_stats(). Referenced by srv_printf_innodb_monitor(). */ extern ulint btr_cur_n_sea_old; #endif /* !UNIV_HOTBACKUP */ #ifndef UNIV_NONINL #include "btr0cur.ic" #endif #endif haildb-2.3.2/include/mtr0log.h0000644000175000017500000002164211513177357017030 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/mtr0log.h Mini-transaction logging routines Created 12/7/1995 Heikki Tuuri *******************************************************/ #ifndef mtr0log_h #define mtr0log_h #include "univ.i" #include "mtr0mtr.h" #include "dict0types.h" #ifndef UNIV_HOTBACKUP /********************************************************//** Writes 1 - 4 bytes to a file page buffered in the buffer pool. Writes the corresponding log record to the mini-transaction log. */ UNIV_INTERN void mlog_write_ulint( /*=============*/ byte* ptr, /*!< in: pointer where to write */ ulint val, /*!< in: value to write */ byte type, /*!< in: MLOG_1BYTE, MLOG_2BYTES, MLOG_4BYTES */ mtr_t* mtr); /*!< in: mini-transaction handle */ /********************************************************//** Writes 8 bytes to a file page buffered in the buffer pool. Writes the corresponding log record to the mini-transaction log. */ UNIV_INTERN void mlog_write_dulint( /*==============*/ byte* ptr, /*!< in: pointer where to write */ dulint val, /*!< in: value to write */ mtr_t* mtr); /*!< in: mini-transaction handle */ /********************************************************//** Writes a string to a file page buffered in the buffer pool. Writes the corresponding log record to the mini-transaction log. */ UNIV_INTERN void mlog_write_string( /*==============*/ byte* ptr, /*!< in: pointer where to write */ const byte* str, /*!< in: string to write */ ulint len, /*!< in: string length */ mtr_t* mtr); /*!< in: mini-transaction handle */ /********************************************************//** Logs a write of a string to a file page buffered in the buffer pool. Writes the corresponding log record to the mini-transaction log. */ UNIV_INTERN void mlog_log_string( /*============*/ byte* ptr, /*!< in: pointer written to */ ulint len, /*!< in: string length */ mtr_t* mtr); /*!< in: mini-transaction handle */ /********************************************************//** Writes initial part of a log record consisting of one-byte item type and four-byte space and page numbers. */ UNIV_INTERN void mlog_write_initial_log_record( /*==========================*/ const byte* ptr, /*!< in: pointer to (inside) a buffer frame holding the file page where modification is made */ byte type, /*!< in: log item type: MLOG_1BYTE, ... */ mtr_t* mtr); /*!< in: mini-transaction handle */ /********************************************************//** Writes a log record about an .ibd file create/delete/rename. @return new value of log_ptr */ UNIV_INLINE byte* mlog_write_initial_log_record_for_file_op( /*======================================*/ ulint type, /*!< in: MLOG_FILE_CREATE, MLOG_FILE_DELETE, or MLOG_FILE_RENAME */ ulint space_id,/*!< in: space id, if applicable */ ulint page_no,/*!< in: page number (not relevant currently) */ byte* log_ptr,/*!< in: pointer to mtr log which has been opened */ mtr_t* mtr); /*!< in: mtr */ /********************************************************//** Catenates 1 - 4 bytes to the mtr log. */ UNIV_INLINE void mlog_catenate_ulint( /*================*/ mtr_t* mtr, /*!< in: mtr */ ulint val, /*!< in: value to write */ ulint type); /*!< in: MLOG_1BYTE, MLOG_2BYTES, MLOG_4BYTES */ /********************************************************//** Catenates n bytes to the mtr log. */ UNIV_INTERN void mlog_catenate_string( /*=================*/ mtr_t* mtr, /*!< in: mtr */ const byte* str, /*!< in: string to write */ ulint len); /*!< in: string length */ /********************************************************//** Catenates a compressed ulint to mlog. */ UNIV_INLINE void mlog_catenate_ulint_compressed( /*===========================*/ mtr_t* mtr, /*!< in: mtr */ ulint val); /*!< in: value to write */ /********************************************************//** Catenates a compressed dulint to mlog. */ UNIV_INLINE void mlog_catenate_dulint_compressed( /*============================*/ mtr_t* mtr, /*!< in: mtr */ dulint val); /*!< in: value to write */ /********************************************************//** Opens a buffer to mlog. It must be closed with mlog_close. @return buffer, NULL if log mode MTR_LOG_NONE */ UNIV_INLINE byte* mlog_open( /*======*/ mtr_t* mtr, /*!< in: mtr */ ulint size); /*!< in: buffer size in bytes; MUST be smaller than DYN_ARRAY_DATA_SIZE! */ /********************************************************//** Closes a buffer opened to mlog. */ UNIV_INLINE void mlog_close( /*=======*/ mtr_t* mtr, /*!< in: mtr */ byte* ptr); /*!< in: buffer space from ptr up was not used */ /********************************************************//** Writes the initial part of a log record (3..11 bytes). If the implementation of this function is changed, all size parameters to mlog_open() should be adjusted accordingly! @return new value of log_ptr */ UNIV_INLINE byte* mlog_write_initial_log_record_fast( /*===============================*/ const byte* ptr, /*!< in: pointer to (inside) a buffer frame holding the file page where modification is made */ byte type, /*!< in: log item type: MLOG_1BYTE, ... */ byte* log_ptr,/*!< in: pointer to mtr log which has been opened */ mtr_t* mtr); /*!< in: mtr */ #else /* !UNIV_HOTBACKUP */ # define mlog_write_initial_log_record(ptr,type,mtr) ((void) 0) # define mlog_write_initial_log_record_fast(ptr,type,log_ptr,mtr) ((byte *) 0) #endif /* !UNIV_HOTBACKUP */ /********************************************************//** Parses an initial log record written by mlog_write_initial_log_record. @return parsed record end, NULL if not a complete record */ UNIV_INTERN byte* mlog_parse_initial_log_record( /*==========================*/ byte* ptr, /*!< in: buffer */ byte* end_ptr,/*!< in: buffer end */ byte* type, /*!< out: log record type: MLOG_1BYTE, ... */ ulint* space, /*!< out: space id */ ulint* page_no);/*!< out: page number */ /********************************************************//** Parses a log record written by mlog_write_ulint or mlog_write_dulint. @return parsed record end, NULL if not a complete record */ UNIV_INTERN byte* mlog_parse_nbytes( /*==============*/ ulint type, /*!< in: log record type: MLOG_1BYTE, ... */ byte* ptr, /*!< in: buffer */ byte* end_ptr,/*!< in: buffer end */ byte* page, /*!< in: page where to apply the log record, or NULL */ void* page_zip);/*!< in/out: compressed page, or NULL */ /********************************************************//** Parses a log record written by mlog_write_string. @return parsed record end, NULL if not a complete record */ UNIV_INTERN byte* mlog_parse_string( /*==============*/ byte* ptr, /*!< in: buffer */ byte* end_ptr,/*!< in: buffer end */ byte* page, /*!< in: page where to apply the log record, or NULL */ void* page_zip);/*!< in/out: compressed page, or NULL */ #ifndef UNIV_HOTBACKUP /********************************************************//** Opens a buffer for mlog, writes the initial log record and, if needed, the field lengths of an index. Reserves space for further log entries. The log entry must be closed with mtr_close(). @return buffer, NULL if log mode MTR_LOG_NONE */ UNIV_INTERN byte* mlog_open_and_write_index( /*======================*/ mtr_t* mtr, /*!< in: mtr */ const byte* rec, /*!< in: index record or page */ dict_index_t* index, /*!< in: record descriptor */ byte type, /*!< in: log item type */ ulint size); /*!< in: requested buffer size in bytes (if 0, calls mlog_close() and returns NULL) */ #endif /* !UNIV_HOTBACKUP */ /********************************************************//** Parses a log record written by mlog_open_and_write_index. @return parsed record end, NULL if not a complete record */ UNIV_INTERN byte* mlog_parse_index( /*=============*/ byte* ptr, /*!< in: buffer */ const byte* end_ptr,/*!< in: buffer end */ ibool comp, /*!< in: TRUE=compact record format */ dict_index_t** index); /*!< out, own: dummy index */ #ifndef UNIV_HOTBACKUP /* Insert, update, and maybe other functions may use this value to define an extra mlog buffer size for variable size data */ #define MLOG_BUF_MARGIN 256 #endif /* !UNIV_HOTBACKUP */ #ifndef UNIV_NONINL #include "mtr0log.ic" #endif #endif haildb-2.3.2/include/ddl0ddl.h0000644000175000017500000001132611513177357016751 0ustar00pcrewspcrews00000000000000/****************************************************** Contains InnoDB DDL operations. (c) 2008 Oracle Corpn/Innobase Oy Created 12 Oct 2008 *******************************************************/ #ifndef ddl0ddl_h #define ddl0ddl_h #include "univ.i" #include "trx0types.h" #include "dict0types.h" /************************************************************************* Get the background drop list length. NOTE: the caller must own the kernel mutex! @return how many tables in list */ UNIV_INTERN ulint ddl_get_background_drop_list_len_low(void); /*======================================*/ /************************************************************************* Creates a table, if the name of the table ends in one of "innodb_monitor", "innodb_lock_monitor", "innodb_tablespace_monitor", "innodb_table_monitor", then this will also start the printing of monitor output by the master thread. If the table name ends in "innodb_mem_validate", InnoDB will try to invoke mem_validate(). @return error code or DB_SUCCESS */ UNIV_INTERN ulint ddl_create_table( /*=============*/ dict_table_t* table, /*!< in: table definition */ trx_t* trx); /*!< in: transaction handle */ /************************************************************************* Does an index creation operation. TODO: currently failure to create an index results in dropping the whole table! This is no problem currently as all indexes must be created at the same time as the table. @return error number or DB_SUCCESS */ UNIV_INTERN ulint ddl_create_index( /*=============*/ dict_index_t* index, /*!< in: index definition */ trx_t* trx); /*!< in: transaction handle */ /************************************************************************* Drops a table but does not commit the transaction. If the name of the dropped table ends in one of "innodb_monitor", "innodb_lock_monitor", "innodb_tablespace_monitor", "innodb_table_monitor", then this will also stop the printing of monitor output by the master thread. @return error code or DB_SUCCESS */ UNIV_INTERN ulint ddl_drop_table( /*===========*/ const char* name, /*!< in: table name */ trx_t* trx, /*!< in: transaction handle */ ibool drop_db); /*!< in: TRUE=dropping whole database */ /************************************************************************* Drops an index. @return error code or DB_SUCCESS */ UNIV_INTERN ulint ddl_drop_index( /*===========*/ dict_table_t* table, /*!< in: table instance */ dict_index_t* index, /*!< in: id of index to drop */ trx_t* trx); /*!< in: transaction handle */ /************************************************************************* The master thread in srv0srv.c calls this regularly to drop tables which we must drop in background after queries to them have ended. Such lazy dropping of tables is needed in ALTER TABLE on Unix. @return how many tables dropped + remaining tables in list */ UNIV_INTERN ulint ddl_drop_tables_in_background(void); /*===============================*/ /************************************************************************* Truncates a table @return error code or DB_SUCCESS */ UNIV_INTERN enum db_err ddl_truncate_table( /*===============*/ dict_table_t* table, /*!< in: table handle */ trx_t* trx); /*!< in: transaction handle */ /************************************************************************* Renames a table. @return error code or DB_SUCCESS */ UNIV_INTERN ulint ddl_rename_table( /*=============*/ const char* old_name, /*!< in: old table name */ const char* new_name, /*!< in: new table name */ trx_t* trx); /*!< in: transaction handle */ /************************************************************************* Renames an index. @return error code or DB_SUCCESS */ UNIV_INTERN ulint ddl_rename_index( /*=============*/ const char* table_name, /*!< in: table that owns the index */ const char* old_name, /*!< in: old table name */ const char* new_name, /*!< in: new table name */ trx_t* trx); /*!< in: transaction handle */ /************************************************************************* Drops a database. @return error code or DB_SUCCESS */ UNIV_INTERN enum db_err ddl_drop_database( /*==============*/ const char* name, /*!< in: database name which ends in '/' */ trx_t* trx); /*!< in: transaction handle */ /*********************************************************************//** Drop all partially created indexes. */ UNIV_INTERN void ddl_drop_all_temp_indexes( /*======================*/ ib_recovery_t recovery); /*!< in: recovery level setting */ /*********************************************************************//** Drop all temporary tables. */ UNIV_INTERN void ddl_drop_all_temp_tables( /*=====================*/ ib_recovery_t recovery); /*!< in: recovery level setting */ #endif /* ddl0ddl_h */ haildb-2.3.2/include/fil0fil.h0000644000175000017500000007060011513177357016767 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2010, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/fil0fil.h The low-level file system Created 10/25/1995 Heikki Tuuri *******************************************************/ #ifndef fil0fil_h #define fil0fil_h #include "univ.i" #ifndef UNIV_HOTBACKUP #include "sync0rw.h" #endif /* !UNIV_HOTBACKUP */ #include "dict0types.h" #include "ut0byte.h" #include "os0file.h" #include "srv0srv.h" /* When program is run, the default directory "." is the current datadir, but in ibbackup we must set it explicitly; the path must NOT contain the trailing '/' or '\' */ extern const char* fil_path_to_client_datadir; /** Initial size of a single-table tablespace in pages */ #define FIL_IBD_FILE_INITIAL_SIZE 4 /** 'null' (undefined) page offset in the context of file spaces */ #define FIL_NULL ULINT32_UNDEFINED /* Space address data type; this is intended to be used when addresses accurate to a byte are stored in file pages. If the page part of the address is FIL_NULL, the address is considered undefined. */ typedef byte fil_faddr_t; /*!< 'type' definition in C: an address stored in a file page is a string of bytes */ #define FIL_ADDR_PAGE 0 /* first in address is the page offset */ #define FIL_ADDR_BYTE 4 /* then comes 2-byte byte offset within page*/ #define FIL_ADDR_SIZE 6 /* address size is 6 bytes */ /** A struct for storing a space address FIL_ADDR, when it is used in C program data structures. */ typedef struct fil_addr_struct fil_addr_t; /** File space address */ struct fil_addr_struct{ ulint page; /*!< page number within a space */ ulint boffset; /*!< byte offset within the page */ }; /** The null file address */ extern const fil_addr_t fil_addr_null; /** The byte offsets on a file page for various variables @{ */ #define FIL_PAGE_SPACE_OR_CHKSUM 0 /*!< in < MySQL-4.0.14 space id the page belongs to (== 0) but in later versions the 'new' checksum of the page */ #define FIL_PAGE_OFFSET 4 /*!< page offset inside space */ #define FIL_PAGE_PREV 8 /*!< if there is a 'natural' predecessor of the page, its offset. Otherwise FIL_NULL. This field is not set on BLOB pages, which are stored as a singly-linked list. See also FIL_PAGE_NEXT. */ #define FIL_PAGE_NEXT 12 /*!< if there is a 'natural' successor of the page, its offset. Otherwise FIL_NULL. B-tree index pages (FIL_PAGE_TYPE contains FIL_PAGE_INDEX) on the same PAGE_LEVEL are maintained as a doubly linked list via FIL_PAGE_PREV and FIL_PAGE_NEXT in the collation order of the smallest user record on each page. */ #define FIL_PAGE_LSN 16 /*!< lsn of the end of the newest modification log record to the page */ #define FIL_PAGE_TYPE 24 /*!< file page type: FIL_PAGE_INDEX,..., 2 bytes. The contents of this field can only be trusted in the following case: if the page is an uncompressed B-tree index page, then it is guaranteed that the value is FIL_PAGE_INDEX. The opposite does not hold. In tablespaces created by InnoDB 5.1.7 or later, the contents of this field is valid for all uncompressed pages. */ #define FIL_PAGE_FILE_FLUSH_LSN 26 /*!< this is only defined for the first page in a system tablespace data file (ibdata*, not *.ibd): the file has been flushed to disk at least up to this lsn */ #define FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID 34 /*!< starting from 4.1.x this contains the space id of the page */ #define FIL_PAGE_DATA 38 /*!< start of the data on the page */ /* @} */ /** File page trailer @{ */ #define FIL_PAGE_END_LSN_OLD_CHKSUM 8 /*!< the low 4 bytes of this are used to store the page checksum, the last 4 bytes should be identical to the last 4 bytes of FIL_PAGE_LSN */ #define FIL_PAGE_DATA_END 8 /*!< size of the page trailer */ /* @} */ /** File page types (values of FIL_PAGE_TYPE) @{ */ #define FIL_PAGE_INDEX 17855 /*!< B-tree node */ #define FIL_PAGE_UNDO_LOG 2 /*!< Undo log page */ #define FIL_PAGE_INODE 3 /*!< Index node */ #define FIL_PAGE_IBUF_FREE_LIST 4 /*!< Insert buffer free list */ /* File page types introduced in InnoDB 5.1.7 */ #define FIL_PAGE_TYPE_ALLOCATED 0 /*!< Freshly allocated page */ #define FIL_PAGE_IBUF_BITMAP 5 /*!< Insert buffer bitmap */ #define FIL_PAGE_TYPE_SYS 6 /*!< System page */ #define FIL_PAGE_TYPE_TRX_SYS 7 /*!< Transaction system data */ #define FIL_PAGE_TYPE_FSP_HDR 8 /*!< File space header */ #define FIL_PAGE_TYPE_XDES 9 /*!< Extent descriptor page */ #define FIL_PAGE_TYPE_BLOB 10 /*!< Uncompressed BLOB page */ #define FIL_PAGE_TYPE_ZBLOB 11 /*!< First compressed BLOB page */ #define FIL_PAGE_TYPE_ZBLOB2 12 /*!< Subsequent compressed BLOB page */ /* @} */ /** Space types @{ */ #define FIL_TABLESPACE 501 /*!< tablespace */ #define FIL_LOG 502 /*!< redo log */ /* @} */ /** The number of fsyncs done to the log */ extern ulint fil_n_log_flushes; /** Number of pending redo log flushes */ extern ulint fil_n_pending_log_flushes; /** Number of pending tablespace flushes */ extern ulint fil_n_pending_tablespace_flushes; #ifndef UNIV_HOTBACKUP /*******************************************************************//** Returns the version number of a tablespace, -1 if not found. @return version number, -1 if the tablespace does not exist in the memory cache */ UNIV_INTERN ib_int64_t fil_space_get_version( /*==================*/ ulint id); /*!< in: space id */ /*******************************************************************//** Returns the latch of a file space. @return latch protecting storage allocation */ UNIV_INTERN rw_lock_t* fil_space_get_latch( /*================*/ ulint id, /*!< in: space id */ ulint* zip_size);/*!< out: compressed page size, or 0 for uncompressed tablespaces */ /*******************************************************************//** Returns the type of a file space. @return FIL_TABLESPACE or FIL_LOG */ UNIV_INTERN ulint fil_space_get_type( /*===============*/ ulint id); /*!< in: space id */ #endif /* !UNIV_HOTBACKUP */ /*******************************************************************//** Appends a new file to the chain of files of a space. File must be closed. */ UNIV_INTERN void fil_node_create( /*============*/ const char* name, /*!< in: file name (file must be closed) */ ulint size, /*!< in: file size in database blocks, rounded downwards to an integer */ ulint id, /*!< in: space id where to append */ ibool is_raw);/*!< in: TRUE if a raw device or a raw disk partition */ #ifdef UNIV_LOG_ARCHIVE /****************************************************************//** Drops files from the start of a file space, so that its size is cut by the amount given. */ UNIV_INTERN void fil_space_truncate_start( /*=====================*/ ulint id, /*!< in: space id */ ulint trunc_len); /*!< in: truncate by this much; it is an error if this does not equal to the combined size of some initial files in the space */ #endif /* UNIV_LOG_ARCHIVE */ /*******************************************************************//** Creates a space memory object and puts it to the 'fil system' hash table. If there is an error, prints an error message to the .err log. @return TRUE if success */ UNIV_INTERN ibool fil_space_create( /*=============*/ const char* name, /*!< in: space name */ ulint id, /*!< in: space id */ ulint zip_size,/*!< in: compressed page size, or 0 for uncompressed tablespaces */ ulint purpose);/*!< in: FIL_TABLESPACE, or FIL_LOG if log */ /*******************************************************************//** Returns the size of the space in pages. The tablespace must be cached in the memory cache. @return space size, 0 if space not found */ UNIV_INTERN ulint fil_space_get_size( /*===============*/ ulint id); /*!< in: space id */ /*******************************************************************//** Returns the flags of the space. The tablespace must be cached in the memory cache. @return flags, ULINT_UNDEFINED if space not found */ UNIV_INTERN ulint fil_space_get_flags( /*================*/ ulint id); /*!< in: space id */ /*******************************************************************//** Returns the compressed page size of the space, or 0 if the space is not compressed. The tablespace must be cached in the memory cache. @return compressed page size, ULINT_UNDEFINED if space not found */ UNIV_INTERN ulint fil_space_get_zip_size( /*===================*/ ulint id); /*!< in: space id */ /*******************************************************************//** Checks if the pair space, page_no refers to an existing page in a tablespace file space. The tablespace must be cached in the memory cache. @return TRUE if the address is meaningful */ UNIV_INTERN ibool fil_check_adress_in_tablespace( /*===========================*/ ulint id, /*!< in: space id */ ulint page_no);/*!< in: page number */ /****************************************************************//** Initializes the tablespace memory cache. */ UNIV_INTERN void fil_init( /*=====*/ ulint hash_size, /*!< in: hash table size */ ulint max_n_open); /*!< in: max number of open files */ /******************************************************************//** Deinitializes the tablespace memory cache. */ UNIV_INTERN void fil_close(void); /*===========*/ /*******************************************************************//** Opens all log files and system tablespace data files. They stay open until the database server shutdown. This should be called at a server startup after the space objects for the log and the system tablespace have been created. The purpose of this operation is to make sure we never run out of file descriptors if we need to read from the insert buffer or to write to the log. */ UNIV_INTERN void fil_open_log_and_system_tablespace_files(void); /*==========================================*/ /*******************************************************************//** Closes all open files. There must not be any pending i/o's or not flushed modifications in the files. */ UNIV_INTERN void fil_close_all_files(void); /*=====================*/ /*******************************************************************//** Sets the max tablespace id counter if the given number is bigger than the previous value. */ UNIV_INTERN void fil_set_max_space_id_if_bigger( /*===========================*/ ulint max_id);/*!< in: maximum known id */ #ifndef UNIV_HOTBACKUP /****************************************************************//** Writes the flushed lsn and the latest archived log number to the page header of the first page of each data file in the system tablespace. @return DB_SUCCESS or error number */ UNIV_INTERN ulint fil_write_flushed_lsn_to_data_files( /*================================*/ ib_uint64_t lsn, /*!< in: lsn to write */ ulint arch_log_no); /*!< in: latest archived log file number */ /*******************************************************************//** Reads the flushed lsn and arch no fields from a data file at database startup. */ UNIV_INTERN void fil_read_flushed_lsn_and_arch_log_no( /*=================================*/ os_file_t data_file, /*!< in: open data file */ ibool one_read_already, /*!< in: TRUE if min and max parameters below already contain sensible data */ #ifdef UNIV_LOG_ARCHIVE ulint* min_arch_log_no, /*!< in/out: */ ulint* max_arch_log_no, /*!< in/out: */ #endif /* UNIV_LOG_ARCHIVE */ ib_uint64_t* min_flushed_lsn, /*!< in/out: */ ib_uint64_t* max_flushed_lsn); /*!< in/out: */ /*******************************************************************//** Increments the count of pending insert buffer page merges, if space is not being deleted. @return TRUE if being deleted, and ibuf merges should be skipped */ UNIV_INTERN ibool fil_inc_pending_ibuf_merges( /*========================*/ ulint id); /*!< in: space id */ /*******************************************************************//** Decrements the count of pending insert buffer page merges. */ UNIV_INTERN void fil_decr_pending_ibuf_merges( /*=========================*/ ulint id); /*!< in: space id */ #endif /* !UNIV_HOTBACKUP */ /*******************************************************************//** Parses the body of a log record written about an .ibd file operation. That is, the log record part after the standard (type, space id, page no) header of the log record. If desired, also replays the delete or rename operation if the .ibd file exists and the space id in it matches. Replays the create operation if a file at that path does not exist yet. If the database directory for the file to be created does not exist, then we create the directory, too. Note that ibbackup --apply-log sets fil_path_to_client_datadir to point to the datadir that we should use in replaying the file operations. @return end of log record, or NULL if the record was not completely contained between ptr and end_ptr */ UNIV_INTERN byte* fil_op_log_parse_or_replay( /*=======================*/ byte* ptr, /*!< in: buffer containing the log record body, or an initial segment of it, if the record does not fir completely between ptr and end_ptr */ byte* end_ptr, /*!< in: buffer end */ ulint type, /*!< in: the type of this log record */ ulint space_id, /*!< in: the space id of the tablespace in question, or 0 if the log record should only be parsed but not replayed */ ulint log_flags); /*!< in: redo log flags (stored in the page number parameter) */ /*******************************************************************//** Deletes a single-table tablespace. The tablespace must be cached in the memory cache. @return TRUE if success */ UNIV_INTERN ibool fil_delete_tablespace( /*==================*/ ulint id); /*!< in: space id */ #ifndef UNIV_HOTBACKUP /*******************************************************************//** Discards a single-table tablespace. The tablespace must be cached in the memory cache. Discarding is like deleting a tablespace, but 1) we do not drop the table from the data dictionary; 2) we remove all insert buffer entries for the tablespace immediately; in DROP TABLE they are only removed gradually in the background; 3) when the user does IMPORT TABLESPACE, the tablespace will have the same id as it originally had. @return TRUE if success */ UNIV_INTERN ibool fil_discard_tablespace( /*===================*/ ulint id); /*!< in: space id */ #endif /* !UNIV_HOTBACKUP */ /*******************************************************************//** Renames a single-table tablespace. The tablespace must be cached in the tablespace memory cache. @return TRUE if success */ UNIV_INTERN ibool fil_rename_tablespace( /*==================*/ const char* old_name, /*!< in: old table name in the standard databasename/tablename format of InnoDB, or NULL if we do the rename based on the space id only */ ulint id, /*!< in: space id */ const char* new_name); /*!< in: new table name in the standard databasename/tablename format of InnoDB */ /*******************************************************************//** Creates a new single-table tablespace in a database directory. The datadir is the current directory of a running program. We can refer to it by simply the path '.'. Tables created with: CREATE TEMPORARY TABLE we place in the configured TEMP dir of the application. @return DB_SUCCESS or error code */ UNIV_INTERN ulint fil_create_new_single_table_tablespace( /*===================================*/ ulint* space_id, /*!< in/out: space id; if this is != 0, then this is an input parameter, otherwise output */ const char* tablename, /*!< in: the table name in the usual databasename/tablename format of InnoDB, or a dir path to a temp table */ ibool is_temp, /*!< in: TRUE if a table created with CREATE TEMPORARY TABLE */ ulint flags, /*!< in: tablespace flags */ ulint size); /*!< in: the initial size of the tablespace file in pages, must be >= FIL_IBD_FILE_INITIAL_SIZE */ #ifndef UNIV_HOTBACKUP /********************************************************************//** Tries to open a single-table tablespace and optionally checks the space id is right in it. If does not succeed, prints an error message to the .err log. This function is used to open a tablespace when we start up the application, and also in IMPORT TABLESPACE. NOTE that we assume this operation is used either at the database startup or under the protection of the dictionary mutex, so that two users cannot race here. This operation does not leave the file associated with the tablespace open, but closes it after we have looked at the space id in it. @return TRUE if success */ UNIV_INTERN ibool fil_open_single_table_tablespace( /*=============================*/ ibool check_space_id, /*!< in: should we check that the space id in the file is right; we assume that this function runs much faster if no check is made, since accessing the file inode probably is much faster (the OS caches them) than accessing the first page of the file */ ulint id, /*!< in: space id */ ulint flags, /*!< in: tablespace flags */ const char* name); /*!< in: table name in the databasename/tablename format */ /********************************************************************//** It is possible, though very improbable, that the lsn's in the tablespace to be imported have risen above the current system lsn, if a lengthy purge, ibuf merge, or rollback was performed on a backup taken with ibbackup. If that is the case, reset page lsn's in the file. We assume that the engine was shutdown after it performed these cleanup operations on the .ibd file, so that it at the shutdown stamped the latest lsn to the FIL_PAGE_FILE_FLUSH_LSN in the first page of the .ibd file, and we can determine whether we need to reset the lsn's just by looking at that flush lsn. @return TRUE if success */ UNIV_INTERN ibool fil_reset_too_high_lsns( /*====================*/ const char* name, /*!< in: table name in the databasename/tablename format */ ib_uint64_t current_lsn); /*!< in: reset lsn's if the lsn stamped to FIL_PAGE_FILE_FLUSH_LSN in the first page is too high */ #endif /* !UNIV_HOTBACKUP */ /********************************************************************//** At the server startup, if we need crash recovery, scans the database directories under the current dir, looking for .ibd files. Those files are single-table tablespaces. We need to know the space id in each of them so that we know into which file we should look to check the contents of a page stored in the doublewrite buffer, also to know where to apply log records where the space id is != 0. @return DB_SUCCESS or error number */ UNIV_INTERN ulint fil_load_single_table_tablespaces( /*==============================*/ ib_recovery_t recovery); /*!< in: recovery flag */ /********************************************************************//** If we need crash recovery, and we have called fil_load_single_table_tablespaces() and dict_load_single_table_tablespaces(), we can call this function to print an error message of orphaned .ibd files for which there is not a data dictionary entry with a matching table name and space id. */ UNIV_INTERN void fil_print_orphaned_tablespaces(void); /*================================*/ /*******************************************************************//** Returns TRUE if a single-table tablespace does not exist in the memory cache, or is being deleted there. @return TRUE if does not exist or is being\ deleted */ UNIV_INTERN ibool fil_tablespace_deleted_or_being_deleted_in_mem( /*===========================================*/ ulint id, /*!< in: space id */ ib_int64_t version);/*!< in: tablespace_version should be this; if you pass -1 as the value of this, then this parameter is ignored */ /*******************************************************************//** Returns TRUE if a single-table tablespace exists in the memory cache. @return TRUE if exists */ UNIV_INTERN ibool fil_tablespace_exists_in_mem( /*=========================*/ ulint id); /*!< in: space id */ #ifndef UNIV_HOTBACKUP /*******************************************************************//** Returns TRUE if a matching tablespace exists in the InnoDB tablespace memory cache. Note that if we have not done a crash recovery at the database startup, there may be many tablespaces which are not yet in the memory cache. @return TRUE if a matching tablespace exists in the memory cache */ UNIV_INTERN ibool fil_space_for_table_exists_in_mem( /*==============================*/ ulint id, /*!< in: space id */ const char* name, /*!< in: table name in the standard 'databasename/tablename' format or the dir path to a temp table */ ibool is_temp, /*!< in: TRUE if created with CREATE TEMPORARY TABLE */ ibool mark_space, /*!< in: in crash recovery, at database startup we mark all spaces which have an associated table in the InnoDB data dictionary, so that we can print a warning about orphaned tablespaces */ ibool print_error_if_does_not_exist); /*!< in: print detailed error information to the .err log if a matching tablespace is not found from memory */ #else /* !UNIV_HOTBACKUP */ /********************************************************************//** Extends all tablespaces to the size stored in the space header. During the ibbackup --apply-log phase we extended the spaces on-demand so that log records could be appllied, but that may have left spaces still too small compared to the size stored in the space header. */ UNIV_INTERN void fil_extend_tablespaces_to_stored_len(void); /*======================================*/ #endif /* !UNIV_HOTBACKUP */ /**********************************************************************//** Tries to extend a data file so that it would accommodate the number of pages given. The tablespace must be cached in the memory cache. If the space is big enough already, does nothing. @return TRUE if success */ UNIV_INTERN ibool fil_extend_space_to_desired_size( /*=============================*/ ulint* actual_size, /*!< out: size of the space after extension; if we ran out of disk space this may be lower than the desired size */ ulint space_id, /*!< in: space id */ ulint size_after_extend);/*!< in: desired size in pages after the extension; if the current space size is bigger than this already, the function does nothing */ /*******************************************************************//** Tries to reserve free extents in a file space. @return TRUE if succeed */ UNIV_INTERN ibool fil_space_reserve_free_extents( /*===========================*/ ulint id, /*!< in: space id */ ulint n_free_now, /*!< in: number of free extents now */ ulint n_to_reserve); /*!< in: how many one wants to reserve */ /*******************************************************************//** Releases free extents in a file space. */ UNIV_INTERN void fil_space_release_free_extents( /*===========================*/ ulint id, /*!< in: space id */ ulint n_reserved); /*!< in: how many one reserved */ /*******************************************************************//** Gets the number of reserved extents. If the database is silent, this number should be zero. */ UNIV_INTERN ulint fil_space_get_n_reserved_extents( /*=============================*/ ulint id); /*!< in: space id */ /********************************************************************//** Reads or writes data. This operation is asynchronous (aio). @return DB_SUCCESS, or DB_TABLESPACE_DELETED if we are trying to do i/o on a tablespace which does not exist */ UNIV_INTERN ulint fil_io( /*===*/ ulint type, /*!< in: OS_FILE_READ or OS_FILE_WRITE, ORed to OS_FILE_LOG, if a log i/o and ORed to OS_AIO_SIMULATED_WAKE_LATER if simulated aio and we want to post a batch of i/os; NOTE that a simulated batch may introduce hidden chances of deadlocks, because i/os are not actually handled until all have been posted: use with great caution! */ ibool sync, /*!< in: TRUE if synchronous aio is desired */ ulint space_id, /*!< in: space id */ ulint zip_size, /*!< in: compressed page size in bytes; 0 for uncompressed pages */ ulint block_offset, /*!< in: offset in number of blocks */ ulint byte_offset, /*!< in: remainder of offset in bytes; in aio this must be divisible by the OS block size */ ulint len, /*!< in: how many bytes to read or write; this must not cross a file boundary; in aio this must be a block size multiple */ void* buf, /*!< in/out: buffer where to store read data or from where to write; in aio this must be appropriately aligned */ void* message); /*!< in: message for aio handler if non-sync aio used, else ignored */ /**********************************************************************//** Waits for an aio operation to complete. This function is used to write the handler for completed requests. The aio array of pending requests is divided into segments (see os0file.c for more info). The thread specifies which segment it wants to wait for. */ UNIV_INTERN void fil_aio_wait( /*=========*/ ulint segment); /*!< in: the number of the segment in the aio array to wait for */ /**********************************************************************//** Flushes to disk possible writes cached by the OS. If the space does not exist or is being dropped, does not do anything. */ UNIV_INTERN void fil_flush( /*======*/ ulint space_id); /*!< in: file space id (this can be a group of log files or a tablespace of the database) */ /**********************************************************************//** Flushes to disk writes in file spaces of the given type possibly cached by the OS. */ UNIV_INTERN void fil_flush_file_spaces( /*==================*/ ulint purpose); /*!< in: FIL_TABLESPACE, FIL_LOG */ /******************************************************************//** Checks the consistency of the tablespace cache. @return TRUE if ok */ UNIV_INTERN ibool fil_validate(void); /*==============*/ /********************************************************************//** Returns TRUE if file address is undefined. @return TRUE if undefined */ UNIV_INTERN ibool fil_addr_is_null( /*=============*/ fil_addr_t addr); /*!< in: address */ /********************************************************************//** Get the predecessor of a file page. @return FIL_PAGE_PREV */ UNIV_INTERN ulint fil_page_get_prev( /*==============*/ const byte* page); /*!< in: file page */ /********************************************************************//** Get the successor of a file page. @return FIL_PAGE_NEXT */ UNIV_INTERN ulint fil_page_get_next( /*==============*/ const byte* page); /*!< in: file page */ /*********************************************************************//** Sets the file page type. */ UNIV_INTERN void fil_page_set_type( /*==============*/ byte* page, /*!< in/out: file page */ ulint type); /*!< in: type */ /*********************************************************************//** Gets the file page type. @return type; NOTE that if the type has not been written to page, the return value not defined */ UNIV_INTERN ulint fil_page_get_type( /*==============*/ const byte* page); /*!< in: file page */ /************************************************************************ Reset variables. */ UNIV_INTERN void fil_var_init(void); /*==============*/ /******************************************************************** Remove the underlying directory where the database .ibd files are stored. @return TRUE on success */ UNIV_INTERN ibool fil_rmdir( /*======*/ const char* dbname); /*!< in: database name */ /******************************************************************** Create the underlying directory where the database .ibd files are stored. @return TRUE on success */ UNIV_INTERN ibool fil_mkdir( /*======*/ const char* dbname); /*!< in: database name */ typedef struct fil_space_struct fil_space_t; #endif haildb-2.3.2/include/srv0que.h0000644000175000017500000000261011513177357017043 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/srv0que.h Server query execution Created 6/5/1996 Heikki Tuuri *******************************************************/ #ifndef srv0que_h #define srv0que_h #include "univ.i" #include "que0types.h" /**********************************************************************//** Enqueues a task to server task queue and releases a worker thread, if there is a suspended one. */ UNIV_INTERN void srv_que_task_enqueue_low( /*=====================*/ que_thr_t* thr); /*!< in: query thread */ #endif haildb-2.3.2/include/ut0list.h0000644000175000017500000001145611513177357017052 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 2006, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /*******************************************************************//** @file include/ut0list.h A double-linked list Created 4/26/2006 Osku Salerma ************************************************************************/ /*******************************************************************//** A double-linked list. This differs from the one in ut0lst.h in that in this one, each list node contains a pointer to the data, whereas the one in ut0lst.h uses a strategy where the list pointers are embedded in the data items themselves. Use this one when you need to store arbitrary data in the list where you can't embed the list pointers in the data, if a data item needs to be stored in multiple lists, etc. Note about the memory management: ib_list_t is a fixed-size struct whose allocation/deallocation is done through ib_list_create/ib_list_free, but the memory for the list nodes is allocated through a user-given memory heap, which can either be the same for all nodes or vary per node. Most users will probably want to create a memory heap to store the item-specific data, and pass in this same heap to the list node creation functions, thus automatically freeing the list node when the item's heap is freed. ************************************************************************/ #ifndef IB_LIST_H #define IB_LIST_H #include "mem0mem.h" typedef struct ib_list_struct ib_list_t; typedef struct ib_list_node_struct ib_list_node_t; typedef struct ib_list_helper_struct ib_list_helper_t; /****************************************************************//** Create a new list using mem_alloc. Lists created with this function must be freed with ib_list_free. @return list */ UNIV_INTERN ib_list_t* ib_list_create(void); /*=================*/ /****************************************************************//** Free a list. */ UNIV_INTERN void ib_list_free( /*=========*/ ib_list_t* list); /*!< in: list */ /****************************************************************//** Add the data to the end of the list. @return new list node */ UNIV_INTERN ib_list_node_t* ib_list_add_last( /*=============*/ ib_list_t* list, /*!< in: list */ void* data, /*!< in: data */ mem_heap_t* heap); /*!< in: memory heap to use */ /****************************************************************//** Add the data after the indicated node. @return new list node */ UNIV_INTERN ib_list_node_t* ib_list_add_after( /*==============*/ ib_list_t* list, /*!< in: list */ ib_list_node_t* prev_node, /*!< in: node preceding new node (can be NULL) */ void* data, /*!< in: data */ mem_heap_t* heap); /*!< in: memory heap to use */ /****************************************************************//** Remove the node from the list. */ UNIV_INTERN void ib_list_remove( /*===========*/ ib_list_t* list, /*!< in: list */ ib_list_node_t* node); /*!< in: node to remove */ /****************************************************************//** Get the first node in the list. @return first node, or NULL */ UNIV_INLINE ib_list_node_t* ib_list_get_first( /*==============*/ ib_list_t* list); /*!< in: list */ /****************************************************************//** Get the last node in the list. @return last node, or NULL */ UNIV_INLINE ib_list_node_t* ib_list_get_last( /*=============*/ ib_list_t* list); /*!< in: list */ /* List. */ struct ib_list_struct { ib_list_node_t* first; /*!< first node */ ib_list_node_t* last; /*!< last node */ ibool is_heap_list; /*!< TRUE if this list was allocated through a heap */ }; /* A list node. */ struct ib_list_node_struct { ib_list_node_t* prev; /*!< previous node */ ib_list_node_t* next; /*!< next node */ void* data; /*!< user data */ }; /* Quite often, the only additional piece of data you need is the per-item memory heap, so we have this generic struct available to use in those cases. */ struct ib_list_helper_struct { mem_heap_t* heap; /*!< memory heap */ void* data; /*!< user data */ }; #ifndef UNIV_NONINL #include "ut0list.ic" #endif #endif haildb-2.3.2/include/os0proc.ic0000644000175000017500000000207611513177357017175 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/os0proc.ic The interface to the operating system process control primitives Created 9/30/1995 Heikki Tuuri *******************************************************/ haildb-2.3.2/include/dict0types.h0000644000175000017500000000333611513177357017534 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/dict0types.h Data dictionary global types Created 1/8/1996 Heikki Tuuri *******************************************************/ #ifndef dict0types_h #define dict0types_h typedef struct dict_sys_struct dict_sys_t; typedef struct dict_col_struct dict_col_t; typedef struct dict_field_struct dict_field_t; typedef struct dict_index_struct dict_index_t; typedef struct dict_table_struct dict_table_t; typedef struct dict_foreign_struct dict_foreign_t; /* A cluster object is a table object with the type field set to DICT_CLUSTERED */ typedef dict_table_t dict_cluster_t; typedef struct ind_node_struct ind_node_t; typedef struct tab_node_struct tab_node_t; /* Space id and page no where the dictionary header resides */ #define DICT_HDR_SPACE 0 /* the SYSTEM tablespace */ #define DICT_HDR_PAGE_NO FSP_DICT_HDR_PAGE_NO #endif haildb-2.3.2/include/lock0lock.ic0000644000175000017500000000676011513177357017475 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/lock0lock.ic The transaction lock system Created 5/7/1996 Heikki Tuuri *******************************************************/ #include "sync0sync.h" #include "srv0srv.h" #include "dict0dict.h" #include "row0row.h" #include "trx0sys.h" #include "trx0trx.h" #include "buf0buf.h" #include "page0page.h" #include "page0cur.h" #include "row0vers.h" #include "que0que.h" #include "btr0cur.h" #include "read0read.h" #include "log0recv.h" /*********************************************************************//** Calculates the fold value of a page file address: used in inserting or searching for a lock in the hash table. @return folded value */ UNIV_INLINE ulint lock_rec_fold( /*==========*/ ulint space, /*!< in: space */ ulint page_no)/*!< in: page number */ { return(ut_fold_ulint_pair(space, page_no)); } /*********************************************************************//** Calculates the hash value of a page file address: used in inserting or searching for a lock in the hash table. @return hashed value */ UNIV_INLINE ulint lock_rec_hash( /*==========*/ ulint space, /*!< in: space */ ulint page_no)/*!< in: page number */ { return(hash_calc_hash(lock_rec_fold(space, page_no), lock_sys->rec_hash)); } /*********************************************************************//** Checks if some transaction has an implicit x-lock on a record in a clustered index. @return transaction which has the x-lock, or NULL */ UNIV_INLINE trx_t* lock_clust_rec_some_has_impl( /*=========================*/ const rec_t* rec, /*!< in: user record */ dict_index_t* dict_index, /*!< in: clustered index */ const ulint* offsets)/*!< in: rec_get_offsets(rec, index) */ { trx_id_t trx_id; ut_ad(mutex_own(&kernel_mutex)); ut_ad(dict_index_is_clust(dict_index)); ut_ad(page_rec_is_user_rec(rec)); trx_id = row_get_rec_trx_id(rec, dict_index, offsets); if (trx_is_active(trx_id)) { /* The modifying or inserting transaction is active */ return(trx_get_on_id(trx_id)); } return(NULL); } /*********************************************************************//** Gets the heap_no of the smallest user record on a page. @return heap_no of smallest user record, or PAGE_HEAP_NO_SUPREMUM */ UNIV_INLINE ulint lock_get_min_heap_no( /*=================*/ const buf_block_t* block) /*!< in: buffer block */ { const page_t* page = block->frame; if (page_is_comp(page)) { return(rec_get_heap_no_new( page + rec_get_next_offs(page + PAGE_NEW_INFIMUM, TRUE))); } else { return(rec_get_heap_no_old( page + rec_get_next_offs(page + PAGE_OLD_INFIMUM, FALSE))); } } haildb-2.3.2/include/pars0grm.h0000644000175000017500000001560411513177357017200 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2009, Innobase Oy. All Rights Reserved. Copyright (c) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc. As a special exception, when this file is copied by Bison into a Bison output file, you may use that output file without restriction. This special exception was added by the Free Software Foundation in version 1.24 of Bison. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /* A Bison parser, made by GNU Bison 1.875d. */ /* Tokens. */ #ifndef YYTOKENTYPE # define YYTOKENTYPE /* Put the tokens into the symbol table, so that GDB and other debuggers know about them. */ enum yytokentype { PARS_INT_LIT = 258, PARS_FLOAT_LIT = 259, PARS_STR_LIT = 260, PARS_FIXBINARY_LIT = 261, PARS_BLOB_LIT = 262, PARS_NULL_LIT = 263, PARS_ID_TOKEN = 264, PARS_AND_TOKEN = 265, PARS_OR_TOKEN = 266, PARS_NOT_TOKEN = 267, PARS_GE_TOKEN = 268, PARS_LE_TOKEN = 269, PARS_NE_TOKEN = 270, PARS_PROCEDURE_TOKEN = 271, PARS_IN_TOKEN = 272, PARS_OUT_TOKEN = 273, PARS_BINARY_TOKEN = 274, PARS_BLOB_TOKEN = 275, PARS_INT_TOKEN = 276, PARS_INTEGER_TOKEN = 277, PARS_FLOAT_TOKEN = 278, PARS_CHAR_TOKEN = 279, PARS_IS_TOKEN = 280, PARS_BEGIN_TOKEN = 281, PARS_END_TOKEN = 282, PARS_IF_TOKEN = 283, PARS_THEN_TOKEN = 284, PARS_ELSE_TOKEN = 285, PARS_ELSIF_TOKEN = 286, PARS_LOOP_TOKEN = 287, PARS_WHILE_TOKEN = 288, PARS_RETURN_TOKEN = 289, PARS_SELECT_TOKEN = 290, PARS_SUM_TOKEN = 291, PARS_COUNT_TOKEN = 292, PARS_DISTINCT_TOKEN = 293, PARS_FROM_TOKEN = 294, PARS_WHERE_TOKEN = 295, PARS_FOR_TOKEN = 296, PARS_DDOT_TOKEN = 297, PARS_READ_TOKEN = 298, PARS_ORDER_TOKEN = 299, PARS_BY_TOKEN = 300, PARS_ASC_TOKEN = 301, PARS_DESC_TOKEN = 302, PARS_INSERT_TOKEN = 303, PARS_INTO_TOKEN = 304, PARS_VALUES_TOKEN = 305, PARS_UPDATE_TOKEN = 306, PARS_SET_TOKEN = 307, PARS_DELETE_TOKEN = 308, PARS_CURRENT_TOKEN = 309, PARS_OF_TOKEN = 310, PARS_CREATE_TOKEN = 311, PARS_TABLE_TOKEN = 312, PARS_INDEX_TOKEN = 313, PARS_UNIQUE_TOKEN = 314, PARS_CLUSTERED_TOKEN = 315, PARS_DOES_NOT_FIT_IN_MEM_TOKEN = 316, PARS_ON_TOKEN = 317, PARS_ASSIGN_TOKEN = 318, PARS_DECLARE_TOKEN = 319, PARS_CURSOR_TOKEN = 320, PARS_SQL_TOKEN = 321, PARS_OPEN_TOKEN = 322, PARS_FETCH_TOKEN = 323, PARS_CLOSE_TOKEN = 324, PARS_NOTFOUND_TOKEN = 325, PARS_TO_CHAR_TOKEN = 326, PARS_TO_NUMBER_TOKEN = 327, PARS_TO_BINARY_TOKEN = 328, PARS_BINARY_TO_NUMBER_TOKEN = 329, PARS_SUBSTR_TOKEN = 330, PARS_REPLSTR_TOKEN = 331, PARS_CONCAT_TOKEN = 332, PARS_INSTR_TOKEN = 333, PARS_LENGTH_TOKEN = 334, PARS_SYSDATE_TOKEN = 335, PARS_PRINTF_TOKEN = 336, PARS_ASSERT_TOKEN = 337, PARS_RND_TOKEN = 338, PARS_RND_STR_TOKEN = 339, PARS_ROW_PRINTF_TOKEN = 340, PARS_COMMIT_TOKEN = 341, PARS_ROLLBACK_TOKEN = 342, PARS_WORK_TOKEN = 343, PARS_UNSIGNED_TOKEN = 344, PARS_EXIT_TOKEN = 345, PARS_FUNCTION_TOKEN = 346, PARS_LOCK_TOKEN = 347, PARS_SHARE_TOKEN = 348, PARS_MODE_TOKEN = 349, NEG = 350 }; #endif /* Tokens. */ #define PARS_INT_LIT 258 #define PARS_FLOAT_LIT 259 #define PARS_STR_LIT 260 #define PARS_FIXBINARY_LIT 261 #define PARS_BLOB_LIT 262 #define PARS_NULL_LIT 263 #define PARS_ID_TOKEN 264 #define PARS_AND_TOKEN 265 #define PARS_OR_TOKEN 266 #define PARS_NOT_TOKEN 267 #define PARS_GE_TOKEN 268 #define PARS_LE_TOKEN 269 #define PARS_NE_TOKEN 270 #define PARS_PROCEDURE_TOKEN 271 #define PARS_IN_TOKEN 272 #define PARS_OUT_TOKEN 273 #define PARS_BINARY_TOKEN 274 #define PARS_BLOB_TOKEN 275 #define PARS_INT_TOKEN 276 #define PARS_INTEGER_TOKEN 277 #define PARS_FLOAT_TOKEN 278 #define PARS_CHAR_TOKEN 279 #define PARS_IS_TOKEN 280 #define PARS_BEGIN_TOKEN 281 #define PARS_END_TOKEN 282 #define PARS_IF_TOKEN 283 #define PARS_THEN_TOKEN 284 #define PARS_ELSE_TOKEN 285 #define PARS_ELSIF_TOKEN 286 #define PARS_LOOP_TOKEN 287 #define PARS_WHILE_TOKEN 288 #define PARS_RETURN_TOKEN 289 #define PARS_SELECT_TOKEN 290 #define PARS_SUM_TOKEN 291 #define PARS_COUNT_TOKEN 292 #define PARS_DISTINCT_TOKEN 293 #define PARS_FROM_TOKEN 294 #define PARS_WHERE_TOKEN 295 #define PARS_FOR_TOKEN 296 #define PARS_DDOT_TOKEN 297 #define PARS_READ_TOKEN 298 #define PARS_ORDER_TOKEN 299 #define PARS_BY_TOKEN 300 #define PARS_ASC_TOKEN 301 #define PARS_DESC_TOKEN 302 #define PARS_INSERT_TOKEN 303 #define PARS_INTO_TOKEN 304 #define PARS_VALUES_TOKEN 305 #define PARS_UPDATE_TOKEN 306 #define PARS_SET_TOKEN 307 #define PARS_DELETE_TOKEN 308 #define PARS_CURRENT_TOKEN 309 #define PARS_OF_TOKEN 310 #define PARS_CREATE_TOKEN 311 #define PARS_TABLE_TOKEN 312 #define PARS_INDEX_TOKEN 313 #define PARS_UNIQUE_TOKEN 314 #define PARS_CLUSTERED_TOKEN 315 #define PARS_DOES_NOT_FIT_IN_MEM_TOKEN 316 #define PARS_ON_TOKEN 317 #define PARS_ASSIGN_TOKEN 318 #define PARS_DECLARE_TOKEN 319 #define PARS_CURSOR_TOKEN 320 #define PARS_SQL_TOKEN 321 #define PARS_OPEN_TOKEN 322 #define PARS_FETCH_TOKEN 323 #define PARS_CLOSE_TOKEN 324 #define PARS_NOTFOUND_TOKEN 325 #define PARS_TO_CHAR_TOKEN 326 #define PARS_TO_NUMBER_TOKEN 327 #define PARS_TO_BINARY_TOKEN 328 #define PARS_BINARY_TO_NUMBER_TOKEN 329 #define PARS_SUBSTR_TOKEN 330 #define PARS_REPLSTR_TOKEN 331 #define PARS_CONCAT_TOKEN 332 #define PARS_INSTR_TOKEN 333 #define PARS_LENGTH_TOKEN 334 #define PARS_SYSDATE_TOKEN 335 #define PARS_PRINTF_TOKEN 336 #define PARS_ASSERT_TOKEN 337 #define PARS_RND_TOKEN 338 #define PARS_RND_STR_TOKEN 339 #define PARS_ROW_PRINTF_TOKEN 340 #define PARS_COMMIT_TOKEN 341 #define PARS_ROLLBACK_TOKEN 342 #define PARS_WORK_TOKEN 343 #define PARS_UNSIGNED_TOKEN 344 #define PARS_EXIT_TOKEN 345 #define PARS_FUNCTION_TOKEN 346 #define PARS_LOCK_TOKEN 347 #define PARS_SHARE_TOKEN 348 #define PARS_MODE_TOKEN 349 #define NEG 350 #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED typedef int YYSTYPE; # define yystype YYSTYPE /* obsolescent; will be withdrawn */ # define YYSTYPE_IS_DECLARED 1 # define YYSTYPE_IS_TRIVIAL 1 #endif extern YYSTYPE yylval; haildb-2.3.2/include/ut0rnd.h0000644000175000017500000001053111513177357016653 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1994, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /******************************************************************//** @file include/ut0rnd.h Random numbers and hashing Created 1/20/1994 Heikki Tuuri ***********************************************************************/ #ifndef ut0rnd_h #define ut0rnd_h #include "univ.i" #include "ut0byte.h" /** The 'character code' for end of field or string (used in folding records */ #define UT_END_OF_FIELD 257 /********************************************************//** This is used to set the random number seed. */ UNIV_INLINE void ut_rnd_set_seed( /*============*/ ulint seed); /*!< in: seed */ /********************************************************//** The following function generates a series of 'random' ulint integers. @return the next 'random' number */ UNIV_INLINE ulint ut_rnd_gen_next_ulint( /*==================*/ ulint rnd); /*!< in: the previous random number value */ /*********************************************************//** The following function generates 'random' ulint integers which enumerate the value space (let there be N of them) of ulint integers in a pseudo-random fashion. Note that the same integer is repeated always after N calls to the generator. @return the 'random' number */ UNIV_INLINE ulint ut_rnd_gen_ulint(void); /*==================*/ /********************************************************//** Generates a random integer from a given interval. @return the 'random' number */ UNIV_INLINE ulint ut_rnd_interval( /*============*/ ulint low, /*!< in: low limit; can generate also this value */ ulint high); /*!< in: high limit; can generate also this value */ /*********************************************************//** Generates a random iboolean value. @return the random value */ UNIV_INLINE ibool ut_rnd_gen_ibool(void); /*=================*/ /*******************************************************//** The following function generates a hash value for a ulint integer to a hash table of size table_size, which should be a prime or some random number to work reliably. @return hash value */ UNIV_INLINE ulint ut_hash_ulint( /*==========*/ ulint key, /*!< in: value to be hashed */ ulint table_size); /*!< in: hash table size */ /*************************************************************//** Folds a pair of ulints. @return folded value */ UNIV_INLINE ulint ut_fold_ulint_pair( /*===============*/ ulint n1, /*!< in: ulint */ ulint n2) /*!< in: ulint */ __attribute__((const)); /*************************************************************//** Folds a dulint. @return folded value */ UNIV_INLINE ulint ut_fold_dulint( /*===========*/ dulint d) /*!< in: dulint */ __attribute__((const)); /*************************************************************//** Folds a character string ending in the null character. @return folded value */ UNIV_INLINE ulint ut_fold_string( /*===========*/ const char* str) /*!< in: null-terminated string */ __attribute__((pure)); /*************************************************************//** Folds a binary string. @return folded value */ UNIV_INLINE ulint ut_fold_binary( /*===========*/ const byte* str, /*!< in: string of bytes */ ulint len) /*!< in: length */ __attribute__((pure)); /***********************************************************//** Looks for a prime number slightly greater than the given argument. The prime is chosen so that it is not near any power of 2. @return prime */ UNIV_INTERN ulint ut_find_prime( /*==========*/ ulint n) /*!< in: positive number > 100 */ __attribute__((const)); #ifndef UNIV_NONINL #include "ut0rnd.ic" #endif #endif haildb-2.3.2/include/data0types.h0000644000175000017500000000236311513177357017521 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 2000, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /********************************************************************//** @file include/data0types.h Some type definitions Created 9/21/2000 Heikki Tuuri *************************************************************************/ #ifndef data0types_h #define data0types_h /* SQL data field struct */ typedef struct dfield_struct dfield_t; /* SQL data tuple struct */ typedef struct dtuple_struct dtuple_t; #endif haildb-2.3.2/include/row0purge.ic0000644000175000017500000000202511513177357017534 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1997, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/row0purge.ic Purge obsolete records Created 3/14/1997 Heikki Tuuri *******************************************************/ haildb-2.3.2/include/api0ucode.h0000644000175000017500000000741711513177357017321 0ustar00pcrewspcrews00000000000000#ifndef INNOBASE_UNI0CODE_H #define INNOBASE_UNI0CODE_H /* Opaque type used by the Unicode implementation. */ typedef struct charset_struct charset_t; /************************************************************************** Determines the connection character set. @return connection character set */ UNIV_INTERN const charset_t* ib_ucode_get_connection_charset(void); /*=================================*/ /************************************************************************** Determines the character set based on id. @return connection character set */ UNIV_INTERN const charset_t* ib_ucode_get_charset( /*=================*/ ulint id); /*!< in: Charset-collation code */ /********************************************************************** Get the variable length bounds of the given (multibyte) character set. */ UNIV_INTERN void ib_ucode_get_charset_width( /*=======================*/ const charset_t*cs, /*!< in: character set */ ulint* mbminlen, /*!< out: min len of a char (in bytes) */ ulint* mbmaxlen); /*!< out: max len of a char (in bytes) */ /********************************************************************** This function is used to find the storage length in bytes of the characters that will fit into prefix_len bytes. @return number of bytes required to copy the characters that will fit into prefix_len bytes. */ UNIV_INTERN ulint ib_ucode_get_storage_size( /*======================*/ const charset_t*cs, /*!< in: character set id */ ulint prefix_len, /*!< in: prefix length in bytes */ ulint str_len, /*!< in: length of the string in bytes */ const char* str); /*!< in: character string */ /********************************************************************** Compares NUL-terminated UTF-8 strings case insensitively. @return 0 if a=b, <0 if a1 if a>b */ UNIV_INTERN int ib_utf8_strcasecmp( /*===============*/ const char* a, /*!< in: first string to compare */ const char* b); /*!< in: second string to compare */ /********************************************************************** Compares NUL-terminated UTF-8 strings case insensitively. @return 0 if a=b, <0 if a1 if a>b */ UNIV_INTERN int ib_utf8_strncasecmp( /*================*/ const char* a, /*!< in: first string to compare */ const char* b, /*!< in: second string to compare */ ulint n); /*!< in: no. of bytes to compare */ /********************************************************************** Makes all characters in a NUL-terminated UTF-8 string lower case. */ UNIV_INTERN void ib_utf8_casedown( /*============*/ char* a); /*!< in/out: str to put in lower case */ /************************************************************************** Test whether a UTF-8 character is a space or not. @return TRUE if isspace(c) */ UNIV_INTERN int ib_utf8_isspace( /*============*/ const charset_t*cs, /*!< in: character set */ char c); /*!< in: character to test */ /********************************************************************** Converts an identifier to a UTF-8 table name. */ UNIV_INTERN void ib_utf8_convert_from_table_id( /*==========================*/ const charset_t*cs, /*!< in: the 'from' character set */ char* to, /*!< out: converted identifier */ const char* from, /*!< in: identifier to convert */ ulint to_len); /*!< in: length of 'to', in bytes; should be at least 5 * strlen(to) + 1 */ /********************************************************************** Converts an identifier to UTF-8. */ UNIV_INTERN void ib_utf8_convert_from_id( /*=====================*/ const charset_t*cs, /*!< in: the 'from' character set */ char* to, /*!< out: converted identifier */ const char* from, /*!< in: identifier to convert */ ulint to_len); /*!< in: length of 'to', in bytes; should be at least 3 * strlen(to) + 1 */ #endif /* INNOBASE_UNI0CODE_H */ haildb-2.3.2/include/mtr0mtr.h0000644000175000017500000003404611513177357017053 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/mtr0mtr.h Mini-transaction buffer Created 11/26/1995 Heikki Tuuri *******************************************************/ #ifndef mtr0mtr_h #define mtr0mtr_h #include "univ.i" #include "mem0mem.h" #include "dyn0dyn.h" #include "buf0types.h" #include "sync0rw.h" #include "ut0byte.h" #include "mtr0types.h" #include "page0types.h" /* Logging modes for a mini-transaction */ #define MTR_LOG_ALL 21 /* default mode: log all operations modifying disk-based data */ #define MTR_LOG_NONE 22 /* log no operations */ /*#define MTR_LOG_SPACE 23 */ /* log only operations modifying file space page allocation data (operations in fsp0fsp.* ) */ #define MTR_LOG_SHORT_INSERTS 24 /* inserts are logged in a shorter form */ /* Types for the mlock objects to store in the mtr memo; NOTE that the first 3 values must be RW_S_LATCH, RW_X_LATCH, RW_NO_LATCH */ #define MTR_MEMO_PAGE_S_FIX RW_S_LATCH #define MTR_MEMO_PAGE_X_FIX RW_X_LATCH #define MTR_MEMO_BUF_FIX RW_NO_LATCH #define MTR_MEMO_MODIFY 54 #define MTR_MEMO_S_LOCK 55 #define MTR_MEMO_X_LOCK 56 /** @name Log item types The log items are declared 'byte' so that the compiler can warn if val and type parameters are switched in a call to mlog_write_ulint. NOTE! For 1 - 8 bytes, the flag value must give the length also! @{ */ #define MLOG_SINGLE_REC_FLAG 128 /*!< if the mtr contains only one log record for one page, i.e., write_initial_log_record has been called only once, this flag is ORed to the type of that first log record */ #define MLOG_1BYTE (1) /*!< one byte is written */ #define MLOG_2BYTES (2) /*!< 2 bytes ... */ #define MLOG_4BYTES (4) /*!< 4 bytes ... */ #define MLOG_8BYTES (8) /*!< 8 bytes ... */ #define MLOG_REC_INSERT ((byte)9) /*!< record insert */ #define MLOG_REC_CLUST_DELETE_MARK ((byte)10) /*!< mark clustered index record deleted */ #define MLOG_REC_SEC_DELETE_MARK ((byte)11) /*!< mark secondary index record deleted */ #define MLOG_REC_UPDATE_IN_PLACE ((byte)13) /*!< update of a record, preserves record field sizes */ #define MLOG_REC_DELETE ((byte)14) /*!< delete a record from a page */ #define MLOG_LIST_END_DELETE ((byte)15) /*!< delete record list end on index page */ #define MLOG_LIST_START_DELETE ((byte)16) /*!< delete record list start on index page */ #define MLOG_LIST_END_COPY_CREATED ((byte)17) /*!< copy record list end to a new created index page */ #define MLOG_PAGE_REORGANIZE ((byte)18) /*!< reorganize an index page in ROW_FORMAT=REDUNDANT */ #define MLOG_PAGE_CREATE ((byte)19) /*!< create an index page */ #define MLOG_UNDO_INSERT ((byte)20) /*!< insert entry in an undo log */ #define MLOG_UNDO_ERASE_END ((byte)21) /*!< erase an undo log page end */ #define MLOG_UNDO_INIT ((byte)22) /*!< initialize a page in an undo log */ #define MLOG_UNDO_HDR_DISCARD ((byte)23) /*!< discard an update undo log header */ #define MLOG_UNDO_HDR_REUSE ((byte)24) /*!< reuse an insert undo log header */ #define MLOG_UNDO_HDR_CREATE ((byte)25) /*!< create an undo log header */ #define MLOG_REC_MIN_MARK ((byte)26) /*!< mark an index record as the predefined minimum record */ #define MLOG_IBUF_BITMAP_INIT ((byte)27) /*!< initialize an ibuf bitmap page */ /*#define MLOG_FULL_PAGE ((byte)28) full contents of a page */ #ifdef UNIV_LOG_LSN_DEBUG # define MLOG_LSN ((byte)28) /* current LSN */ #endif #define MLOG_INIT_FILE_PAGE ((byte)29) /*!< this means that a file page is taken into use and the prior contents of the page should be ignored: in recovery we must not trust the lsn values stored to the file page */ #define MLOG_WRITE_STRING ((byte)30) /*!< write a string to a page */ #define MLOG_MULTI_REC_END ((byte)31) /*!< if a single mtr writes several log records, this log record ends the sequence of these records */ #define MLOG_DUMMY_RECORD ((byte)32) /*!< dummy log record used to pad a log block full */ #define MLOG_FILE_CREATE ((byte)33) /*!< log record about an .ibd file creation */ #define MLOG_FILE_RENAME ((byte)34) /*!< log record about an .ibd file rename */ #define MLOG_FILE_DELETE ((byte)35) /*!< log record about an .ibd file deletion */ #define MLOG_COMP_REC_MIN_MARK ((byte)36) /*!< mark a compact index record as the predefined minimum record */ #define MLOG_COMP_PAGE_CREATE ((byte)37) /*!< create a compact index page */ #define MLOG_COMP_REC_INSERT ((byte)38) /*!< compact record insert */ #define MLOG_COMP_REC_CLUST_DELETE_MARK ((byte)39) /*!< mark compact clustered index record deleted */ #define MLOG_COMP_REC_SEC_DELETE_MARK ((byte)40)/*!< mark compact secondary index record deleted; this log record type is redundant, as MLOG_REC_SEC_DELETE_MARK is independent of the record format. */ #define MLOG_COMP_REC_UPDATE_IN_PLACE ((byte)41)/*!< update of a compact record, preserves record field sizes */ #define MLOG_COMP_REC_DELETE ((byte)42) /*!< delete a compact record from a page */ #define MLOG_COMP_LIST_END_DELETE ((byte)43) /*!< delete compact record list end on index page */ #define MLOG_COMP_LIST_START_DELETE ((byte)44) /*!< delete compact record list start on index page */ #define MLOG_COMP_LIST_END_COPY_CREATED ((byte)45) /*!< copy compact record list end to a new created index page */ #define MLOG_COMP_PAGE_REORGANIZE ((byte)46) /*!< reorganize an index page */ #define MLOG_FILE_CREATE2 ((byte)47) /*!< log record about creating an .ibd file, with format */ #define MLOG_ZIP_WRITE_NODE_PTR ((byte)48) /*!< write the node pointer of a record on a compressed non-leaf B-tree page */ #define MLOG_ZIP_WRITE_BLOB_PTR ((byte)49) /*!< write the BLOB pointer of an externally stored column on a compressed page */ #define MLOG_ZIP_WRITE_HEADER ((byte)50) /*!< write to compressed page header */ #define MLOG_ZIP_PAGE_COMPRESS ((byte)51) /*!< compress an index page */ #define MLOG_BIGGEST_TYPE ((byte)51) /*!< biggest value (used in assertions) */ /* @} */ /** @name Flags for MLOG_FILE operations (stored in the page number parameter, called log_flags in the functions). The page number parameter was originally written as 0. @{ */ #define MLOG_FILE_FLAG_TEMP 1 /*!< identifies TEMPORARY TABLE in MLOG_FILE_CREATE, MLOG_FILE_CREATE2 */ /* @} */ /***************************************************************//** Starts a mini-transaction and creates a mini-transaction handle and buffer in the memory buffer given by the caller. @return mtr buffer which also acts as the mtr handle */ UNIV_INLINE mtr_t* mtr_start( /*======*/ mtr_t* mtr); /*!< in: memory buffer for the mtr buffer */ /***************************************************************//** Commits a mini-transaction. */ UNIV_INTERN void mtr_commit( /*=======*/ mtr_t* mtr); /*!< in: mini-transaction */ /**********************************************************//** Sets and returns a savepoint in mtr. @return savepoint */ UNIV_INLINE ulint mtr_set_savepoint( /*==============*/ mtr_t* mtr); /*!< in: mtr */ /**********************************************************//** Releases the latches stored in an mtr memo down to a savepoint. NOTE! The mtr must not have made changes to buffer pages after the savepoint, as these can be handled only by mtr_commit. */ UNIV_INTERN void mtr_rollback_to_savepoint( /*======================*/ mtr_t* mtr, /*!< in: mtr */ ulint savepoint); /*!< in: savepoint */ #ifndef UNIV_HOTBACKUP /**********************************************************//** Releases the (index tree) s-latch stored in an mtr memo after a savepoint. */ UNIV_INLINE void mtr_release_s_latch_at_savepoint( /*=============================*/ mtr_t* mtr, /*!< in: mtr */ ulint savepoint, /*!< in: savepoint */ rw_lock_t* lock); /*!< in: latch to release */ #else /* !UNIV_HOTBACKUP */ # define mtr_release_s_latch_at_savepoint(mtr,savepoint,lock) ((void) 0) #endif /* !UNIV_HOTBACKUP */ /***************************************************************//** Gets the logging mode of a mini-transaction. @return logging mode: MTR_LOG_NONE, ... */ UNIV_INLINE ulint mtr_get_log_mode( /*=============*/ mtr_t* mtr); /*!< in: mtr */ /***************************************************************//** Changes the logging mode of a mini-transaction. @return old mode */ UNIV_INLINE ulint mtr_set_log_mode( /*=============*/ mtr_t* mtr, /*!< in: mtr */ ulint mode); /*!< in: logging mode: MTR_LOG_NONE, ... */ /********************************************************//** Reads 1 - 4 bytes from a file page buffered in the buffer pool. @return value read */ UNIV_INTERN ulint mtr_read_ulint( /*===========*/ const byte* ptr, /*!< in: pointer from where to read */ ulint type, /*!< in: MLOG_1BYTE, MLOG_2BYTES, MLOG_4BYTES */ mtr_t* mtr); /*!< in: mini-transaction handle */ /********************************************************//** Reads 8 bytes from a file page buffered in the buffer pool. @return value read */ UNIV_INTERN dulint mtr_read_dulint( /*============*/ const byte* ptr, /*!< in: pointer from where to read */ mtr_t* mtr); /*!< in: mini-transaction handle */ #ifndef UNIV_HOTBACKUP /*********************************************************************//** This macro locks an rw-lock in s-mode. */ #define mtr_s_lock(B, MTR) mtr_s_lock_func((B), __FILE__, __LINE__,\ (MTR)) /*********************************************************************//** This macro locks an rw-lock in x-mode. */ #define mtr_x_lock(B, MTR) mtr_x_lock_func((B), __FILE__, __LINE__,\ (MTR)) /*********************************************************************//** NOTE! Use the macro above! Locks a lock in s-mode. */ UNIV_INLINE void mtr_s_lock_func( /*============*/ rw_lock_t* lock, /*!< in: rw-lock */ const char* file, /*!< in: file name */ ulint line, /*!< in: line number */ mtr_t* mtr); /*!< in: mtr */ /*********************************************************************//** NOTE! Use the macro above! Locks a lock in x-mode. */ UNIV_INLINE void mtr_x_lock_func( /*============*/ rw_lock_t* lock, /*!< in: rw-lock */ const char* file, /*!< in: file name */ ulint line, /*!< in: line number */ mtr_t* mtr); /*!< in: mtr */ #endif /* !UNIV_HOTBACKUP */ /***************************************************//** Releases an object in the memo stack. */ UNIV_INTERN void mtr_memo_release( /*=============*/ mtr_t* mtr, /*!< in: mtr */ void* object, /*!< in: object */ ulint type); /*!< in: object type: MTR_MEMO_S_LOCK, ... */ #ifdef UNIV_DEBUG # ifndef UNIV_HOTBACKUP /**********************************************************//** Checks if memo contains the given item. @return TRUE if contains */ UNIV_INLINE ibool mtr_memo_contains( /*==============*/ mtr_t* mtr, /*!< in: mtr */ const void* object, /*!< in: object to search */ ulint type); /*!< in: type of object */ /**********************************************************//** Checks if memo contains the given page. @return TRUE if contains */ UNIV_INTERN ibool mtr_memo_contains_page( /*===================*/ mtr_t* mtr, /*!< in: mtr */ const byte* ptr, /*!< in: pointer to buffer frame */ ulint type); /*!< in: type of object */ /*********************************************************//** Prints info of an mtr handle. */ UNIV_INTERN void mtr_print( /*======*/ mtr_t* mtr); /*!< in: mtr */ # else /* !UNIV_HOTBACKUP */ # define mtr_memo_contains(mtr, object, type) TRUE # define mtr_memo_contains_page(mtr, ptr, type) TRUE # endif /* !UNIV_HOTBACKUP */ #endif /* UNIV_DEBUG */ /*######################################################################*/ #define MTR_BUF_MEMO_SIZE 200 /* number of slots in memo */ /***************************************************//** Pushes an object to an mtr memo stack. */ UNIV_INLINE void mtr_memo_push( /*==========*/ mtr_t* mtr, /*!< in: mtr */ void* object, /*!< in: object */ ulint type); /*!< in: object type: MTR_MEMO_S_LOCK, ... */ /* Type definition of a mini-transaction memo stack slot. */ typedef struct mtr_memo_slot_struct mtr_memo_slot_t; struct mtr_memo_slot_struct{ ulint type; /*!< type of the stored object (MTR_MEMO_S_LOCK, ...) */ void* object; /*!< pointer to the object */ }; /* Mini-transaction handle and buffer */ struct mtr_struct{ #ifdef UNIV_DEBUG ulint state; /*!< MTR_ACTIVE, MTR_COMMITTING, MTR_COMMITTED */ #endif dyn_array_t memo; /*!< memo stack for locks etc. */ dyn_array_t log; /*!< mini-transaction log */ ibool modifications; /* TRUE if the mtr made modifications to buffer pool pages */ ulint n_log_recs; /* count of how many page initial log records have been written to the mtr log */ ulint log_mode; /* specifies which operations should be logged; default value MTR_LOG_ALL */ ib_uint64_t start_lsn;/* start lsn of the possible log entry for this mtr */ ib_uint64_t end_lsn;/* end lsn of the possible log entry for this mtr */ #ifdef UNIV_DEBUG ulint magic_n; #endif /* UNIV_DEBUG */ }; #ifdef UNIV_DEBUG # define MTR_MAGIC_N 54551 #endif /* UNIV_DEBUG */ #define MTR_ACTIVE 12231 #define MTR_COMMITTING 56456 #define MTR_COMMITTED 34676 #ifndef UNIV_NONINL #include "mtr0mtr.ic" #endif #endif haildb-2.3.2/include/row0ins.ic0000644000175000017500000000202111513177357017177 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/row0ins.ic Insert into a table Created 4/20/1996 Heikki Tuuri *******************************************************/ haildb-2.3.2/include/db0err.h0000644000175000017500000000213511513177357016616 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/db0err.h Global error codes for the database Created 5/24/1996 Heikki Tuuri *******************************************************/ #ifndef db0err_h #define db0err_h #include "haildb.h" #endif haildb-2.3.2/include/log0recv.h0000644000175000017500000004500111513177357017160 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1997, 2010, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/log0recv.h Recovery Created 9/20/1997 Heikki Tuuri *******************************************************/ #ifndef log0recv_h #define log0recv_h #include "univ.i" #include "ut0byte.h" #include "buf0types.h" #include "hash0hash.h" #include "log0log.h" #include "srv0srv.h" #include "api0api.h" #ifdef UNIV_HOTBACKUP extern ibool recv_replay_file_ops; /*******************************************************************//** Reads the checkpoint info needed in hot backup. @return TRUE if success */ UNIV_INTERN ibool recv_read_cp_info_for_backup( /*=========================*/ const byte* hdr, /*!< in: buffer containing the log group header */ ib_uint64_t* lsn, /*!< out: checkpoint lsn */ ulint* offset, /*!< out: checkpoint offset in the log group */ ulint* fsp_limit,/*!< out: fsp limit of space 0, 1000000000 if the database is running with < version 3.23.50 of InnoDB */ ib_uint64_t* cp_no, /*!< out: checkpoint number */ ib_uint64_t* first_header_lsn); /*!< out: lsn of of the start of the first log file */ /*******************************************************************//** Scans the log segment and n_bytes_scanned is set to the length of valid log scanned. */ UNIV_INTERN void recv_scan_log_seg_for_backup( /*=========================*/ byte* buf, /*!< in: buffer containing log data */ ulint buf_len, /*!< in: data length in that buffer */ ib_uint64_t* scanned_lsn, /*!< in/out: lsn of buffer start, we return scanned lsn */ ulint* scanned_checkpoint_no, /*!< in/out: 4 lowest bytes of the highest scanned checkpoint number so far */ ulint* n_bytes_scanned);/*!< out: how much we were able to scan, smaller than buf_len if log data ended here */ #endif /* UNIV_HOTBACKUP */ /*******************************************************************//** Returns TRUE if recovery is currently running. @return recv_recovery_on */ UNIV_INLINE ibool recv_recovery_is_on(void); /*=====================*/ #ifdef UNIV_LOG_ARCHIVE /*******************************************************************//** Returns TRUE if recovery from backup is currently running. @return recv_recovery_from_backup_on */ UNIV_INLINE ibool recv_recovery_from_backup_is_on(void); /*=================================*/ #endif /* UNIV_LOG_ARCHIVE */ /************************************************************************//** Applies the hashed log records to the page, if the page lsn is less than the lsn of a log record. This can be called when a buffer page has just been read in, or also for a page already in the buffer pool. */ UNIV_INTERN void recv_recover_page_func( /*===================*/ #ifndef UNIV_HOTBACKUP ibool just_read_in, /*!< in: TRUE if the i/o handler calls this for a freshly read page */ #endif /* !UNIV_HOTBACKUP */ buf_block_t* block); /*!< in/out: buffer block */ #ifndef UNIV_HOTBACKUP /** Wrapper for recv_recover_page_func(). Applies the hashed log records to the page, if the page lsn is less than the lsn of a log record. This can be called when a buffer page has just been read in, or also for a page already in the buffer pool. @param jri in: TRUE if just read in (the i/o handler calls this for a freshly read page) @param block in/out: the buffer block */ # define recv_recover_page(jri, block) recv_recover_page_func(jri, block) #else /* !UNIV_HOTBACKUP */ /** Wrapper for recv_recover_page_func(). Applies the hashed log records to the page, if the page lsn is less than the lsn of a log record. This can be called when a buffer page has just been read in, or also for a page already in the buffer pool. @param jri in: TRUE if just read in (the i/o handler calls this for a freshly read page) @param block in/out: the buffer block */ # define recv_recover_page(jri, block) recv_recover_page_func(block) #endif /* !UNIV_HOTBACKUP */ /********************************************************//** Recovers from a checkpoint. When this function returns, the database is able to start processing of new user transactions, but the function recv_recovery_from_checkpoint_finish should be called later to complete the recovery and free the resources used in it. @return error code or DB_SUCCESS */ UNIV_INTERN ulint recv_recovery_from_checkpoint_start_func( /*=====================================*/ ib_recovery_t recovery, /*!< in: recovery flag */ #ifdef UNIV_LOG_ARCHIVE ulint type, /*!< in: LOG_CHECKPOINT or LOG_ARCHIVE */ ib_uint64_t limit_lsn, /*!< in: recover up to this lsn if possible */ #endif /* UNIV_LOG_ARCHIVE */ ib_uint64_t min_flushed_lsn,/*!< in: min flushed lsn from data files */ ib_uint64_t max_flushed_lsn);/*!< in: max flushed lsn from data files */ #ifdef UNIV_LOG_ARCHIVE /** Wrapper for recv_recovery_from_checkpoint_start_func(). Recovers from a checkpoint. When this function returns, the database is able to start processing of new user transactions, but the function recv_recovery_from_checkpoint_finish should be called later to complete the recovery and free the resources used in it. @param type in: LOG_CHECKPOINT or LOG_ARCHIVE @param lim in: recover up to this log sequence number if possible @param min in: minimum flushed log sequence number from data files @param max in: maximum flushed log sequence number from data files @return error code or DB_SUCCESS */ # define recv_recovery_from_checkpoint_start(recv,type,lim,min,max) \ recv_recovery_from_checkpoint_start_func(recv,type,lim,min,max) #else /* UNIV_LOG_ARCHIVE */ /** Wrapper for recv_recovery_from_checkpoint_start_func(). Recovers from a checkpoint. When this function returns, the database is able to start processing of new user transactions, but the function recv_recovery_from_checkpoint_finish should be called later to complete the recovery and free the resources used in it. @param type ignored: LOG_CHECKPOINT or LOG_ARCHIVE @param lim ignored: recover up to this log sequence number if possible @param min in: minimum flushed log sequence number from data files @param max in: maximum flushed log sequence number from data files @return error code or DB_SUCCESS */ # define recv_recovery_from_checkpoint_start(recv,type,lim,min,max) \ recv_recovery_from_checkpoint_start_func(recv,min,max) #endif /* UNIV_LOG_ARCHIVE */ /********************************************************//** Completes recovery from a checkpoint. */ UNIV_INTERN void recv_recovery_from_checkpoint_finish( /*=================================*/ ib_recovery_t recovery); /*!< in: recovery flag */ /********************************************************//** Initiates the rollback of active transactions. */ UNIV_INTERN void recv_recovery_rollback_active(void); /*===============================*/ /*******************************************************//** Scans log from a buffer and stores new log data to the parsing buffer. Parses and hashes the log records if new data found. Unless UNIV_HOTBACKUP is defined, this function will apply log records automatically when the hash table becomes full. @return TRUE if limit_lsn has been reached, or not able to scan any more in this log group */ UNIV_INTERN ibool recv_scan_log_recs( /*===============*/ ib_recovery_t recovery, /*!< in: recovery flag */ ulint available_memory,/*!< in: we let the hash table of recs to grow to this size, at the maximum */ ibool store_to_hash, /*!< in: TRUE if the records should be stored to the hash table; this is set to FALSE if just debug checking is needed */ const byte* buf, /*!< in: buffer containing a log segment or garbage */ ulint len, /*!< in: buffer length */ ib_uint64_t start_lsn, /*!< in: buffer start lsn */ ib_uint64_t* contiguous_lsn, /*!< in/out: it is known that all log groups contain contiguous log data up to this lsn */ ib_uint64_t* group_scanned_lsn);/*!< out: scanning succeeded up to this lsn */ /******************************************************//** Resets the logs. The contents of log files will be lost! */ UNIV_INTERN void recv_reset_logs( /*============*/ ib_uint64_t lsn, /*!< in: reset to this lsn rounded up to be divisible by OS_FILE_LOG_BLOCK_SIZE, after which we add LOG_BLOCK_HDR_SIZE */ #ifdef UNIV_LOG_ARCHIVE ulint arch_log_no, /*!< in: next archived log file number */ #endif /* UNIV_LOG_ARCHIVE */ ibool new_logs_created);/*!< in: TRUE if resetting logs is done at the log creation; FALSE if it is done after archive recovery */ #ifdef UNIV_HOTBACKUP /******************************************************//** Creates new log files after a backup has been restored. */ UNIV_INTERN void recv_reset_log_files_for_backup( /*============================*/ const char* log_dir, /*!< in: log file directory path */ ulint n_log_files, /*!< in: number of log files */ ulint log_file_size, /*!< in: log file size */ ib_uint64_t lsn); /*!< in: new start lsn, must be divisible by OS_FILE_LOG_BLOCK_SIZE */ #endif /* UNIV_HOTBACKUP */ /********************************************************//** Creates the recovery system. */ UNIV_INTERN void recv_sys_create(void); /*=================*/ /**********************************************************//** Release recovery system mutexes. */ UNIV_INTERN void recv_sys_close(void); /*================*/ /********************************************************//** Frees the recovery system memory. */ UNIV_INTERN void recv_sys_mem_free(void); /*====================*/ /**********************************************************//** Inits the recovery system for a recovery operation. */ UNIV_INTERN void recv_sys_init( /*==========*/ ulint available_memory); /*!< in: available memory in bytes */ #ifndef UNIV_HOTBACKUP /**********************************************************//** Reset the state of the recovery system variables. */ UNIV_INTERN void recv_sys_var_init(void); /*===================*/ #endif /* !UNIV_HOTBACKUP */ /*******************************************************************//** Empties the hash table of stored log records, applying them to appropriate pages. */ UNIV_INTERN void recv_apply_hashed_log_recs( /*=======================*/ ibool allow_ibuf); /*!< in: if TRUE, also ibuf operations are allowed during the application; if FALSE, no ibuf operations are allowed, and after the application all file pages are flushed to disk and invalidated in buffer pool: this alternative means that no new log records can be generated during the application */ #ifdef UNIV_HOTBACKUP /*******************************************************************//** Applies log records in the hash table to a backup. */ UNIV_INTERN void recv_apply_log_recs_for_backup(void); /*================================*/ #endif #ifdef UNIV_LOG_ARCHIVE /********************************************************//** Recovers from archived log files, and also from log files, if they exist. @return error code or DB_SUCCESS */ UNIV_INTERN ulint recv_recovery_from_archive_start( /*=============================*/ ib_uint64_t min_flushed_lsn,/*!< in: min flushed lsn field from the data files */ ib_uint64_t limit_lsn, /*!< in: recover up to this lsn if possible */ ulint first_log_no); /*!< in: number of the first archived log file to use in the recovery; the file will be searched from INNOBASE_LOG_ARCH_DIR specified in server config file */ /********************************************************//** Completes recovery from archive. */ UNIV_INTERN void recv_recovery_from_archive_finish(void); /*===================================*/ #endif /* UNIV_LOG_ARCHIVE */ /** Block of log record data */ typedef struct recv_data_struct recv_data_t; /** Block of log record data */ struct recv_data_struct{ recv_data_t* next; /*!< pointer to the next block or NULL */ /*!< the log record data is stored physically immediately after this struct, max amount RECV_DATA_BLOCK_SIZE bytes of it */ }; /** Stored log record struct */ typedef struct recv_struct recv_t; /** Stored log record struct */ struct recv_struct{ byte type; /*!< log record type */ ulint len; /*!< log record body length in bytes */ recv_data_t* data; /*!< chain of blocks containing the log record body */ ib_uint64_t start_lsn;/*!< start lsn of the log segment written by the mtr which generated this log record: NOTE that this is not necessarily the start lsn of this log record */ ib_uint64_t end_lsn;/*!< end lsn of the log segment written by the mtr which generated this log record: NOTE that this is not necessarily the end lsn of this log record */ UT_LIST_NODE_T(recv_t) rec_list;/*!< list of log records for this page */ }; /** States of recv_addr_struct */ enum recv_addr_state { /** not yet processed */ RECV_NOT_PROCESSED, /** page is being read */ RECV_BEING_READ, /** log records are being applied on the page */ RECV_BEING_PROCESSED, /** log records have been applied on the page, or they have been discarded because the tablespace does not exist */ RECV_PROCESSED }; /** Hashed page file address struct */ typedef struct recv_addr_struct recv_addr_t; /** Hashed page file address struct */ struct recv_addr_struct{ enum recv_addr_state state; /*!< recovery state of the page */ ulint space; /*!< space id */ ulint page_no;/*!< page number */ UT_LIST_BASE_NODE_T(recv_t) rec_list;/*!< list of log records for this page */ hash_node_t addr_hash;/*!< hash node in the hash bucket chain */ }; /** Recovery system data structure */ typedef struct recv_sys_struct recv_sys_t; /** Recovery system data structure */ struct recv_sys_struct{ #ifndef UNIV_HOTBACKUP mutex_t mutex; /*!< mutex protecting the fields apply_log_recs, n_addrs, and the state field in each recv_addr struct */ #endif /* !UNIV_HOTBACKUP */ ibool apply_log_recs; /*!< this is TRUE when log rec application to pages is allowed; this flag tells the i/o-handler if it should do log record application */ ibool apply_batch_on; /*!< this is TRUE when a log rec application batch is running */ ib_uint64_t lsn; /*!< log sequence number */ ulint last_log_buf_size; /*!< size of the log buffer when the database last time wrote to the log */ byte* last_block; /*!< possible incomplete last recovered log block */ byte* last_block_buf_start; /*!< the nonaligned start address of the preceding buffer */ byte* buf; /*!< buffer for parsing log records */ ulint len; /*!< amount of data in buf */ ib_uint64_t parse_start_lsn; /*!< this is the lsn from which we were able to start parsing log records and adding them to the hash table; zero if a suitable start point not found yet */ ib_uint64_t scanned_lsn; /*!< the log data has been scanned up to this lsn */ ulint scanned_checkpoint_no; /*!< the log data has been scanned up to this checkpoint number (lowest 4 bytes) */ ulint recovered_offset; /*!< start offset of non-parsed log records in buf */ ib_uint64_t recovered_lsn; /*!< the log records have been parsed up to this lsn */ ib_uint64_t limit_lsn;/*!< recovery should be made at most up to this lsn */ ibool found_corrupt_log; /*!< this is set to TRUE if we during log scan find a corrupt log block, or a corrupt log record, or there is a log parsing buffer overflow */ #ifdef UNIV_LOG_ARCHIVE log_group_t* archive_group; /*!< in archive recovery: the log group whose archive is read */ #endif /* !UNIV_LOG_ARCHIVE */ mem_heap_t* heap; /*!< memory heap of log records and file addresses*/ hash_table_t* addr_hash;/*!< hash table of file addresses of pages */ ulint n_addrs;/*!< number of not processed hashed file addresses in the hash table */ }; /** The recovery system */ extern recv_sys_t* recv_sys; /** TRUE when applying redo log records during crash recovery; FALSE otherwise. Note that this is FALSE while a background thread is rolling back incomplete transactions. */ extern ibool recv_recovery_on; /** If the following is TRUE, the buffer pool file pages must be invalidated after recovery and no ibuf operations are allowed; this becomes TRUE if the log record hash table becomes too full, and log records must be merged to file pages already before the recovery is finished: in this case no ibuf operations are allowed, as they could modify the pages read in the buffer pool before the pages have been recovered to the up-to-date state. TRUE means that recovery is running and no operations on the log files are allowed yet: the variable name is misleading. */ extern ibool recv_no_ibuf_operations; /** TRUE when recv_init_crash_recovery() has been called. */ extern ibool recv_needed_recovery; #ifdef UNIV_DEBUG /** TRUE if writing to the redo log (mtr_commit) is forbidden. Protected by log_sys->mutex. */ extern ibool recv_no_log_write; #endif /* UNIV_DEBUG */ /** TRUE if buf_page_is_corrupted() should check if the log sequence number (FIL_PAGE_LSN) is in the future. Initially FALSE, and set by recv_recovery_from_checkpoint_start_func(). */ extern ibool recv_lsn_checks_on; extern ib_cb_t recv_pre_rollback_hook; #ifdef UNIV_HOTBACKUP /** TRUE when the redo log is being backed up */ extern ibool recv_is_making_a_backup; #endif /* UNIV_HOTBACKUP */ /** Maximum page number encountered in the redo log */ extern ulint recv_max_parsed_page_no; /** Size of the parsing buffer; it must accommodate RECV_SCAN_SIZE many times! */ #define RECV_PARSING_BUF_SIZE (2 * 1024 * 1024) /** Size of block reads when the log groups are scanned forward to do a roll-forward */ #define RECV_SCAN_SIZE (4 * UNIV_PAGE_SIZE) /** This many frames must be left free in the buffer pool when we scan the log and store the scanned log records in the buffer pool: we will use these free frames to read in pages when we start applying the log records to the database. */ extern ulint recv_n_pool_free_frames; #ifndef UNIV_NONINL #include "log0recv.ic" #endif #endif haildb-2.3.2/include/rem0rec.h0000644000175000017500000007107611513177357017007 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1994, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /********************************************************************//** @file include/rem0rec.h Record manager Created 5/30/1994 Heikki Tuuri *************************************************************************/ #ifndef rem0rec_h #define rem0rec_h #include "univ.i" #include "data0data.h" #include "rem0types.h" #include "mtr0types.h" #include "page0types.h" /* Info bit denoting the predefined minimum record: this bit is set if and only if the record is the first user record on a non-leaf B-tree page that is the leftmost page on its level (PAGE_LEVEL is nonzero and FIL_PAGE_PREV is FIL_NULL). */ #define REC_INFO_MIN_REC_FLAG 0x10UL /* The deleted flag in info bits */ #define REC_INFO_DELETED_FLAG 0x20UL /* when bit is set to 1, it means the record has been delete marked */ /* Number of extra bytes in an old-style record, in addition to the data and the offsets */ #define REC_N_OLD_EXTRA_BYTES 6 /* Number of extra bytes in a new-style record, in addition to the data and the offsets */ #define REC_N_NEW_EXTRA_BYTES 5 /* Record status values */ #define REC_STATUS_ORDINARY 0 #define REC_STATUS_NODE_PTR 1 #define REC_STATUS_INFIMUM 2 #define REC_STATUS_SUPREMUM 3 /* The following four constants are needed in page0zip.c in order to efficiently compress and decompress pages. */ /* The offset of heap_no in a compact record */ #define REC_NEW_HEAP_NO 4 /* The shift of heap_no in a compact record. The status is stored in the low-order bits. */ #define REC_HEAP_NO_SHIFT 3 /* Length of a B-tree node pointer, in bytes */ #define REC_NODE_PTR_SIZE 4 #ifdef UNIV_DEBUG /* Length of the rec_get_offsets() header */ # define REC_OFFS_HEADER_SIZE 4 #else /* UNIV_DEBUG */ /* Length of the rec_get_offsets() header */ # define REC_OFFS_HEADER_SIZE 2 #endif /* UNIV_DEBUG */ /* Number of elements that should be initially allocated for the offsets[] array, first passed to rec_get_offsets() */ #define REC_OFFS_NORMAL_SIZE 100 #define REC_OFFS_SMALL_SIZE 10 /******************************************************//** The following function is used to get the pointer of the next chained record on the same page. @return pointer to the next chained record, or NULL if none */ UNIV_INLINE const rec_t* rec_get_next_ptr_const( /*===================*/ const rec_t* rec, /*!< in: physical record */ ulint comp); /*!< in: nonzero=compact page format */ /******************************************************//** The following function is used to get the pointer of the next chained record on the same page. @return pointer to the next chained record, or NULL if none */ UNIV_INLINE rec_t* rec_get_next_ptr( /*=============*/ rec_t* rec, /*!< in: physical record */ ulint comp); /*!< in: nonzero=compact page format */ /******************************************************//** The following function is used to get the offset of the next chained record on the same page. @return the page offset of the next chained record, or 0 if none */ UNIV_INLINE ulint rec_get_next_offs( /*==============*/ const rec_t* rec, /*!< in: physical record */ ulint comp); /*!< in: nonzero=compact page format */ /******************************************************//** The following function is used to set the next record offset field of an old-style record. */ UNIV_INLINE void rec_set_next_offs_old( /*==================*/ rec_t* rec, /*!< in: old-style physical record */ ulint next); /*!< in: offset of the next record */ /******************************************************//** The following function is used to set the next record offset field of a new-style record. */ UNIV_INLINE void rec_set_next_offs_new( /*==================*/ rec_t* rec, /*!< in/out: new-style physical record */ ulint next); /*!< in: offset of the next record */ /******************************************************//** The following function is used to get the number of fields in an old-style record. @return number of data fields */ UNIV_INLINE ulint rec_get_n_fields_old( /*=================*/ const rec_t* rec); /*!< in: physical record */ /******************************************************//** The following function is used to get the number of fields in a record. @return number of data fields */ UNIV_INLINE ulint rec_get_n_fields( /*=============*/ const rec_t* rec, /*!< in: physical record */ const dict_index_t* index); /*!< in: record descriptor */ /******************************************************//** The following function is used to get the number of records owned by the previous directory record. @return number of owned records */ UNIV_INLINE ulint rec_get_n_owned_old( /*================*/ const rec_t* rec); /*!< in: old-style physical record */ /******************************************************//** The following function is used to set the number of owned records. */ UNIV_INLINE void rec_set_n_owned_old( /*================*/ rec_t* rec, /*!< in: old-style physical record */ ulint n_owned); /*!< in: the number of owned */ /******************************************************//** The following function is used to get the number of records owned by the previous directory record. @return number of owned records */ UNIV_INLINE ulint rec_get_n_owned_new( /*================*/ const rec_t* rec); /*!< in: new-style physical record */ /******************************************************//** The following function is used to set the number of owned records. */ UNIV_INLINE void rec_set_n_owned_new( /*================*/ rec_t* rec, /*!< in/out: new-style physical record */ page_zip_des_t* page_zip,/*!< in/out: compressed page, or NULL */ ulint n_owned);/*!< in: the number of owned */ /******************************************************//** The following function is used to retrieve the info bits of a record. @return info bits */ UNIV_INLINE ulint rec_get_info_bits( /*==============*/ const rec_t* rec, /*!< in: physical record */ ulint comp); /*!< in: nonzero=compact page format */ /******************************************************//** The following function is used to set the info bits of a record. */ UNIV_INLINE void rec_set_info_bits_old( /*==================*/ rec_t* rec, /*!< in: old-style physical record */ ulint bits); /*!< in: info bits */ /******************************************************//** The following function is used to set the info bits of a record. */ UNIV_INLINE void rec_set_info_bits_new( /*==================*/ rec_t* rec, /*!< in/out: new-style physical record */ ulint bits); /*!< in: info bits */ /******************************************************//** The following function retrieves the status bits of a new-style record. @return status bits */ UNIV_INLINE ulint rec_get_status( /*===========*/ const rec_t* rec); /*!< in: physical record */ /******************************************************//** The following function is used to set the status bits of a new-style record. */ UNIV_INLINE void rec_set_status( /*===========*/ rec_t* rec, /*!< in/out: physical record */ ulint bits); /*!< in: info bits */ /******************************************************//** The following function is used to retrieve the info and status bits of a record. (Only compact records have status bits.) @return info bits */ UNIV_INLINE ulint rec_get_info_and_status_bits( /*=========================*/ const rec_t* rec, /*!< in: physical record */ ulint comp); /*!< in: nonzero=compact page format */ /******************************************************//** The following function is used to set the info and status bits of a record. (Only compact records have status bits.) */ UNIV_INLINE void rec_set_info_and_status_bits( /*=========================*/ rec_t* rec, /*!< in/out: compact physical record */ ulint bits); /*!< in: info bits */ /******************************************************//** The following function tells if record is delete marked. @return nonzero if delete marked */ UNIV_INLINE ulint rec_get_deleted_flag( /*=================*/ const rec_t* rec, /*!< in: physical record */ ulint comp); /*!< in: nonzero=compact page format */ /******************************************************//** The following function is used to set the deleted bit. */ UNIV_INLINE void rec_set_deleted_flag_old( /*=====================*/ rec_t* rec, /*!< in: old-style physical record */ ulint flag); /*!< in: nonzero if delete marked */ /******************************************************//** The following function is used to set the deleted bit. */ UNIV_INLINE void rec_set_deleted_flag_new( /*=====================*/ rec_t* rec, /*!< in/out: new-style physical record */ page_zip_des_t* page_zip,/*!< in/out: compressed page, or NULL */ ulint flag); /*!< in: nonzero if delete marked */ /******************************************************//** The following function tells if a new-style record is a node pointer. @return TRUE if node pointer */ UNIV_INLINE ibool rec_get_node_ptr_flag( /*==================*/ const rec_t* rec); /*!< in: physical record */ /******************************************************//** The following function is used to get the order number of an old-style record in the heap of the index page. @return heap order number */ UNIV_INLINE ulint rec_get_heap_no_old( /*================*/ const rec_t* rec); /*!< in: physical record */ /******************************************************//** The following function is used to set the heap number field in an old-style record. */ UNIV_INLINE void rec_set_heap_no_old( /*================*/ rec_t* rec, /*!< in: physical record */ ulint heap_no);/*!< in: the heap number */ /******************************************************//** The following function is used to get the order number of a new-style record in the heap of the index page. @return heap order number */ UNIV_INLINE ulint rec_get_heap_no_new( /*================*/ const rec_t* rec); /*!< in: physical record */ /******************************************************//** The following function is used to set the heap number field in a new-style record. */ UNIV_INLINE void rec_set_heap_no_new( /*================*/ rec_t* rec, /*!< in/out: physical record */ ulint heap_no);/*!< in: the heap number */ /******************************************************//** The following function is used to test whether the data offsets in the record are stored in one-byte or two-byte format. @return TRUE if 1-byte form */ UNIV_INLINE ibool rec_get_1byte_offs_flag( /*====================*/ const rec_t* rec); /*!< in: physical record */ /******************************************************//** Determine how many of the first n columns in a compact physical record are stored externally. @return number of externally stored columns */ UNIV_INTERN ulint rec_get_n_extern_new( /*=================*/ const rec_t* rec, /*!< in: compact physical record */ dict_index_t* index, /*!< in: record descriptor */ ulint n); /*!< in: number of columns to scan */ /******************************************************//** The following function determines the offsets to each field in the record. It can reuse a previously allocated array. @return the new offsets */ UNIV_INTERN ulint* rec_get_offsets_func( /*=================*/ const rec_t* rec, /*!< in: physical record */ const dict_index_t* index, /*!< in: record descriptor */ ulint* offsets,/*!< in/out: array consisting of offsets[0] allocated elements, or an array from rec_get_offsets(), or NULL */ ulint n_fields,/*!< in: maximum number of initialized fields (ULINT_UNDEFINED if all fields) */ mem_heap_t** heap, /*!< in/out: memory heap */ const char* file, /*!< in: file name where called */ ulint line); /*!< in: line number where called */ #define rec_get_offsets(rec,index,offsets,n,heap) \ rec_get_offsets_func(rec,index,offsets,n,heap,__FILE__,__LINE__) /******************************************************//** Determine the offset to each field in a leaf-page record in ROW_FORMAT=COMPACT. This is a special case of rec_init_offsets() and rec_get_offsets_func(). */ UNIV_INTERN void rec_init_offsets_comp_ordinary( /*===========================*/ const rec_t* rec, /*!< in: physical record in ROW_FORMAT=COMPACT */ ulint extra, /*!< in: number of bytes to reserve between the record header and the data payload (usually REC_N_NEW_EXTRA_BYTES) */ const dict_index_t* index, /*!< in: record descriptor */ ulint* offsets);/*!< in/out: array of offsets; in: n=rec_offs_n_fields(offsets) */ /******************************************************//** The following function determines the offsets to each field in the record. It can reuse a previously allocated array. */ UNIV_INTERN void rec_get_offsets_reverse( /*====================*/ const byte* extra, /*!< in: the extra bytes of a compact record in reverse order, excluding the fixed-size REC_N_NEW_EXTRA_BYTES */ const dict_index_t* index, /*!< in: record descriptor */ ulint node_ptr,/*!< in: nonzero=node pointer, 0=leaf node */ ulint* offsets);/*!< in/out: array consisting of offsets[0] allocated elements */ /************************************************************//** Validates offsets returned by rec_get_offsets(). @return TRUE if valid */ UNIV_INLINE ibool rec_offs_validate( /*==============*/ const rec_t* rec, /*!< in: record or NULL */ const dict_index_t* index, /*!< in: record descriptor or NULL */ const ulint* offsets);/*!< in: array returned by rec_get_offsets() */ #ifdef UNIV_DEBUG /************************************************************//** Updates debug data in offsets, in order to avoid bogus rec_offs_validate() failures. */ UNIV_INLINE void rec_offs_make_valid( /*================*/ const rec_t* rec, /*!< in: record */ const dict_index_t* index, /*!< in: record descriptor */ ulint* offsets);/*!< in: array returned by rec_get_offsets() */ #else # define rec_offs_make_valid(rec, index, offsets) ((void) 0) #endif /* UNIV_DEBUG */ /************************************************************//** The following function is used to get the offset to the nth data field in an old-style record. @return offset to the field */ UNIV_INTERN ulint rec_get_nth_field_offs_old( /*=======================*/ const rec_t* rec, /*!< in: record */ ulint n, /*!< in: index of the field */ ulint* len); /*!< out: length of the field; UNIV_SQL_NULL if SQL null */ #define rec_get_nth_field_old(rec, n, len) \ ((rec) + rec_get_nth_field_offs_old(rec, n, len)) /************************************************************//** Gets the physical size of an old-style field. Also an SQL null may have a field of size > 0, if the data type is of a fixed size. @return field size in bytes */ UNIV_INLINE ulint rec_get_nth_field_size( /*===================*/ const rec_t* rec, /*!< in: record */ ulint n); /*!< in: index of the field */ /************************************************************//** The following function is used to get an offset to the nth data field in a record. @return offset from the origin of rec */ UNIV_INLINE ulint rec_get_nth_field_offs( /*===================*/ const ulint* offsets,/*!< in: array returned by rec_get_offsets() */ ulint n, /*!< in: index of the field */ ulint* len); /*!< out: length of the field; UNIV_SQL_NULL if SQL null */ #define rec_get_nth_field(rec, offsets, n, len) \ ((rec) + rec_get_nth_field_offs(offsets, n, len)) /******************************************************//** Determine if the offsets are for a record in the new compact format. @return nonzero if compact format */ UNIV_INLINE ulint rec_offs_comp( /*==========*/ const ulint* offsets);/*!< in: array returned by rec_get_offsets() */ /******************************************************//** Determine if the offsets are for a record containing externally stored columns. @return nonzero if externally stored */ UNIV_INLINE ulint rec_offs_any_extern( /*================*/ const ulint* offsets);/*!< in: array returned by rec_get_offsets() */ /******************************************************//** Returns nonzero if the extern bit is set in nth field of rec. @return nonzero if externally stored */ UNIV_INLINE ulint rec_offs_nth_extern( /*================*/ const ulint* offsets,/*!< in: array returned by rec_get_offsets() */ ulint n); /*!< in: nth field */ /******************************************************//** Returns nonzero if the SQL NULL bit is set in nth field of rec. @return nonzero if SQL NULL */ UNIV_INLINE ulint rec_offs_nth_sql_null( /*==================*/ const ulint* offsets,/*!< in: array returned by rec_get_offsets() */ ulint n); /*!< in: nth field */ /******************************************************//** Gets the physical size of a field. @return length of field */ UNIV_INLINE ulint rec_offs_nth_size( /*==============*/ const ulint* offsets,/*!< in: array returned by rec_get_offsets() */ ulint n); /*!< in: nth field */ /******************************************************//** Returns the number of extern bits set in a record. @return number of externally stored fields */ UNIV_INLINE ulint rec_offs_n_extern( /*==============*/ const ulint* offsets);/*!< in: array returned by rec_get_offsets() */ /***********************************************************//** This is used to modify the value of an already existing field in a record. The previous value must have exactly the same size as the new value. If len is UNIV_SQL_NULL then the field is treated as an SQL null. For records in ROW_FORMAT=COMPACT (new-style records), len must not be UNIV_SQL_NULL unless the field already is SQL null. */ UNIV_INLINE void rec_set_nth_field( /*==============*/ rec_t* rec, /*!< in: record */ const ulint* offsets,/*!< in: array returned by rec_get_offsets() */ ulint n, /*!< in: index number of the field */ const void* data, /*!< in: pointer to the data if not SQL null */ ulint len); /*!< in: length of the data or UNIV_SQL_NULL */ /**********************************************************//** The following function returns the data size of an old-style physical record, that is the sum of field lengths. SQL null fields are counted as length 0 fields. The value returned by the function is the distance from record origin to record end in bytes. @return size */ UNIV_INLINE ulint rec_get_data_size_old( /*==================*/ const rec_t* rec); /*!< in: physical record */ /**********************************************************//** The following function returns the number of allocated elements for an array of offsets. @return number of elements */ UNIV_INLINE ulint rec_offs_get_n_alloc( /*=================*/ const ulint* offsets);/*!< in: array for rec_get_offsets() */ /**********************************************************//** The following function sets the number of allocated elements for an array of offsets. */ UNIV_INLINE void rec_offs_set_n_alloc( /*=================*/ ulint* offsets, /*!< out: array for rec_get_offsets(), must be allocated */ ulint n_alloc); /*!< in: number of elements */ #define rec_offs_init(offsets) \ rec_offs_set_n_alloc(offsets, (sizeof offsets) / sizeof *offsets) /**********************************************************//** The following function returns the number of fields in a record. @return number of fields */ UNIV_INLINE ulint rec_offs_n_fields( /*==============*/ const ulint* offsets);/*!< in: array returned by rec_get_offsets() */ /**********************************************************//** The following function returns the data size of a physical record, that is the sum of field lengths. SQL null fields are counted as length 0 fields. The value returned by the function is the distance from record origin to record end in bytes. @return size */ UNIV_INLINE ulint rec_offs_data_size( /*===============*/ const ulint* offsets);/*!< in: array returned by rec_get_offsets() */ /**********************************************************//** Returns the total size of record minus data size of record. The value returned by the function is the distance from record start to record origin in bytes. @return size */ UNIV_INLINE ulint rec_offs_extra_size( /*================*/ const ulint* offsets);/*!< in: array returned by rec_get_offsets() */ /**********************************************************//** Returns the total size of a physical record. @return size */ UNIV_INLINE ulint rec_offs_size( /*==========*/ const ulint* offsets);/*!< in: array returned by rec_get_offsets() */ /**********************************************************//** Returns a pointer to the start of the record. @return pointer to start */ UNIV_INLINE byte* rec_get_start( /*==========*/ rec_t* rec, /*!< in: pointer to record */ const ulint* offsets);/*!< in: array returned by rec_get_offsets() */ /**********************************************************//** Returns a pointer to the end of the record. @return pointer to end */ UNIV_INLINE byte* rec_get_end( /*========*/ rec_t* rec, /*!< in: pointer to record */ const ulint* offsets);/*!< in: array returned by rec_get_offsets() */ /***************************************************************//** Copies a physical record to a buffer. @return pointer to the origin of the copy */ UNIV_INLINE rec_t* rec_copy( /*=====*/ void* buf, /*!< in: buffer */ const rec_t* rec, /*!< in: physical record */ const ulint* offsets);/*!< in: array returned by rec_get_offsets() */ #ifndef UNIV_HOTBACKUP /**************************************************************//** Copies the first n fields of a physical record to a new physical record in a buffer. @return own: copied record */ UNIV_INTERN rec_t* rec_copy_prefix_to_buf( /*===================*/ const rec_t* rec, /*!< in: physical record */ const dict_index_t* index, /*!< in: record descriptor */ ulint n_fields, /*!< in: number of fields to copy */ byte** buf, /*!< in/out: memory buffer for the copied prefix, or NULL */ ulint* buf_size); /*!< in/out: buffer size */ /************************************************************//** Folds a prefix of a physical record to a ulint. @return the folded value */ UNIV_INLINE ulint rec_fold( /*=====*/ const rec_t* rec, /*!< in: the physical record */ const ulint* offsets, /*!< in: array returned by rec_get_offsets() */ ulint n_fields, /*!< in: number of complete fields to fold */ ulint n_bytes, /*!< in: number of bytes to fold in an incomplete last field */ dulint tree_id) /*!< in: index tree id */ __attribute__((pure)); #endif /* !UNIV_HOTBACKUP */ /*********************************************************//** Builds a ROW_FORMAT=COMPACT record out of a data tuple. */ UNIV_INTERN void rec_convert_dtuple_to_rec_comp( /*===========================*/ rec_t* rec, /*!< in: origin of record */ ulint extra, /*!< in: number of bytes to reserve between the record header and the data payload (normally REC_N_NEW_EXTRA_BYTES) */ const dict_index_t* index, /*!< in: record descriptor */ ulint status, /*!< in: status bits of the record */ const dfield_t* fields, /*!< in: array of data fields */ ulint n_fields);/*!< in: number of data fields */ /*********************************************************//** Builds a physical record out of a data tuple and stores it into the given buffer. @return pointer to the origin of physical record */ UNIV_INTERN rec_t* rec_convert_dtuple_to_rec( /*======================*/ byte* buf, /*!< in: start address of the physical record */ const dict_index_t* index, /*!< in: record descriptor */ const dtuple_t* dtuple, /*!< in: data tuple */ ulint n_ext); /*!< in: number of externally stored columns */ /**********************************************************//** Returns the extra size of an old-style physical record if we know its data size and number of fields. @return extra size */ UNIV_INLINE ulint rec_get_converted_extra_size( /*=========================*/ ulint data_size, /*!< in: data size */ ulint n_fields, /*!< in: number of fields */ ulint n_ext) /*!< in: number of externally stored columns */ __attribute__((const)); /**********************************************************//** Determines the size of a data tuple prefix in ROW_FORMAT=COMPACT. @return total size */ UNIV_INTERN ulint rec_get_converted_size_comp_prefix( /*===============================*/ const dict_index_t* index, /*!< in: record descriptor; dict_table_is_comp() is assumed to hold, even if it does not */ const dfield_t* fields, /*!< in: array of data fields */ ulint n_fields,/*!< in: number of data fields */ ulint* extra); /*!< out: extra size */ /**********************************************************//** Determines the size of a data tuple in ROW_FORMAT=COMPACT. @return total size */ UNIV_INTERN ulint rec_get_converted_size_comp( /*========================*/ const dict_index_t* index, /*!< in: record descriptor; dict_table_is_comp() is assumed to hold, even if it does not */ ulint status, /*!< in: status bits of the record */ const dfield_t* fields, /*!< in: array of data fields */ ulint n_fields,/*!< in: number of data fields */ ulint* extra); /*!< out: extra size */ /**********************************************************//** The following function returns the size of a data tuple when converted to a physical record. @return size */ UNIV_INLINE ulint rec_get_converted_size( /*===================*/ dict_index_t* index, /*!< in: record descriptor */ const dtuple_t* dtuple, /*!< in: data tuple */ ulint n_ext); /*!< in: number of externally stored columns */ #ifndef UNIV_HOTBACKUP /**************************************************************//** Copies the first n fields of a physical record to a data tuple. The fields are copied to the memory heap. */ UNIV_INTERN void rec_copy_prefix_to_dtuple( /*======================*/ dtuple_t* tuple, /*!< out: data tuple */ const rec_t* rec, /*!< in: physical record */ const dict_index_t* index, /*!< in: record descriptor */ ulint n_fields, /*!< in: number of fields to copy */ mem_heap_t* heap); /*!< in: memory heap */ #endif /* !UNIV_HOTBACKUP */ /***************************************************************//** Validates the consistency of a physical record. @return TRUE if ok */ UNIV_INTERN ibool rec_validate( /*=========*/ const rec_t* rec, /*!< in: physical record */ const ulint* offsets);/*!< in: array returned by rec_get_offsets() */ /***************************************************************//** Prints an old-style physical record. */ UNIV_INTERN void rec_print_old( /*==========*/ ib_stream_t ib_stream, /*!< in: stream where to print */ const rec_t* rec); /*!< in: physical record */ #ifndef UNIV_HOTBACKUP /***************************************************************//** Prints a physical record in ROW_FORMAT=COMPACT. Ignores the record header. */ UNIV_INTERN void rec_print_comp( /*===========*/ ib_stream_t ib_stream, /*!< in: stream where to print */ const rec_t* rec, /*!< in: physical record */ const ulint* offsets); /*!< in: array returned by rec_get_offsets() */ /***************************************************************//** Prints a physical record. */ UNIV_INTERN void rec_print_new( /*==========*/ ib_stream_t ib_stream, /*!< in: stream where to print */ const rec_t* rec, /*!< in: physical record */ const ulint* offsets); /*!< in: array returned by rec_get_offsets() */ /***************************************************************//** Prints a physical record. */ UNIV_INTERN void rec_print( /*======*/ ib_stream_t ib_stream, /*!< in: stream where to print */ const rec_t* rec, /*!< in: physical record */ dict_index_t* index); /*!< in: record descriptor */ #endif /* UNIV_HOTBACKUP */ #define REC_INFO_BITS 6 /* This is single byte bit-field */ /* Maximum lengths for the data in a physical record if the offsets are given in one byte (resp. two byte) format. */ #define REC_1BYTE_OFFS_LIMIT 0x7FUL #define REC_2BYTE_OFFS_LIMIT 0x7FFFUL /* The data size of record must be smaller than this because we reserve two upmost bits in a two byte offset for special purposes */ #define REC_MAX_DATA_SIZE (16 * 1024) #ifndef UNIV_NONINL #include "rem0rec.ic" #endif #endif haildb-2.3.2/include/row0sel.h0000644000175000017500000003351611513177357017042 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1997, 2010, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/row0sel.h Select Created 12/19/1997 Heikki Tuuri *******************************************************/ #ifndef row0sel_h #define row0sel_h #include "univ.i" #include "data0data.h" #include "que0types.h" #include "dict0types.h" #include "trx0types.h" #include "row0types.h" #include "que0types.h" #include "pars0sym.h" #include "btr0pcur.h" #include "read0read.h" #include "api0api.h" /*********************************************************************//** Creates a select node struct. @return own: select node struct */ UNIV_INTERN sel_node_t* sel_node_create( /*============*/ mem_heap_t* heap); /*!< in: memory heap where created */ /*********************************************************************//** Frees the memory private to a select node when a query graph is freed, does not free the heap where the node was originally created. */ UNIV_INTERN void sel_node_free_private( /*==================*/ sel_node_t* node); /*!< in: select node struct */ /*********************************************************************//** Frees a prefetch buffer for a column, including the dynamically allocated memory for data stored there. */ UNIV_INTERN void sel_col_prefetch_buf_free( /*======================*/ sel_buf_t* prefetch_buf); /*!< in, own: prefetch buffer */ /*********************************************************************//** Gets the plan node for the nth table in a join. @return plan node */ UNIV_INLINE plan_t* sel_node_get_nth_plan( /*==================*/ sel_node_t* node, /*!< in: select node */ ulint i); /*!< in: get ith plan node */ /**********************************************************************//** Performs a select step. This is a high-level function used in SQL execution graphs. @return query thread to run next or NULL */ UNIV_INTERN que_thr_t* row_sel_step( /*=========*/ que_thr_t* thr); /*!< in: query thread */ /**********************************************************************//** Performs an execution step of an open or close cursor statement node. @return query thread to run next or NULL */ UNIV_INLINE que_thr_t* open_step( /*======*/ que_thr_t* thr); /*!< in: query thread */ /**********************************************************************//** Performs a fetch for a cursor. @return query thread to run next or NULL */ UNIV_INTERN que_thr_t* fetch_step( /*=======*/ que_thr_t* thr); /*!< in: query thread */ /****************************************************************//** Sample callback function for fetch that prints each row. @return always returns non-NULL */ UNIV_INTERN void* row_fetch_print( /*============*/ void* row, /*!< in: sel_node_t* */ void* user_arg); /*!< in: not used */ /****************************************************************//** Callback function for fetch that stores an unsigned 4 byte integer to the location pointed. The column's type must be DATA_INT, DATA_UNSIGNED, length = 4. @return always returns NULL */ UNIV_INTERN void* row_fetch_store_uint4( /*==================*/ void* row, /*!< in: sel_node_t* */ void* user_arg); /*!< in: data pointer */ /***********************************************************//** Prints a row in a select result. @return query thread to run next or NULL */ UNIV_INTERN que_thr_t* row_printf_step( /*============*/ que_thr_t* thr); /*!< in: query thread */ /***********************************************************************//** Builds a dummy query graph used in selects. */ UNIV_INTERN void row_sel_prebuild_graph( /*===================*/ row_prebuilt_t* prebuilt);/*!< in: prebuilt handle */ /********************************************************************//** This function does several things, in fact too many things: 1. Moveto/Search for a record 2. Next record 3. Prev record 4. Set appropriate locks (gap and table locks). 5. Handle rollback This function opens a cursor, and also implements fetch next and fetch prev. NOTE that if we do a search with a full key value from a unique index (ROW_SEL_EXACT), then we will not store the cursor position and fetch next or fetch prev must not be tried to the cursor! @return DB_SUCCESS, DB_RECORD_NOT_FOUND, DB_END_OF_INDEX, DB_DEADLOCK, DB_LOCK_TABLE_FULL, DB_CORRUPTION, or DB_TOO_BIG_RECORD */ UNIV_INTERN enum db_err row_search_for_client( /*==================*/ ib_recovery_t recovery, /*!< in: recovery flag */ ib_srch_mode_t mode, /*!< in: search mode */ row_prebuilt_t* prebuilt, /*!< in: prebuilt struct for the table handle; this contains the info of search_tuple, index; if search tuple contains 0 fields then we position the cursor at the start or the end of the index, depending on 'mode' */ ib_match_t match_mode, /*!< in: mode for matching the key */ ib_cur_op_t direction); /*!< in: cursor operation, NOTE: if this is != ROW_SEL_MOVETO, then prebuilt must have a pcur with stored position! In opening of a cursor 'direction' should be ROW_SEL_MOVETO */ /**********************************************************************//** Reads the current row from the fetch cache. @return current row from the row cache. */ UNIV_INTERN const rec_t* row_sel_row_cache_get( /*==================*/ row_prebuilt_t* prebuilt); /*!< in: prebuilt struct */ /**********************************************************************//** Pops a cached row from the fetch cache. @return DB_SUCCESS if all OK else error code */ UNIV_INTERN void row_sel_row_cache_next( /*===================*/ row_prebuilt_t* prebuilt); /*!< in: prebuilt struct */ /**********************************************************************//** Check if there are any rows in the cache. @return true if row cache is empty. */ UNIV_INTERN ibool row_sel_row_cache_is_empty( /*=======================*/ row_prebuilt_t* prebuilt); /*!< in: prebuilt struct */ /** A structure for caching column values for prefetched rows */ struct sel_buf_struct{ byte* data; /*!< data, or NULL; if not NULL, this field has allocated memory which must be explicitly freed; can be != NULL even when len is UNIV_SQL_NULL */ ulint len; /*!< data length or UNIV_SQL_NULL */ ulint val_buf_size; /*!< size of memory buffer allocated for data: this can be more than len; this is defined when data != NULL */ }; /** Query plan */ struct plan_struct{ dict_table_t* table; /*!< table struct in the dictionary cache */ dict_index_t* index; /*!< table index used in the search */ btr_pcur_t pcur; /*!< persistent cursor used to search the index */ ibool asc; /*!< TRUE if cursor traveling upwards */ ibool pcur_is_open; /*!< TRUE if pcur has been positioned and we can try to fetch new rows */ ibool cursor_at_end; /*!< TRUE if the cursor is open but we know that there are no more qualifying rows left to retrieve from the index tree; NOTE though, that there may still be unprocessed rows in the prefetch stack; always FALSE when pcur_is_open is FALSE */ ibool stored_cursor_rec_processed; /*!< TRUE if the pcur position has been stored and the record it is positioned on has already been processed */ que_node_t** tuple_exps; /*!< array of expressions which are used to calculate the field values in the search tuple: there is one expression for each field in the search tuple */ dtuple_t* tuple; /*!< search tuple */ ulint mode; /*!< search mode: PAGE_CUR_G, ... */ ulint n_exact_match; /*!< number of first fields in the search tuple which must be exactly matched */ ibool unique_search; /*!< TRUE if we are searching an index record with a unique key */ ulint n_rows_fetched; /*!< number of rows fetched using pcur after it was opened */ ulint n_rows_prefetched;/*!< number of prefetched rows cached for fetch: fetching several rows in the same mtr saves CPU time */ ulint first_prefetched;/*!< index of the first cached row in select buffer arrays for each column */ ibool no_prefetch; /*!< no prefetch for this table */ sym_node_list_t columns; /*!< symbol table nodes for the columns to retrieve from the table */ UT_LIST_BASE_NODE_T(func_node_t) end_conds; /*!< conditions which determine the fetch limit of the index segment we have to look at: when one of these fails, the result set has been exhausted for the cursor in this index; these conditions are normalized so that in a comparison the column for this table is the first argument */ UT_LIST_BASE_NODE_T(func_node_t) other_conds; /*!< the rest of search conditions we can test at this table in a join */ ibool must_get_clust; /*!< TRUE if index is a non-clustered index and we must also fetch the clustered index record; this is the case if the non-clustered record does not contain all the needed columns, or if this is a single-table explicit cursor, or a searched update or delete */ ulint* clust_map; /*!< map telling how clust_ref is built from the fields of a non-clustered record */ dtuple_t* clust_ref; /*!< the reference to the clustered index entry is built here if index is a non-clustered index */ btr_pcur_t clust_pcur; /*!< if index is non-clustered, we use this pcur to search the clustered index */ mem_heap_t* old_vers_heap; /*!< memory heap used in building an old version of a row, or NULL */ }; /** Select node states */ enum sel_node_state { SEL_NODE_CLOSED, /*!< it is a declared cursor which is not currently open */ SEL_NODE_OPEN, /*!< intention locks not yet set on tables */ SEL_NODE_FETCH, /*!< intention locks have been set */ SEL_NODE_NO_MORE_ROWS /*!< cursor has reached the result set end */ }; /** Select statement node */ struct sel_node_struct{ que_common_t common; /*!< node type: QUE_NODE_SELECT */ enum sel_node_state state; /*!< node state */ que_node_t* select_list; /*!< select list */ sym_node_t* into_list; /*!< variables list or NULL */ sym_node_t* table_list; /*!< table list */ ibool asc; /*!< TRUE if the rows should be fetched in an ascending order */ ibool set_x_locks; /*!< TRUE if the cursor is for update or delete, which means that a row x-lock should be placed on the cursor row */ ulint row_lock_mode; /*!< LOCK_X or LOCK_S */ ulint n_tables; /*!< number of tables */ ulint fetch_table; /*!< number of the next table to access in the join */ plan_t* plans; /*!< array of n_tables many plan nodes containing the search plan and the search data structures */ que_node_t* search_cond; /*!< search condition */ read_view_t* read_view; /*!< if the query is a non-locking consistent read, its read view is placed here, otherwise NULL */ ibool consistent_read;/*!< TRUE if the select is a consistent, non-locking read */ order_node_t* order_by; /*!< order by column definition, or NULL */ ibool is_aggregate; /*!< TRUE if the select list consists of aggregate functions */ ibool aggregate_already_fetched; /*!< TRUE if the aggregate row has already been fetched for the current cursor */ ibool can_get_updated;/*!< this is TRUE if the select is in a single-table explicit cursor which can get updated within the stored procedure, or in a searched update or delete; NOTE that to determine of an explicit cursor if it can get updated, the parser checks from a stored procedure if it contains positioned update or delete statements */ sym_node_t* explicit_cursor;/*!< not NULL if an explicit cursor */ UT_LIST_BASE_NODE_T(sym_node_t) copy_variables; /*!< variables whose values we have to copy when an explicit cursor is opened, so that they do not change between fetches */ }; /** Fetch statement node */ struct fetch_node_struct{ que_common_t common; /*!< type: QUE_NODE_FETCH */ sel_node_t* cursor_def; /*!< cursor definition */ sym_node_t* into_list; /*!< variables to set */ pars_user_func_t* func; /*!< User callback function or NULL. The first argument to the function is a sel_node_t*, containing the results of the SELECT operation for one row. If the function returns NULL, it is not interested in further rows and the cursor is modified so (cursor % NOTFOUND) is true. If it returns not-NULL, continue normally. See row_fetch_print() for an example (and a useful debugging tool). */ }; /** Open or close cursor operation type */ enum open_node_op { ROW_SEL_OPEN_CURSOR, /*!< open cursor */ ROW_SEL_CLOSE_CURSOR /*!< close cursor */ }; /** Open or close cursor statement node */ struct open_node_struct{ que_common_t common; /*!< type: QUE_NODE_OPEN */ enum open_node_op op_type; /*!< operation type: open or close cursor */ sel_node_t* cursor_def; /*!< cursor definition */ }; /** Row printf statement node */ struct row_printf_node_struct{ que_common_t common; /*!< type: QUE_NODE_ROW_PRINTF */ sel_node_t* sel_node; /*!< select */ }; #define ROW_SEL_OPEN_CURSOR 0 #define ROW_SEL_CLOSE_CURSOR 1 #ifndef UNIV_NONINL #include "row0sel.ic" #endif #endif haildb-2.3.2/include/ut0list.ic0000644000175000017500000000306711513177357017215 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 2006, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /*******************************************************************//** @file include/ut0list.ic A double-linked list Created 4/26/2006 Osku Salerma ************************************************************************/ /****************************************************************//** Get the first node in the list. @return first node, or NULL */ UNIV_INLINE ib_list_node_t* ib_list_get_first( /*==============*/ ib_list_t* list) /*!< in: list */ { return(list->first); } /****************************************************************//** Get the last node in the list. @return last node, or NULL */ UNIV_INLINE ib_list_node_t* ib_list_get_last( /*=============*/ ib_list_t* list) /*!< in: list */ { return(list->last); } haildb-2.3.2/include/hash0hash.ic0000644000175000017500000001116211513177357017453 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1997, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/hash0hash.ic The simple hash table utility Created 5/20/1997 Heikki Tuuri *******************************************************/ #include "ut0rnd.h" /************************************************************//** Gets the nth cell in a hash table. @return pointer to cell */ UNIV_INLINE hash_cell_t* hash_get_nth_cell( /*==============*/ hash_table_t* table, /*!< in: hash table */ ulint n) /*!< in: cell index */ { ut_ad(table); ut_ad(table->magic_n == HASH_TABLE_MAGIC_N); ut_ad(n < table->n_cells); return(table->array + n); } /*************************************************************//** Clears a hash table so that all the cells become empty. */ UNIV_INLINE void hash_table_clear( /*=============*/ hash_table_t* table) /*!< in/out: hash table */ { ut_ad(table); ut_ad(table->magic_n == HASH_TABLE_MAGIC_N); memset(table->array, 0x0, table->n_cells * sizeof(*table->array)); } /*************************************************************//** Returns the number of cells in a hash table. @return number of cells */ UNIV_INLINE ulint hash_get_n_cells( /*=============*/ hash_table_t* table) /*!< in: table */ { ut_ad(table); ut_ad(table->magic_n == HASH_TABLE_MAGIC_N); return(table->n_cells); } /**************************************************************//** Calculates the hash value from a folded value. @return hashed value */ UNIV_INLINE ulint hash_calc_hash( /*===========*/ ulint fold, /*!< in: folded value */ hash_table_t* table) /*!< in: hash table */ { ut_ad(table); ut_ad(table->magic_n == HASH_TABLE_MAGIC_N); return(ut_hash_ulint(fold, table->n_cells)); } #ifndef UNIV_HOTBACKUP /************************************************************//** Gets the mutex index for a fold value in a hash table. @return mutex number */ UNIV_INLINE ulint hash_get_mutex_no( /*==============*/ hash_table_t* table, /*!< in: hash table */ ulint fold) /*!< in: fold */ { ut_ad(table); ut_ad(table->magic_n == HASH_TABLE_MAGIC_N); ut_ad(ut_is_2pow(table->n_mutexes)); return(ut_2pow_remainder(hash_calc_hash(fold, table), table->n_mutexes)); } /************************************************************//** Gets the nth heap in a hash table. @return mem heap */ UNIV_INLINE mem_heap_t* hash_get_nth_heap( /*==============*/ hash_table_t* table, /*!< in: hash table */ ulint i) /*!< in: index of the heap */ { ut_ad(table); ut_ad(table->magic_n == HASH_TABLE_MAGIC_N); ut_ad(i < table->n_mutexes); return(table->heaps[i]); } /************************************************************//** Gets the heap for a fold value in a hash table. @return mem heap */ UNIV_INLINE mem_heap_t* hash_get_heap( /*==========*/ hash_table_t* table, /*!< in: hash table */ ulint fold) /*!< in: fold */ { ulint i; ut_ad(table); ut_ad(table->magic_n == HASH_TABLE_MAGIC_N); if (table->heap) { return(table->heap); } i = hash_get_mutex_no(table, fold); return(hash_get_nth_heap(table, i)); } /************************************************************//** Gets the nth mutex in a hash table. @return mutex */ UNIV_INLINE mutex_t* hash_get_nth_mutex( /*===============*/ hash_table_t* table, /*!< in: hash table */ ulint i) /*!< in: index of the mutex */ { ut_ad(table); ut_ad(table->magic_n == HASH_TABLE_MAGIC_N); ut_ad(i < table->n_mutexes); return(table->mutexes + i); } /************************************************************//** Gets the mutex for a fold value in a hash table. @return mutex */ UNIV_INLINE mutex_t* hash_get_mutex( /*===========*/ hash_table_t* table, /*!< in: hash table */ ulint fold) /*!< in: fold */ { ulint i; ut_ad(table); ut_ad(table->magic_n == HASH_TABLE_MAGIC_N); i = hash_get_mutex_no(table, fold); return(hash_get_nth_mutex(table, i)); } #endif /* !UNIV_HOTBACKUP */ haildb-2.3.2/include/buf0buddy.h0000644000175000017500000000644311513177357017332 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 2006, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/buf0buddy.h Binary buddy allocator for compressed pages Created December 2006 by Marko Makela *******************************************************/ #ifndef buf0buddy_h #define buf0buddy_h #ifdef UNIV_MATERIALIZE # undef UNIV_INLINE # define UNIV_INLINE #endif #include "univ.i" #include "buf0types.h" /**********************************************************************//** Allocate a block. The thread calling this function must hold buf_pool_mutex and must not hold buf_pool_zip_mutex or any block->mutex. The buf_pool_mutex may only be released and reacquired if lru != NULL. This function should only be used for allocating compressed page frames or control blocks (buf_page_t). Allocated control blocks must be properly initialized immediately after buf_buddy_alloc() has returned the memory, before releasing buf_pool_mutex. @return allocated block, possibly NULL if lru == NULL */ UNIV_INLINE void* buf_buddy_alloc( /*============*/ ulint size, /*!< in: block size, up to UNIV_PAGE_SIZE */ ibool* lru) /*!< in: pointer to a variable that will be assigned TRUE if storage was allocated from the LRU list and buf_pool_mutex was temporarily released, or NULL if the LRU list should not be used */ __attribute__((malloc)); /**********************************************************************//** Release a block. */ UNIV_INLINE void buf_buddy_free( /*===========*/ void* buf, /*!< in: block to be freed, must not be pointed to by the buffer pool */ ulint size) /*!< in: block size, up to UNIV_PAGE_SIZE */ __attribute__((nonnull)); /************************************************************************** Get the offset of the buddy of a compressed page frame. */ UNIV_INTERN void buf_buddy_var_init(void); /*====================*/ /** Statistics of buddy blocks of a given size. */ struct buf_buddy_stat_struct { /** Number of blocks allocated from the buddy system. */ ulint used; /** Number of blocks relocated by the buddy system. */ ib_uint64_t relocated; /** Total duration of block relocations, in microseconds. */ ib_uint64_t relocated_usec; }; /** Statistics of buddy blocks of a given size. */ typedef struct buf_buddy_stat_struct buf_buddy_stat_t; /** Statistics of the buddy system, indexed by block size. Protected by buf_pool_mutex. */ extern buf_buddy_stat_t buf_buddy_stat[BUF_BUDDY_SIZES + 1]; #ifndef UNIV_NONINL # include "buf0buddy.ic" #endif #endif /* buf0buddy_h */ haildb-2.3.2/include/btr0btr.ic0000644000175000017500000002214711513177357017170 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1994, 2010, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/btr0btr.ic The B-tree Created 6/2/1994 Heikki Tuuri *******************************************************/ #include "mach0data.h" #ifndef UNIV_HOTBACKUP #include "mtr0mtr.h" #include "mtr0log.h" #ifdef WITH_ZIP #include "page0zip.h" #endif /* WITH_ZIP */ #define BTR_MAX_NODE_LEVEL 50 /*!< Maximum B-tree page level (not really a hard limit). Used in debug assertions in btr_page_set_level and btr_page_get_level_low */ /**************************************************************//** Gets a buffer page and declares its latching order level. */ UNIV_INLINE buf_block_t* btr_block_get( /*==========*/ ulint space, /*!< in: space id */ ulint zip_size, /*!< in: compressed page size in bytes or 0 for uncompressed pages */ ulint page_no, /*!< in: page number */ ulint mode, /*!< in: latch mode */ mtr_t* mtr) /*!< in: mtr */ { buf_block_t* block; block = buf_page_get(space, zip_size, page_no, mode, mtr); if (mode != RW_NO_LATCH) { buf_block_dbg_add_level(block, SYNC_TREE_NODE); } return(block); } /**************************************************************//** Gets a buffer page and declares its latching order level. */ UNIV_INLINE page_t* btr_page_get( /*=========*/ ulint space, /*!< in: space id */ ulint zip_size, /*!< in: compressed page size in bytes or 0 for uncompressed pages */ ulint page_no, /*!< in: page number */ ulint mode, /*!< in: latch mode */ mtr_t* mtr) /*!< in: mtr */ { return(buf_block_get_frame(btr_block_get(space, zip_size, page_no, mode, mtr))); } /**************************************************************//** Sets the index id field of a page. */ UNIV_INLINE void btr_page_set_index_id( /*==================*/ page_t* page, /*!< in: page to be created */ page_zip_des_t* page_zip,/*!< in: compressed page whose uncompressed part will be updated, or NULL */ dulint id, /*!< in: index id */ mtr_t* mtr) /*!< in: mtr */ { #ifdef WITH_ZIP if (UNIV_LIKELY_NULL(page_zip)) { mach_write_to_8(page + (PAGE_HEADER + PAGE_INDEX_ID), id); page_zip_write_header(page_zip, page + (PAGE_HEADER + PAGE_INDEX_ID), 8, mtr); } else { #endif /* WITH_ZIP */ mlog_write_dulint(page + (PAGE_HEADER + PAGE_INDEX_ID), id, mtr); #ifdef WITH_ZIP } #endif /* WITH_ZIP */ } #endif /* !UNIV_HOTBACKUP */ /**************************************************************//** Gets the index id field of a page. @return index id */ UNIV_INLINE dulint btr_page_get_index_id( /*==================*/ const page_t* page) /*!< in: index page */ { return(mach_read_from_8(page + PAGE_HEADER + PAGE_INDEX_ID)); } #ifndef UNIV_HOTBACKUP /********************************************************//** Gets the node level field in an index page. @return level, leaf level == 0 */ UNIV_INLINE ulint btr_page_get_level_low( /*===================*/ const page_t* page) /*!< in: index page */ { ulint level; ut_ad(page); level = mach_read_from_2(page + PAGE_HEADER + PAGE_LEVEL); ut_ad(level <= BTR_MAX_NODE_LEVEL); return(level); } /********************************************************//** Gets the node level field in an index page. @return level, leaf level == 0 */ UNIV_INLINE ulint btr_page_get_level( /*===============*/ const page_t* page, /*!< in: index page */ mtr_t* mtr __attribute__((unused))) /*!< in: mini-transaction handle */ { ut_ad(page && mtr); return(btr_page_get_level_low(page)); } /********************************************************//** Sets the node level field in an index page. */ UNIV_INLINE void btr_page_set_level( /*===============*/ page_t* page, /*!< in: index page */ page_zip_des_t* page_zip,/*!< in: compressed page whose uncompressed part will be updated, or NULL */ ulint level, /*!< in: level, leaf level == 0 */ mtr_t* mtr) /*!< in: mini-transaction handle */ { ut_ad(page && mtr); ut_ad(level <= BTR_MAX_NODE_LEVEL); #ifdef WITH_ZIP if (UNIV_LIKELY_NULL(page_zip)) { mach_write_to_2(page + (PAGE_HEADER + PAGE_LEVEL), level); page_zip_write_header(page_zip, page + (PAGE_HEADER + PAGE_LEVEL), 2, mtr); } else { #endif /* WITH_ZIP */ mlog_write_ulint(page + (PAGE_HEADER + PAGE_LEVEL), level, MLOG_2BYTES, mtr); #ifdef WITH_ZIP } #endif /* WITH_ZIP */ } /********************************************************//** Gets the next index page number. @return next page number */ UNIV_INLINE ulint btr_page_get_next( /*==============*/ const page_t* page, /*!< in: index page */ mtr_t* mtr __attribute__((unused))) /*!< in: mini-transaction handle */ { ut_ad(page && mtr); ut_ad(mtr_memo_contains_page(mtr, page, MTR_MEMO_PAGE_X_FIX) || mtr_memo_contains_page(mtr, page, MTR_MEMO_PAGE_S_FIX)); return(mach_read_from_4(page + FIL_PAGE_NEXT)); } /********************************************************//** Sets the next index page field. */ UNIV_INLINE void btr_page_set_next( /*==============*/ page_t* page, /*!< in: index page */ page_zip_des_t* page_zip,/*!< in: compressed page whose uncompressed part will be updated, or NULL */ ulint next, /*!< in: next page number */ mtr_t* mtr) /*!< in: mini-transaction handle */ { ut_ad(page && mtr); #ifdef WITH_ZIP if (UNIV_LIKELY_NULL(page_zip)) { mach_write_to_4(page + FIL_PAGE_NEXT, next); page_zip_write_header(page_zip, page + FIL_PAGE_NEXT, 4, mtr); } else { #endif /* WITH_ZIP */ mlog_write_ulint(page + FIL_PAGE_NEXT, next, MLOG_4BYTES, mtr); #ifdef WITH_ZIP } #endif /* WITH_ZIP */ } /********************************************************//** Gets the previous index page number. @return prev page number */ UNIV_INLINE ulint btr_page_get_prev( /*==============*/ const page_t* page, /*!< in: index page */ mtr_t* mtr __attribute__((unused))) /*!< in: mini-transaction handle */ { ut_ad(page && mtr); return(mach_read_from_4(page + FIL_PAGE_PREV)); } /********************************************************//** Sets the previous index page field. */ UNIV_INLINE void btr_page_set_prev( /*==============*/ page_t* page, /*!< in: index page */ page_zip_des_t* page_zip,/*!< in: compressed page whose uncompressed part will be updated, or NULL */ ulint prev, /*!< in: previous page number */ mtr_t* mtr) /*!< in: mini-transaction handle */ { ut_ad(page && mtr); #ifdef WITH_ZIP if (UNIV_LIKELY_NULL(page_zip)) { mach_write_to_4(page + FIL_PAGE_PREV, prev); page_zip_write_header(page_zip, page + FIL_PAGE_PREV, 4, mtr); } else { #endif /* WITH_ZIP */ mlog_write_ulint(page + FIL_PAGE_PREV, prev, MLOG_4BYTES, mtr); #ifdef WITH_ZIP } #endif /* WITH_ZIP */ } /**************************************************************//** Gets the child node file address in a node pointer. NOTE: the offsets array must contain all offsets for the record since we read the last field according to offsets and assume that it contains the child page number. In other words offsets must have been retrieved with rec_get_offsets(n_fields=ULINT_UNDEFINED). @return child node address */ UNIV_INLINE ulint btr_node_ptr_get_child_page_no( /*===========================*/ const rec_t* rec, /*!< in: node pointer record */ const ulint* offsets)/*!< in: array returned by rec_get_offsets() */ { const byte* field; ulint len; ulint page_no; ut_ad(!rec_offs_comp(offsets) || rec_get_node_ptr_flag(rec)); /* The child address is in the last field */ field = rec_get_nth_field(rec, offsets, rec_offs_n_fields(offsets) - 1, &len); ut_ad(len == 4); page_no = mach_read_from_4(field); if (UNIV_UNLIKELY(page_no == 0)) { ib_logger(ib_stream, "InnoDB: a nonsensical page number 0" " in a node ptr record at offset %lu\n", (ulong) page_offset(rec)); buf_page_print(page_align(rec), 0); } return(page_no); } /**************************************************************//** Releases the latches on a leaf page and bufferunfixes it. */ UNIV_INLINE void btr_leaf_page_release( /*==================*/ buf_block_t* block, /*!< in: buffer block */ ulint latch_mode, /*!< in: BTR_SEARCH_LEAF or BTR_MODIFY_LEAF */ mtr_t* mtr) /*!< in: mtr */ { ut_ad(latch_mode == BTR_SEARCH_LEAF || latch_mode == BTR_MODIFY_LEAF); ut_ad(!mtr_memo_contains(mtr, block, MTR_MEMO_MODIFY)); mtr_memo_release(mtr, block, latch_mode == BTR_SEARCH_LEAF ? MTR_MEMO_PAGE_S_FIX : MTR_MEMO_PAGE_X_FIX); } #endif /* !UNIV_HOTBACKUP */ haildb-2.3.2/include/thr0loc.ic0000644000175000017500000000202011513177357017150 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/thr0loc.ic Thread local storage Created 10/4/1995 Heikki Tuuri *******************************************************/ haildb-2.3.2/include/row0vers.h0000644000175000017500000001333211513177357017230 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1997, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/row0vers.h Row versions Created 2/6/1997 Heikki Tuuri *******************************************************/ #ifndef row0vers_h #define row0vers_h #include "univ.i" #include "data0data.h" #include "dict0types.h" #include "trx0types.h" #include "que0types.h" #include "rem0types.h" #include "mtr0mtr.h" #include "read0types.h" /*****************************************************************//** Finds out if an active transaction has inserted or modified a secondary index record. NOTE: the kernel mutex is temporarily released in this function! @return NULL if committed, else the active transaction */ UNIV_INTERN trx_t* row_vers_impl_x_locked_off_kernel( /*==============================*/ const rec_t* rec, /*!< in: record in a secondary index */ dict_index_t* index, /*!< in: the secondary index */ const ulint* offsets);/*!< in: rec_get_offsets(rec, index) */ /*****************************************************************//** Finds out if we must preserve a delete marked earlier version of a clustered index record, because it is >= the purge view. @return TRUE if earlier version should be preserved */ UNIV_INTERN ibool row_vers_must_preserve_del_marked( /*==============================*/ trx_id_t trx_id, /*!< in: transaction id in the version */ mtr_t* mtr); /*!< in: mtr holding the latch on the clustered index record; it will also hold the latch on purge_view */ /*****************************************************************//** Finds out if a version of the record, where the version >= the current purge view, should have ientry as its secondary index entry. We check if there is any not delete marked version of the record where the trx id >= purge view, and the secondary index entry == ientry; exactly in this case we return TRUE. @return TRUE if earlier version should have */ UNIV_INTERN ibool row_vers_old_has_index_entry( /*=========================*/ ibool also_curr,/*!< in: TRUE if also rec is included in the versions to search; otherwise only versions prior to it are searched */ const rec_t* rec, /*!< in: record in the clustered index; the caller must have a latch on the page */ mtr_t* mtr, /*!< in: mtr holding the latch on rec; it will also hold the latch on purge_view */ dict_index_t* index, /*!< in: the secondary index */ const dtuple_t* ientry);/*!< in: the secondary index entry */ /*****************************************************************//** Constructs the version of a clustered index record which a consistent read should see. We assume that the trx id stored in rec is such that the consistent read should not see rec in its present version. @return DB_SUCCESS or DB_MISSING_HISTORY */ UNIV_INTERN ulint row_vers_build_for_consistent_read( /*===============================*/ const rec_t* rec, /*!< in: record in a clustered index; the caller must have a latch on the page; this latch locks the top of the stack of versions of this records */ mtr_t* mtr, /*!< in: mtr holding the latch on rec; it will also hold the latch on purge_view */ dict_index_t* index, /*!< in: the clustered index */ ulint** offsets,/*!< in/out: offsets returned by rec_get_offsets(rec, index) */ read_view_t* view, /*!< in: the consistent read view */ mem_heap_t** offset_heap,/*!< in/out: memory heap from which the offsets are allocated */ mem_heap_t* in_heap,/*!< in: memory heap from which the memory for *old_vers is allocated; memory for possible intermediate versions is allocated and freed locally within the function */ rec_t** old_vers);/*!< out, own: old version, or NULL if the record does not exist in the view, that is, it was freshly inserted afterwards */ /*****************************************************************//** Constructs the last committed version of a clustered index record, which should be seen by a semi-consistent read. @return DB_SUCCESS or DB_MISSING_HISTORY */ UNIV_INTERN ulint row_vers_build_for_semi_consistent_read( /*====================================*/ const rec_t* rec, /*!< in: record in a clustered index; the caller must have a latch on the page; this latch locks the top of the stack of versions of this records */ mtr_t* mtr, /*!< in: mtr holding the latch on rec */ dict_index_t* index, /*!< in: the clustered index */ ulint** offsets,/*!< in/out: offsets returned by rec_get_offsets(rec, index) */ mem_heap_t** offset_heap,/*!< in/out: memory heap from which the offsets are allocated */ mem_heap_t* in_heap,/*!< in: memory heap from which the memory for *old_vers is allocated; memory for possible intermediate versions is allocated and freed locally within the function */ const rec_t** old_vers);/*!< out: rec, old version, or NULL if the record does not exist in the view, that is, it was freshly inserted afterwards */ #ifndef UNIV_NONINL #include "row0vers.ic" #endif #endif haildb-2.3.2/include/trx0rec.h0000644000175000017500000003155311513177357017035 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/trx0rec.h Transaction undo log record Created 3/26/1996 Heikki Tuuri *******************************************************/ #ifndef trx0rec_h #define trx0rec_h #include "univ.i" #include "trx0types.h" #include "row0types.h" #include "mtr0mtr.h" #include "dict0types.h" #include "data0data.h" #include "rem0types.h" #ifndef UNIV_HOTBACKUP # include "que0types.h" /***********************************************************************//** Copies the undo record to the heap. @return own: copy of undo log record */ UNIV_INLINE trx_undo_rec_t* trx_undo_rec_copy( /*==============*/ const trx_undo_rec_t* undo_rec, /*!< in: undo log record */ mem_heap_t* heap); /*!< in: heap where copied */ /**********************************************************************//** Reads the undo log record type. @return record type */ UNIV_INLINE ulint trx_undo_rec_get_type( /*==================*/ const trx_undo_rec_t* undo_rec); /*!< in: undo log record */ /**********************************************************************//** Reads from an undo log record the record compiler info. @return compiler info */ UNIV_INLINE ulint trx_undo_rec_get_cmpl_info( /*=======================*/ const trx_undo_rec_t* undo_rec); /*!< in: undo log record */ /**********************************************************************//** Returns TRUE if an undo log record contains an extern storage field. @return TRUE if extern */ UNIV_INLINE ibool trx_undo_rec_get_extern_storage( /*============================*/ const trx_undo_rec_t* undo_rec); /*!< in: undo log record */ /**********************************************************************//** Reads the undo log record number. @return undo no */ UNIV_INLINE undo_no_t trx_undo_rec_get_undo_no( /*=====================*/ const trx_undo_rec_t* undo_rec); /*!< in: undo log record */ /**********************************************************************//** Returns the start of the undo record data area. @return offset to the data area */ UNIV_INLINE ulint trx_undo_rec_get_offset( /*====================*/ undo_no_t undo_no) /*!< in: undo no read from node */ __attribute__((const)); /* FIXME: From merge */ /**********************************************************************//** Returns the start of the undo record data area. */ #define trx_undo_rec_get_ptr(undo_rec, undo_no) \ ((undo_rec) + trx_undo_rec_get_offset(undo_no)) /**********************************************************************//** Reads from an undo log record the general parameters. @return remaining part of undo log record after reading these values */ UNIV_INTERN byte* trx_undo_rec_get_pars( /*==================*/ trx_undo_rec_t* undo_rec, /*!< in: undo log record */ ulint* type, /*!< out: undo record type: TRX_UNDO_INSERT_REC, ... */ ulint* cmpl_info, /*!< out: compiler info, relevant only for update type records */ ibool* updated_extern, /*!< out: TRUE if we updated an externally stored fild */ undo_no_t* undo_no, /*!< out: undo log record number */ dulint* table_id); /*!< out: table id */ /*******************************************************************//** Builds a row reference from an undo log record. @return pointer to remaining part of undo record */ UNIV_INTERN byte* trx_undo_rec_get_row_ref( /*=====================*/ byte* ptr, /*!< in: remaining part of a copy of an undo log record, at the start of the row reference; NOTE that this copy of the undo log record must be preserved as long as the row reference is used, as we do NOT copy the data in the record! */ dict_index_t* index, /*!< in: clustered index */ dtuple_t** ref, /*!< out, own: row reference */ mem_heap_t* heap); /*!< in: memory heap from which the memory needed is allocated */ /*******************************************************************//** Skips a row reference from an undo log record. @return pointer to remaining part of undo record */ UNIV_INTERN byte* trx_undo_rec_skip_row_ref( /*======================*/ byte* ptr, /*!< in: remaining part in update undo log record, at the start of the row reference */ dict_index_t* index); /*!< in: clustered index */ /**********************************************************************//** Reads from an undo log update record the system field values of the old version. @return remaining part of undo log record after reading these values */ UNIV_INTERN byte* trx_undo_update_rec_get_sys_cols( /*=============================*/ byte* ptr, /*!< in: remaining part of undo log record after reading general parameters */ trx_id_t* trx_id, /*!< out: trx id */ roll_ptr_t* roll_ptr, /*!< out: roll ptr */ ulint* info_bits); /*!< out: info bits state */ /*******************************************************************//** Builds an update vector based on a remaining part of an undo log record. @return remaining part of the record, NULL if an error detected, which means that the record is corrupted */ UNIV_INTERN byte* trx_undo_update_rec_get_update( /*===========================*/ byte* ptr, /*!< in: remaining part in update undo log record, after reading the row reference NOTE that this copy of the undo log record must be preserved as long as the update vector is used, as we do NOT copy the data in the record! */ dict_index_t* index, /*!< in: clustered index */ ulint type, /*!< in: TRX_UNDO_UPD_EXIST_REC, TRX_UNDO_UPD_DEL_REC, or TRX_UNDO_DEL_MARK_REC; in the last case, only trx id and roll ptr fields are added to the update vector */ trx_id_t trx_id, /*!< in: transaction id from this undorecord */ roll_ptr_t roll_ptr,/*!< in: roll pointer from this undo record */ ulint info_bits,/*!< in: info bits from this undo record */ trx_t* trx, /*!< in: transaction */ mem_heap_t* heap, /*!< in: memory heap from which the memory needed is allocated */ upd_t** upd); /*!< out, own: update vector */ /*******************************************************************//** Builds a partial row from an update undo log record. It contains the columns which occur as ordering in any index of the table. @return pointer to remaining part of undo record */ UNIV_INTERN byte* trx_undo_rec_get_partial_row( /*=========================*/ byte* ptr, /*!< in: remaining part in update undo log record of a suitable type, at the start of the stored index columns; NOTE that this copy of the undo log record must be preserved as long as the partial row is used, as we do NOT copy the data in the record! */ dict_index_t* index, /*!< in: clustered index */ dtuple_t** row, /*!< out, own: partial row */ ibool ignore_prefix, /*!< in: flag to indicate if we expect blob prefixes in undo. Used only in the assertion. */ mem_heap_t* heap); /*!< in: memory heap from which the memory needed is allocated */ /***********************************************************************//** Writes information to an undo log about an insert, update, or a delete marking of a clustered index record. This information is used in a rollback of the transaction and in consistent reads that must look to the history of this transaction. @return DB_SUCCESS or error code */ UNIV_INTERN ulint trx_undo_report_row_operation( /*==========================*/ ulint flags, /*!< in: if BTR_NO_UNDO_LOG_FLAG bit is set, does nothing */ ulint op_type, /*!< in: TRX_UNDO_INSERT_OP or TRX_UNDO_MODIFY_OP */ que_thr_t* thr, /*!< in: query thread */ dict_index_t* index, /*!< in: clustered index */ const dtuple_t* clust_entry, /*!< in: in the case of an insert, index entry to insert into the clustered index, otherwise NULL */ const upd_t* update, /*!< in: in the case of an update, the update vector, otherwise NULL */ ulint cmpl_info, /*!< in: compiler info on secondary index updates */ const rec_t* rec, /*!< in: case of an update or delete marking, the record in the clustered index, otherwise NULL */ roll_ptr_t* roll_ptr); /*!< out: rollback pointer to the inserted undo log record, ut_dulint_zero if BTR_NO_UNDO_LOG flag was specified */ /******************************************************************//** Copies an undo record to heap. This function can be called if we know that the undo log record exists. @return own: copy of the record */ UNIV_INTERN trx_undo_rec_t* trx_undo_get_undo_rec_low( /*======================*/ roll_ptr_t roll_ptr, /*!< in: roll pointer to record */ mem_heap_t* heap); /*!< in: memory heap where copied */ /******************************************************************//** Copies an undo record to heap. NOTE: the caller must have latches on the clustered index page and purge_view. @return DB_SUCCESS, or DB_MISSING_HISTORY if the undo log has been truncated and we cannot fetch the old version */ UNIV_INTERN ulint trx_undo_get_undo_rec( /*==================*/ roll_ptr_t roll_ptr, /*!< in: roll pointer to record */ trx_id_t trx_id, /*!< in: id of the trx that generated the roll pointer: it points to an undo log of this transaction */ trx_undo_rec_t** undo_rec, /*!< out, own: copy of the record */ mem_heap_t* heap); /*!< in: memory heap where copied */ /*******************************************************************//** Build a previous version of a clustered index record. This function checks that the caller has a latch on the index page of the clustered index record and an s-latch on the purge_view. This guarantees that the stack of versions is locked. @return DB_SUCCESS, or DB_MISSING_HISTORY if the previous version is earlier than purge_view, which means that it may have been removed, DB_ERROR if corrupted record */ UNIV_INTERN ulint trx_undo_prev_version_build( /*========================*/ const rec_t* index_rec,/*!< in: clustered index record in the index tree */ mtr_t* index_mtr,/*!< in: mtr which contains the latch to index_rec page and purge_view */ const rec_t* rec, /*!< in: version of a clustered index record */ dict_index_t* index, /*!< in: clustered index */ ulint* offsets,/*!< in: rec_get_offsets(rec, index) */ mem_heap_t* heap, /*!< in: memory heap from which the memory needed is allocated */ rec_t** old_vers);/*!< out, own: previous version, or NULL if rec is the first inserted version, or if history data has been deleted */ #endif /* !UNIV_HOTBACKUP */ /***********************************************************//** Parses a redo log record of adding an undo log record. @return end of log record or NULL */ UNIV_INTERN byte* trx_undo_parse_add_undo_rec( /*========================*/ byte* ptr, /*!< in: buffer */ byte* end_ptr,/*!< in: buffer end */ page_t* page); /*!< in: page or NULL */ /***********************************************************//** Parses a redo log record of erasing of an undo page end. @return end of log record or NULL */ UNIV_INTERN byte* trx_undo_parse_erase_page_end( /*==========================*/ byte* ptr, /*!< in: buffer */ byte* end_ptr,/*!< in: buffer end */ page_t* page, /*!< in: page or NULL */ mtr_t* mtr); /*!< in: mtr or NULL */ #ifndef UNIV_HOTBACKUP /* Types of an undo log record: these have to be smaller than 16, as the compilation info multiplied by 16 is ORed to this value in an undo log record */ #define TRX_UNDO_INSERT_REC 11 /* fresh insert into clustered index */ #define TRX_UNDO_UPD_EXIST_REC 12 /* update of a non-delete-marked record */ #define TRX_UNDO_UPD_DEL_REC 13 /* update of a delete marked record to a not delete marked record; also the fields of the record can change */ #define TRX_UNDO_DEL_MARK_REC 14 /* delete marking of a record; fields do not change */ #define TRX_UNDO_CMPL_INFO_MULT 16 /* compilation info is multiplied by this and ORed to the type above */ #define TRX_UNDO_UPD_EXTERN 128 /* This bit can be ORed to type_cmpl to denote that we updated external storage fields: used by purge to free the external storage */ /* Operation type flags used in trx_undo_report_row_operation */ #define TRX_UNDO_INSERT_OP 1 #define TRX_UNDO_MODIFY_OP 2 #ifndef UNIV_NONINL #include "trx0rec.ic" #endif #endif /* !UNIV_HOTBACKUP */ #endif /* trx0rec_h */ haildb-2.3.2/include/dyn0dyn.ic0000644000175000017500000002043411513177357017173 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/dyn0dyn.ic The dynamically allocated array Created 2/5/1996 Heikki Tuuri *******************************************************/ /** Value of dyn_block_struct::magic_n */ #define DYN_BLOCK_MAGIC_N 375767 /** Flag for dyn_block_struct::used that indicates a full block */ #define DYN_BLOCK_FULL_FLAG 0x1000000UL /************************************************************//** Adds a new block to a dyn array. @return created block */ UNIV_INTERN dyn_block_t* dyn_array_add_block( /*================*/ dyn_array_t* arr); /*!< in: dyn array */ /************************************************************//** Gets the first block in a dyn array. */ UNIV_INLINE dyn_block_t* dyn_array_get_first_block( /*======================*/ dyn_array_t* arr) /*!< in: dyn array */ { return(arr); } /************************************************************//** Gets the last block in a dyn array. */ UNIV_INLINE dyn_block_t* dyn_array_get_last_block( /*=====================*/ dyn_array_t* arr) /*!< in: dyn array */ { if (arr->heap == NULL) { return(arr); } return(UT_LIST_GET_LAST(arr->base)); } /********************************************************************//** Gets the next block in a dyn array. @return pointer to next, NULL if end of list */ UNIV_INLINE dyn_block_t* dyn_array_get_next_block( /*=====================*/ dyn_array_t* arr, /*!< in: dyn array */ dyn_block_t* block) /*!< in: dyn array block */ { ut_ad(arr && block); if (arr->heap == NULL) { ut_ad(arr == block); return(NULL); } return(UT_LIST_GET_NEXT(list, block)); } /********************************************************************//** Gets the number of used bytes in a dyn array block. @return number of bytes used */ UNIV_INLINE ulint dyn_block_get_used( /*===============*/ dyn_block_t* block) /*!< in: dyn array block */ { ut_ad(block); return((block->used) & ~DYN_BLOCK_FULL_FLAG); } /********************************************************************//** Gets pointer to the start of data in a dyn array block. @return pointer to data */ UNIV_INLINE byte* dyn_block_get_data( /*===============*/ dyn_block_t* block) /*!< in: dyn array block */ { ut_ad(block); return(block->data); } /*********************************************************************//** Initializes a dynamic array. @return initialized dyn array */ UNIV_INLINE dyn_array_t* dyn_array_create( /*=============*/ dyn_array_t* arr) /*!< in: pointer to a memory buffer of size sizeof(dyn_array_t) */ { ut_ad(arr); #if DYN_ARRAY_DATA_SIZE >= DYN_BLOCK_FULL_FLAG # error "DYN_ARRAY_DATA_SIZE >= DYN_BLOCK_FULL_FLAG" #endif arr->heap = NULL; arr->used = 0; #ifdef UNIV_DEBUG arr->buf_end = 0; arr->magic_n = DYN_BLOCK_MAGIC_N; #endif return(arr); } /************************************************************//** Frees a dynamic array. */ UNIV_INLINE void dyn_array_free( /*===========*/ dyn_array_t* arr) /*!< in: dyn array */ { if (arr->heap != NULL) { mem_heap_free(arr->heap); } #ifdef UNIV_DEBUG arr->magic_n = 0; #endif } /*********************************************************************//** Makes room on top of a dyn array and returns a pointer to the added element. The caller must copy the element to the pointer returned. @return pointer to the element */ UNIV_INLINE void* dyn_array_push( /*===========*/ dyn_array_t* arr, /*!< in: dynamic array */ ulint size) /*!< in: size in bytes of the element */ { dyn_block_t* block; ulint used; ut_ad(arr); ut_ad(arr->magic_n == DYN_BLOCK_MAGIC_N); ut_ad(size <= DYN_ARRAY_DATA_SIZE); ut_ad(size); block = arr; used = block->used; if (used + size > DYN_ARRAY_DATA_SIZE) { /* Get the last array block */ block = dyn_array_get_last_block(arr); used = block->used; if (used + size > DYN_ARRAY_DATA_SIZE) { block = dyn_array_add_block(arr); used = block->used; } } block->used = used + size; ut_ad(block->used <= DYN_ARRAY_DATA_SIZE); return((block->data) + used); } /*********************************************************************//** Makes room on top of a dyn array and returns a pointer to a buffer in it. After copying the elements, the caller must close the buffer using dyn_array_close. @return pointer to the buffer */ UNIV_INLINE byte* dyn_array_open( /*===========*/ dyn_array_t* arr, /*!< in: dynamic array */ ulint size) /*!< in: size in bytes of the buffer; MUST be smaller than DYN_ARRAY_DATA_SIZE! */ { dyn_block_t* block; ulint used; ut_ad(arr); ut_ad(arr->magic_n == DYN_BLOCK_MAGIC_N); ut_ad(size <= DYN_ARRAY_DATA_SIZE); ut_ad(size); block = arr; used = block->used; if (used + size > DYN_ARRAY_DATA_SIZE) { /* Get the last array block */ block = dyn_array_get_last_block(arr); used = block->used; if (used + size > DYN_ARRAY_DATA_SIZE) { block = dyn_array_add_block(arr); used = block->used; ut_a(size <= DYN_ARRAY_DATA_SIZE); } } ut_ad(block->used <= DYN_ARRAY_DATA_SIZE); #ifdef UNIV_DEBUG ut_ad(arr->buf_end == 0); arr->buf_end = used + size; #endif return((block->data) + used); } /*********************************************************************//** Closes the buffer returned by dyn_array_open. */ UNIV_INLINE void dyn_array_close( /*============*/ dyn_array_t* arr, /*!< in: dynamic array */ byte* ptr) /*!< in: buffer space from ptr up was not used */ { dyn_block_t* block; ut_ad(arr); ut_ad(arr->magic_n == DYN_BLOCK_MAGIC_N); block = dyn_array_get_last_block(arr); ut_ad(arr->buf_end + block->data >= ptr); block->used = ptr - block->data; ut_ad(block->used <= DYN_ARRAY_DATA_SIZE); #ifdef UNIV_DEBUG arr->buf_end = 0; #endif } /************************************************************//** Returns pointer to an element in dyn array. @return pointer to element */ UNIV_INLINE void* dyn_array_get_element( /*==================*/ dyn_array_t* arr, /*!< in: dyn array */ ulint pos) /*!< in: position of element as bytes from array start */ { dyn_block_t* block; ulint used; ut_ad(arr); ut_ad(arr->magic_n == DYN_BLOCK_MAGIC_N); /* Get the first array block */ block = dyn_array_get_first_block(arr); if (arr->heap != NULL) { used = dyn_block_get_used(block); while (pos >= used) { pos -= used; block = UT_LIST_GET_NEXT(list, block); ut_ad(block); used = dyn_block_get_used(block); } } ut_ad(block); ut_ad(dyn_block_get_used(block) >= pos); return(block->data + pos); } /************************************************************//** Returns the size of stored data in a dyn array. @return data size in bytes */ UNIV_INLINE ulint dyn_array_get_data_size( /*====================*/ dyn_array_t* arr) /*!< in: dyn array */ { dyn_block_t* block; ulint sum = 0; ut_ad(arr); ut_ad(arr->magic_n == DYN_BLOCK_MAGIC_N); if (arr->heap == NULL) { return(arr->used); } /* Get the first array block */ block = dyn_array_get_first_block(arr); while (block != NULL) { sum += dyn_block_get_used(block); block = dyn_array_get_next_block(arr, block); } return(sum); } /********************************************************//** Pushes n bytes to a dyn array. */ UNIV_INLINE void dyn_push_string( /*============*/ dyn_array_t* arr, /*!< in: dyn array */ const byte* str, /*!< in: string to write */ ulint len) /*!< in: string length */ { ulint n_copied; while (len > 0) { if (len > DYN_ARRAY_DATA_SIZE) { n_copied = DYN_ARRAY_DATA_SIZE; } else { n_copied = len; } memcpy(dyn_array_push(arr, n_copied), str, n_copied); str += n_copied; len -= n_copied; } } haildb-2.3.2/include/rem0cmp.ic0000644000175000017500000000651411513177357017154 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1994, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /*******************************************************************//** @file include/rem0cmp.ic Comparison services for records Created 7/1/1994 Heikki Tuuri ************************************************************************/ /*************************************************************//** This function is used to compare two data fields for which we know the data type. @return 1, 0, -1, if data1 is greater, equal, less than data2, respectively */ UNIV_INLINE int cmp_data_data( /*==========*/ void* cmp_ctx,/*!< in: client compare context */ ulint mtype, /*!< in: main type */ ulint prtype, /*!< in: precise type */ const byte* data1, /*!< in: data field (== a pointer to a memory buffer) */ ulint len1, /*!< in: data field length or UNIV_SQL_NULL */ const byte* data2, /*!< in: data field (== a pointer to a memory buffer) */ ulint len2) /*!< in: data field length or UNIV_SQL_NULL */ { return(cmp_data_data_slow( cmp_ctx, mtype, prtype, data1, len1, data2, len2)); } /*************************************************************//** This function is used to compare two dfields where at least the first has its data type field set. @return 1, 0, -1, if dfield1 is greater, equal, less than dfield2, respectively */ UNIV_INLINE int cmp_dfield_dfield( /*==============*/ void* cmp_ctx,/*!< in: client compare context */ const dfield_t* dfield1,/*!< in: data field; must have type field set */ const dfield_t* dfield2)/*!< in: data field */ { const dtype_t* type; ut_ad(dfield_check_typed(dfield1)); type = dfield_get_type(dfield1); return(cmp_data_data( cmp_ctx, type->mtype, type->prtype, (const byte*) dfield_get_data(dfield1), dfield_get_len(dfield1), (const byte*) dfield_get_data(dfield2), dfield_get_len(dfield2))); } /*************************************************************//** This function is used to compare two physical records. Only the common first fields are compared. @return 1, 0 , -1 if rec1 is greater, equal, less, respectively, than rec2; only the common first fields are compared */ UNIV_INLINE int cmp_rec_rec( /*========*/ const rec_t* rec1, /*!< in: physical record */ const rec_t* rec2, /*!< in: physical record */ const ulint* offsets1,/*!< in: rec_get_offsets(rec1, index) */ const ulint* offsets2,/*!< in: rec_get_offsets(rec2, index) */ dict_index_t* dict_index) /*!< in: data dictionary index */ { ulint match_f = 0; ulint match_b = 0; return(cmp_rec_rec_with_match(rec1, rec2, offsets1, offsets2, dict_index, &match_f, &match_b)); } haildb-2.3.2/include/row0upd.ic0000644000175000017500000001277111513177357017213 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/row0upd.ic Update of a row Created 12/27/1996 Heikki Tuuri *******************************************************/ #include "mtr0log.h" #ifndef UNIV_HOTBACKUP # include "trx0trx.h" # include "trx0undo.h" # include "row0row.h" # include "btr0sea.h" #endif /* !UNIV_HOTBACKUP */ #ifdef WITH_ZIP #include "page0zip.h" #endif /* WITH_ZIP */ /*********************************************************************//** Creates an update vector object. @return own: update vector object */ UNIV_INLINE upd_t* upd_create( /*=======*/ ulint n, /*!< in: number of fields */ mem_heap_t* heap) /*!< in: heap from which memory allocated */ { upd_t* update; update = (upd_t*) mem_heap_alloc(heap, sizeof(upd_t)); update->info_bits = 0; update->n_fields = n; update->fields = (upd_field_t*) mem_heap_alloc(heap, sizeof(upd_field_t) * n); return(update); } /*********************************************************************//** Returns the number of fields in the update vector == number of columns to be updated by an update vector. @return number of fields */ UNIV_INLINE ulint upd_get_n_fields( /*=============*/ const upd_t* update) /*!< in: update vector */ { ut_ad(update); return(update->n_fields); } #ifdef UNIV_DEBUG /*********************************************************************//** Returns the nth field of an update vector. @return update vector field */ UNIV_INLINE upd_field_t* upd_get_nth_field( /*==============*/ const upd_t* update, /*!< in: update vector */ ulint n) /*!< in: field position in update vector */ { ut_ad(update); ut_ad(n < update->n_fields); return((upd_field_t*) update->fields + n); } #endif /* UNIV_DEBUG */ #ifndef UNIV_HOTBACKUP /*********************************************************************//** Sets an index field number to be updated by an update vector field. */ UNIV_INLINE void upd_field_set_field_no( /*===================*/ upd_field_t* upd_field, /*!< in: update vector field */ ulint field_no, /*!< in: field number in a clustered index */ dict_index_t* dict_index, /*!< in: dict_index */ trx_t* trx) /*!< in: transaction */ { upd_field->field_no = field_no; upd_field->orig_len = 0; if (UNIV_UNLIKELY(field_no >= dict_index_get_n_fields(dict_index))) { ib_logger(ib_stream, "InnoDB: Error: trying to access field %lu in ", (ulong) field_no); dict_index_name_print(ib_stream, trx, dict_index); ib_logger(ib_stream, "\n" "InnoDB: but index only has %lu fields\n", (ulong) dict_index_get_n_fields(dict_index)); } dict_col_copy_type(dict_index_get_nth_col(dict_index, field_no), dfield_get_type(&upd_field->new_val)); } /*********************************************************************//** Returns a field of an update vector by field_no. @return update vector field, or NULL */ UNIV_INLINE const upd_field_t* upd_get_field_by_field_no( /*======================*/ const upd_t* update, /*!< in: update vector */ ulint no) /*!< in: field_no */ { ulint i; for (i = 0; i < upd_get_n_fields(update); i++) { const upd_field_t* uf = upd_get_nth_field(update, i); if (uf->field_no == no) { return(uf); } } return(NULL); } /*********************************************************************//** Updates the trx id and roll ptr field in a clustered index record when a row is updated or marked deleted. */ UNIV_INLINE void row_upd_rec_sys_fields( /*===================*/ rec_t* rec, /*!< in/out: record */ page_zip_des_t* page_zip,/*!< in/out: compressed page whose uncompressed part will be updated, or NULL */ dict_index_t* dict_index, /*!< in: clustered index */ const ulint* offsets,/*!< in: rec_get_offsets(rec, index) */ trx_t* trx, /*!< in: transaction */ roll_ptr_t roll_ptr)/*!< in: roll ptr of the undo log record */ { #ifdef WITH_ZIP ut_ad(dict_index_is_clust(dict_index)); ut_ad(rec_offs_validate(rec, dict_index, offsets)); #endif /* WITH_ZIP */ #ifdef UNIV_SYNC_DEBUG if (!rw_lock_own(&btr_search_latch, RW_LOCK_EX)) { ut_ad(!buf_block_align(rec)->is_hashed); } #endif /* UNIV_SYNC_DEBUG */ #ifdef WITH_ZIP if (UNIV_LIKELY_NULL(page_zip)) { ulint pos = dict_index_get_sys_col_pos(dict_index, DATA_TRX_ID); page_zip_write_trx_id_and_roll_ptr(page_zip, rec, offsets, pos, trx->id, roll_ptr); } else { #endif /* WITH_ZIP */ ulint offset = dict_index->trx_id_offset; if (!offset) { offset = row_get_trx_id_offset(rec, dict_index, offsets); } #if DATA_TRX_ID + 1 != DATA_ROLL_PTR # error "DATA_TRX_ID + 1 != DATA_ROLL_PTR" #endif trx_write_trx_id(rec + offset, trx->id); trx_write_roll_ptr(rec + offset + DATA_TRX_ID_LEN, roll_ptr); #ifdef WITH_ZIP } #endif /* WITH_ZIP */ } #endif /* !UNIV_HOTBACKUP */ haildb-2.3.2/include/ibuf0ibuf.h0000644000175000017500000003627511513177357017327 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1997, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/ibuf0ibuf.h Insert buffer Created 7/19/1997 Heikki Tuuri *******************************************************/ #ifndef ibuf0ibuf_h #define ibuf0ibuf_h #include "univ.i" #include "mtr0mtr.h" #include "dict0mem.h" #include "fsp0fsp.h" #ifndef UNIV_HOTBACKUP # include "ibuf0types.h" /** Combinations of operations that can be buffered. Because the enum values are used for indexing innobase_change_buffering_values[], they should start at 0 and there should not be any gaps. */ typedef enum { IBUF_USE_NONE = 0, IBUF_USE_INSERT, /* insert */ IBUF_USE_COUNT /* number of entries in ibuf_use_t */ } ibuf_use_t; /** Operations that can currently be buffered. */ extern ibuf_use_t ibuf_use; /** The insert buffer control structure */ extern ibuf_t* ibuf; /* The purpose of the insert buffer is to reduce random disk access. When we wish to insert a record into a non-unique secondary index and the B-tree leaf page where the record belongs to is not in the buffer pool, we insert the record into the insert buffer B-tree, indexed by (space_id, page_no). When the page is eventually read into the buffer pool, we look up the insert buffer B-tree for any modifications to the page, and apply these upon the completion of the read operation. This is called the insert buffer merge. */ /* The insert buffer merge must always succeed. To guarantee this, the insert buffer subsystem keeps track of the free space in pages for which it can buffer operations. Two bits per page in the insert buffer bitmap indicate the available space in coarse increments. The free bits in the insert buffer bitmap must never exceed the free space on a page. It is safe to decrement or reset the bits in the bitmap in a mini-transaction that is committed before the mini-transaction that affects the free space. It is unsafe to increment the bits in a separately committed mini-transaction, because in crash recovery, the free bits could momentarily be set too high. */ /******************************************************************//** Creates the insert buffer data structure at a database startup and initializes the data structures for the insert buffer of each tablespace. */ UNIV_INTERN void ibuf_init_at_db_start(void); /*=======================*/ /*********************************************************************//** Reads the biggest tablespace id from the high end of the insert buffer tree and updates the counter in fil_system. */ UNIV_INTERN void ibuf_update_max_tablespace_id(void); /*===============================*/ /*********************************************************************//** Initializes an ibuf bitmap page. */ UNIV_INTERN void ibuf_bitmap_page_init( /*==================*/ buf_block_t* block, /*!< in: bitmap page */ mtr_t* mtr); /*!< in: mtr */ /************************************************************************//** Resets the free bits of the page in the ibuf bitmap. This is done in a separate mini-transaction, hence this operation does not restrict further work to only ibuf bitmap operations, which would result if the latch to the bitmap page were kept. NOTE: The free bits in the insert buffer bitmap must never exceed the free space on a page. It is safe to decrement or reset the bits in the bitmap in a mini-transaction that is committed before the mini-transaction that affects the free space. */ UNIV_INTERN void ibuf_reset_free_bits( /*=================*/ buf_block_t* block); /*!< in: index page; free bits are set to 0 if the index is a non-clustered non-unique, and page level is 0 */ /************************************************************************//** Updates the free bits of an uncompressed page in the ibuf bitmap if there is not enough free on the page any more. This is done in a separate mini-transaction, hence this operation does not restrict further work to only ibuf bitmap operations, which would result if the latch to the bitmap page were kept. NOTE: The free bits in the insert buffer bitmap must never exceed the free space on a page. It is unsafe to increment the bits in a separately committed mini-transaction, because in crash recovery, the free bits could momentarily be set too high. It is only safe to use this function for decrementing the free bits. Should more free space become available, we must not update the free bits here, because that would break crash recovery. */ UNIV_INLINE void ibuf_update_free_bits_if_full( /*==========================*/ buf_block_t* block, /*!< in: index page to which we have added new records; the free bits are updated if the index is non-clustered and non-unique and the page level is 0, and the page becomes fuller */ ulint max_ins_size,/*!< in: value of maximum insert size with reorganize before the latest operation performed to the page */ ulint increase);/*!< in: upper limit for the additional space used in the latest operation, if known, or ULINT_UNDEFINED */ /**********************************************************************//** Updates the free bits for an uncompressed page to reflect the present state. Does this in the mtr given, which means that the latching order rules virtually prevent any further operations for this OS thread until mtr is committed. NOTE: The free bits in the insert buffer bitmap must never exceed the free space on a page. It is safe to set the free bits in the same mini-transaction that updated the page. */ UNIV_INTERN void ibuf_update_free_bits_low( /*======================*/ const buf_block_t* block, /*!< in: index page */ ulint max_ins_size, /*!< in: value of maximum insert size with reorganize before the latest operation performed to the page */ mtr_t* mtr); /*!< in/out: mtr */ /**********************************************************************//** Updates the free bits for a compressed page to reflect the present state. Does this in the mtr given, which means that the latching order rules virtually prevent any further operations for this OS thread until mtr is committed. NOTE: The free bits in the insert buffer bitmap must never exceed the free space on a page. It is safe to set the free bits in the same mini-transaction that updated the page. */ UNIV_INTERN void ibuf_update_free_bits_zip( /*======================*/ buf_block_t* block, /*!< in/out: index page */ mtr_t* mtr); /*!< in/out: mtr */ /**********************************************************************//** Updates the free bits for the two pages to reflect the present state. Does this in the mtr given, which means that the latching order rules virtually prevent any further operations until mtr is committed. NOTE: The free bits in the insert buffer bitmap must never exceed the free space on a page. It is safe to set the free bits in the same mini-transaction that updated the pages. */ UNIV_INTERN void ibuf_update_free_bits_for_two_pages_low( /*====================================*/ ulint zip_size,/*!< in: compressed page size in bytes; 0 for uncompressed pages */ buf_block_t* block1, /*!< in: index page */ buf_block_t* block2, /*!< in: index page */ mtr_t* mtr); /*!< in: mtr */ /**********************************************************************//** A basic partial test if an insert to the insert buffer could be possible and recommended. */ UNIV_INLINE ibool ibuf_should_try( /*============*/ dict_index_t* index, /*!< in: index where to insert */ ulint ignore_sec_unique); /*!< in: if != 0, we should ignore UNIQUE constraint on a secondary index when we decide */ /******************************************************************//** Returns TRUE if the current OS thread is performing an insert buffer routine. For instance, a read-ahead of non-ibuf pages is forbidden by threads that are executing an insert buffer routine. @return TRUE if inside an insert buffer routine */ UNIV_INTERN ibool ibuf_inside(void); /*=============*/ /***********************************************************************//** Checks if a page address is an ibuf bitmap page (level 3 page) address. @return TRUE if a bitmap page */ UNIV_INLINE ibool ibuf_bitmap_page( /*=============*/ ulint zip_size,/*!< in: compressed page size in bytes; 0 for uncompressed pages */ ulint page_no);/*!< in: page number */ /***********************************************************************//** Checks if a page is a level 2 or 3 page in the ibuf hierarchy of pages. Must not be called when recv_no_ibuf_operations==TRUE. @return TRUE if level 2 or level 3 page */ UNIV_INTERN ibool ibuf_page( /*======*/ ulint space, /*!< in: space id */ ulint zip_size,/*!< in: compressed page size in bytes, or 0 */ ulint page_no,/*!< in: page number */ mtr_t* mtr); /*!< in: mtr which will contain an x-latch to the bitmap page if the page is not one of the fixed address ibuf pages, or NULL, in which case a new transaction is created. */ /***********************************************************************//** Frees excess pages from the ibuf free list. This function is called when an OS thread calls fsp services to allocate a new file segment, or a new page to a file segment, and the thread did not own the fsp latch before this call. */ UNIV_INTERN void ibuf_free_excess_pages(void); /*========================*/ /*********************************************************************//** Makes an index insert to the insert buffer, instead of directly to the disk page, if this is possible. Does not do insert if the index is clustered or unique. @return TRUE if success */ UNIV_INTERN ibool ibuf_insert( /*========*/ const dtuple_t* entry, /*!< in: index entry to insert */ dict_index_t* index, /*!< in: index where to insert */ ulint space, /*!< in: space id where to insert */ ulint zip_size,/*!< in: compressed page size in bytes, or 0 */ ulint page_no,/*!< in: page number where to insert */ que_thr_t* thr); /*!< in: query thread */ /*********************************************************************//** When an index page is read from a disk to the buffer pool, this function inserts to the page the possible index entries buffered in the insert buffer. The entries are deleted from the insert buffer. If the page is not read, but created in the buffer pool, this function deletes its buffered entries from the insert buffer; there can exist entries for such a page if the page belonged to an index which subsequently was dropped. */ UNIV_INTERN void ibuf_merge_or_delete_for_page( /*==========================*/ buf_block_t* block, /*!< in: if page has been read from disk, pointer to the page x-latched, else NULL */ ulint space, /*!< in: space id of the index page */ ulint page_no,/*!< in: page number of the index page */ ulint zip_size,/*!< in: compressed page size in bytes, or 0 */ ibool update_ibuf_bitmap);/*!< in: normally this is set to TRUE, but if we have deleted or are deleting the tablespace, then we naturally do not want to update a non-existent bitmap page */ /*********************************************************************//** Deletes all entries in the insert buffer for a given space id. This is used in DISCARD TABLESPACE and IMPORT TABLESPACE. NOTE: this does not update the page free bitmaps in the space. The space will become CORRUPT when you call this function! */ UNIV_INTERN void ibuf_delete_for_discarded_space( /*============================*/ ulint space); /*!< in: space id */ /*********************************************************************//** Contracts insert buffer trees by reading pages to the buffer pool. @return a lower limit for the combined size in bytes of entries which will be merged from ibuf trees to the pages read, 0 if ibuf is empty */ UNIV_INTERN ulint ibuf_contract( /*==========*/ ibool sync); /*!< in: TRUE if the caller wants to wait for the issued read with the highest tablespace address to complete */ /*********************************************************************//** Contracts insert buffer trees by reading pages to the buffer pool. @return a lower limit for the combined size in bytes of entries which will be merged from ibuf trees to the pages read, 0 if ibuf is empty */ UNIV_INTERN ulint ibuf_contract_for_n_pages( /*======================*/ ibool sync, /*!< in: TRUE if the caller wants to wait for the issued read with the highest tablespace address to complete */ ulint n_pages);/*!< in: try to read at least this many pages to the buffer pool and merge the ibuf contents to them */ #endif /* !UNIV_HOTBACKUP */ /*********************************************************************//** Parses a redo log record of an ibuf bitmap page init. @return end of log record or NULL */ UNIV_INTERN byte* ibuf_parse_bitmap_init( /*===================*/ byte* ptr, /*!< in: buffer */ byte* end_ptr,/*!< in: buffer end */ buf_block_t* block, /*!< in: block or NULL */ mtr_t* mtr); /*!< in: mtr or NULL */ #ifndef UNIV_HOTBACKUP #ifdef UNIV_IBUF_COUNT_DEBUG /******************************************************************//** Gets the ibuf count for a given page. @return number of entries in the insert buffer currently buffered for this page */ UNIV_INTERN ulint ibuf_count_get( /*===========*/ ulint space, /*!< in: space id */ ulint page_no);/*!< in: page number */ #endif /******************************************************************//** Looks if the insert buffer is empty. @return TRUE if empty */ UNIV_INTERN ibool ibuf_is_empty(void); /*===============*/ /******************************************************************//** Prints info of ibuf. */ UNIV_INTERN void ibuf_print( /*=======*/ ib_stream_t ib_stram); /*!< in: stream where to print */ /********************************************************************** Reset the variables. */ UNIV_INTERN void ibuf_var_init(void); /*===============*/ /********************************************************************** Closes insert buffer and frees the data structures. */ UNIV_INTERN void ibuf_close(void); /*============*/ #define IBUF_HEADER_PAGE_NO FSP_IBUF_HEADER_PAGE_NO #define IBUF_TREE_ROOT_PAGE_NO FSP_IBUF_TREE_ROOT_PAGE_NO #endif /* !UNIV_HOTBACKUP */ /* The ibuf header page currently contains only the file segment header for the file segment from which the pages for the ibuf tree are allocated */ #define IBUF_HEADER PAGE_DATA #define IBUF_TREE_SEG_HEADER 0 /* fseg header for ibuf tree */ /* The insert buffer tree itself is always located in space 0. */ #define IBUF_SPACE_ID 0 #ifndef UNIV_NONINL #include "ibuf0ibuf.ic" #endif #endif haildb-2.3.2/include/page0zip.ic0000644000175000017500000003110311513177357017320 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 2005, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/page0zip.ic Compressed page interface Created June 2005 by Marko Makela *******************************************************/ #ifdef UNIV_MATERIALIZE # undef UNIV_INLINE # define UNIV_INLINE #endif #include "page0zip.h" #include "page0page.h" /* The format of compressed pages is as follows. The header and trailer of the uncompressed pages, excluding the page directory in the trailer, are copied as is to the header and trailer of the compressed page. At the end of the compressed page, there is a dense page directory pointing to every user record contained on the page, including deleted records on the free list. The dense directory is indexed in the collation order, i.e., in the order in which the record list is linked on the uncompressed page. The infimum and supremum records are excluded. The two most significant bits of the entries are allocated for the delete-mark and an n_owned flag indicating the last record in a chain of records pointed to from the sparse page directory on the uncompressed page. The data between PAGE_ZIP_START and the last page directory entry will be written in compressed format, starting at offset PAGE_DATA. Infimum and supremum records are not stored. We exclude the REC_N_NEW_EXTRA_BYTES in every record header. These can be recovered from the dense page directory stored at the end of the compressed page. The fields node_ptr (in non-leaf B-tree nodes; level>0), trx_id and roll_ptr (in leaf B-tree nodes; level=0), and BLOB pointers of externally stored columns are stored separately, in ascending order of heap_no and column index, starting backwards from the dense page directory. The compressed data stream may be followed by a modification log covering the compressed portion of the page, as follows. MODIFICATION LOG ENTRY FORMAT - write record: - (heap_no - 1) << 1 (1..2 bytes) - extra bytes backwards - data bytes - clear record: - (heap_no - 1) << 1 | 1 (1..2 bytes) The integer values are stored in a variable-length format: - 0xxxxxxx: 0..127 - 1xxxxxxx xxxxxxxx: 0..32767 The end of the modification log is marked by a 0 byte. In summary, the compressed page looks like this: (1) Uncompressed page header (PAGE_DATA bytes) (2) Compressed index information (3) Compressed page data (4) Page modification log (page_zip->m_start..page_zip->m_end) (5) Empty zero-filled space (6) BLOB pointers (on leaf pages) - BTR_EXTERN_FIELD_REF_SIZE for each externally stored column - in descending collation order (7) Uncompressed columns of user records, n_dense * uncompressed_size bytes, - indexed by heap_no - DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN for leaf pages of clustered indexes - REC_NODE_PTR_SIZE for non-leaf pages - 0 otherwise (8) dense page directory, stored backwards - n_dense = n_heap - 2 - existing records in ascending collation order - deleted records (free list) in link order */ /** Start offset of the area that will be compressed */ #define PAGE_ZIP_START PAGE_NEW_SUPREMUM_END /** Size of an compressed page directory entry */ #define PAGE_ZIP_DIR_SLOT_SIZE 2 /** Mask of record offsets */ #define PAGE_ZIP_DIR_SLOT_MASK 0x3fff /** 'owned' flag */ #define PAGE_ZIP_DIR_SLOT_OWNED 0x4000 /** 'deleted' flag */ #define PAGE_ZIP_DIR_SLOT_DEL 0x8000 /**********************************************************************//** Determine the size of a compressed page in bytes. @return size in bytes */ UNIV_INLINE ulint page_zip_get_size( /*==============*/ const page_zip_des_t* page_zip) /*!< in: compressed page */ { ulint size; if (UNIV_UNLIKELY(!page_zip->ssize)) { return(0); } size = (PAGE_ZIP_MIN_SIZE >> 1) << page_zip->ssize; ut_ad(size >= PAGE_ZIP_MIN_SIZE); ut_ad(size <= UNIV_PAGE_SIZE); return(size); } /**********************************************************************//** Set the size of a compressed page in bytes. */ UNIV_INLINE void page_zip_set_size( /*==============*/ page_zip_des_t* page_zip, /*!< in/out: compressed page */ ulint size) /*!< in: size in bytes */ { if (size) { int ssize; ut_ad(ut_is_2pow(size)); for (ssize = 1; size > (ulint) (512 << ssize); ssize++) { } page_zip->ssize = ssize; } else { page_zip->ssize = 0; } ut_ad(page_zip_get_size(page_zip) == size); } #ifndef UNIV_HOTBACKUP /**********************************************************************//** Determine if a record is so big that it needs to be stored externally. @return FALSE if the entire record can be stored locally on the page */ UNIV_INLINE ibool page_zip_rec_needs_ext( /*===================*/ ulint rec_size, /*!< in: length of the record in bytes */ ulint comp, /*!< in: nonzero=compact format */ ulint n_fields, /*!< in: number of fields in the record; ignored if zip_size == 0 */ ulint zip_size) /*!< in: compressed page size in bytes, or 0 */ { ut_ad(rec_size > comp ? REC_N_NEW_EXTRA_BYTES : REC_N_OLD_EXTRA_BYTES); ut_ad(ut_is_2pow(zip_size)); ut_ad(comp || !zip_size); #ifdef WITH_ZIP #if UNIV_PAGE_SIZE > REC_MAX_DATA_SIZE if (UNIV_UNLIKELY(rec_size >= REC_MAX_DATA_SIZE)) { return(TRUE); } #endif if (UNIV_UNLIKELY(zip_size)) { ut_ad(comp); /* On a compressed page, there is a two-byte entry in the dense page directory for every record. But there is no record header. There should be enough room for one record on an empty leaf page. Subtract 1 byte for the encoded heap number. Check also the available space on the uncompressed page. */ return(rec_size - (REC_N_NEW_EXTRA_BYTES - 2) >= (page_zip_empty_size(n_fields, zip_size) - 1) || rec_size >= page_get_free_space_of_empty(TRUE) / 2); } #endif /* WITH_ZIP */ return(rec_size >= page_get_free_space_of_empty(comp) / 2); } #endif /* !UNIV_HOTBACKUP */ #ifdef UNIV_DEBUG /**********************************************************************//** Validate a compressed page descriptor. @return TRUE if ok */ UNIV_INLINE ibool page_zip_simple_validate( /*=====================*/ const page_zip_des_t* page_zip)/*!< in: compressed page descriptor */ { ut_ad(page_zip); ut_ad(page_zip->data); ut_ad(page_zip->ssize < PAGE_ZIP_NUM_SSIZE); ut_ad(page_zip_get_size(page_zip) > PAGE_DATA + PAGE_ZIP_DIR_SLOT_SIZE); ut_ad(page_zip->m_start <= page_zip->m_end); ut_ad(page_zip->m_end < page_zip_get_size(page_zip)); ut_ad(page_zip->n_blobs < page_zip_get_size(page_zip) / BTR_EXTERN_FIELD_REF_SIZE); return(TRUE); } #endif /* UNIV_DEBUG */ /**********************************************************************//** Determine if the length of the page trailer. @return length of the page trailer, in bytes, not including the terminating zero byte of the modification log */ UNIV_INLINE ibool page_zip_get_trailer_len( /*=====================*/ const page_zip_des_t* page_zip,/*!< in: compressed page */ ibool is_clust,/*!< in: TRUE if clustered index */ ulint* entry_size)/*!< out: size of the uncompressed portion of a user record */ { ulint uncompressed_size; ut_ad(page_zip_simple_validate(page_zip)); UNIV_MEM_ASSERT_RW(page_zip->data, page_zip_get_size(page_zip)); if (UNIV_UNLIKELY(!page_is_leaf(page_zip->data))) { uncompressed_size = PAGE_ZIP_DIR_SLOT_SIZE + REC_NODE_PTR_SIZE; ut_ad(!page_zip->n_blobs); } else if (UNIV_UNLIKELY(is_clust)) { uncompressed_size = PAGE_ZIP_DIR_SLOT_SIZE + DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN; } else { uncompressed_size = PAGE_ZIP_DIR_SLOT_SIZE; ut_ad(!page_zip->n_blobs); } if (entry_size) { *entry_size = uncompressed_size; } return((page_dir_get_n_heap(page_zip->data) - 2) * uncompressed_size + page_zip->n_blobs * BTR_EXTERN_FIELD_REF_SIZE); } /**********************************************************************//** Determine how big record can be inserted without recompressing the page. @return a positive number indicating the maximum size of a record whose insertion is guaranteed to succeed, or zero or negative */ UNIV_INLINE lint page_zip_max_ins_size( /*==================*/ const page_zip_des_t* page_zip,/*!< in: compressed page */ ibool is_clust)/*!< in: TRUE if clustered index */ { ulint uncompressed_size; ulint trailer_len; trailer_len = page_zip_get_trailer_len(page_zip, is_clust, &uncompressed_size); /* When a record is created, a pointer may be added to the dense directory. Likewise, space for the columns that will not be compressed will be allocated from the page trailer. Also the BLOB pointers will be allocated from there, but we may as well count them in the length of the record. */ trailer_len += uncompressed_size; return((lint) page_zip_get_size(page_zip) - trailer_len - page_zip->m_end - (REC_N_NEW_EXTRA_BYTES - 2)); } /**********************************************************************//** Determine if enough space is available in the modification log. @return TRUE if enough space is available */ UNIV_INLINE ibool page_zip_available( /*===============*/ const page_zip_des_t* page_zip,/*!< in: compressed page */ ibool is_clust,/*!< in: TRUE if clustered index */ ulint length, /*!< in: combined size of the record */ ulint create) /*!< in: nonzero=add the record to the heap */ { ulint uncompressed_size; ulint trailer_len; ut_ad(length > REC_N_NEW_EXTRA_BYTES); trailer_len = page_zip_get_trailer_len(page_zip, is_clust, &uncompressed_size); /* Subtract the fixed extra bytes and add the maximum space needed for identifying the record (encoded heap_no). */ length -= REC_N_NEW_EXTRA_BYTES - 2; if (UNIV_UNLIKELY(create)) { /* When a record is created, a pointer may be added to the dense directory. Likewise, space for the columns that will not be compressed will be allocated from the page trailer. Also the BLOB pointers will be allocated from there, but we may as well count them in the length of the record. */ trailer_len += uncompressed_size; } return(UNIV_LIKELY(length + trailer_len + page_zip->m_end < page_zip_get_size(page_zip))); } /**********************************************************************//** Initialize a compressed page descriptor. */ UNIV_INLINE void page_zip_des_init( /*==============*/ page_zip_des_t* page_zip) /*!< in/out: compressed page descriptor */ { memset(page_zip, 0, sizeof *page_zip); } /**********************************************************************//** Write a log record of writing to the uncompressed header portion of a page. */ UNIV_INTERN void page_zip_write_header_log( /*======================*/ const byte* data,/*!< in: data on the uncompressed page */ ulint length, /*!< in: length of the data */ mtr_t* mtr); /*!< in: mini-transaction */ /**********************************************************************//** Write data to the uncompressed header portion of a page. The data must already have been written to the uncompressed page. However, the data portion of the uncompressed page may differ from the compressed page when a record is being inserted in page_cur_insert_rec_zip(). */ UNIV_INLINE void page_zip_write_header( /*==================*/ page_zip_des_t* page_zip,/*!< in/out: compressed page */ const byte* str, /*!< in: address on the uncompressed page */ ulint length, /*!< in: length of the data */ mtr_t* mtr) /*!< in: mini-transaction, or NULL */ { ulint pos; ut_ad(PAGE_ZIP_MATCH(str, page_zip)); ut_ad(page_zip_simple_validate(page_zip)); UNIV_MEM_ASSERT_RW(page_zip->data, page_zip_get_size(page_zip)); pos = page_offset(str); ut_ad(pos < PAGE_DATA); memcpy(page_zip->data + pos, str, length); /* The following would fail in page_cur_insert_rec_zip(). */ /* ut_ad(page_zip_validate(page_zip, str - pos)); */ if (UNIV_LIKELY_NULL(mtr)) { #ifndef UNIV_HOTBACKUP page_zip_write_header_log(str, length, mtr); #endif /* !UNIV_HOTBACKUP */ } } #ifdef UNIV_MATERIALIZE # undef UNIV_INLINE # define UNIV_INLINE UNIV_INLINE_ORIGINAL #endif haildb-2.3.2/include/data0data.ic0000644000175000017500000003524611513177357017440 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1994, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /********************************************************************//** @file include/data0data.ic SQL data field and tuple Created 5/30/1994 Heikki Tuuri *************************************************************************/ #include "mem0mem.h" #include "ut0rnd.h" #ifdef UNIV_DEBUG /** Dummy variable to catch access to uninitialized fields. In the debug version, dtuple_create() will make all fields of dtuple_t point to data_error. */ extern byte data_error; /*********************************************************************//** Gets pointer to the type struct of SQL data field. @return pointer to the type struct */ UNIV_INLINE dtype_t* dfield_get_type( /*============*/ const dfield_t* field) /*!< in: SQL data field */ { ut_ad(field); return((dtype_t*) &(field->type)); } #endif /* UNIV_DEBUG */ /*********************************************************************//** Sets the type struct of SQL data field. */ UNIV_INLINE void dfield_set_type( /*============*/ dfield_t* field, /*!< in: SQL data field */ dtype_t* type) /*!< in: pointer to data type struct */ { ut_ad(field && type); field->type = *type; } #ifdef UNIV_DEBUG /*********************************************************************//** Gets pointer to the data in a field. @return pointer to data */ UNIV_INLINE void* dfield_get_data( /*============*/ const dfield_t* field) /*!< in: field */ { ut_ad(field); ut_ad((field->len == UNIV_SQL_NULL) || (field->data != &data_error)); return((void*) field->data); } #endif /* UNIV_DEBUG */ /*********************************************************************//** Gets length of field data. @return length of data; UNIV_SQL_NULL if SQL null data */ UNIV_INLINE ulint dfield_get_len( /*===========*/ const dfield_t* field) /*!< in: field */ { ut_ad(field); ut_ad((field->len == UNIV_SQL_NULL) || (field->data != &data_error)); return(field->len); } /*********************************************************************//** Sets length in a field. */ UNIV_INLINE void dfield_set_len( /*===========*/ dfield_t* field, /*!< in: field */ ulint len) /*!< in: length or UNIV_SQL_NULL */ { ut_ad(field); #ifdef UNIV_VALGRIND_DEBUG if (len != UNIV_SQL_NULL) UNIV_MEM_ASSERT_RW(field->data, len); #endif /* UNIV_VALGRIND_DEBUG */ field->ext = 0; field->len = len; } /*********************************************************************//** Determines if a field is SQL NULL @return nonzero if SQL null data */ UNIV_INLINE ulint dfield_is_null( /*===========*/ const dfield_t* field) /*!< in: field */ { ut_ad(field); return(field->len == UNIV_SQL_NULL); } /*********************************************************************//** Determines if a field is externally stored @return nonzero if externally stored */ UNIV_INLINE ulint dfield_is_ext( /*==========*/ const dfield_t* field) /*!< in: field */ { ut_ad(field); return(UNIV_UNLIKELY(field->ext)); } /*********************************************************************//** Sets the "external storage" flag */ UNIV_INLINE void dfield_set_ext( /*===========*/ dfield_t* field) /*!< in/out: field */ { ut_ad(field); field->ext = 1; } /*********************************************************************//** Sets pointer to the data and length in a field. */ UNIV_INLINE void dfield_set_data( /*============*/ dfield_t* field, /*!< in: field */ const void* data, /*!< in: data */ ulint len) /*!< in: length or UNIV_SQL_NULL */ { ut_ad(field); #ifdef UNIV_VALGRIND_DEBUG if (len != UNIV_SQL_NULL) UNIV_MEM_ASSERT_RW(data, len); #endif /* UNIV_VALGRIND_DEBUG */ field->data = (void*) data; field->ext = 0; field->len = len; } /*********************************************************************//** Sets a data field to SQL NULL. */ UNIV_INLINE void dfield_set_null( /*============*/ dfield_t* field) /*!< in/out: field */ { dfield_set_data(field, NULL, UNIV_SQL_NULL); } /*********************************************************************//** Copies the data and len fields. */ UNIV_INLINE void dfield_copy_data( /*=============*/ dfield_t* field1, /*!< out: field to copy to */ const dfield_t* field2) /*!< in: field to copy from */ { ut_ad(field1 && field2); field1->data = field2->data; field1->len = field2->len; field1->ext = field2->ext; } /*********************************************************************//** Copies a data field to another. */ UNIV_INLINE void dfield_copy( /*========*/ dfield_t* field1, /*!< out: field to copy to */ const dfield_t* field2) /*!< in: field to copy from */ { *field1 = *field2; } /*********************************************************************//** Copies the data pointed to by a data field. */ UNIV_INLINE void dfield_dup( /*=======*/ dfield_t* field, /*!< in/out: data field */ mem_heap_t* heap) /*!< in: memory heap where allocated */ { if (!dfield_is_null(field)) { UNIV_MEM_ASSERT_RW(field->data, field->len); field->data = mem_heap_dup(heap, field->data, field->len); } } /*********************************************************************//** Tests if data length and content is equal for two dfields. @return TRUE if equal */ UNIV_INLINE ibool dfield_datas_are_binary_equal( /*==========================*/ const dfield_t* field1, /*!< in: field */ const dfield_t* field2) /*!< in: field */ { ulint len; len = field1->len; return(len == field2->len && (len == UNIV_SQL_NULL || !memcmp(field1->data, field2->data, len))); } /*********************************************************************//** Gets info bits in a data tuple. @return info bits */ UNIV_INLINE ulint dtuple_get_info_bits( /*=================*/ const dtuple_t* tuple) /*!< in: tuple */ { ut_ad(tuple); return(tuple->info_bits); } /*********************************************************************//** Sets info bits in a data tuple. */ UNIV_INLINE void dtuple_set_info_bits( /*=================*/ dtuple_t* tuple, /*!< in: tuple */ ulint info_bits) /*!< in: info bits */ { ut_ad(tuple); tuple->info_bits = info_bits; } /*********************************************************************//** Gets number of fields used in record comparisons. @return number of fields used in comparisons in rem0cmp.* */ UNIV_INLINE ulint dtuple_get_n_fields_cmp( /*====================*/ const dtuple_t* tuple) /*!< in: tuple */ { ut_ad(tuple); return(tuple->n_fields_cmp); } /*********************************************************************//** Sets number of fields used in record comparisons. */ UNIV_INLINE void dtuple_set_n_fields_cmp( /*====================*/ dtuple_t* tuple, /*!< in: tuple */ ulint n_fields_cmp) /*!< in: number of fields used in comparisons in rem0cmp.* */ { ut_ad(tuple); ut_ad(n_fields_cmp <= tuple->n_fields); tuple->n_fields_cmp = n_fields_cmp; } /*********************************************************************//** Gets number of fields in a data tuple. @return number of fields */ UNIV_INLINE ulint dtuple_get_n_fields( /*================*/ const dtuple_t* tuple) /*!< in: tuple */ { ut_ad(tuple); return(tuple->n_fields); } #ifdef UNIV_DEBUG /*********************************************************************//** Gets nth field of a tuple. @return nth field */ UNIV_INLINE dfield_t* dtuple_get_nth_field( /*=================*/ const dtuple_t* tuple, /*!< in: tuple */ ulint n) /*!< in: index of field */ { ut_ad(tuple); ut_ad(n < tuple->n_fields); return((dfield_t*) tuple->fields + n); } #endif /* UNIV_DEBUG */ /**********************************************************//** Creates a data tuple to a memory heap. The default value for number of fields used in record comparisons for this tuple is n_fields. @return own: created tuple */ UNIV_INLINE dtuple_t* dtuple_create( /*==========*/ mem_heap_t* heap, /*!< in: memory heap where the tuple is created */ ulint n_fields) /*!< in: number of fields */ { dtuple_t* tuple; ut_ad(heap); tuple = (dtuple_t*) mem_heap_alloc(heap, sizeof(dtuple_t) + n_fields * sizeof(dfield_t)); tuple->info_bits = 0; tuple->n_fields = n_fields; tuple->n_fields_cmp = n_fields; tuple->fields = (dfield_t*) &tuple[1]; #ifdef UNIV_DEBUG tuple->magic_n = DATA_TUPLE_MAGIC_N; { /* In the debug version, initialize fields to an error value */ ulint i; for (i = 0; i < n_fields; i++) { dfield_t* field; field = dtuple_get_nth_field(tuple, i); dfield_set_len(field, UNIV_SQL_NULL); field->data = &data_error; dfield_get_type(field)->mtype = DATA_ERROR; } } UNIV_MEM_INVALID(tuple->fields, n_fields * sizeof *tuple->fields); #endif return(tuple); } /**********************************************************//** Wrap data fields in a tuple. The default value for number of fields used in record comparisons for this tuple is n_fields. @return data tuple */ UNIV_INLINE const dtuple_t* dtuple_from_fields( /*===============*/ dtuple_t* tuple, /*!< in: storage for data tuple */ const dfield_t* fields, /*!< in: fields */ ulint n_fields) /*!< in: number of fields */ { tuple->info_bits = 0; tuple->n_fields = tuple->n_fields_cmp = n_fields; tuple->fields = (dfield_t*) fields; ut_d(tuple->magic_n = DATA_TUPLE_MAGIC_N); return(tuple); } /*********************************************************************//** Copies a data tuple to another. This is a shallow copy; if a deep copy is desired, dfield_dup() will have to be invoked on each field. @return own: copy of tuple */ UNIV_INLINE dtuple_t* dtuple_copy( /*========*/ const dtuple_t* tuple, /*!< in: tuple to copy from */ mem_heap_t* heap) /*!< in: memory heap where the tuple is created */ { ulint n_fields = dtuple_get_n_fields(tuple); dtuple_t* new_tuple = dtuple_create(heap, n_fields); ulint i; for (i = 0; i < n_fields; i++) { dfield_copy(dtuple_get_nth_field(new_tuple, i), dtuple_get_nth_field(tuple, i)); } return(new_tuple); } /**********************************************************//** The following function returns the sum of data lengths of a tuple. The space occupied by the field structs or the tuple struct is not counted. Neither is possible space in externally stored parts of the field. @return sum of data lengths */ UNIV_INLINE ulint dtuple_get_data_size( /*=================*/ const dtuple_t* tuple, /*!< in: typed data tuple */ ulint comp) /*!< in: nonzero=ROW_FORMAT=COMPACT */ { const dfield_t* field; ulint n_fields; ulint len; ulint i; ulint sum = 0; ut_ad(tuple); ut_ad(dtuple_check_typed(tuple)); ut_ad(tuple->magic_n == DATA_TUPLE_MAGIC_N); n_fields = tuple->n_fields; for (i = 0; i < n_fields; i++) { field = dtuple_get_nth_field(tuple, i); len = dfield_get_len(field); if (len == UNIV_SQL_NULL) { len = dtype_get_sql_null_size(dfield_get_type(field), comp); } sum += len; } return(sum); } /*********************************************************************//** Computes the number of externally stored fields in a data tuple. @return number of externally stored fields */ UNIV_INLINE ulint dtuple_get_n_ext( /*=============*/ const dtuple_t* tuple) /*!< in: tuple */ { ulint n_ext = 0; ulint n_fields = tuple->n_fields; ulint i; ut_ad(tuple); ut_ad(dtuple_check_typed(tuple)); ut_ad(tuple->magic_n == DATA_TUPLE_MAGIC_N); for (i = 0; i < n_fields; i++) { n_ext += dtuple_get_nth_field(tuple, i)->ext; } return(n_ext); } /*******************************************************************//** Sets types of fields binary in a tuple. */ UNIV_INLINE void dtuple_set_types_binary( /*====================*/ dtuple_t* tuple, /*!< in: data tuple */ ulint n) /*!< in: number of fields to set */ { dtype_t* dfield_type; ulint i; for (i = 0; i < n; i++) { dfield_type = dfield_get_type(dtuple_get_nth_field(tuple, i)); dtype_set(dfield_type, DATA_BINARY, 0, 0); } } /************************************************************//** Folds a prefix given as the number of fields of a tuple. @return the folded value */ UNIV_INLINE ulint dtuple_fold( /*========*/ const dtuple_t* tuple, /*!< in: the tuple */ ulint n_fields,/*!< in: number of complete fields to fold */ ulint n_bytes,/*!< in: number of bytes to fold in an incomplete last field */ dulint tree_id)/*!< in: index tree id */ { const dfield_t* field; ulint i; const byte* data; ulint len; ulint fold; ut_ad(tuple); ut_ad(tuple->magic_n == DATA_TUPLE_MAGIC_N); ut_ad(dtuple_check_typed(tuple)); fold = ut_fold_dulint(tree_id); for (i = 0; i < n_fields; i++) { field = dtuple_get_nth_field(tuple, i); data = (const byte*) dfield_get_data(field); len = dfield_get_len(field); if (len != UNIV_SQL_NULL) { fold = ut_fold_ulint_pair(fold, ut_fold_binary(data, len)); } } if (n_bytes > 0) { field = dtuple_get_nth_field(tuple, i); data = (const byte*) dfield_get_data(field); len = dfield_get_len(field); if (len != UNIV_SQL_NULL) { if (len > n_bytes) { len = n_bytes; } fold = ut_fold_ulint_pair(fold, ut_fold_binary(data, len)); } } return(fold); } /**********************************************************************//** Writes an SQL null field full of zeros. */ UNIV_INLINE void data_write_sql_null( /*================*/ byte* data, /*!< in: pointer to a buffer of size len */ ulint len) /*!< in: SQL null size in bytes */ { memset(data, 0, len); } /**********************************************************************//** Checks if a dtuple contains an SQL null value. @return TRUE if some field is SQL null */ UNIV_INLINE ibool dtuple_contains_null( /*=================*/ const dtuple_t* tuple) /*!< in: dtuple */ { ulint n; ulint i; n = dtuple_get_n_fields(tuple); for (i = 0; i < n; i++) { if (dfield_is_null(dtuple_get_nth_field(tuple, i))) { return(TRUE); } } return(FALSE); } /**************************************************************//** Frees the memory in a big rec vector. */ UNIV_INLINE void dtuple_big_rec_free( /*================*/ big_rec_t* vector) /*!< in, own: big rec vector; it is freed in this function */ { mem_heap_free(vector->heap); } haildb-2.3.2/include/pars0opt.ic0000644000175000017500000000202211513177357017347 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1997, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/pars0opt.ic Simple SQL optimizer Created 12/21/1997 Heikki Tuuri *******************************************************/ haildb-2.3.2/include/ha0ha.ic0000644000175000017500000001332111513177357016564 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1994, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /********************************************************************//** @file include/ha0ha.ic The hash table with external chains Created 8/18/1994 Heikki Tuuri *************************************************************************/ #include "ut0rnd.h" #include "mem0mem.h" /***********************************************************//** Deletes a hash node. */ UNIV_INTERN void ha_delete_hash_node( /*================*/ hash_table_t* table, /*!< in: hash table */ ha_node_t* del_node); /*!< in: node to be deleted */ /******************************************************************//** Gets a hash node data. @return pointer to the data */ UNIV_INLINE void* ha_node_get_data( /*=============*/ ha_node_t* node) /*!< in: hash chain node */ { return(node->data); } /******************************************************************//** Sets hash node data. */ UNIV_INLINE void ha_node_set_data_func( /*==================*/ ha_node_t* node, /*!< in: hash chain node */ #if defined UNIV_AHI_DEBUG || defined UNIV_DEBUG buf_block_t* block, /*!< in: buffer block containing the data */ #endif /* UNIV_AHI_DEBUG || UNIV_DEBUG */ void* data) /*!< in: pointer to the data */ { #if defined UNIV_AHI_DEBUG || defined UNIV_DEBUG node->block = block; #endif /* UNIV_AHI_DEBUG || UNIV_DEBUG */ node->data = data; } #if defined UNIV_AHI_DEBUG || defined UNIV_DEBUG /** Sets hash node data. @param n in: hash chain node @param b in: buffer block containing the data @param d in: pointer to the data */ # define ha_node_set_data(n,b,d) ha_node_set_data_func(n,b,d) #else /* UNIV_AHI_DEBUG || UNIV_DEBUG */ /** Sets hash node data. @param n in: hash chain node @param b in: buffer block containing the data @param d in: pointer to the data */ # define ha_node_set_data(n,b,d) ha_node_set_data_func(n,d) #endif /* UNIV_AHI_DEBUG || UNIV_DEBUG */ /******************************************************************//** Gets the next node in a hash chain. @return next node, NULL if none */ UNIV_INLINE ha_node_t* ha_chain_get_next( /*==============*/ ha_node_t* node) /*!< in: hash chain node */ { return(node->next); } /******************************************************************//** Gets the first node in a hash chain. @return first node, NULL if none */ UNIV_INLINE ha_node_t* ha_chain_get_first( /*===============*/ hash_table_t* table, /*!< in: hash table */ ulint fold) /*!< in: fold value determining the chain */ { return((ha_node_t*) hash_get_nth_cell(table, hash_calc_hash(fold, table))->node); } /*************************************************************//** Looks for an element in a hash table. @return pointer to the first hash table node in chain having the fold number, NULL if not found */ UNIV_INLINE ha_node_t* ha_search( /*======*/ hash_table_t* table, /*!< in: hash table */ ulint fold) /*!< in: folded value of the searched data */ { ha_node_t* node; ASSERT_HASH_MUTEX_OWN(table, fold); node = ha_chain_get_first(table, fold); while (node) { if (node->fold == fold) { return(node); } node = ha_chain_get_next(node); } return(NULL); } /*************************************************************//** Looks for an element in a hash table. @return pointer to the data of the first hash table node in chain having the fold number, NULL if not found */ UNIV_INLINE void* ha_search_and_get_data( /*===================*/ hash_table_t* table, /*!< in: hash table */ ulint fold) /*!< in: folded value of the searched data */ { ha_node_t* node; ASSERT_HASH_MUTEX_OWN(table, fold); node = ha_chain_get_first(table, fold); while (node) { if (node->fold == fold) { return(node->data); } node = ha_chain_get_next(node); } return(NULL); } /*********************************************************//** Looks for an element when we know the pointer to the data. @return pointer to the hash table node, NULL if not found in the table */ UNIV_INLINE ha_node_t* ha_search_with_data( /*================*/ hash_table_t* table, /*!< in: hash table */ ulint fold, /*!< in: folded value of the searched data */ void* data) /*!< in: pointer to the data */ { ha_node_t* node; ASSERT_HASH_MUTEX_OWN(table, fold); node = ha_chain_get_first(table, fold); while (node) { if (node->data == data) { return(node); } node = ha_chain_get_next(node); } return(NULL); } /*********************************************************//** Looks for an element when we know the pointer to the data, and deletes it from the hash table, if found. @return TRUE if found */ UNIV_INLINE ibool ha_search_and_delete_if_found( /*==========================*/ hash_table_t* table, /*!< in: hash table */ ulint fold, /*!< in: folded value of the searched data */ void* data) /*!< in: pointer to the data */ { ha_node_t* node; ASSERT_HASH_MUTEX_OWN(table, fold); node = ha_search_with_data(table, fold, data); if (node) { ha_delete_hash_node(table, node); return(TRUE); } return(FALSE); } haildb-2.3.2/include/page0cur.h0000644000175000017500000003062111513177357017147 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1994, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /********************************************************************//** @file include/page0cur.h The page cursor Created 10/4/1994 Heikki Tuuri *************************************************************************/ #ifndef page0cur_h #define page0cur_h #include "univ.i" #include "buf0types.h" #include "page0page.h" #include "rem0rec.h" #include "data0data.h" #include "mtr0mtr.h" #define PAGE_CUR_ADAPT /* Page cursor search modes; the values must be in this order! */ #define PAGE_CUR_UNSUPP 0 #define PAGE_CUR_G 1 #define PAGE_CUR_GE 2 #define PAGE_CUR_L 3 #define PAGE_CUR_LE 4 /*#define PAGE_CUR_LE_OR_EXTENDS 5*/ /* This is a search mode used in "column LIKE 'abc%' ORDER BY column DESC"; we have to find strings which are <= 'abc' or which extend it */ #ifdef UNIV_SEARCH_DEBUG # define PAGE_CUR_DBG 6 /* As PAGE_CUR_LE, but skips search shortcut */ #endif /* UNIV_SEARCH_DEBUG */ #ifdef UNIV_DEBUG /*********************************************************//** Gets pointer to the page frame where the cursor is positioned. @return page */ UNIV_INLINE page_t* page_cur_get_page( /*==============*/ page_cur_t* cur); /*!< in: page cursor */ /*********************************************************//** Gets pointer to the buffer block where the cursor is positioned. @return page */ UNIV_INLINE buf_block_t* page_cur_get_block( /*===============*/ page_cur_t* cur); /*!< in: page cursor */ /*********************************************************//** Gets pointer to the page frame where the cursor is positioned. @return page */ UNIV_INLINE page_zip_des_t* page_cur_get_page_zip( /*==================*/ page_cur_t* cur); /*!< in: page cursor */ /*********************************************************//** Gets the record where the cursor is positioned. @return record */ UNIV_INLINE rec_t* page_cur_get_rec( /*=============*/ page_cur_t* cur); /*!< in: page cursor */ #else /* UNIV_DEBUG */ # define page_cur_get_page(cur) page_align((cur)->rec) # define page_cur_get_block(cur) (cur)->block # define page_cur_get_page_zip(cur) buf_block_get_page_zip((cur)->block) # define page_cur_get_rec(cur) (cur)->rec #endif /* UNIV_DEBUG */ /*********************************************************//** Sets the cursor object to point before the first user record on the page. */ UNIV_INLINE void page_cur_set_before_first( /*======================*/ const buf_block_t* block, /*!< in: index page */ page_cur_t* cur); /*!< in: cursor */ /*********************************************************//** Sets the cursor object to point after the last user record on the page. */ UNIV_INLINE void page_cur_set_after_last( /*====================*/ const buf_block_t* block, /*!< in: index page */ page_cur_t* cur); /*!< in: cursor */ /*********************************************************//** Returns TRUE if the cursor is before first user record on page. @return TRUE if at start */ UNIV_INLINE ibool page_cur_is_before_first( /*=====================*/ const page_cur_t* cur); /*!< in: cursor */ /*********************************************************//** Returns TRUE if the cursor is after last user record. @return TRUE if at end */ UNIV_INLINE ibool page_cur_is_after_last( /*===================*/ const page_cur_t* cur); /*!< in: cursor */ /**********************************************************//** Positions the cursor on the given record. */ UNIV_INLINE void page_cur_position( /*==============*/ const rec_t* rec, /*!< in: record on a page */ const buf_block_t* block, /*!< in: buffer block containing the record */ page_cur_t* cur); /*!< out: page cursor */ /**********************************************************//** Invalidates a page cursor by setting the record pointer NULL. */ UNIV_INLINE void page_cur_invalidate( /*================*/ page_cur_t* cur); /*!< out: page cursor */ /**********************************************************//** Moves the cursor to the next record on page. */ UNIV_INLINE void page_cur_move_to_next( /*==================*/ page_cur_t* cur); /*!< in/out: cursor; must not be after last */ /**********************************************************//** Moves the cursor to the previous record on page. */ UNIV_INLINE void page_cur_move_to_prev( /*==================*/ page_cur_t* cur); /*!< in/out: cursor; not before first */ #ifndef UNIV_HOTBACKUP /***********************************************************//** Inserts a record next to page cursor. Returns pointer to inserted record if succeed, i.e., enough space available, NULL otherwise. The cursor stays at the same logical position, but the physical position may change if it is pointing to a compressed page that was reorganized. @return pointer to record if succeed, NULL otherwise */ UNIV_INLINE rec_t* page_cur_tuple_insert( /*==================*/ page_cur_t* cursor, /*!< in/out: a page cursor */ const dtuple_t* tuple, /*!< in: pointer to a data tuple */ dict_index_t* index, /*!< in: record descriptor */ ulint n_ext, /*!< in: number of externally stored columns */ mtr_t* mtr); /*!< in: mini-transaction handle, or NULL */ #endif /* !UNIV_HOTBACKUP */ /***********************************************************//** Inserts a record next to page cursor. Returns pointer to inserted record if succeed, i.e., enough space available, NULL otherwise. The cursor stays at the same logical position, but the physical position may change if it is pointing to a compressed page that was reorganized. @return pointer to record if succeed, NULL otherwise */ UNIV_INLINE rec_t* page_cur_rec_insert( /*================*/ page_cur_t* cursor, /*!< in/out: a page cursor */ const rec_t* rec, /*!< in: record to insert */ dict_index_t* index, /*!< in: record descriptor */ ulint* offsets,/*!< in/out: rec_get_offsets(rec, index) */ mtr_t* mtr); /*!< in: mini-transaction handle, or NULL */ /***********************************************************//** Inserts a record next to page cursor on an uncompressed page. Returns pointer to inserted record if succeed, i.e., enough space available, NULL otherwise. The cursor stays at the same position. @return pointer to record if succeed, NULL otherwise */ UNIV_INTERN rec_t* page_cur_insert_rec_low( /*====================*/ rec_t* current_rec,/*!< in: pointer to current record after which the new record is inserted */ dict_index_t* index, /*!< in: record descriptor */ const rec_t* rec, /*!< in: pointer to a physical record */ ulint* offsets,/*!< in/out: rec_get_offsets(rec, index) */ mtr_t* mtr); /*!< in: mini-transaction handle, or NULL */ /***********************************************************//** Inserts a record next to page cursor on a compressed and uncompressed page. Returns pointer to inserted record if succeed, i.e., enough space available, NULL otherwise. The cursor stays at the same position. @return pointer to record if succeed, NULL otherwise */ UNIV_INTERN rec_t* page_cur_insert_rec_zip( /*====================*/ rec_t** current_rec,/*!< in/out: pointer to current record after which the new record is inserted */ buf_block_t* block, /*!< in: buffer block of *current_rec */ dict_index_t* index, /*!< in: record descriptor */ const rec_t* rec, /*!< in: pointer to a physical record */ ulint* offsets,/*!< in/out: rec_get_offsets(rec, index) */ mtr_t* mtr); /*!< in: mini-transaction handle, or NULL */ /*************************************************************//** Copies records from page to a newly created page, from a given record onward, including that record. Infimum and supremum records are not copied. */ UNIV_INTERN void page_copy_rec_list_end_to_created_page( /*===================================*/ page_t* new_page, /*!< in/out: index page to copy to */ rec_t* rec, /*!< in: first record to copy */ dict_index_t* index, /*!< in: record descriptor */ mtr_t* mtr); /*!< in: mtr */ /***********************************************************//** Deletes a record at the page cursor. The cursor is moved to the next record after the deleted one. */ UNIV_INTERN void page_cur_delete_rec( /*================*/ page_cur_t* cursor, /*!< in/out: a page cursor */ dict_index_t* index, /*!< in: record descriptor */ const ulint* offsets,/*!< in: rec_get_offsets(cursor->rec, index) */ mtr_t* mtr); /*!< in: mini-transaction handle */ #ifndef UNIV_HOTBACKUP /****************************************************************//** Searches the right position for a page cursor. @return number of matched fields on the left */ UNIV_INLINE ulint page_cur_search( /*============*/ const buf_block_t* block, /*!< in: buffer block */ const dict_index_t* index, /*!< in: record descriptor */ const dtuple_t* tuple, /*!< in: data tuple */ ulint mode, /*!< in: PAGE_CUR_L, PAGE_CUR_LE, PAGE_CUR_G, or PAGE_CUR_GE */ page_cur_t* cursor);/*!< out: page cursor */ /****************************************************************//** Searches the right position for a page cursor. */ UNIV_INTERN void page_cur_search_with_match( /*=======================*/ const buf_block_t* block, /*!< in: buffer block */ const dict_index_t* index, /*!< in: record descriptor */ const dtuple_t* tuple, /*!< in: data tuple */ ulint mode, /*!< in: PAGE_CUR_L, PAGE_CUR_LE, PAGE_CUR_G, or PAGE_CUR_GE */ ulint* iup_matched_fields, /*!< in/out: already matched fields in upper limit record */ ulint* iup_matched_bytes, /*!< in/out: already matched bytes in a field not yet completely matched */ ulint* ilow_matched_fields, /*!< in/out: already matched fields in lower limit record */ ulint* ilow_matched_bytes, /*!< in/out: already matched bytes in a field not yet completely matched */ page_cur_t* cursor);/*!< out: page cursor */ /***********************************************************//** Positions a page cursor on a randomly chosen user record on a page. If there are no user records, sets the cursor on the infimum record. */ UNIV_INTERN void page_cur_open_on_rnd_user_rec( /*==========================*/ buf_block_t* block, /*!< in: page */ page_cur_t* cursor);/*!< out: page cursor */ #endif /* !UNIV_HOTBACKUP */ /***********************************************************//** Parses a log record of a record insert on a page. @return end of log record or NULL */ UNIV_INTERN byte* page_cur_parse_insert_rec( /*======================*/ ibool is_short,/*!< in: TRUE if short inserts */ byte* ptr, /*!< in: buffer */ byte* end_ptr,/*!< in: buffer end */ buf_block_t* block, /*!< in: page or NULL */ dict_index_t* index, /*!< in: record descriptor */ mtr_t* mtr); /*!< in: mtr or NULL */ /**********************************************************//** Parses a log record of copying a record list end to a new created page. @return end of log record or NULL */ UNIV_INTERN byte* page_parse_copy_rec_list_to_created_page( /*=====================================*/ byte* ptr, /*!< in: buffer */ byte* end_ptr,/*!< in: buffer end */ buf_block_t* block, /*!< in: page or NULL */ dict_index_t* index, /*!< in: record descriptor */ mtr_t* mtr); /*!< in: mtr or NULL */ /***********************************************************//** Parses log record of a record delete on a page. @return pointer to record end or NULL */ UNIV_INTERN byte* page_cur_parse_delete_rec( /*======================*/ byte* ptr, /*!< in: buffer */ byte* end_ptr,/*!< in: buffer end */ buf_block_t* block, /*!< in: page or NULL */ dict_index_t* index, /*!< in: record descriptor */ mtr_t* mtr); /*!< in: mtr or NULL */ /** Index page cursor */ struct page_cur_struct{ byte* rec; /*!< pointer to a record on page */ buf_block_t* block; /*!< pointer to the block containing rec */ }; #ifndef UNIV_NONINL #include "page0cur.ic" #endif #endif haildb-2.3.2/include/ut0lst.h0000644000175000017500000002057411513177357016702 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /******************************************************************//** @file include/ut0lst.h List utilities Created 9/10/1995 Heikki Tuuri ***********************************************************************/ #ifndef ut0lst_h #define ut0lst_h #include "univ.i" /* This module implements the two-way linear list which should be used if a list is used in the database. Note that a single struct may belong to two or more lists, provided that the list are given different names. An example of the usage of the lists can be found in fil0fil.c. */ /*******************************************************************//** This macro expands to the unnamed type definition of a struct which acts as the two-way list base node. The base node contains pointers to both ends of the list and a count of nodes in the list (excluding the base node from the count). @param TYPE the name of the list node data type */ #define UT_LIST_BASE_NODE_T(TYPE)\ struct {\ ulint count; /*!< count of nodes in list */\ TYPE * start; /*!< pointer to list start, NULL if empty */\ TYPE * end; /*!< pointer to list end, NULL if empty */\ }\ /*******************************************************************//** This macro expands to the unnamed type definition of a struct which should be embedded in the nodes of the list, the node type must be a struct. This struct contains the pointers to next and previous nodes in the list. The name of the field in the node struct should be the name given to the list. @param TYPE the list node type name */ /* Example: typedef struct LRU_node_struct LRU_node_t; struct LRU_node_struct { UT_LIST_NODE_T(LRU_node_t) LRU_list; ... } The example implements an LRU list of name LRU_list. Its nodes are of type LRU_node_t. */ #define UT_LIST_NODE_T(TYPE)\ struct {\ TYPE * prev; /*!< pointer to the previous node,\ NULL if start of list */\ TYPE * next; /*!< pointer to next node, NULL if end of list */\ }\ /*******************************************************************//** Initializes the base node of a two-way list. @param BASE the list base node */ #define UT_LIST_INIT(BASE)\ {\ (BASE).count = 0;\ (BASE).start = NULL;\ (BASE).end = NULL;\ }\ /*******************************************************************//** Adds the node as the first element in a two-way linked list. @param NAME list name @param BASE the base node (not a pointer to it) @param N pointer to the node to be added to the list. */ #define UT_LIST_ADD_FIRST(NAME, BASE, N)\ {\ ut_ad(N);\ ((BASE).count)++;\ ((N)->NAME).next = (BASE).start;\ ((N)->NAME).prev = NULL;\ if (UNIV_LIKELY((BASE).start != NULL)) {\ ut_ad((BASE).start != (N));\ (((BASE).start)->NAME).prev = (N);\ }\ (BASE).start = (N);\ if (UNIV_UNLIKELY((BASE).end == NULL)) {\ (BASE).end = (N);\ }\ }\ /*******************************************************************//** Adds the node as the last element in a two-way linked list. @param NAME list name @param BASE the base node (not a pointer to it) @param N pointer to the node to be added to the list */ #define UT_LIST_ADD_LAST(NAME, BASE, N)\ {\ ut_ad(N);\ ((BASE).count)++;\ ((N)->NAME).prev = (BASE).end;\ ((N)->NAME).next = NULL;\ if ((BASE).end != NULL) {\ ut_ad((BASE).end != (N));\ (((BASE).end)->NAME).next = (N);\ }\ (BASE).end = (N);\ if ((BASE).start == NULL) {\ (BASE).start = (N);\ }\ }\ /*******************************************************************//** Inserts a NODE2 after NODE1 in a list. @param NAME list name @param BASE the base node (not a pointer to it) @param NODE1 pointer to node after which NODE2 is inserted @param NODE2 pointer to node being inserted after NODE1 */ #define UT_LIST_INSERT_AFTER(NAME, BASE, NODE1, NODE2)\ {\ ut_ad(NODE1);\ ut_ad(NODE2);\ ut_ad((NODE1) != (NODE2));\ ((BASE).count)++;\ ((NODE2)->NAME).prev = (NODE1);\ ((NODE2)->NAME).next = ((NODE1)->NAME).next;\ if (((NODE1)->NAME).next != NULL) {\ ((((NODE1)->NAME).next)->NAME).prev = (NODE2);\ }\ ((NODE1)->NAME).next = (NODE2);\ if ((BASE).end == (NODE1)) {\ (BASE).end = (NODE2);\ }\ }\ #ifdef UNIV_LIST_DEBUG /** Invalidate the pointers in a list node. @param NAME list name @param N pointer to the node that was removed */ # define UT_LIST_REMOVE_CLEAR(NAME, N) \ ((N)->NAME.prev = (N)->NAME.next = (void*) -1) #else /** Invalidate the pointers in a list node. @param NAME list name @param N pointer to the node that was removed */ # define UT_LIST_REMOVE_CLEAR(NAME, N) while (0) #endif /*******************************************************************//** Removes a node from a two-way linked list. @param NAME list name @param BASE the base node (not a pointer to it) @param N pointer to the node to be removed from the list */ #define UT_LIST_REMOVE(NAME, BASE, N) \ do { \ ut_ad(N); \ ut_a((BASE).count > 0); \ ((BASE).count)--; \ if (((N)->NAME).next != NULL) { \ ((((N)->NAME).next)->NAME).prev = ((N)->NAME).prev; \ } else { \ (BASE).end = ((N)->NAME).prev; \ } \ if (((N)->NAME).prev != NULL) { \ ((((N)->NAME).prev)->NAME).next = ((N)->NAME).next; \ } else { \ (BASE).start = ((N)->NAME).next; \ } \ UT_LIST_REMOVE_CLEAR(NAME, N); \ } while (0) /********************************************************************//** Gets the next node in a two-way list. @param NAME list name @param N pointer to a node @return the successor of N in NAME, or NULL */ #define UT_LIST_GET_NEXT(NAME, N)\ (((N)->NAME).next) /********************************************************************//** Gets the previous node in a two-way list. @param NAME list name @param N pointer to a node @return the predecessor of N in NAME, or NULL */ #define UT_LIST_GET_PREV(NAME, N)\ (((N)->NAME).prev) /********************************************************************//** Alternative macro to get the number of nodes in a two-way list, i.e., its length. @param BASE the base node (not a pointer to it). @return the number of nodes in the list */ #define UT_LIST_GET_LEN(BASE)\ (BASE).count /********************************************************************//** Gets the first node in a two-way list. @param BASE the base node (not a pointer to it) @return first node, or NULL if the list is empty */ #define UT_LIST_GET_FIRST(BASE)\ (BASE).start /********************************************************************//** Gets the last node in a two-way list. @param BASE the base node (not a pointer to it) @return last node, or NULL if the list is empty */ #define UT_LIST_GET_LAST(BASE)\ (BASE).end /********************************************************************//** Checks the consistency of a two-way list. @param NAME the name of the list @param TYPE node type @param BASE base node (not a pointer to it) @param ASSERTION a condition on ut_list_node_313 */ #define UT_LIST_VALIDATE(NAME, TYPE, BASE, ASSERTION) \ do { \ ulint ut_list_i_313; \ TYPE* ut_list_node_313; \ \ ut_list_node_313 = (BASE).start; \ \ for (ut_list_i_313 = (BASE).count; ut_list_i_313--; ) { \ ut_a(ut_list_node_313); \ ASSERTION; \ ut_ad((ut_list_node_313->NAME).next || !ut_list_i_313); \ ut_list_node_313 = (ut_list_node_313->NAME).next; \ } \ \ ut_a(ut_list_node_313 == NULL); \ \ ut_list_node_313 = (BASE).end; \ \ for (ut_list_i_313 = (BASE).count; ut_list_i_313--; ) { \ ut_a(ut_list_node_313); \ ASSERTION; \ ut_ad((ut_list_node_313->NAME).prev || !ut_list_i_313); \ ut_list_node_313 = (ut_list_node_313->NAME).prev; \ } \ \ ut_a(ut_list_node_313 == NULL); \ } while (0) #endif haildb-2.3.2/include/fut0lst.ic0000644000175000017500000001235311513177357017210 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /******************************************************************//** @file include/fut0lst.ic File-based list utilities Created 11/28/1995 Heikki Tuuri ***********************************************************************/ #include "fut0fut.h" #include "mtr0log.h" #include "buf0buf.h" /* We define the field offsets of a node for the list */ #define FLST_PREV 0 /* 6-byte address of the previous list element; the page part of address is FIL_NULL, if no previous element */ #define FLST_NEXT FIL_ADDR_SIZE /* 6-byte address of the next list element; the page part of address is FIL_NULL, if no next element */ /* We define the field offsets of a base node for the list */ #define FLST_LEN 0 /* 32-bit list length field */ #define FLST_FIRST 4 /* 6-byte address of the first element of the list; undefined if empty list */ #define FLST_LAST (4 + FIL_ADDR_SIZE) /* 6-byte address of the last element of the list; undefined if empty list */ /********************************************************************//** Writes a file address. */ UNIV_INLINE void flst_write_addr( /*============*/ fil_faddr_t* faddr, /*!< in: pointer to file faddress */ fil_addr_t addr, /*!< in: file address */ mtr_t* mtr) /*!< in: mini-transaction handle */ { ut_ad(faddr && mtr); ut_ad(mtr_memo_contains_page(mtr, faddr, MTR_MEMO_PAGE_X_FIX)); ut_a(addr.page == FIL_NULL || addr.boffset >= FIL_PAGE_DATA); ut_a(ut_align_offset(faddr, UNIV_PAGE_SIZE) >= FIL_PAGE_DATA); mlog_write_ulint(faddr + FIL_ADDR_PAGE, addr.page, MLOG_4BYTES, mtr); mlog_write_ulint(faddr + FIL_ADDR_BYTE, addr.boffset, MLOG_2BYTES, mtr); } /********************************************************************//** Reads a file address. @return file address */ UNIV_INLINE fil_addr_t flst_read_addr( /*===========*/ const fil_faddr_t* faddr, /*!< in: pointer to file faddress */ mtr_t* mtr) /*!< in: mini-transaction handle */ { fil_addr_t addr; ut_ad(faddr && mtr); addr.page = mtr_read_ulint(faddr + FIL_ADDR_PAGE, MLOG_4BYTES, mtr); addr.boffset = mtr_read_ulint(faddr + FIL_ADDR_BYTE, MLOG_2BYTES, mtr); ut_a(addr.page == FIL_NULL || addr.boffset >= FIL_PAGE_DATA); ut_a(ut_align_offset(faddr, UNIV_PAGE_SIZE) >= FIL_PAGE_DATA); return(addr); } /********************************************************************//** Initializes a list base node. */ UNIV_INLINE void flst_init( /*======*/ flst_base_node_t* base, /*!< in: pointer to base node */ mtr_t* mtr) /*!< in: mini-transaction handle */ { ut_ad(mtr_memo_contains_page(mtr, base, MTR_MEMO_PAGE_X_FIX)); mlog_write_ulint(base + FLST_LEN, 0, MLOG_4BYTES, mtr); flst_write_addr(base + FLST_FIRST, fil_addr_null, mtr); flst_write_addr(base + FLST_LAST, fil_addr_null, mtr); } /********************************************************************//** Gets list length. @return length */ UNIV_INLINE ulint flst_get_len( /*=========*/ const flst_base_node_t* base, /*!< in: pointer to base node */ mtr_t* mtr) /*!< in: mini-transaction handle */ { return(mtr_read_ulint(base + FLST_LEN, MLOG_4BYTES, mtr)); } /********************************************************************//** Gets list first node address. @return file address */ UNIV_INLINE fil_addr_t flst_get_first( /*===========*/ const flst_base_node_t* base, /*!< in: pointer to base node */ mtr_t* mtr) /*!< in: mini-transaction handle */ { return(flst_read_addr(base + FLST_FIRST, mtr)); } /********************************************************************//** Gets list last node address. @return file address */ UNIV_INLINE fil_addr_t flst_get_last( /*==========*/ const flst_base_node_t* base, /*!< in: pointer to base node */ mtr_t* mtr) /*!< in: mini-transaction handle */ { return(flst_read_addr(base + FLST_LAST, mtr)); } /********************************************************************//** Gets list next node address. @return file address */ UNIV_INLINE fil_addr_t flst_get_next_addr( /*===============*/ const flst_node_t* node, /*!< in: pointer to node */ mtr_t* mtr) /*!< in: mini-transaction handle */ { return(flst_read_addr(node + FLST_NEXT, mtr)); } /********************************************************************//** Gets list prev node address. @return file address */ UNIV_INLINE fil_addr_t flst_get_prev_addr( /*===============*/ const flst_node_t* node, /*!< in: pointer to node */ mtr_t* mtr) /*!< in: mini-transaction handle */ { return(flst_read_addr(node + FLST_PREV, mtr)); } haildb-2.3.2/include/row0undo.h0000644000175000017500000001251311513177357017216 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1997, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/row0undo.h Row undo Created 1/8/1997 Heikki Tuuri *******************************************************/ #ifndef row0undo_h #define row0undo_h #include "univ.i" #include "mtr0mtr.h" #include "trx0sys.h" #include "btr0types.h" #include "btr0pcur.h" #include "dict0types.h" #include "trx0types.h" #include "que0types.h" #include "row0types.h" /********************************************************************//** Creates a row undo node to a query graph. @return own: undo node */ UNIV_INTERN undo_node_t* row_undo_node_create( /*=================*/ trx_t* trx, /*!< in: transaction */ que_thr_t* parent, /*!< in: parent node, i.e., a thr node */ mem_heap_t* heap); /*!< in: memory heap where created */ /***********************************************************//** Looks for the clustered index record when node has the row reference. The pcur in node is used in the search. If found, stores the row to node, and stores the position of pcur, and detaches it. The pcur must be closed by the caller in any case. @return TRUE if found; NOTE the node->pcur must be closed by the caller, regardless of the return value */ UNIV_INTERN ibool row_undo_search_clust_to_pcur( /*==========================*/ undo_node_t* node); /*!< in: row undo node */ /***********************************************************//** Undoes a row operation in a table. This is a high-level function used in SQL execution graphs. @return query thread to run next or NULL */ UNIV_INTERN que_thr_t* row_undo_step( /*==========*/ que_thr_t* thr); /*!< in: query thread */ /* A single query thread will try to perform the undo for all successive versions of a clustered index record, if the transaction has modified it several times during the execution which is rolled back. It may happen that the task is transferred to another query thread, if the other thread is assigned to handle an undo log record in the chain of different versions of the record, and the other thread happens to get the x-latch to the clustered index record at the right time. If a query thread notices that the clustered index record it is looking for is missing, or the roll ptr field in the record doed not point to the undo log record the thread was assigned to handle, then it gives up the undo task for that undo log record, and fetches the next. This situation can occur just in the case where the transaction modified the same record several times and another thread is currently doing the undo for successive versions of that index record. */ /** Execution state of an undo node */ enum undo_exec { UNDO_NODE_FETCH_NEXT = 1, /*!< we should fetch the next undo log record */ UNDO_NODE_PREV_VERS, /*!< the roll ptr to previous version of a row is stored in node, and undo should be done based on it */ UNDO_NODE_INSERT, /*!< undo a fresh insert of a row to a table */ UNDO_NODE_MODIFY /*!< undo a modify operation (DELETE or UPDATE) on a row of a table */ }; /** Undo node structure */ struct undo_node_struct{ que_common_t common; /*!< node type: QUE_NODE_UNDO */ enum undo_exec state; /*!< node execution state */ trx_t* trx; /*!< trx for which undo is done */ roll_ptr_t roll_ptr;/*!< roll pointer to undo log record */ trx_undo_rec_t* undo_rec;/*!< undo log record */ undo_no_t undo_no;/*!< undo number of the record */ ulint rec_type;/*!< undo log record type: TRX_UNDO_INSERT_REC, ... */ roll_ptr_t new_roll_ptr; /*!< roll ptr to restore to clustered index record */ trx_id_t new_trx_id; /*!< trx id to restore to clustered index record */ btr_pcur_t pcur; /*!< persistent cursor used in searching the clustered index record */ dict_table_t* table; /*!< table where undo is done */ ulint cmpl_info;/*!< compiler analysis of an update */ upd_t* update; /*!< update vector for a clustered index record */ dtuple_t* ref; /*!< row reference to the next row to handle */ dtuple_t* row; /*!< a copy (also fields copied to heap) of the row to handle */ row_ext_t* ext; /*!< NULL, or prefixes of the externally stored columns of the row */ dtuple_t* undo_row;/*!< NULL, or the row after undo */ row_ext_t* undo_ext;/*!< NULL, or prefixes of the externally stored columns of undo_row */ dict_index_t* index; /*!< the next index whose record should be handled */ mem_heap_t* heap; /*!< memory heap used as auxiliary storage for row; this must be emptied after undo is tried on a row */ }; #ifndef UNIV_NONINL #include "row0undo.ic" #endif #endif haildb-2.3.2/include/mtr0log.ic0000644000175000017500000001573711513177357017204 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/mtr0log.ic Mini-transaction logging routines Created 12/7/1995 Heikki Tuuri *******************************************************/ #include "mach0data.h" #include "ut0lst.h" #include "buf0buf.h" #include "fsp0types.h" #include "trx0sys.h" /********************************************************//** Opens a buffer to mlog. It must be closed with mlog_close. @return buffer, NULL if log mode MTR_LOG_NONE */ UNIV_INLINE byte* mlog_open( /*======*/ mtr_t* mtr, /*!< in: mtr */ ulint size) /*!< in: buffer size in bytes; MUST be smaller than DYN_ARRAY_DATA_SIZE! */ { dyn_array_t* mlog; mtr->modifications = TRUE; if (mtr_get_log_mode(mtr) == MTR_LOG_NONE) { return(NULL); } mlog = &(mtr->log); return(dyn_array_open(mlog, size)); } /********************************************************//** Closes a buffer opened to mlog. */ UNIV_INLINE void mlog_close( /*=======*/ mtr_t* mtr, /*!< in: mtr */ byte* ptr) /*!< in: buffer space from ptr up was not used */ { dyn_array_t* mlog; ut_ad(mtr_get_log_mode(mtr) != MTR_LOG_NONE); mlog = &(mtr->log); dyn_array_close(mlog, ptr); } #ifndef UNIV_HOTBACKUP /********************************************************//** Catenates 1 - 4 bytes to the mtr log. The value is not compressed. */ UNIV_INLINE void mlog_catenate_ulint( /*================*/ mtr_t* mtr, /*!< in: mtr */ ulint val, /*!< in: value to write */ ulint type) /*!< in: MLOG_1BYTE, MLOG_2BYTES, MLOG_4BYTES */ { dyn_array_t* mlog; byte* ptr; if (mtr_get_log_mode(mtr) == MTR_LOG_NONE) { return; } mlog = &(mtr->log); #if MLOG_1BYTE != 1 # error "MLOG_1BYTE != 1" #endif #if MLOG_2BYTES != 2 # error "MLOG_2BYTES != 2" #endif #if MLOG_4BYTES != 4 # error "MLOG_4BYTES != 4" #endif #if MLOG_8BYTES != 8 # error "MLOG_8BYTES != 8" #endif ptr = (byte*) dyn_array_push(mlog, type); if (type == MLOG_4BYTES) { mach_write_to_4(ptr, val); } else if (type == MLOG_2BYTES) { mach_write_to_2(ptr, val); } else { ut_ad(type == MLOG_1BYTE); mach_write_to_1(ptr, val); } } /********************************************************//** Catenates a compressed ulint to mlog. */ UNIV_INLINE void mlog_catenate_ulint_compressed( /*===========================*/ mtr_t* mtr, /*!< in: mtr */ ulint val) /*!< in: value to write */ { byte* log_ptr; log_ptr = mlog_open(mtr, 10); /* If no logging is requested, we may return now */ if (log_ptr == NULL) { return; } log_ptr += mach_write_compressed(log_ptr, val); mlog_close(mtr, log_ptr); } /********************************************************//** Catenates a compressed dulint to mlog. */ UNIV_INLINE void mlog_catenate_dulint_compressed( /*============================*/ mtr_t* mtr, /*!< in: mtr */ dulint val) /*!< in: value to write */ { byte* log_ptr; log_ptr = mlog_open(mtr, 15); /* If no logging is requested, we may return now */ if (log_ptr == NULL) { return; } log_ptr += mach_dulint_write_compressed(log_ptr, val); mlog_close(mtr, log_ptr); } /********************************************************//** Writes the initial part of a log record (3..11 bytes). If the implementation of this function is changed, all size parameters to mlog_open() should be adjusted accordingly! @return new value of log_ptr */ UNIV_INLINE byte* mlog_write_initial_log_record_fast( /*===============================*/ const byte* ptr, /*!< in: pointer to (inside) a buffer frame holding the file page where modification is made */ byte type, /*!< in: log item type: MLOG_1BYTE, ... */ byte* log_ptr,/*!< in: pointer to mtr log which has been opened */ mtr_t* mtr) /*!< in: mtr */ { #ifdef UNIV_DEBUG buf_block_t* block; #endif const byte* page; ulint space; ulint offset; ut_ad(mtr_memo_contains_page(mtr, ptr, MTR_MEMO_PAGE_X_FIX)); ut_ad(type <= MLOG_BIGGEST_TYPE); ut_ad(ptr && log_ptr); page = (const byte*) ut_align_down(ptr, UNIV_PAGE_SIZE); space = mach_read_from_4(page + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID); offset = mach_read_from_4(page + FIL_PAGE_OFFSET); /* check whether the page is in the doublewrite buffer; the doublewrite buffer is located in pages FSP_EXTENT_SIZE, ..., 3 * FSP_EXTENT_SIZE - 1 in the system tablespace */ if (space == TRX_SYS_SPACE && offset >= FSP_EXTENT_SIZE && offset < 3 * FSP_EXTENT_SIZE) { if (trx_doublewrite_buf_is_being_created) { /* Do nothing: we only come to this branch in an InnoDB database creation. We do not redo log anything for the doublewrite buffer pages. */ return(log_ptr); } else { ib_logger(ib_stream, "Error: trying to redo log a record of type " "%d on page %lu of space %lu in the " "doublewrite buffer, continuing anyway.\n" "Please post a bug report to " "bugs.mysql.com.\n", type, offset, space); } } mach_write_to_1(log_ptr, type); log_ptr++; log_ptr += mach_write_compressed(log_ptr, space); log_ptr += mach_write_compressed(log_ptr, offset); mtr->n_log_recs++; #ifdef UNIV_LOG_DEBUG ib_logger(ib_stream, "Adding to mtr log record type %lu space %lu page no %lu\n", (ulong) type, space, offset); #endif #ifdef UNIV_DEBUG /* We now assume that all x-latched pages have been modified! */ block = (buf_block_t*) buf_block_align(ptr); if (!mtr_memo_contains(mtr, block, MTR_MEMO_MODIFY)) { mtr_memo_push(mtr, block, MTR_MEMO_MODIFY); } #endif return(log_ptr); } /********************************************************//** Writes a log record about an .ibd file create/delete/rename. @return new value of log_ptr */ UNIV_INLINE byte* mlog_write_initial_log_record_for_file_op( /*======================================*/ ulint type, /*!< in: MLOG_FILE_CREATE, MLOG_FILE_DELETE, or MLOG_FILE_RENAME */ ulint space_id,/*!< in: space id, if applicable */ ulint page_no,/*!< in: page number (not relevant currently) */ byte* log_ptr,/*!< in: pointer to mtr log which has been opened */ mtr_t* mtr) /*!< in: mtr */ { ut_ad(log_ptr); mach_write_to_1(log_ptr, type); log_ptr++; /* We write dummy space id and page number */ log_ptr += mach_write_compressed(log_ptr, space_id); log_ptr += mach_write_compressed(log_ptr, page_no); mtr->n_log_recs++; return(log_ptr); } #endif /* !UNIV_HOTBACKUP */ haildb-2.3.2/include/ha0storage.ic0000644000175000017500000001050511513177357017641 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 2007, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/ha0storage.ic Hash storage. Provides a data structure that stores chunks of data in its own storage, avoiding duplicates. Created September 24, 2007 Vasil Dimov *******************************************************/ #include "univ.i" #include "ha0storage.h" #include "hash0hash.h" #include "mem0mem.h" /** Hash storage for strings */ struct ha_storage_struct { mem_heap_t* heap; /*!< memory heap from which memory is allocated */ hash_table_t* hash; /*!< hash table used to avoid duplicates */ }; /** Objects of this type are stored in ha_storage_t */ typedef struct ha_storage_node_struct ha_storage_node_t; /** Objects of this type are stored in ha_storage_struct */ struct ha_storage_node_struct { ulint data_len;/*!< length of the data */ const void* data; /*!< pointer to data */ ha_storage_node_t* next; /*!< next node in hash chain */ }; /*******************************************************************//** Creates a hash storage. If any of the parameters is 0, then a default value is used. @return own: hash storage */ UNIV_INLINE ha_storage_t* ha_storage_create( /*==============*/ ulint initial_heap_bytes, /*!< in: initial heap's size */ ulint initial_hash_cells) /*!< in: initial number of cells in the hash table */ { ha_storage_t* storage; mem_heap_t* heap; if (initial_heap_bytes == 0) { initial_heap_bytes = HA_STORAGE_DEFAULT_HEAP_BYTES; } if (initial_hash_cells == 0) { initial_hash_cells = HA_STORAGE_DEFAULT_HASH_CELLS; } /* we put "storage" within "storage->heap" */ heap = mem_heap_create(sizeof(ha_storage_t) + initial_heap_bytes); storage = (ha_storage_t*) mem_heap_alloc(heap, sizeof(ha_storage_t)); storage->heap = heap; storage->hash = hash_create(initial_hash_cells); return(storage); } /*******************************************************************//** Empties a hash storage, freeing memory occupied by data chunks. This invalidates any pointers previously returned by ha_storage_put(). The hash storage is not invalidated itself and can be used again. */ UNIV_INLINE void ha_storage_empty( /*=============*/ ha_storage_t** storage) /*!< in/out: hash storage */ { ha_storage_t temp_storage; temp_storage.heap = (*storage)->heap; temp_storage.hash = (*storage)->hash; hash_table_clear(temp_storage.hash); mem_heap_empty(temp_storage.heap); *storage = (ha_storage_t*) mem_heap_alloc(temp_storage.heap, sizeof(ha_storage_t)); (*storage)->heap = temp_storage.heap; (*storage)->hash = temp_storage.hash; } /*******************************************************************//** Frees a hash storage and everything it contains, it cannot be used after this call. This invalidates any pointers previously returned by ha_storage_put(). */ UNIV_INLINE void ha_storage_free( /*============*/ ha_storage_t* storage) /*!< in, own: hash storage */ { /* order is important because the pointer storage->hash is within the heap */ hash_table_free(storage->hash); mem_heap_free(storage->heap); } /*******************************************************************//** Gets the size of the memory used by a storage. @return bytes used */ UNIV_INLINE ulint ha_storage_get_size( /*================*/ const ha_storage_t* storage) /*!< in: hash storage */ { ulint ret; ret = mem_heap_get_size(storage->heap); /* this assumes hash->heap and hash->heaps are NULL */ ret += sizeof(hash_table_t); ret += sizeof(hash_cell_t) * hash_get_n_cells(storage->hash); return(ret); } haildb-2.3.2/include/trx0undo.h0000644000175000017500000005256211513177357017234 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2010, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/trx0undo.h Transaction undo log Created 3/26/1996 Heikki Tuuri *******************************************************/ #ifndef trx0undo_h #define trx0undo_h #include "univ.i" #include "trx0types.h" #include "mtr0mtr.h" #include "trx0sys.h" #include "page0types.h" #include "trx0xa.h" #ifndef UNIV_HOTBACKUP /***********************************************************************//** Builds a roll pointer. @return roll pointer */ UNIV_INLINE roll_ptr_t trx_undo_build_roll_ptr( /*====================*/ ibool is_insert, /*!< in: TRUE if insert undo log */ ulint rseg_id, /*!< in: rollback segment id */ ulint page_no, /*!< in: page number */ ulint offset); /*!< in: offset of the undo entry within page */ /***********************************************************************//** Decodes a roll pointer. */ UNIV_INLINE void trx_undo_decode_roll_ptr( /*=====================*/ roll_ptr_t roll_ptr, /*!< in: roll pointer */ ibool* is_insert, /*!< out: TRUE if insert undo log */ ulint* rseg_id, /*!< out: rollback segment id */ ulint* page_no, /*!< out: page number */ ulint* offset); /*!< out: offset of the undo entry within page */ /***********************************************************************//** Returns TRUE if the roll pointer is of the insert type. @return TRUE if insert undo log */ UNIV_INLINE ibool trx_undo_roll_ptr_is_insert( /*========================*/ roll_ptr_t roll_ptr); /*!< in: roll pointer */ #endif /* !UNIV_HOTBACKUP */ /*****************************************************************//** Writes a roll ptr to an index page. In case that the size changes in some future version, this function should be used instead of mach_write_... */ UNIV_INLINE void trx_write_roll_ptr( /*===============*/ byte* ptr, /*!< in: pointer to memory where written */ roll_ptr_t roll_ptr); /*!< in: roll ptr */ /*****************************************************************//** Reads a roll ptr from an index page. In case that the roll ptr size changes in some future version, this function should be used instead of mach_read_... @return roll ptr */ UNIV_INLINE roll_ptr_t trx_read_roll_ptr( /*==============*/ const byte* ptr); /*!< in: pointer to memory from where to read */ #ifndef UNIV_HOTBACKUP /******************************************************************//** Gets an undo log page and x-latches it. @return pointer to page x-latched */ UNIV_INLINE page_t* trx_undo_page_get( /*==============*/ ulint space, /*!< in: space where placed */ ulint zip_size, /*!< in: compressed page size in bytes or 0 for uncompressed pages */ ulint page_no, /*!< in: page number */ mtr_t* mtr); /*!< in: mtr */ /******************************************************************//** Gets an undo log page and s-latches it. @return pointer to page s-latched */ UNIV_INLINE page_t* trx_undo_page_get_s_latched( /*========================*/ ulint space, /*!< in: space where placed */ ulint zip_size, /*!< in: compressed page size in bytes or 0 for uncompressed pages */ ulint page_no, /*!< in: page number */ mtr_t* mtr); /*!< in: mtr */ /******************************************************************//** Returns the previous undo record on the page in the specified log, or NULL if none exists. @return pointer to record, NULL if none */ UNIV_INLINE trx_undo_rec_t* trx_undo_page_get_prev_rec( /*=======================*/ trx_undo_rec_t* rec, /*!< in: undo log record */ ulint page_no,/*!< in: undo log header page number */ ulint offset);/*!< in: undo log header offset on page */ /******************************************************************//** Returns the next undo log record on the page in the specified log, or NULL if none exists. @return pointer to record, NULL if none */ UNIV_INLINE trx_undo_rec_t* trx_undo_page_get_next_rec( /*=======================*/ trx_undo_rec_t* rec, /*!< in: undo log record */ ulint page_no,/*!< in: undo log header page number */ ulint offset);/*!< in: undo log header offset on page */ /******************************************************************//** Returns the last undo record on the page in the specified undo log, or NULL if none exists. @return pointer to record, NULL if none */ UNIV_INLINE trx_undo_rec_t* trx_undo_page_get_last_rec( /*=======================*/ page_t* undo_page,/*!< in: undo log page */ ulint page_no,/*!< in: undo log header page number */ ulint offset); /*!< in: undo log header offset on page */ /******************************************************************//** Returns the first undo record on the page in the specified undo log, or NULL if none exists. @return pointer to record, NULL if none */ UNIV_INLINE trx_undo_rec_t* trx_undo_page_get_first_rec( /*========================*/ page_t* undo_page,/*!< in: undo log page */ ulint page_no,/*!< in: undo log header page number */ ulint offset);/*!< in: undo log header offset on page */ /***********************************************************************//** Gets the previous record in an undo log. @return undo log record, the page s-latched, NULL if none */ UNIV_INTERN trx_undo_rec_t* trx_undo_get_prev_rec( /*==================*/ trx_undo_rec_t* rec, /*!< in: undo record */ ulint page_no,/*!< in: undo log header page number */ ulint offset, /*!< in: undo log header offset on page */ mtr_t* mtr); /*!< in: mtr */ /***********************************************************************//** Gets the next record in an undo log. @return undo log record, the page s-latched, NULL if none */ UNIV_INTERN trx_undo_rec_t* trx_undo_get_next_rec( /*==================*/ trx_undo_rec_t* rec, /*!< in: undo record */ ulint page_no,/*!< in: undo log header page number */ ulint offset, /*!< in: undo log header offset on page */ mtr_t* mtr); /*!< in: mtr */ /***********************************************************************//** Gets the first record in an undo log. @return undo log record, the page latched, NULL if none */ UNIV_INTERN trx_undo_rec_t* trx_undo_get_first_rec( /*===================*/ ulint space, /*!< in: undo log header space */ ulint zip_size,/*!< in: compressed page size in bytes or 0 for uncompressed pages */ ulint page_no,/*!< in: undo log header page number */ ulint offset, /*!< in: undo log header offset on page */ ulint mode, /*!< in: latching mode: RW_S_LATCH or RW_X_LATCH */ mtr_t* mtr); /*!< in: mtr */ /********************************************************************//** Tries to add a page to the undo log segment where the undo log is placed. @return page number if success, else FIL_NULL */ UNIV_INTERN ulint trx_undo_add_page( /*==============*/ trx_t* trx, /*!< in: transaction */ trx_undo_t* undo, /*!< in: undo log memory object */ mtr_t* mtr); /*!< in: mtr which does not have a latch to any undo log page; the caller must have reserved the rollback segment mutex */ /***********************************************************************//** Truncates an undo log from the end. This function is used during a rollback to free space from an undo log. */ UNIV_INTERN void trx_undo_truncate_end( /*==================*/ trx_t* trx, /*!< in: transaction whose undo log it is */ trx_undo_t* undo, /*!< in: undo log */ undo_no_t limit); /*!< in: all undo records with undo number >= this value should be truncated */ /***********************************************************************//** Truncates an undo log from the start. This function is used during a purge operation. */ UNIV_INTERN void trx_undo_truncate_start( /*====================*/ trx_rseg_t* rseg, /*!< in: rollback segment */ ulint space, /*!< in: space id of the log */ ulint hdr_page_no, /*!< in: header page number */ ulint hdr_offset, /*!< in: header offset on the page */ undo_no_t limit); /*!< in: all undo pages with undo numbers < this value should be truncated; NOTE that the function only frees whole pages; the header page is not freed, but emptied, if all the records there are < limit */ /********************************************************************//** Initializes the undo log lists for a rollback segment memory copy. This function is only called when the database is started or a new rollback segment created. @return the combined size of undo log segments in pages */ UNIV_INTERN ulint trx_undo_lists_init( /*================*/ ib_recovery_t recovery,/*!< in: recovery flag */ trx_rseg_t* rseg); /*!< in: rollback segment memory object */ /**********************************************************************//** Assigns an undo log for a transaction. A new undo log is created or a cached undo log reused. @return DB_SUCCESS if undo log assign successful, possible error codes are: DB_TOO_MANY_CONCURRENT_TRXS DB_OUT_OF_FILE_SPACE DB_OUT_OF_MEMORY */ UNIV_INTERN ulint trx_undo_assign_undo( /*=================*/ trx_t* trx, /*!< in: transaction */ ulint type); /*!< in: TRX_UNDO_INSERT or TRX_UNDO_UPDATE */ /******************************************************************//** Sets the state of the undo log segment at a transaction finish. @return undo log segment header page, x-latched */ UNIV_INTERN page_t* trx_undo_set_state_at_finish( /*=========================*/ trx_rseg_t* rseg, /*!< in: rollback segment memory object */ trx_t* trx, /*!< in: transaction */ trx_undo_t* undo, /*!< in: undo log memory copy */ mtr_t* mtr); /*!< in: mtr */ /******************************************************************//** Sets the state of the undo log segment at a transaction prepare. @return undo log segment header page, x-latched */ UNIV_INTERN page_t* trx_undo_set_state_at_prepare( /*==========================*/ trx_t* trx, /*!< in: transaction */ trx_undo_t* undo, /*!< in: undo log memory copy */ mtr_t* mtr); /*!< in: mtr */ /**********************************************************************//** Adds the update undo log header as the first in the history list, and frees the memory object, or puts it to the list of cached update undo log segments. */ UNIV_INTERN void trx_undo_update_cleanup( /*====================*/ trx_t* trx, /*!< in: trx owning the update undo log */ page_t* undo_page, /*!< in: update undo log header page, x-latched */ mtr_t* mtr); /*!< in: mtr */ /******************************************************************//** Frees or caches an insert undo log after a transaction commit or rollback. Knowledge of inserts is not needed after a commit or rollback, therefore the data can be discarded. */ UNIV_INTERN void trx_undo_insert_cleanup( /*====================*/ trx_t* trx); /*!< in: transaction handle */ #endif /* !UNIV_HOTBACKUP */ /***********************************************************//** Parses the redo log entry of an undo log page initialization. @return end of log record or NULL */ UNIV_INTERN byte* trx_undo_parse_page_init( /*=====================*/ byte* ptr, /*!< in: buffer */ byte* end_ptr,/*!< in: buffer end */ page_t* page, /*!< in: page or NULL */ mtr_t* mtr); /*!< in: mtr or NULL */ /***********************************************************//** Parses the redo log entry of an undo log page header create or reuse. @return end of log record or NULL */ UNIV_INTERN byte* trx_undo_parse_page_header( /*=======================*/ ulint type, /*!< in: MLOG_UNDO_HDR_CREATE or MLOG_UNDO_HDR_REUSE */ byte* ptr, /*!< in: buffer */ byte* end_ptr,/*!< in: buffer end */ page_t* page, /*!< in: page or NULL */ mtr_t* mtr); /*!< in: mtr or NULL */ /***********************************************************//** Parses the redo log entry of an undo log page header discard. @return end of log record or NULL */ UNIV_INTERN byte* trx_undo_parse_discard_latest( /*==========================*/ byte* ptr, /*!< in: buffer */ byte* end_ptr,/*!< in: buffer end */ page_t* page, /*!< in: page or NULL */ mtr_t* mtr); /*!< in: mtr or NULL */ /************************************************************************ Frees an undo log memory copy. */ UNIV_INTERN void trx_undo_mem_free( /*==============*/ trx_undo_t* undo); /*!< in: the undo object to be freed */ /* Types of an undo log segment */ #define TRX_UNDO_INSERT 1 /* contains undo entries for inserts */ #define TRX_UNDO_UPDATE 2 /* contains undo entries for updates and delete markings: in short, modifys (the name 'UPDATE' is a historical relic) */ /* States of an undo log segment */ #define TRX_UNDO_ACTIVE 1 /* contains an undo log of an active transaction */ #define TRX_UNDO_CACHED 2 /* cached for quick reuse */ #define TRX_UNDO_TO_FREE 3 /* insert undo segment can be freed */ #define TRX_UNDO_TO_PURGE 4 /* update undo segment will not be reused: it can be freed in purge when all undo data in it is removed */ #define TRX_UNDO_PREPARED 5 /* contains an undo log of an prepared transaction */ #ifndef UNIV_HOTBACKUP /** Transaction undo log memory object; this is protected by the undo_mutex in the corresponding transaction object */ struct trx_undo_struct{ /*-----------------------------*/ ulint id; /*!< undo log slot number within the rollback segment */ ulint type; /*!< TRX_UNDO_INSERT or TRX_UNDO_UPDATE */ ulint state; /*!< state of the corresponding undo log segment */ ibool del_marks; /*!< relevant only in an update undo log: this is TRUE if the transaction may have delete marked records, because of a delete of a row or an update of an indexed field; purge is then necessary; also TRUE if the transaction has updated an externally stored field */ trx_id_t trx_id; /*!< id of the trx assigned to the undo log */ #ifdef WITH_XOPEN XID xid; /*!< X/Open XA transaction identification */ #endif /* WITH_XOPEN */ ibool dict_operation; /*!< TRUE if a dict operation trx */ dulint table_id; /*!< if a dict operation, then the table id */ trx_rseg_t* rseg; /*!< rseg where the undo log belongs */ /*-----------------------------*/ ulint space; /*!< space id where the undo log placed */ ulint zip_size; /*!< compressed page size of space in bytes, or 0 for uncompressed */ ulint hdr_page_no; /*!< page number of the header page in the undo log */ ulint hdr_offset; /*!< header offset of the undo log on the page */ ulint last_page_no; /*!< page number of the last page in the undo log; this may differ from top_page_no during a rollback */ ulint size; /*!< current size in pages */ /*-----------------------------*/ ulint empty; /*!< TRUE if the stack of undo log records is currently empty */ ulint top_page_no; /*!< page number where the latest undo log record was catenated; during rollback the page from which the latest undo record was chosen */ ulint top_offset; /*!< offset of the latest undo record, i.e., the topmost element in the undo log if we think of it as a stack */ undo_no_t top_undo_no; /*!< undo number of the latest record */ buf_block_t* guess_block; /*!< guess for the buffer block where the top page might reside */ /*-----------------------------*/ UT_LIST_NODE_T(trx_undo_t) undo_list; /*!< undo log objects in the rollback segment are chained into lists */ }; #endif /* !UNIV_HOTBACKUP */ /** The offset of the undo log page header on pages of the undo log */ #define TRX_UNDO_PAGE_HDR FSEG_PAGE_DATA /*-------------------------------------------------------------*/ /** Transaction undo log page header offsets */ /* @{ */ #define TRX_UNDO_PAGE_TYPE 0 /*!< TRX_UNDO_INSERT or TRX_UNDO_UPDATE */ #define TRX_UNDO_PAGE_START 2 /*!< Byte offset where the undo log records for the LATEST transaction start on this page (remember that in an update undo log, the first page can contain several undo logs) */ #define TRX_UNDO_PAGE_FREE 4 /*!< On each page of the undo log this field contains the byte offset of the first free byte on the page */ #define TRX_UNDO_PAGE_NODE 6 /*!< The file list node in the chain of undo log pages */ /*-------------------------------------------------------------*/ #define TRX_UNDO_PAGE_HDR_SIZE (6 + FLST_NODE_SIZE) /*!< Size of the transaction undo log page header, in bytes */ /* @} */ /** An update undo segment with just one page can be reused if it has at most this many bytes used; we must leave space at least for one new undo log header on the page */ #define TRX_UNDO_PAGE_REUSE_LIMIT (3 * UNIV_PAGE_SIZE / 4) /* An update undo log segment may contain several undo logs on its first page if the undo logs took so little space that the segment could be cached and reused. All the undo log headers are then on the first page, and the last one owns the undo log records on subsequent pages if the segment is bigger than one page. If an undo log is stored in a segment, then on the first page it is allowed to have zero undo records, but if the segment extends to several pages, then all the rest of the pages must contain at least one undo log record. */ /** The offset of the undo log segment header on the first page of the undo log segment */ #define TRX_UNDO_SEG_HDR (TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_HDR_SIZE) /** Undo log segment header */ /* @{ */ /*-------------------------------------------------------------*/ #define TRX_UNDO_STATE 0 /*!< TRX_UNDO_ACTIVE, ... */ #define TRX_UNDO_LAST_LOG 2 /*!< Offset of the last undo log header on the segment header page, 0 if none */ #define TRX_UNDO_FSEG_HEADER 4 /*!< Header for the file segment which the undo log segment occupies */ #define TRX_UNDO_PAGE_LIST (4 + FSEG_HEADER_SIZE) /*!< Base node for the list of pages in the undo log segment; defined only on the undo log segment's first page */ /*-------------------------------------------------------------*/ /** Size of the undo log segment header */ #define TRX_UNDO_SEG_HDR_SIZE (4 + FSEG_HEADER_SIZE + FLST_BASE_NODE_SIZE) /* @} */ /** The undo log header. There can be several undo log headers on the first page of an update undo log segment. */ /* @{ */ /*-------------------------------------------------------------*/ #define TRX_UNDO_TRX_ID 0 /*!< Transaction id */ #define TRX_UNDO_TRX_NO 8 /*!< Transaction number of the transaction; defined only if the log is in a history list */ #define TRX_UNDO_DEL_MARKS 16 /*!< Defined only in an update undo log: TRUE if the transaction may have done delete markings of records, and thus purge is necessary */ #define TRX_UNDO_LOG_START 18 /*!< Offset of the first undo log record of this log on the header page; purge may remove undo log record from the log start, and therefore this is not necessarily the same as this log header end offset */ #define TRX_UNDO_XID_EXISTS 20 /*!< TRUE if undo log header includes X/Open XA transaction identification XID */ #define TRX_UNDO_DICT_TRANS 21 /*!< TRUE if the transaction is a table create, index create, or drop transaction: in recovery the transaction cannot be rolled back in the usual way: a 'rollback' rather means dropping the created or dropped table, if it still exists */ #define TRX_UNDO_TABLE_ID 22 /*!< Id of the table if the preceding field is TRUE */ #define TRX_UNDO_NEXT_LOG 30 /*!< Offset of the next undo log header on this page, 0 if none */ #define TRX_UNDO_PREV_LOG 32 /*!< Offset of the previous undo log header on this page, 0 if none */ #define TRX_UNDO_HISTORY_NODE 34 /*!< If the log is put to the history list, the file list node is here */ /*-------------------------------------------------------------*/ /** Size of the undo log header without XID information */ #define TRX_UNDO_LOG_OLD_HDR_SIZE (34 + FLST_NODE_SIZE) /* Note: the writing of the undo log old header is coded by a log record MLOG_UNDO_HDR_CREATE or MLOG_UNDO_HDR_REUSE. The appending of an XID to the header is logged separately. In this sense, the XID is not really a member of the undo log header. TODO: do not append the XID to the log header if XA is not needed by the user. The XID wastes about 150 bytes of space in every undo log. In the history list we may have millions of undo logs, which means quite a large overhead. */ /** X/Open XA Transaction Identification (XID) */ /* @{ */ /** xid_t::formatID */ #define TRX_UNDO_XA_FORMAT (TRX_UNDO_LOG_OLD_HDR_SIZE) /** xid_t::gtrid_length */ #define TRX_UNDO_XA_TRID_LEN (TRX_UNDO_XA_FORMAT + 4) /** xid_t::bqual_length */ #define TRX_UNDO_XA_BQUAL_LEN (TRX_UNDO_XA_TRID_LEN + 4) /** Distributed transaction identifier data */ #define TRX_UNDO_XA_XID (TRX_UNDO_XA_BQUAL_LEN + 4) /*--------------------------------------------------------------*/ #define TRX_UNDO_LOG_XA_HDR_SIZE (TRX_UNDO_XA_XID + XIDDATASIZE) /*!< Total size of the undo log header with the XA XID */ /* @} */ #ifndef UNIV_NONINL #include "trx0undo.ic" #endif #endif haildb-2.3.2/include/dict0crea.ic0000644000175000017500000000202611513177357017441 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/dict0crea.ic Database object creation Created 1/8/1996 Heikki Tuuri *******************************************************/ haildb-2.3.2/include/row0sel.ic0000644000175000017500000000545111513177357017203 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1997, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/row0sel.ic Select Created 12/19/1997 Heikki Tuuri *******************************************************/ #include "que0que.h" /*********************************************************************//** Gets the plan node for the nth table in a join. @return plan node */ UNIV_INLINE plan_t* sel_node_get_nth_plan( /*==================*/ sel_node_t* node, /*!< in: select node */ ulint i) /*!< in: get ith plan node */ { ut_ad(i < node->n_tables); return(node->plans + i); } /*********************************************************************//** Resets the cursor defined by sel_node to the SEL_NODE_OPEN state, which means that it will start fetching from the start of the result set again, regardless of where it was before, and it will set intention locks on the tables. */ UNIV_INLINE void sel_node_reset_cursor( /*==================*/ sel_node_t* node) /*!< in: select node */ { node->state = SEL_NODE_OPEN; } /**********************************************************************//** Performs an execution step of an open or close cursor statement node. @return query thread to run next or NULL */ UNIV_INLINE que_thr_t* open_step( /*======*/ que_thr_t* thr) /*!< in: query thread */ { sel_node_t* sel_node; open_node_t* node; ulint err; ut_ad(thr); node = (open_node_t*) thr->run_node; ut_ad(que_node_get_type(node) == QUE_NODE_OPEN); sel_node = node->cursor_def; err = DB_SUCCESS; if (node->op_type == ROW_SEL_OPEN_CURSOR) { /* if (sel_node->state == SEL_NODE_CLOSED) { */ sel_node_reset_cursor(sel_node); /* } else { err = DB_ERROR; } */ } else { if (sel_node->state != SEL_NODE_CLOSED) { sel_node->state = SEL_NODE_CLOSED; } else { err = DB_ERROR; } } if (UNIV_EXPECT(err, DB_SUCCESS) != DB_SUCCESS) { /* SQL error detected */ ib_logger(ib_stream, "SQL error %lu\n", (ulong) err); ut_error; } thr->run_node = que_node_get_parent(node); return(thr); } haildb-2.3.2/include/page0zip.h0000644000175000017500000004243211513177357017163 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 2005, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/page0zip.h Compressed page interface Created June 2005 by Marko Makela *******************************************************/ #ifndef page0zip_h #define page0zip_h #ifdef UNIV_MATERIALIZE # undef UNIV_INLINE # define UNIV_INLINE #endif #include "mtr0types.h" #include "page0types.h" #include "buf0types.h" #include "dict0types.h" #include "trx0types.h" #include "mem0mem.h" /**********************************************************************//** Determine the size of a compressed page in bytes. @return size in bytes */ UNIV_INLINE ulint page_zip_get_size( /*==============*/ const page_zip_des_t* page_zip) /*!< in: compressed page */ __attribute__((nonnull, pure)); /**********************************************************************//** Set the size of a compressed page in bytes. */ UNIV_INLINE void page_zip_set_size( /*==============*/ page_zip_des_t* page_zip, /*!< in/out: compressed page */ ulint size); /*!< in: size in bytes */ #ifndef UNIV_HOTBACKUP /**********************************************************************//** Determine if a record is so big that it needs to be stored externally. @return FALSE if the entire record can be stored locally on the page */ UNIV_INLINE ibool page_zip_rec_needs_ext( /*===================*/ ulint rec_size, /*!< in: length of the record in bytes */ ulint comp, /*!< in: nonzero=compact format */ ulint n_fields, /*!< in: number of fields in the record; ignored if zip_size == 0 */ ulint zip_size) /*!< in: compressed page size in bytes, or 0 */ __attribute__((const)); /**********************************************************************//** Determine the guaranteed free space on an empty page. @return minimum payload size on the page */ UNIV_INTERN ulint page_zip_empty_size( /*================*/ ulint n_fields, /*!< in: number of columns in the index */ ulint zip_size) /*!< in: compressed page size in bytes */ __attribute__((const)); #endif /* !UNIV_HOTBACKUP */ /**********************************************************************//** Initialize a compressed page descriptor. */ UNIV_INLINE void page_zip_des_init( /*==============*/ page_zip_des_t* page_zip); /*!< in/out: compressed page descriptor */ /**********************************************************************//** Configure the zlib allocator to use the given memory heap. */ UNIV_INTERN void page_zip_set_alloc( /*===============*/ void* stream, /*!< in/out: zlib stream */ mem_heap_t* heap); /*!< in: memory heap to use */ /**********************************************************************//** Compress a page. @return TRUE on success, FALSE on failure; page_zip will be left intact on failure. */ UNIV_INTERN ibool page_zip_compress( /*==============*/ page_zip_des_t* page_zip,/*!< in: size; out: data, n_blobs, m_start, m_end, m_nonempty */ const page_t* page, /*!< in: uncompressed page */ dict_index_t* index, /*!< in: index of the B-tree node */ mtr_t* mtr) /*!< in: mini-transaction, or NULL */ __attribute__((nonnull(1,2,3))); /**********************************************************************//** Decompress a page. This function should tolerate errors on the compressed page. Instead of letting assertions fail, it will return FALSE if an inconsistency is detected. @return TRUE on success, FALSE on failure */ UNIV_INTERN ibool page_zip_decompress( /*================*/ page_zip_des_t* page_zip,/*!< in: data, ssize; out: m_start, m_end, m_nonempty, n_blobs */ page_t* page, /*!< out: uncompressed page, may be trashed */ ibool all) /*!< in: TRUE=decompress the whole page; FALSE=verify but do not copy some page header fields that should not change after page creation */ __attribute__((nonnull(1,2))); #ifdef UNIV_DEBUG /**********************************************************************//** Validate a compressed page descriptor. @return TRUE if ok */ UNIV_INLINE ibool page_zip_simple_validate( /*=====================*/ const page_zip_des_t* page_zip); /*!< in: compressed page descriptor */ #endif /* UNIV_DEBUG */ #ifdef UNIV_ZIP_DEBUG /**********************************************************************//** Check that the compressed and decompressed pages match. @return TRUE if valid, FALSE if not */ UNIV_INTERN ibool page_zip_validate_low( /*==================*/ const page_zip_des_t* page_zip,/*!< in: compressed page */ const page_t* page, /*!< in: uncompressed page */ ibool sloppy) /*!< in: FALSE=strict, TRUE=ignore the MIN_REC_FLAG */ __attribute__((nonnull)); /**********************************************************************//** Check that the compressed and decompressed pages match. */ UNIV_INTERN ibool page_zip_validate( /*==============*/ const page_zip_des_t* page_zip,/*!< in: compressed page */ const page_t* page) /*!< in: uncompressed page */ __attribute__((nonnull)); #endif /* UNIV_ZIP_DEBUG */ /**********************************************************************//** Determine how big record can be inserted without recompressing the page. @return a positive number indicating the maximum size of a record whose insertion is guaranteed to succeed, or zero or negative */ UNIV_INLINE lint page_zip_max_ins_size( /*==================*/ const page_zip_des_t* page_zip,/*!< in: compressed page */ ibool is_clust)/*!< in: TRUE if clustered index */ __attribute__((nonnull, pure)); /**********************************************************************//** Determine if enough space is available in the modification log. @return TRUE if page_zip_write_rec() will succeed */ UNIV_INLINE ibool page_zip_available( /*===============*/ const page_zip_des_t* page_zip,/*!< in: compressed page */ ibool is_clust,/*!< in: TRUE if clustered index */ ulint length, /*!< in: combined size of the record */ ulint create) /*!< in: nonzero=add the record to the heap */ __attribute__((nonnull, pure)); /**********************************************************************//** Write data to the uncompressed header portion of a page. The data must already have been written to the uncompressed page. */ UNIV_INLINE void page_zip_write_header( /*==================*/ page_zip_des_t* page_zip,/*!< in/out: compressed page */ const byte* str, /*!< in: address on the uncompressed page */ ulint length, /*!< in: length of the data */ mtr_t* mtr) /*!< in: mini-transaction, or NULL */ __attribute__((nonnull(1,2))); /**********************************************************************//** Write an entire record on the compressed page. The data must already have been written to the uncompressed page. */ UNIV_INTERN void page_zip_write_rec( /*===============*/ page_zip_des_t* page_zip,/*!< in/out: compressed page */ const byte* rec, /*!< in: record being written */ dict_index_t* index, /*!< in: the index the record belongs to */ const ulint* offsets,/*!< in: rec_get_offsets(rec, index) */ ulint create) /*!< in: nonzero=insert, zero=update */ __attribute__((nonnull)); /***********************************************************//** Parses a log record of writing a BLOB pointer of a record. @return end of log record or NULL */ UNIV_INTERN byte* page_zip_parse_write_blob_ptr( /*==========================*/ byte* ptr, /*!< in: redo log buffer */ byte* end_ptr,/*!< in: redo log buffer end */ page_t* page, /*!< in/out: uncompressed page */ page_zip_des_t* page_zip);/*!< in/out: compressed page */ /**********************************************************************//** Write a BLOB pointer of a record on the leaf page of a clustered index. The information must already have been updated on the uncompressed page. */ UNIV_INTERN void page_zip_write_blob_ptr( /*====================*/ page_zip_des_t* page_zip,/*!< in/out: compressed page */ const byte* rec, /*!< in/out: record whose data is being written */ dict_index_t* index, /*!< in: index of the page */ const ulint* offsets,/*!< in: rec_get_offsets(rec, index) */ ulint n, /*!< in: column index */ mtr_t* mtr) /*!< in: mini-transaction handle, or NULL if no logging is needed */ __attribute__((nonnull(1,2,3,4))); /***********************************************************//** Parses a log record of writing the node pointer of a record. @return end of log record or NULL */ UNIV_INTERN byte* page_zip_parse_write_node_ptr( /*==========================*/ byte* ptr, /*!< in: redo log buffer */ byte* end_ptr,/*!< in: redo log buffer end */ page_t* page, /*!< in/out: uncompressed page */ page_zip_des_t* page_zip);/*!< in/out: compressed page */ /**********************************************************************//** Write the node pointer of a record on a non-leaf compressed page. */ UNIV_INTERN void page_zip_write_node_ptr( /*====================*/ page_zip_des_t* page_zip,/*!< in/out: compressed page */ byte* rec, /*!< in/out: record */ ulint size, /*!< in: data size of rec */ ulint ptr, /*!< in: node pointer */ mtr_t* mtr) /*!< in: mini-transaction, or NULL */ __attribute__((nonnull(1,2))); /**********************************************************************//** Write the trx_id and roll_ptr of a record on a B-tree leaf node page. */ UNIV_INTERN void page_zip_write_trx_id_and_roll_ptr( /*===============================*/ page_zip_des_t* page_zip,/*!< in/out: compressed page */ byte* rec, /*!< in/out: record */ const ulint* offsets,/*!< in: rec_get_offsets(rec, index) */ ulint trx_id_col,/*!< in: column number of TRX_ID in rec */ trx_id_t trx_id, /*!< in: transaction identifier */ roll_ptr_t roll_ptr)/*!< in: roll_ptr */ __attribute__((nonnull)); /**********************************************************************//** Write the "deleted" flag of a record on a compressed page. The flag must already have been written on the uncompressed page. */ UNIV_INTERN void page_zip_rec_set_deleted( /*=====================*/ page_zip_des_t* page_zip,/*!< in/out: compressed page */ const byte* rec, /*!< in: record on the uncompressed page */ ulint flag) /*!< in: the deleted flag (nonzero=TRUE) */ __attribute__((nonnull)); /**********************************************************************//** Write the "owned" flag of a record on a compressed page. The n_owned field must already have been written on the uncompressed page. */ UNIV_INTERN void page_zip_rec_set_owned( /*===================*/ page_zip_des_t* page_zip,/*!< in/out: compressed page */ const byte* rec, /*!< in: record on the uncompressed page */ ulint flag) /*!< in: the owned flag (nonzero=TRUE) */ __attribute__((nonnull)); /**********************************************************************//** Insert a record to the dense page directory. */ UNIV_INTERN void page_zip_dir_insert( /*================*/ page_zip_des_t* page_zip,/*!< in/out: compressed page */ const byte* prev_rec,/*!< in: record after which to insert */ const byte* free_rec,/*!< in: record from which rec was allocated, or NULL */ byte* rec); /*!< in: record to insert */ /**********************************************************************//** Shift the dense page directory and the array of BLOB pointers when a record is deleted. */ UNIV_INTERN void page_zip_dir_delete( /*================*/ page_zip_des_t* page_zip,/*!< in/out: compressed page */ byte* rec, /*!< in: deleted record */ dict_index_t* index, /*!< in: index of rec */ const ulint* offsets,/*!< in: rec_get_offsets(rec) */ const byte* free) /*!< in: previous start of the free list */ __attribute__((nonnull(1,2,3,4))); /**********************************************************************//** Add a slot to the dense page directory. */ UNIV_INTERN void page_zip_dir_add_slot( /*==================*/ page_zip_des_t* page_zip, /*!< in/out: compressed page */ ulint is_clustered) /*!< in: nonzero for clustered index, zero for others */ __attribute__((nonnull)); /***********************************************************//** Parses a log record of writing to the header of a page. @return end of log record or NULL */ UNIV_INTERN byte* page_zip_parse_write_header( /*========================*/ byte* ptr, /*!< in: redo log buffer */ byte* end_ptr,/*!< in: redo log buffer end */ page_t* page, /*!< in/out: uncompressed page */ page_zip_des_t* page_zip);/*!< in/out: compressed page */ /**********************************************************************//** Write data to the uncompressed header portion of a page. The data must already have been written to the uncompressed page. However, the data portion of the uncompressed page may differ from the compressed page when a record is being inserted in page_cur_insert_rec_low(). */ UNIV_INLINE void page_zip_write_header( /*==================*/ page_zip_des_t* page_zip,/*!< in/out: compressed page */ const byte* str, /*!< in: address on the uncompressed page */ ulint length, /*!< in: length of the data */ mtr_t* mtr) /*!< in: mini-transaction, or NULL */ __attribute__((nonnull(1,2))); /**********************************************************************//** Reorganize and compress a page. This is a low-level operation for compressed pages, to be used when page_zip_compress() fails. On success, a redo log entry MLOG_ZIP_PAGE_COMPRESS will be written. The function btr_page_reorganize() should be preferred whenever possible. IMPORTANT: if page_zip_reorganize() is invoked on a leaf page of a non-clustered index, the caller must update the insert buffer free bits in the same mini-transaction in such a way that the modification will be redo-logged. @return TRUE on success, FALSE on failure; page_zip will be left intact on failure, but page will be overwritten. */ UNIV_INTERN ibool page_zip_reorganize( /*================*/ buf_block_t* block, /*!< in/out: page with compressed page; on the compressed page, in: size; out: data, n_blobs, m_start, m_end, m_nonempty */ dict_index_t* index, /*!< in: index of the B-tree node */ mtr_t* mtr) /*!< in: mini-transaction */ __attribute__((nonnull)); #ifndef UNIV_HOTBACKUP /**********************************************************************//** Copy the records of a page byte for byte. Do not copy the page header or trailer, except those B-tree header fields that are directly related to the storage of records. Also copy PAGE_MAX_TRX_ID. NOTE: The caller must update the lock table and the adaptive hash index. */ UNIV_INTERN void page_zip_copy_recs( /*===============*/ page_zip_des_t* page_zip, /*!< out: copy of src_zip (n_blobs, m_start, m_end, m_nonempty, data[0..size-1]) */ page_t* page, /*!< out: copy of src */ const page_zip_des_t* src_zip, /*!< in: compressed page */ const page_t* src, /*!< in: page */ dict_index_t* index, /*!< in: index of the B-tree */ mtr_t* mtr) /*!< in: mini-transaction */ __attribute__((nonnull(1,2,3,4))); #endif /* !UNIV_HOTBACKUP */ /**********************************************************************//** Parses a log record of compressing an index page. @return end of log record or NULL */ UNIV_INTERN byte* page_zip_parse_compress( /*====================*/ byte* ptr, /*!< in: buffer */ byte* end_ptr,/*!< in: buffer end */ page_t* page, /*!< out: uncompressed page */ page_zip_des_t* page_zip)/*!< out: compressed page */ __attribute__((nonnull(1,2))); /**********************************************************************//** Calculate the compressed page checksum. @return page checksum */ UNIV_INTERN ulint page_zip_calc_checksum( /*===================*/ const void* data, /*!< in: compressed page */ ulint size) /*!< in: size of compressed page */ __attribute__((nonnull)); /************************************************************************** Reset variables. */ UNIV_INTERN void page_zip_var_init(void); /*===================*/ #ifndef UNIV_HOTBACKUP /** Check if a pointer to an uncompressed page matches a compressed page. @param ptr pointer to an uncompressed page frame @param page_zip compressed page descriptor @return TRUE if ptr and page_zip refer to the same block */ # define PAGE_ZIP_MATCH(ptr, page_zip) \ (buf_frame_get_page_zip(ptr) == (page_zip)) #else /* !UNIV_HOTBACKUP */ /** Check if a pointer to an uncompressed page matches a compressed page. @param ptr pointer to an uncompressed page frame @param page_zip compressed page descriptor @return TRUE if ptr and page_zip refer to the same block */ # define PAGE_ZIP_MATCH(ptr, page_zip) \ (page_align(ptr) + UNIV_PAGE_SIZE == (page_zip)->data) #endif /* !UNIV_HOTBACKUP */ #ifdef UNIV_MATERIALIZE # undef UNIV_INLINE # define UNIV_INLINE UNIV_INLINE_ORIGINAL #endif #ifndef UNIV_NONINL # include "page0zip.ic" #endif #endif /* page0zip_h */ haildb-2.3.2/include/data0type.h0000644000175000017500000004171311513177357017340 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/data0type.h Data types Created 1/16/1996 Heikki Tuuri *******************************************************/ #ifndef data0type_h #define data0type_h #include "univ.i" extern ulint data_client_default_charset_coll; #define DATA_CLIENT_LATIN1_SWEDISH_CHARSET_COLL 8 #define DATA_CLIENT_BINARY_CHARSET_COLL 63 /* SQL data type struct */ typedef struct dtype_struct dtype_t; /*-------------------------------------------*/ /* The 'MAIN TYPE' of a column */ #define DATA_VARCHAR 1 /* character varying of the latin1_swedish_ci charset-collation */ #define DATA_CHAR 2 /* fixed length character of the latin1_swedish_ci charset-collation */ #define DATA_FIXBINARY 3 /* binary string of fixed length */ #define DATA_BINARY 4 /* binary string */ #define DATA_BLOB 5 /* binary large object, or a TEXT type; if prtype & DATA_BINARY_TYPE == 0, then this is actually a TEXT column (or a BLOB created with < 4.0.14; since column prefix indexes came only in 4.0.14, the missing flag in BLOBs created before that does not cause any harm) */ #define DATA_INT 6 /* integer: can be any size 1 - 8 bytes */ #define DATA_SYS_CHILD 7 /* address of the child page in node pointer */ #define DATA_SYS 8 /* system column */ /* Data types >= DATA_FLOAT must be compared using the whole field, not as binary strings */ #define DATA_FLOAT 9 #define DATA_DOUBLE 10 #define DATA_DECIMAL 11 /* decimal number stored as an ASCII string */ #define DATA_VARCLIENT 12 /* any charset varying length char */ #define DATA_CLIENT 13 /* any charset fixed length char */ /* NOTE that 4.1.1 used DATA_CLIENT and DATA_VARCLIENT for all character sets, and the charset-collation for tables created with it can also be latin1_swedish_ci */ #define DATA_MTYPE_MAX 63 /* dtype_store_for_order_and_null_size() requires the values are <= 63 */ /*-------------------------------------------*/ /* The 'PRECISE TYPE' of a column */ /* User tables have the following convention: - In the least significant byte in the precise type we store the user type code (not applicable for system columns). - In the second least significant byte we OR flags DATA_NOT_NULL, DATA_UNSIGNED, DATA_BINARY_TYPE. - In the third least significant byte of the precise type of string types we store the user charset-collation code. In DATA_BLOB columns created with < 4.0.14 we do not actually know if it is a BLOB or a TEXT column. Since there are no indexes on prefixes of BLOB or TEXT columns in < 4.0.14, this is no problem, though. If the stored charset code is 0 in the system table SYS_COLUMNS of InnoDB, that means that the default charset of this installation should be used. When loading a table definition from the system tables to the InnoDB data dictionary cache in main memory, if the stored charset-collation is 0, and the type is a non-binary string, replace that 0 by the default charset-collation code of the installation. In short, in old tables, the charset-collation code in the system tables on disk can be 0, but in in-memory data structures (dtype_t), the charset-collation code is always != 0 for non-binary string types. In new tables, in binary string types, the charset-collation code is the user code for the 'binary charset', that is, != 0. For binary string types and for DATA_CHAR, DATA_VARCHAR, and for those DATA_BLOB which are binary or have the charset-collation latin1_swedish_ci, InnoDB performs all comparisons internally, without resorting to the user comparison functions. This is to save CPU time. InnoDB's own internal system tables have different precise types for their columns, and for them the precise type is usually not used at all. */ #define DATA_ENGLISH 4 /* English language character string: only used for InnoDB's own system tables */ #define DATA_ERROR 111 /* Used for error checking and debugging */ #define DATA_CLIENT_TYPE_MASK 255 /* AND with this mask to extract the user type from the precise type */ /* Precise data types for system columns and the length of those columns; NOTE: the values must run from 0 up in the order given! All codes must be less than 256 */ #define DATA_ROW_ID 0 /* row id: a dulint */ #define DATA_ROW_ID_LEN 6 /* stored length for row id */ #define DATA_TRX_ID 1 /* transaction id: 6 bytes */ #define DATA_TRX_ID_LEN 6 #define DATA_ROLL_PTR 2 /* rollback data pointer: 7 bytes */ #define DATA_ROLL_PTR_LEN 7 #define DATA_N_SYS_COLS 3 /* number of system columns defined above */ #define DATA_SYS_PRTYPE_MASK 0xF /* mask to extract the above from prtype */ /* Flags ORed to the precise data type */ #define DATA_NOT_NULL 256 /* this is ORed to the precise type when the column is declared as NOT NULL */ #define DATA_UNSIGNED 512 /* this id ORed to the precise type when we have an unsigned integer type */ #define DATA_BINARY_TYPE 1024 /* if the data type is a binary character string, this is ORed to the precise type. */ #define DATA_CUSTOM_TYPE 2048 /* first custom type starts here */ /*-------------------------------------------*/ /* This many bytes we need to store the type information affecting the alphabetical order for a single field and decide the storage size of an SQL null*/ #define DATA_ORDER_NULL_TYPE_BUF_SIZE 4 /* In the >= 4.1.x storage format we add 2 bytes more so that we can also store the charset-collation number; one byte is left unused, though */ #define DATA_NEW_ORDER_NULL_TYPE_BUF_SIZE 6 #ifndef UNIV_HOTBACKUP /*********************************************************************//** Determine how many bytes the first n characters of the given string occupy. If the string is shorter than n characters, returns the number of bytes the characters in the string occupy. @return length of the prefix, in bytes */ UNIV_INTERN ulint dtype_get_at_most_n_mbchars( /*========================*/ ulint prtype, /*!< in: precise type */ ulint mbminlen, /*!< in: minimum length of a multi-byte character */ ulint mbmaxlen, /*!< in: maximum length of a multi-byte character */ ulint prefix_len, /*!< in: length of the requested prefix, in characters, multiplied by dtype_get_mbmaxlen(dtype) */ ulint data_len, /*!< in: length of str (in bytes) */ const char* str); /*!< in: the string whose prefix length is being determined */ #endif /* !UNIV_HOTBACKUP */ /*********************************************************************//** Checks if a data main type is a string type. Also a BLOB is considered a string type. @return TRUE if string type */ UNIV_INTERN ibool dtype_is_string_type( /*=================*/ ulint mtype); /*!< in: InnoDB main data type code: DATA_CHAR, ... */ /*********************************************************************//** Checks if a type is a binary string type. Note that for tables created with < 4.0.14, we do not know if a DATA_BLOB column is a BLOB or a TEXT column. For those DATA_BLOB columns this function currently returns FALSE. @return TRUE if binary string type */ UNIV_INTERN ibool dtype_is_binary_string_type( /*========================*/ ulint mtype, /*!< in: main data type */ ulint prtype);/*!< in: precise type */ /*********************************************************************//** Checks if a type is a non-binary string type. That is, dtype_is_string_type is TRUE and dtype_is_binary_string_type is FALSE. Note that for tables created with < 4.0.14, we do not know if a DATA_BLOB column is a BLOB or a TEXT column. For those DATA_BLOB columns this function currently returns TRUE. @return TRUE if non-binary string type */ UNIV_INTERN ibool dtype_is_non_binary_string_type( /*============================*/ ulint mtype, /*!< in: main data type */ ulint prtype);/*!< in: precise type */ /*********************************************************************//** Sets a data type structure. */ UNIV_INLINE void dtype_set( /*======*/ dtype_t* type, /*!< in: type struct to init */ ulint mtype, /*!< in: main data type */ ulint prtype, /*!< in: precise type */ ulint len); /*!< in: precision of type */ /*********************************************************************//** Copies a data type structure. */ UNIV_INLINE void dtype_copy( /*=======*/ dtype_t* type1, /*!< in: type struct to copy to */ const dtype_t* type2); /*!< in: type struct to copy from */ /*********************************************************************//** Gets the SQL main data type. @return SQL main data type */ UNIV_INLINE ulint dtype_get_mtype( /*============*/ const dtype_t* type); /*!< in: data type */ /*********************************************************************//** Gets the precise data type. @return precise data type */ UNIV_INLINE ulint dtype_get_prtype( /*=============*/ const dtype_t* type); /*!< in: data type */ #ifndef UNIV_HOTBACKUP /*********************************************************************//** Compute the mbminlen and mbmaxlen members of a data type structure. */ UNIV_INLINE void dtype_get_mblen( /*============*/ ulint mtype, /*!< in: main type */ ulint prtype, /*!< in: precise type (and collation) */ ulint* mbminlen, /*!< out: minimum length of a multi-byte character */ ulint* mbmaxlen); /*!< out: maximum length of a multi-byte character */ /************************************************************************* Gets the user charset-collation code for user string types. */ UNIV_INLINE ulint dtype_get_charset_coll( /*===================*/ ulint prtype);/*!< in: precise data type */ /*********************************************************************//** Forms a precise type from the < 4.1.2 format precise type plus the charset-collation code. @return precise type, including the charset-collation code */ UNIV_INTERN ulint dtype_form_prtype( /*==============*/ ulint old_prtype, /*!< in: the user type code and the flags DATA_BINARY_TYPE etc. */ ulint charset_coll); /*!< in: user charset-collation code */ /*********************************************************************//** Gets the type length. @return fixed length of the type, in bytes, or 0 if variable-length */ UNIV_INLINE ulint dtype_get_len( /*==========*/ const dtype_t* type); /*!< in: data type */ #ifndef UNIV_HOTBACKUP /*********************************************************************//** Gets the minimum length of a character, in bytes. @return minimum length of a char, in bytes, or 0 if this is not a character type */ UNIV_INLINE ulint dtype_get_mbminlen( /*===============*/ const dtype_t* type); /*!< in: type */ /*********************************************************************//** Gets the maximum length of a character, in bytes. @return maximum length of a char, in bytes, or 0 if this is not a character type */ UNIV_INLINE ulint dtype_get_mbmaxlen( /*===============*/ const dtype_t* type); /*!< in: type */ /*********************************************************************//** Gets the padding character code for the type. @return padding character code, or ULINT_UNDEFINED if no padding specified */ UNIV_INLINE ulint dtype_get_pad_char( /*===============*/ ulint mtype, /*!< in: main type */ ulint prtype); /*!< in: precise type */ #endif /* !UNIV_HOTBACKUP */ /***********************************************************************//** Returns the size of a fixed size data type, 0 if not a fixed size type. @return fixed size, or 0 */ UNIV_INLINE ulint dtype_get_fixed_size_low( /*=====================*/ ulint mtype, /*!< in: main type */ ulint prtype, /*!< in: precise type */ ulint len, /*!< in: length */ ulint mbminlen, /*!< in: minimum length of a multibyte char */ ulint mbmaxlen, /*!< in: maximum length of a multibyte char */ ulint comp); /*!< in: nonzero=ROW_FORMAT=COMPACT */ #ifndef UNIV_HOTBACKUP /***********************************************************************//** Returns the minimum size of a data type. @return minimum size */ UNIV_INLINE ulint dtype_get_min_size_low( /*===================*/ ulint mtype, /*!< in: main type */ ulint prtype, /*!< in: precise type */ ulint len, /*!< in: length */ ulint mbminlen, /*!< in: minimum length of a multibyte char */ ulint mbmaxlen); /*!< in: maximum length of a multibyte char */ /***********************************************************************//** Returns the maximum size of a data type. Note: types in system tables may be incomplete and return incorrect information. @return maximum size */ UNIV_INLINE ulint dtype_get_max_size_low( /*===================*/ ulint mtype, /*!< in: main type */ ulint len); /*!< in: length */ #endif /* !UNIV_HOTBACKUP */ /***********************************************************************//** Returns the ROW_FORMAT=REDUNDANT stored SQL NULL size of a type. For fixed length types it is the fixed length of the type, otherwise 0. @return SQL null storage size in ROW_FORMAT=REDUNDANT */ UNIV_INLINE ulint dtype_get_sql_null_size( /*====================*/ const dtype_t* type, /*!< in: type */ ulint comp); /*!< in: nonzero=ROW_FORMAT=COMPACT */ #ifndef UNIV_HOTBACKUP /**********************************************************************//** Reads to a type the stored information which determines its alphabetical ordering and the storage size of an SQL NULL value. */ UNIV_INLINE void dtype_read_for_order_and_null_size( /*===============================*/ dtype_t* type, /*!< in: type struct */ const byte* buf); /*!< in: buffer for the stored order info */ /**********************************************************************//** Stores for a type the information which determines its alphabetical ordering and the storage size of an SQL NULL value. */ UNIV_INLINE void dtype_new_store_for_order_and_null_size( /*====================================*/ byte* buf, /*!< in: buffer for DATA_NEW_ORDER_NULL_TYPE_BUF_SIZE bytes where we store the info */ const dtype_t* type, /*!< in: type struct */ ulint prefix_len);/*!< in: prefix length to replace type->len, or 0 */ /**********************************************************************//** Reads to a type the stored information which determines its alphabetical ordering and the storage size of an SQL NULL value. This is the 4.1.x storage format. */ UNIV_INLINE void dtype_new_read_for_order_and_null_size( /*===================================*/ dtype_t* type, /*!< in: type struct */ const byte* buf); /*!< in: buffer for stored type order info */ #endif /* !UNIV_HOTBACKUP */ /*********************************************************************//** Validates a data type structure. @return TRUE if ok */ UNIV_INTERN ibool dtype_validate( /*===========*/ const dtype_t* type); /*!< in: type struct to validate */ /*********************************************************************//** Prints a data type structure. */ UNIV_INTERN void dtype_print( /*========*/ const dtype_t* type); /*!< in: type */ /************************************************************************* Gets the user type code from a dtype. @return type code; this is NOT an InnoDB type code! */ UNIV_INLINE ulint dtype_get_attrib( /*=============*/ const dtype_t* type); /*!< in: type struct */ /************************************************************************* Reset dtype variables. */ UNIV_INTERN void dtype_var_init(void); /*================*/ /* Structure for an SQL data type. If you add fields to this structure, be sure to initialize them everywhere. This structure is initialized in the following functions: dtype_set() dtype_read_for_order_and_null_size() dtype_new_read_for_order_and_null_size() sym_tab_add_null_lit() */ /* The following are used in two places, dtype_t and dict_field_t, we want to ensure that they are identical and also want to ensure that all bit-fields can be packed tightly in both structs. */ #define DTYPE_FIELDS \ unsigned mtype:8; /*!< main data type */ \ unsigned prtype:24; /*!< precise type; user data \ type, charset code, flags to \ indicate nullability, \ signedness, whether this is a \ binary string */ \ /* the remaining fields do not affect alphabetical ordering: */ \ unsigned len:16; /*!< length */ \ unsigned mbminlen:2; /*!< minimum length of a \ character, in bytes */ \ unsigned mbmaxlen:3; /*!< maximum length of bytes \ to store the string length) */ struct dtype_struct { DTYPE_FIELDS #endif /* !UNIV_HOTBACKUP */ }; #ifndef UNIV_NONINL #include "data0type.ic" #endif #endif haildb-2.3.2/include/row0types.h0000644000175000017500000000574111513177357017422 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/row0types.h Row operation global types Created 12/27/1996 Heikki Tuuri *******************************************************/ #ifndef row0types_h #define row0types_h typedef struct plan_struct plan_t; typedef struct upd_struct upd_t; typedef struct upd_field_struct upd_field_t; typedef struct upd_node_struct upd_node_t; typedef struct del_node_struct del_node_t; typedef struct ins_node_struct ins_node_t; typedef struct sel_node_struct sel_node_t; typedef struct open_node_struct open_node_t; typedef struct fetch_node_struct fetch_node_t; typedef struct row_printf_node_struct row_printf_node_t; typedef struct sel_buf_struct sel_buf_t; typedef struct undo_node_struct undo_node_t; typedef struct purge_node_struct purge_node_t; typedef struct row_ext_struct row_ext_t; typedef void* table_handle_t; typedef struct row_prebuilt_struct row_prebuilt_t; /* Insert node types */ typedef enum ib_ins_mode_enum { /* The first two modes are only used by the internal SQL parser */ INS_SEARCHED, /* INSERT INTO ... SELECT ... */ INS_VALUES, /* INSERT INTO ... VALUES ... */ INS_DIRECT /* Insert the row directly */ } ib_ins_mode_t; /* Node execution states */ typedef enum ib_ins_state_enum { INS_NODE_SET_IX_LOCK = 1, /* we should set an IX lock on table */ INS_NODE_ALLOC_ROW_ID, /* row id should be allocated */ INS_NODE_INSERT_ENTRIES /* index entries should be built and inserted */ } ib_ins_state_t; /* Flags for positioning the cursor. */ typedef enum ib_cur_op_enum { ROW_SEL_MOVETO, /* required when openeing a cursor */ ROW_SEL_NEXT, /* move to next record */ ROW_SEL_PREV /* move to previous record */ } ib_cur_op_t; /* Various match modes when fetching a record from a table. */ typedef enum ib_match_enum { ROW_SEL_DEFAULT, /* closest match possible */ ROW_SEL_EXACT, /* search using a complete key value */ ROW_SEL_EXACT_PREFIX /* search using a key prefix which must match to rows: the prefix may contain an incomplete field (the last field in prefix may be just a prefix of a fixed length column) */ } ib_match_t; #endif haildb-2.3.2/include/trx0rseg.ic0000644000175000017500000001021011513177357017353 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/trx0rseg.ic Rollback segment Created 3/26/1996 Heikki Tuuri *******************************************************/ #include "srv0srv.h" #include "mtr0log.h" /******************************************************************//** Gets a rollback segment header. @return rollback segment header, page x-latched */ UNIV_INLINE trx_rsegf_t* trx_rsegf_get( /*==========*/ ulint space, /*!< in: space where placed */ ulint zip_size, /*!< in: compressed page size in bytes or 0 for uncompressed pages */ ulint page_no, /*!< in: page number of the header */ mtr_t* mtr) /*!< in: mtr */ { buf_block_t* block; trx_rsegf_t* header; block = buf_page_get(space, zip_size, page_no, RW_X_LATCH, mtr); buf_block_dbg_add_level(block, SYNC_RSEG_HEADER); header = TRX_RSEG + buf_block_get_frame(block); return(header); } /******************************************************************//** Gets a newly created rollback segment header. @return rollback segment header, page x-latched */ UNIV_INLINE trx_rsegf_t* trx_rsegf_get_new( /*==============*/ ulint space, /*!< in: space where placed */ ulint zip_size, /*!< in: compressed page size in bytes or 0 for uncompressed pages */ ulint page_no, /*!< in: page number of the header */ mtr_t* mtr) /*!< in: mtr */ { buf_block_t* block; trx_rsegf_t* header; block = buf_page_get(space, zip_size, page_no, RW_X_LATCH, mtr); buf_block_dbg_add_level(block, SYNC_RSEG_HEADER_NEW); header = TRX_RSEG + buf_block_get_frame(block); return(header); } /***************************************************************//** Gets the file page number of the nth undo log slot. @return page number of the undo log segment */ UNIV_INLINE ulint trx_rsegf_get_nth_undo( /*===================*/ trx_rsegf_t* rsegf, /*!< in: rollback segment header */ ulint n, /*!< in: index of slot */ mtr_t* mtr) /*!< in: mtr */ { if (UNIV_UNLIKELY(n >= TRX_RSEG_N_SLOTS)) { ib_logger(ib_stream, "InnoDB: Error: trying to get slot %lu of rseg\n", (ulong) n); ut_error; } return(mtr_read_ulint(rsegf + TRX_RSEG_UNDO_SLOTS + n * TRX_RSEG_SLOT_SIZE, MLOG_4BYTES, mtr)); } /***************************************************************//** Sets the file page number of the nth undo log slot. */ UNIV_INLINE void trx_rsegf_set_nth_undo( /*===================*/ trx_rsegf_t* rsegf, /*!< in: rollback segment header */ ulint n, /*!< in: index of slot */ ulint page_no,/*!< in: page number of the undo log segment */ mtr_t* mtr) /*!< in: mtr */ { if (UNIV_UNLIKELY(n >= TRX_RSEG_N_SLOTS)) { ib_logger(ib_stream, "InnoDB: Error: trying to set slot %lu of rseg\n", (ulong) n); ut_error; } mlog_write_ulint(rsegf + TRX_RSEG_UNDO_SLOTS + n * TRX_RSEG_SLOT_SIZE, page_no, MLOG_4BYTES, mtr); } /****************************************************************//** Looks for a free slot for an undo log segment. @return slot index or ULINT_UNDEFINED if not found */ UNIV_INLINE ulint trx_rsegf_undo_find_free( /*=====================*/ trx_rsegf_t* rsegf, /*!< in: rollback segment header */ mtr_t* mtr) /*!< in: mtr */ { ulint i; ulint page_no; for (i = 0; i < TRX_RSEG_N_SLOTS; i++) { page_no = trx_rsegf_get_nth_undo(rsegf, i, mtr); if (page_no == FIL_NULL) { return(i); } } return(ULINT_UNDEFINED); } haildb-2.3.2/include/row0row.ic0000644000175000017500000000677211513177357017236 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/row0row.ic General row routines Created 4/20/1996 Heikki Tuuri *******************************************************/ #include "dict0dict.h" #include "rem0rec.h" #include "trx0undo.h" /*********************************************************************//** Reads the trx id field from a clustered index record. @return value of the field */ UNIV_INLINE trx_id_t row_get_rec_trx_id( /*===============*/ const rec_t* rec, /*!< in: record */ dict_index_t* dict_index, /*!< in: clustered index */ const ulint* offsets)/*!< in: rec_get_offsets(rec, index) */ { ulint offset; ut_ad(dict_index_is_clust(dict_index)); ut_ad(rec_offs_validate(rec, dict_index, offsets)); offset = dict_index->trx_id_offset; if (!offset) { offset = row_get_trx_id_offset(rec, dict_index, offsets); } return(trx_read_trx_id(rec + offset)); } /*********************************************************************//** Reads the roll pointer field from a clustered index record. @return value of the field */ UNIV_INLINE roll_ptr_t row_get_rec_roll_ptr( /*=================*/ const rec_t* rec, /*!< in: record */ dict_index_t* dict_index, /*!< in: clustered index */ const ulint* offsets)/*!< in: rec_get_offsets(rec, index) */ { ulint offset; ut_ad(dict_index_is_clust(dict_index)); ut_ad(rec_offs_validate(rec, dict_index, offsets)); offset = dict_index->trx_id_offset; if (!offset) { offset = row_get_trx_id_offset(rec, dict_index, offsets); } return(trx_read_roll_ptr(rec + offset + DATA_TRX_ID_LEN)); } /*******************************************************************//** Builds from a secondary index record a row reference with which we can search the clustered index record. */ UNIV_INLINE void row_build_row_ref_fast( /*===================*/ dtuple_t* ref, /*!< in/out: typed data tuple where the reference is built */ const ulint* map, /*!< in: array of field numbers in rec telling how ref should be built from the fields of rec */ const rec_t* rec, /*!< in: record in the index; must be preserved while ref is used, as we do not copy field values to heap */ const ulint* offsets)/*!< in: array returned by rec_get_offsets() */ { dfield_t* dfield; const byte* field; ulint len; ulint ref_len; ulint field_no; ulint i; ut_ad(rec_offs_validate(rec, NULL, offsets)); ut_ad(!rec_offs_any_extern(offsets)); ref_len = dtuple_get_n_fields(ref); for (i = 0; i < ref_len; i++) { dfield = dtuple_get_nth_field(ref, i); field_no = *(map + i); if (field_no != ULINT_UNDEFINED) { field = rec_get_nth_field(rec, offsets, field_no, &len); dfield_set_data(dfield, field, len); } } } haildb-2.3.2/include/ha0ha.h0000644000175000017500000002204711513177357016425 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1994, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/ha0ha.h The hash table with external chains Created 8/18/1994 Heikki Tuuri *******************************************************/ #ifndef ha0ha_h #define ha0ha_h #include "univ.i" #include "hash0hash.h" #include "page0types.h" #include "buf0types.h" /*************************************************************//** Looks for an element in a hash table. @return pointer to the data of the first hash table node in chain having the fold number, NULL if not found */ UNIV_INLINE void* ha_search_and_get_data( /*===================*/ hash_table_t* table, /*!< in: hash table */ ulint fold); /*!< in: folded value of the searched data */ /*********************************************************//** Looks for an element when we know the pointer to the data and updates the pointer to data if found. */ UNIV_INTERN void ha_search_and_update_if_found_func( /*===============================*/ hash_table_t* table, /*!< in/out: hash table */ ulint fold, /*!< in: folded value of the searched data */ void* data, /*!< in: pointer to the data */ #if defined UNIV_AHI_DEBUG || defined UNIV_DEBUG buf_block_t* new_block,/*!< in: block containing new_data */ #endif /* UNIV_AHI_DEBUG || UNIV_DEBUG */ void* new_data);/*!< in: new pointer to the data */ #if defined UNIV_AHI_DEBUG || defined UNIV_DEBUG /** Looks for an element when we know the pointer to the data and updates the pointer to data if found. @param table in/out: hash table @param fold in: folded value of the searched data @param data in: pointer to the data @param new_block in: block containing new_data @param new_data in: new pointer to the data */ # define ha_search_and_update_if_found(table,fold,data,new_block,new_data) \ ha_search_and_update_if_found_func(table,fold,data,new_block,new_data) #else /* UNIV_AHI_DEBUG || UNIV_DEBUG */ /** Looks for an element when we know the pointer to the data and updates the pointer to data if found. @param table in/out: hash table @param fold in: folded value of the searched data @param data in: pointer to the data @param new_block ignored: block containing new_data @param new_data in: new pointer to the data */ # define ha_search_and_update_if_found(table,fold,data,new_block,new_data) \ ha_search_and_update_if_found_func(table,fold,data,new_data) #endif /* UNIV_AHI_DEBUG || UNIV_DEBUG */ /*************************************************************//** Creates a hash table with at least n array cells. The actual number of cells is chosen to be a prime number slightly bigger than n. @return own: created table */ UNIV_INTERN hash_table_t* ha_create_func( /*===========*/ ulint n, /*!< in: number of array cells */ #ifdef UNIV_SYNC_DEBUG ulint mutex_level, /*!< in: level of the mutexes in the latching order: this is used in the debug version */ #endif /* UNIV_SYNC_DEBUG */ ulint n_mutexes); /*!< in: number of mutexes to protect the hash table: must be a power of 2, or 0 */ #ifdef UNIV_SYNC_DEBUG /** Creates a hash table. @return own: created table @param n_c in: number of array cells. The actual number of cells is chosen to be a slightly bigger prime number. @param level in: level of the mutexes in the latching order @param n_m in: number of mutexes to protect the hash table; must be a power of 2, or 0 */ # define ha_create(n_c,n_m,level) ha_create_func(n_c,level,n_m) #else /* UNIV_SYNC_DEBUG */ /** Creates a hash table. @return own: created table @param n_c in: number of array cells. The actual number of cells is chosen to be a slightly bigger prime number. @param level in: level of the mutexes in the latching order @param n_m in: number of mutexes to protect the hash table; must be a power of 2, or 0 */ # define ha_create(n_c,n_m,level) ha_create_func(n_c,n_m) #endif /* UNIV_SYNC_DEBUG */ /*************************************************************//** Empties a hash table and frees the memory heaps. */ UNIV_INTERN void ha_clear( /*=====*/ hash_table_t* table); /*!< in, own: hash table */ /*************************************************************//** Inserts an entry into a hash table. If an entry with the same fold number is found, its node is updated to point to the new data, and no new node is inserted. @return TRUE if succeed, FALSE if no more memory could be allocated */ UNIV_INTERN ibool ha_insert_for_fold_func( /*====================*/ hash_table_t* table, /*!< in: hash table */ ulint fold, /*!< in: folded value of data; if a node with the same fold value already exists, it is updated to point to the same data, and no new node is created! */ #if defined UNIV_AHI_DEBUG || defined UNIV_DEBUG buf_block_t* block, /*!< in: buffer block containing the data */ #endif /* UNIV_AHI_DEBUG || UNIV_DEBUG */ void* data); /*!< in: data, must not be NULL */ #if defined UNIV_AHI_DEBUG || defined UNIV_DEBUG /** Inserts an entry into a hash table. If an entry with the same fold number is found, its node is updated to point to the new data, and no new node is inserted. @return TRUE if succeed, FALSE if no more memory could be allocated @param t in: hash table @param f in: folded value of data @param b in: buffer block containing the data @param d in: data, must not be NULL */ # define ha_insert_for_fold(t,f,b,d) ha_insert_for_fold_func(t,f,b,d) #else /* UNIV_AHI_DEBUG || UNIV_DEBUG */ /** Inserts an entry into a hash table. If an entry with the same fold number is found, its node is updated to point to the new data, and no new node is inserted. @return TRUE if succeed, FALSE if no more memory could be allocated @param t in: hash table @param f in: folded value of data @param b ignored: buffer block containing the data @param d in: data, must not be NULL */ # define ha_insert_for_fold(t,f,b,d) ha_insert_for_fold_func(t,f,d) #endif /* UNIV_AHI_DEBUG || UNIV_DEBUG */ /*********************************************************//** Looks for an element when we know the pointer to the data and deletes it from the hash table if found. @return TRUE if found */ UNIV_INLINE ibool ha_search_and_delete_if_found( /*==========================*/ hash_table_t* table, /*!< in: hash table */ ulint fold, /*!< in: folded value of the searched data */ void* data); /*!< in: pointer to the data */ #ifndef UNIV_HOTBACKUP /*****************************************************************//** Removes from the chain determined by fold all nodes whose data pointer points to the page given. */ UNIV_INTERN void ha_remove_all_nodes_to_page( /*========================*/ hash_table_t* table, /*!< in: hash table */ ulint fold, /*!< in: fold value */ const page_t* page); /*!< in: buffer page */ /*************************************************************//** Validates a given range of the cells in hash table. @return TRUE if ok */ UNIV_INTERN ibool ha_validate( /*========*/ hash_table_t* table, /*!< in: hash table */ ulint start_index, /*!< in: start index */ ulint end_index); /*!< in: end index */ /*************************************************************//** Prints info of a hash table. */ UNIV_INTERN void ha_print_info( /*==========*/ ib_stream_t ib_stream, /*!< in: stream where to print */ hash_table_t* table); /*!< in: hash table */ #endif /* !UNIV_HOTBACKUP */ /** The hash table external chain node */ typedef struct ha_node_struct ha_node_t; /** The hash table external chain node */ struct ha_node_struct { ha_node_t* next; /*!< next chain node or NULL if none */ #if defined UNIV_AHI_DEBUG || defined UNIV_DEBUG buf_block_t* block; /*!< buffer block containing the data, or NULL */ #endif /* UNIV_AHI_DEBUG || UNIV_DEBUG */ void* data; /*!< pointer to the data */ ulint fold; /*!< fold value for the data */ }; #ifndef UNIV_HOTBACKUP /** Assert that the current thread is holding the mutex protecting a hash bucket corresponding to a fold value. @param table in: hash table @param fold in: fold value */ # define ASSERT_HASH_MUTEX_OWN(table, fold) \ ut_ad(!(table)->mutexes || mutex_own(hash_get_mutex(table, fold))) #else /* !UNIV_HOTBACKUP */ /** Assert that the current thread is holding the mutex protecting a hash bucket corresponding to a fold value. @param table in: hash table @param fold in: fold value */ # define ASSERT_HASH_MUTEX_OWN(table, fold) ((void) 0) #endif /* !UNIV_HOTBACKUP */ #ifndef UNIV_NONINL #include "ha0ha.ic" #endif #endif haildb-2.3.2/include/mtr0types.h0000644000175000017500000000216511513177357017412 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/mtr0types.h Mini-transaction buffer global types Created 11/26/1995 Heikki Tuuri *******************************************************/ #ifndef mtr0types_h #define mtr0types_h typedef struct mtr_struct mtr_t; #endif haildb-2.3.2/include/hash0hash.h0000644000175000017500000003162111513177357017311 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1997, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/hash0hash.h The simple hash table utility Created 5/20/1997 Heikki Tuuri *******************************************************/ #ifndef hash0hash_h #define hash0hash_h #include "univ.i" #include "mem0mem.h" #ifndef UNIV_HOTBACKUP # include "sync0sync.h" #endif /* !UNIV_HOTBACKUP */ typedef struct hash_table_struct hash_table_t; typedef struct hash_cell_struct hash_cell_t; typedef void* hash_node_t; /* To avoid symbol name clashes. */ #define hash_create ib_hash_create /*************************************************************//** Creates a hash table with >= n array cells. The actual number of cells is chosen to be a prime number slightly bigger than n. @return own: created table */ UNIV_INTERN hash_table_t* hash_create( /*========*/ ulint n); /*!< in: number of array cells */ #ifndef UNIV_HOTBACKUP /*************************************************************//** Creates a mutex array to protect a hash table. */ UNIV_INTERN void hash_create_mutexes_func( /*=====================*/ hash_table_t* table, /*!< in: hash table */ #ifdef UNIV_SYNC_DEBUG ulint sync_level, /*!< in: latching order level of the mutexes: used in the debug version */ #endif /* UNIV_SYNC_DEBUG */ ulint n_mutexes); /*!< in: number of mutexes */ #ifdef UNIV_SYNC_DEBUG # define hash_create_mutexes(t,n,level) hash_create_mutexes_func(t,level,n) #else /* UNIV_SYNC_DEBUG */ # define hash_create_mutexes(t,n,level) hash_create_mutexes_func(t,n) #endif /* UNIV_SYNC_DEBUG */ #endif /* !UNIV_HOTBACKUP */ /*************************************************************//** Frees a mutex array created with hash_create_mutexes_func(). */ UNIV_INTERN void hash_free_mutexes_func( /*===================*/ hash_table_t* table); /*!< in,own: hash table */ # define hash_free_mutexes(t) hash_free_mutexes_func(t) /***************************************************************** Frees a hash table. */ UNIV_INTERN void hash_table_free( /*============*/ hash_table_t* table); /*!< in, own: hash table */ /**************************************************************//** Calculates the hash value from a folded value. @return hashed value */ UNIV_INLINE ulint hash_calc_hash( /*===========*/ ulint fold, /*!< in: folded value */ hash_table_t* table); /*!< in: hash table */ #ifndef UNIV_HOTBACKUP /********************************************************************//** Assert that the mutex for the table in a hash operation is owned. */ # define HASH_ASSERT_OWNED(TABLE, FOLD) \ ut_ad(!(TABLE)->mutexes || mutex_own(hash_get_mutex(TABLE, FOLD))); #else /* !UNIV_HOTBACKUP */ # define HASH_ASSERT_OWNED(TABLE, FOLD) #endif /* !UNIV_HOTBACKUP */ /*******************************************************************//** Inserts a struct to a hash table. */ #define HASH_INSERT(TYPE, NAME, TABLE, FOLD, DATA)\ do {\ hash_cell_t* cell3333;\ TYPE* struct3333;\ \ HASH_ASSERT_OWNED(TABLE, FOLD)\ \ (DATA)->NAME = NULL;\ \ cell3333 = hash_get_nth_cell(TABLE, hash_calc_hash(FOLD, TABLE));\ \ if (cell3333->node == NULL) {\ cell3333->node = DATA;\ } else {\ struct3333 = (TYPE*) cell3333->node;\ \ while (struct3333->NAME != NULL) {\ \ struct3333 = (TYPE*) struct3333->NAME;\ }\ \ struct3333->NAME = DATA;\ }\ } while (0) #ifdef UNIV_HASH_DEBUG # define HASH_ASSERT_VALID(DATA) ut_a((void*) (DATA) != (void*) -1) # define HASH_INVALIDATE(DATA, NAME) DATA->NAME = (void*) -1 #else # define HASH_ASSERT_VALID(DATA) do {} while (0) # define HASH_INVALIDATE(DATA, NAME) do {} while (0) #endif /*******************************************************************//** Deletes a struct from a hash table. */ #define HASH_DELETE(TYPE, NAME, TABLE, FOLD, DATA)\ do {\ hash_cell_t* cell3333;\ TYPE* struct3333;\ \ HASH_ASSERT_OWNED(TABLE, FOLD)\ \ cell3333 = hash_get_nth_cell(TABLE, hash_calc_hash(FOLD, TABLE));\ \ if (cell3333->node == DATA) {\ HASH_ASSERT_VALID(DATA->NAME);\ cell3333->node = DATA->NAME;\ } else {\ struct3333 = (TYPE*) cell3333->node;\ \ while (struct3333->NAME != DATA) {\ \ struct3333 = (TYPE*) struct3333->NAME;\ ut_a(struct3333);\ }\ \ struct3333->NAME = DATA->NAME;\ }\ HASH_INVALIDATE(DATA, NAME);\ } while (0) /*******************************************************************//** Gets the first struct in a hash chain, NULL if none. */ #define HASH_GET_FIRST(TABLE, HASH_VAL)\ (hash_get_nth_cell(TABLE, HASH_VAL)->node) /*******************************************************************//** Gets the next struct in a hash chain, NULL if none. */ #define HASH_GET_NEXT(NAME, DATA) ((DATA)->NAME) /********************************************************************//** Looks for a struct in a hash table. */ #define HASH_SEARCH(NAME, TABLE, FOLD, TYPE, DATA, ASSERTION, TEST)\ {\ \ HASH_ASSERT_OWNED(TABLE, FOLD)\ \ (DATA) = (TYPE) HASH_GET_FIRST(TABLE, hash_calc_hash(FOLD, TABLE));\ HASH_ASSERT_VALID(DATA);\ \ while ((DATA) != NULL) {\ ASSERTION;\ if (TEST) {\ break;\ } else {\ HASH_ASSERT_VALID(HASH_GET_NEXT(NAME, DATA));\ (DATA) = (TYPE) HASH_GET_NEXT(NAME, DATA);\ }\ }\ } /********************************************************************//** Looks for an item in all hash buckets. */ #define HASH_SEARCH_ALL(NAME, TABLE, TYPE, DATA, ASSERTION, TEST) \ do { \ ulint i3333; \ \ for (i3333 = (TABLE)->n_cells; i3333--; ) { \ (DATA) = (TYPE) HASH_GET_FIRST(TABLE, i3333); \ \ while ((DATA) != NULL) { \ HASH_ASSERT_VALID(DATA); \ ASSERTION; \ \ if (TEST) { \ break; \ } \ \ (DATA) = (TYPE) HASH_GET_NEXT(NAME, DATA); \ } \ \ if ((DATA) != NULL) { \ break; \ } \ } \ } while (0) /************************************************************//** Gets the nth cell in a hash table. @return pointer to cell */ UNIV_INLINE hash_cell_t* hash_get_nth_cell( /*==============*/ hash_table_t* table, /*!< in: hash table */ ulint n); /*!< in: cell index */ /*************************************************************//** Clears a hash table so that all the cells become empty. */ UNIV_INLINE void hash_table_clear( /*=============*/ hash_table_t* table); /*!< in/out: hash table */ /*************************************************************//** Returns the number of cells in a hash table. @return number of cells */ UNIV_INLINE ulint hash_get_n_cells( /*=============*/ hash_table_t* table); /*!< in: table */ /*******************************************************************//** Deletes a struct which is stored in the heap of the hash table, and compacts the heap. The fold value must be stored in the struct NODE in a field named 'fold'. */ #define HASH_DELETE_AND_COMPACT(TYPE, NAME, TABLE, NODE)\ do {\ TYPE* node111;\ TYPE* top_node111;\ hash_cell_t* cell111;\ ulint fold111;\ \ fold111 = (NODE)->fold;\ \ HASH_DELETE(TYPE, NAME, TABLE, fold111, NODE);\ \ top_node111 = (TYPE*)mem_heap_get_top(\ hash_get_heap(TABLE, fold111),\ sizeof(TYPE));\ \ /* If the node to remove is not the top node in the heap, compact the\ heap of nodes by moving the top node in the place of NODE. */\ \ if (NODE != top_node111) {\ \ /* Copy the top node in place of NODE */\ \ *(NODE) = *top_node111;\ \ cell111 = hash_get_nth_cell(TABLE,\ hash_calc_hash(top_node111->fold, TABLE));\ \ /* Look for the pointer to the top node, to update it */\ \ if (cell111->node == top_node111) {\ /* The top node is the first in the chain */\ \ cell111->node = NODE;\ } else {\ /* We have to look for the predecessor of the top\ node */\ node111 = cell111->node;\ \ while (top_node111 != HASH_GET_NEXT(NAME, node111)) {\ \ node111 = HASH_GET_NEXT(NAME, node111);\ }\ \ /* Now we have the predecessor node */\ \ node111->NAME = NODE;\ }\ }\ \ /* Free the space occupied by the top node */\ \ mem_heap_free_top(hash_get_heap(TABLE, fold111), sizeof(TYPE));\ } while (0) #ifndef UNIV_HOTBACKUP /****************************************************************//** Move all hash table entries from OLD_TABLE to NEW_TABLE. */ #define HASH_MIGRATE(OLD_TABLE, NEW_TABLE, NODE_TYPE, PTR_NAME, FOLD_FUNC) \ do {\ ulint i2222;\ ulint cell_count2222;\ \ cell_count2222 = hash_get_n_cells(OLD_TABLE);\ \ for (i2222 = 0; i2222 < cell_count2222; i2222++) {\ NODE_TYPE* node2222 = HASH_GET_FIRST((OLD_TABLE), i2222);\ \ while (node2222) {\ NODE_TYPE* next2222 = node2222->PTR_NAME;\ ulint fold2222 = FOLD_FUNC(node2222);\ \ HASH_INSERT(NODE_TYPE, PTR_NAME, (NEW_TABLE),\ fold2222, node2222);\ \ node2222 = next2222;\ }\ }\ } while (0) /************************************************************//** Gets the mutex index for a fold value in a hash table. @return mutex number */ UNIV_INLINE ulint hash_get_mutex_no( /*==============*/ hash_table_t* table, /*!< in: hash table */ ulint fold); /*!< in: fold */ /************************************************************//** Gets the nth heap in a hash table. @return mem heap */ UNIV_INLINE mem_heap_t* hash_get_nth_heap( /*==============*/ hash_table_t* table, /*!< in: hash table */ ulint i); /*!< in: index of the heap */ /************************************************************//** Gets the heap for a fold value in a hash table. @return mem heap */ UNIV_INLINE mem_heap_t* hash_get_heap( /*==========*/ hash_table_t* table, /*!< in: hash table */ ulint fold); /*!< in: fold */ /************************************************************//** Gets the nth mutex in a hash table. @return mutex */ UNIV_INLINE mutex_t* hash_get_nth_mutex( /*===============*/ hash_table_t* table, /*!< in: hash table */ ulint i); /*!< in: index of the mutex */ /************************************************************//** Gets the mutex for a fold value in a hash table. @return mutex */ UNIV_INLINE mutex_t* hash_get_mutex( /*===========*/ hash_table_t* table, /*!< in: hash table */ ulint fold); /*!< in: fold */ /************************************************************//** Reserves the mutex for a fold value in a hash table. */ UNIV_INTERN void hash_mutex_enter( /*=============*/ hash_table_t* table, /*!< in: hash table */ ulint fold); /*!< in: fold */ /************************************************************//** Releases the mutex for a fold value in a hash table. */ UNIV_INTERN void hash_mutex_exit( /*============*/ hash_table_t* table, /*!< in: hash table */ ulint fold); /*!< in: fold */ /************************************************************//** Reserves all the mutexes of a hash table, in an ascending order. */ UNIV_INTERN void hash_mutex_enter_all( /*=================*/ hash_table_t* table); /*!< in: hash table */ /************************************************************//** Releases all the mutexes of a hash table. */ UNIV_INTERN void hash_mutex_exit_all( /*================*/ hash_table_t* table); /*!< in: hash table */ #else /* !UNIV_HOTBACKUP */ # define hash_get_heap(table, fold) ((table)->heap) # define hash_mutex_enter(table, fold) ((void) 0) # define hash_mutex_exit(table, fold) ((void) 0) #endif /* !UNIV_HOTBACKUP */ struct hash_cell_struct{ void* node; /*!< hash chain node, NULL if none */ }; /* The hash table structure */ struct hash_table_struct { #if defined UNIV_AHI_DEBUG || defined UNIV_DEBUG # ifndef UNIV_HOTBACKUP ibool adaptive;/* TRUE if this is the hash table of the adaptive hash index */ # endif /* !UNIV_HOTBACKUP */ #endif /* UNIV_AHI_DEBUG || UNIV_DEBUG */ ulint n_cells;/* number of cells in the hash table */ hash_cell_t* array; /*!< pointer to cell array */ #ifndef UNIV_HOTBACKUP ulint n_mutexes;/* if mutexes != NULL, then the number of mutexes, must be a power of 2 */ mutex_t* mutexes;/* NULL, or an array of mutexes used to protect segments of the hash table */ mem_heap_t** heaps; /*!< if this is non-NULL, hash chain nodes for external chaining can be allocated from these memory heaps; there are then n_mutexes many of these heaps */ #endif /* !UNIV_HOTBACKUP */ mem_heap_t* heap; #ifdef UNIV_DEBUG ulint magic_n; # define HASH_TABLE_MAGIC_N 76561114 #endif /* UNIV_DEBUG */ }; #ifndef UNIV_NONINL #include "hash0hash.ic" #endif #endif haildb-2.3.2/include/rem0types.h0000644000175000017500000000345011513177357017371 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1994, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /********************************************************************//** @file include/rem0types.h Record manager global types Created 5/30/1994 Heikki Tuuri *************************************************************************/ #ifndef rem0types_h #define rem0types_h /* We define the physical record simply as an array of bytes */ typedef byte rec_t; /* Maximum values for various fields (for non-blob tuples) */ #define REC_MAX_N_FIELDS (1024 - 1) #define REC_MAX_HEAP_NO (2 * 8192 - 1) #define REC_MAX_N_OWNED (16 - 1) /* REC_MAX_INDEX_COL_LEN is measured in bytes and is the maximum indexed column length (or indexed prefix length). It is set to 3*256, so that one can create a column prefix index on 256 characters of a TEXT or VARCHAR column also in the UTF-8 charset. In that charset, a character may take at most 3 bytes. This constant MUST NOT BE CHANGED, or the compatibility of InnoDB data files would be at risk! */ #define REC_MAX_INDEX_COL_LEN 768 #endif haildb-2.3.2/include/row0umod.h0000644000175000017500000000305311513177357017214 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1997, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/row0umod.h Undo modify of a row Created 2/27/1997 Heikki Tuuri *******************************************************/ #ifndef row0umod_h #define row0umod_h #include "univ.i" #include "data0data.h" #include "dict0types.h" #include "trx0types.h" #include "que0types.h" #include "row0types.h" #include "mtr0mtr.h" /***********************************************************//** Undoes a modify operation on a row of a table. @return DB_SUCCESS or error code */ UNIV_INTERN ulint row_undo_mod( /*=========*/ undo_node_t* node, /*!< in: row undo node */ que_thr_t* thr); /*!< in: query thread */ #ifndef UNIV_NONINL #include "row0umod.ic" #endif #endif haildb-2.3.2/include/fut0fut.h0000644000175000017500000000334111513177357017035 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /******************************************************************//** @file include/fut0fut.h File-based utilities Created 12/13/1995 Heikki Tuuri ***********************************************************************/ #ifndef fut0fut_h #define fut0fut_h #include "univ.i" #include "fil0fil.h" #include "mtr0mtr.h" /********************************************************************//** Gets a pointer to a file address and latches the page. @return pointer to a byte in a frame; the file page in the frame is bufferfixed and latched */ UNIV_INLINE byte* fut_get_ptr( /*========*/ ulint space, /*!< in: space id */ ulint zip_size,/*!< in: compressed page size in bytes or 0 for uncompressed pages */ fil_addr_t addr, /*!< in: file address */ ulint rw_latch, /*!< in: RW_S_LATCH, RW_X_LATCH */ mtr_t* mtr); /*!< in: mtr handle */ #ifndef UNIV_NONINL #include "fut0fut.ic" #endif #endif haildb-2.3.2/include/trx0undo.ic0000644000175000017500000002266611513177357017402 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/trx0undo.ic Transaction undo log Created 3/26/1996 Heikki Tuuri *******************************************************/ #include "data0type.h" #include "page0page.h" #ifndef UNIV_HOTBACKUP /***********************************************************************//** Builds a roll pointer. @return roll pointer */ UNIV_INLINE roll_ptr_t trx_undo_build_roll_ptr( /*====================*/ ibool is_insert, /*!< in: TRUE if insert undo log */ ulint rseg_id, /*!< in: rollback segment id */ ulint page_no, /*!< in: page number */ ulint offset) /*!< in: offset of the undo entry within page */ { #if DATA_ROLL_PTR_LEN != 7 # error "DATA_ROLL_PTR_LEN != 7" #endif ut_ad(rseg_id < 128); return(ut_dulint_create(is_insert * 128 * 256 * 256 + rseg_id * 256 * 256 + (page_no / 256) / 256, (page_no % (256 * 256)) * 256 * 256 + offset)); } /***********************************************************************//** Decodes a roll pointer. */ UNIV_INLINE void trx_undo_decode_roll_ptr( /*=====================*/ roll_ptr_t roll_ptr, /*!< in: roll pointer */ ibool* is_insert, /*!< out: TRUE if insert undo log */ ulint* rseg_id, /*!< out: rollback segment id */ ulint* page_no, /*!< out: page number */ ulint* offset) /*!< out: offset of the undo entry within page */ { ulint low; ulint high; #if DATA_ROLL_PTR_LEN != 7 # error "DATA_ROLL_PTR_LEN != 7" #endif #if TRUE != 1 # error "TRUE != 1" #endif high = ut_dulint_get_high(roll_ptr); low = ut_dulint_get_low(roll_ptr); *offset = low % (256 * 256); *is_insert = high / (256 * 256 * 128); /* TRUE == 1 */ *rseg_id = (high / (256 * 256)) % 128; *page_no = (high % (256 * 256)) * 256 * 256 + (low / 256) / 256; } /***********************************************************************//** Returns TRUE if the roll pointer is of the insert type. @return TRUE if insert undo log */ UNIV_INLINE ibool trx_undo_roll_ptr_is_insert( /*========================*/ roll_ptr_t roll_ptr) /*!< in: roll pointer */ { ulint high; #if DATA_ROLL_PTR_LEN != 7 # error "DATA_ROLL_PTR_LEN != 7" #endif #if TRUE != 1 # error "TRUE != 1" #endif high = ut_dulint_get_high(roll_ptr); return(high / (256 * 256 * 128)); } #endif /* !UNIV_HOTBACKUP */ /*****************************************************************//** Writes a roll ptr to an index page. In case that the size changes in some future version, this function should be used instead of mach_write_... */ UNIV_INLINE void trx_write_roll_ptr( /*===============*/ byte* ptr, /*!< in: pointer to memory where written */ roll_ptr_t roll_ptr) /*!< in: roll ptr */ { #if DATA_ROLL_PTR_LEN != 7 # error "DATA_ROLL_PTR_LEN != 7" #endif mach_write_to_7(ptr, roll_ptr); } /*****************************************************************//** Reads a roll ptr from an index page. In case that the roll ptr size changes in some future version, this function should be used instead of mach_read_... @return roll ptr */ UNIV_INLINE roll_ptr_t trx_read_roll_ptr( /*==============*/ const byte* ptr) /*!< in: pointer to memory from where to read */ { #if DATA_ROLL_PTR_LEN != 7 # error "DATA_ROLL_PTR_LEN != 7" #endif return(mach_read_from_7(ptr)); } #ifndef UNIV_HOTBACKUP /******************************************************************//** Gets an undo log page and x-latches it. @return pointer to page x-latched */ UNIV_INLINE page_t* trx_undo_page_get( /*==============*/ ulint space, /*!< in: space where placed */ ulint zip_size, /*!< in: compressed page size in bytes or 0 for uncompressed pages */ ulint page_no, /*!< in: page number */ mtr_t* mtr) /*!< in: mtr */ { buf_block_t* block = buf_page_get(space, zip_size, page_no, RW_X_LATCH, mtr); buf_block_dbg_add_level(block, SYNC_TRX_UNDO_PAGE); return(buf_block_get_frame(block)); } /******************************************************************//** Gets an undo log page and s-latches it. @return pointer to page s-latched */ UNIV_INLINE page_t* trx_undo_page_get_s_latched( /*========================*/ ulint space, /*!< in: space where placed */ ulint zip_size, /*!< in: compressed page size in bytes or 0 for uncompressed pages */ ulint page_no, /*!< in: page number */ mtr_t* mtr) /*!< in: mtr */ { buf_block_t* block = buf_page_get(space, zip_size, page_no, RW_S_LATCH, mtr); buf_block_dbg_add_level(block, SYNC_TRX_UNDO_PAGE); return(buf_block_get_frame(block)); } /******************************************************************//** Returns the start offset of the undo log records of the specified undo log on the page. @return start offset */ UNIV_INLINE ulint trx_undo_page_get_start( /*====================*/ page_t* undo_page,/*!< in: undo log page */ ulint page_no,/*!< in: undo log header page number */ ulint offset) /*!< in: undo log header offset on page */ { ulint start; if (page_no == page_get_page_no(undo_page)) { start = mach_read_from_2(offset + undo_page + TRX_UNDO_LOG_START); } else { start = TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_HDR_SIZE; } return(start); } /******************************************************************//** Returns the end offset of the undo log records of the specified undo log on the page. @return end offset */ UNIV_INLINE ulint trx_undo_page_get_end( /*==================*/ page_t* undo_page,/*!< in: undo log page */ ulint page_no,/*!< in: undo log header page number */ ulint offset) /*!< in: undo log header offset on page */ { trx_ulogf_t* log_hdr; ulint end; if (page_no == page_get_page_no(undo_page)) { log_hdr = undo_page + offset; end = mach_read_from_2(log_hdr + TRX_UNDO_NEXT_LOG); if (end == 0) { end = mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_FREE); } } else { end = mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_FREE); } return(end); } /******************************************************************//** Returns the previous undo record on the page in the specified log, or NULL if none exists. @return pointer to record, NULL if none */ UNIV_INLINE trx_undo_rec_t* trx_undo_page_get_prev_rec( /*=======================*/ trx_undo_rec_t* rec, /*!< in: undo log record */ ulint page_no,/*!< in: undo log header page number */ ulint offset) /*!< in: undo log header offset on page */ { page_t* undo_page; ulint start; undo_page = (page_t*) ut_align_down(rec, UNIV_PAGE_SIZE); start = trx_undo_page_get_start(undo_page, page_no, offset); if (start + undo_page == rec) { return(NULL); } return(undo_page + mach_read_from_2(rec - 2)); } /******************************************************************//** Returns the next undo log record on the page in the specified log, or NULL if none exists. @return pointer to record, NULL if none */ UNIV_INLINE trx_undo_rec_t* trx_undo_page_get_next_rec( /*=======================*/ trx_undo_rec_t* rec, /*!< in: undo log record */ ulint page_no,/*!< in: undo log header page number */ ulint offset) /*!< in: undo log header offset on page */ { page_t* undo_page; ulint end; ulint next; undo_page = (page_t*) ut_align_down(rec, UNIV_PAGE_SIZE); end = trx_undo_page_get_end(undo_page, page_no, offset); next = mach_read_from_2(rec); if (next == end) { return(NULL); } return(undo_page + next); } /******************************************************************//** Returns the last undo record on the page in the specified undo log, or NULL if none exists. @return pointer to record, NULL if none */ UNIV_INLINE trx_undo_rec_t* trx_undo_page_get_last_rec( /*=======================*/ page_t* undo_page,/*!< in: undo log page */ ulint page_no,/*!< in: undo log header page number */ ulint offset) /*!< in: undo log header offset on page */ { ulint start; ulint end; start = trx_undo_page_get_start(undo_page, page_no, offset); end = trx_undo_page_get_end(undo_page, page_no, offset); if (start == end) { return(NULL); } return(undo_page + mach_read_from_2(undo_page + end - 2)); } /******************************************************************//** Returns the first undo record on the page in the specified undo log, or NULL if none exists. @return pointer to record, NULL if none */ UNIV_INLINE trx_undo_rec_t* trx_undo_page_get_first_rec( /*========================*/ page_t* undo_page,/*!< in: undo log page */ ulint page_no,/*!< in: undo log header page number */ ulint offset) /*!< in: undo log header offset on page */ { ulint start; ulint end; start = trx_undo_page_get_start(undo_page, page_no, offset); end = trx_undo_page_get_end(undo_page, page_no, offset); if (start == end) { return(NULL); } return(undo_page + start); } #endif /* !UNIV_HOTBACKUP */ haildb-2.3.2/include/buf0buf.h0000644000175000017500000015224011513177357016774 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2010, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/buf0buf.h The database buffer pool high-level routines Created 11/5/1995 Heikki Tuuri *******************************************************/ #ifndef buf0buf_h #define buf0buf_h #include "univ.i" #include "fil0fil.h" #include "mtr0types.h" #include "buf0types.h" #include "hash0hash.h" #include "ut0byte.h" #include "page0types.h" #include "ut0rbt.h" #ifndef UNIV_HOTBACKUP #include "os0proc.h" /** @name Modes for buf_page_get_gen */ /* @{ */ #define BUF_GET 10 /*!< get always */ #define BUF_GET_IF_IN_POOL 11 /*!< get if in pool */ #define BUF_GET_NO_LATCH 14 /*!< get and bufferfix, but set no latch; we have separated this case, because it is error-prone programming not to set a latch, and it should be used with care */ /* @} */ /** @name Modes for buf_page_get_known_nowait */ /* @{ */ #define BUF_MAKE_YOUNG 51 /*!< Move the block to the start of the LRU list if there is a danger that the block would drift out of the buffer pool*/ #define BUF_KEEP_OLD 52 /*!< Preserve the current LRU position of the block. */ /* @} */ extern buf_pool_t* buf_pool; /*!< The buffer pool of the database */ #ifdef UNIV_DEBUG extern ibool buf_debug_prints;/*!< If this is set TRUE, the program prints info whenever read or flush occurs */ #endif /* UNIV_DEBUG */ extern ulint srv_buf_pool_write_requests; /*!< variable to count write request issued */ #else /* !UNIV_HOTBACKUP */ extern buf_block_t* back_block1; /*!< first block, for --apply-log */ extern buf_block_t* back_block2; /*!< second block, for page reorganize */ #endif /* !UNIV_HOTBACKUP */ /** Magic value to use instead of checksums when they are disabled */ #define BUF_NO_CHECKSUM_MAGIC 0xDEADBEEFUL /** @brief States of a control block @see buf_page_struct The enumeration values must be 0..7. */ enum buf_page_state { BUF_BLOCK_ZIP_FREE = 0, /*!< contains a free compressed page */ BUF_BLOCK_ZIP_PAGE, /*!< contains a clean compressed page */ BUF_BLOCK_ZIP_DIRTY, /*!< contains a compressed page that is in the buf_pool->flush_list */ BUF_BLOCK_NOT_USED, /*!< is in the free list; must be after the BUF_BLOCK_ZIP_ constants for compressed-only pages @see buf_block_state_valid() */ BUF_BLOCK_READY_FOR_USE, /*!< when buf_LRU_get_free_block returns a block, it is in this state */ BUF_BLOCK_FILE_PAGE, /*!< contains a buffered file page */ BUF_BLOCK_MEMORY, /*!< contains some main memory object */ BUF_BLOCK_REMOVE_HASH /*!< hash index should be removed before putting to the free list */ }; #ifndef UNIV_HOTBACKUP /********************************************************************//** Creates the buffer pool. @return own: buf_pool object, NULL if not enough memory or error */ UNIV_INTERN buf_pool_t* buf_pool_init(void); /*===============*/ /********************************************************************//** Prepares the buffer pool for shutdown. */ UNIV_INTERN void buf_close(void); /*============*/ /************************************************************************ Frees the buffer pool at shutdown. This must not be invoked after freeing all mutexes. */ UNIV_INTERN void buf_mem_free(void); /*==============*/ /********************************************************************//** Drops the adaptive hash index. To prevent a livelock, this function is only to be called while holding btr_search_latch and while btr_search_enabled == FALSE. */ UNIV_INTERN void buf_pool_drop_hash_index(void); /*==========================*/ /********************************************************************//** Relocate a buffer control block. Relocates the block on the LRU list and in buf_pool->page_hash. Does not relocate bpage->list. The caller must take care of relocating bpage->list. */ UNIV_INTERN void buf_relocate( /*=========*/ buf_page_t* bpage, /*!< in/out: control block being relocated; buf_page_get_state(bpage) must be BUF_BLOCK_ZIP_DIRTY or BUF_BLOCK_ZIP_PAGE */ buf_page_t* dpage) /*!< in/out: destination control block */ __attribute__((nonnull)); /********************************************************************//** Resizes the buffer pool. */ UNIV_INTERN void buf_pool_resize(void); /*=================*/ /*********************************************************************//** Gets the current size of buffer buf_pool in bytes. @return size in bytes */ UNIV_INLINE ulint buf_pool_get_curr_size(void); /*========================*/ /********************************************************************//** Gets the smallest oldest_modification lsn for any page in the pool. Returns zero if all modified pages have been flushed to disk. @return oldest modification in pool, zero if none */ UNIV_INLINE ib_uint64_t buf_pool_get_oldest_modification(void); /*==================================*/ /********************************************************************//** Allocates a buffer block. @return own: the allocated block, in state BUF_BLOCK_MEMORY */ UNIV_INLINE buf_block_t* buf_block_alloc( /*============*/ ulint zip_size); /*!< in: compressed page size in bytes, or 0 if uncompressed tablespace */ /********************************************************************//** Frees a buffer block which does not contain a file page. */ UNIV_INLINE void buf_block_free( /*===========*/ buf_block_t* block); /*!< in, own: block to be freed */ #endif /* !UNIV_HOTBACKUP */ /*********************************************************************//** Copies contents of a buffer frame to a given buffer. @return buf */ UNIV_INLINE byte* buf_frame_copy( /*===========*/ byte* buf, /*!< in: buffer to copy to */ const buf_frame_t* frame); /*!< in: buffer frame */ #ifndef UNIV_HOTBACKUP /**************************************************************//** NOTE! The following macros should be used instead of buf_page_get_gen, to improve debugging. Only values RW_S_LATCH and RW_X_LATCH are allowed in LA! */ #define buf_page_get(SP, ZS, OF, LA, MTR) buf_page_get_gen(\ SP, ZS, OF, LA, NULL,\ BUF_GET, __FILE__, __LINE__, MTR) /**************************************************************//** Use these macros to bufferfix a page with no latching. Remember not to read the contents of the page unless you know it is safe. Do not modify the contents of the page! We have separated this case, because it is error-prone programming not to set a latch, and it should be used with care. */ #define buf_page_get_with_no_latch(SP, ZS, OF, MTR) buf_page_get_gen(\ SP, ZS, OF, RW_NO_LATCH, NULL,\ BUF_GET_NO_LATCH, __FILE__, __LINE__, MTR) /********************************************************************//** This is the general function used to get optimistic access to a database page. @return TRUE if success */ UNIV_INTERN ibool buf_page_optimistic_get( /*====================*/ ulint rw_latch,/*!< in: RW_S_LATCH, RW_X_LATCH */ buf_block_t* block, /*!< in: guessed block */ ib_uint64_t modify_clock,/*!< in: modify clock value if mode is ..._GUESS_ON_CLOCK */ const char* file, /*!< in: file name */ ulint line, /*!< in: line where called */ mtr_t* mtr); /*!< in: mini-transaction */ /********************************************************************//** This is used to get access to a known database page, when no waiting can be done. @return TRUE if success */ UNIV_INTERN ibool buf_page_get_known_nowait( /*======================*/ ulint rw_latch,/*!< in: RW_S_LATCH, RW_X_LATCH */ buf_block_t* block, /*!< in: the known page */ ulint mode, /*!< in: BUF_MAKE_YOUNG or BUF_KEEP_OLD */ const char* file, /*!< in: file name */ ulint line, /*!< in: line where called */ mtr_t* mtr); /*!< in: mini-transaction */ /*******************************************************************//** Given a tablespace id and page number tries to get that page. If the page is not in the buffer pool it is not loaded and NULL is returned. Suitable for using when holding the kernel mutex. */ UNIV_INTERN const buf_block_t* buf_page_try_get_func( /*==================*/ ulint space_id,/*!< in: tablespace id */ ulint page_no,/*!< in: page number */ const char* file, /*!< in: file name */ ulint line, /*!< in: line where called */ mtr_t* mtr); /*!< in: mini-transaction */ /** Tries to get a page. If the page is not in the buffer pool it is not loaded. Suitable for using when holding the kernel mutex. @param space_id in: tablespace id @param page_no in: page number @param mtr in: mini-transaction @return the page if in buffer pool, NULL if not */ #define buf_page_try_get(space_id, page_no, mtr) \ buf_page_try_get_func(space_id, page_no, __FILE__, __LINE__, mtr); /********************************************************************//** Get read access to a compressed page (usually of type FIL_PAGE_TYPE_ZBLOB or FIL_PAGE_TYPE_ZBLOB2). The page must be released with buf_page_release_zip(). NOTE: the page is not protected by any latch. Mutual exclusion has to be implemented at a higher level. In other words, all possible accesses to a given page through this function must be protected by the same set of mutexes or latches. @return pointer to the block, or NULL if not compressed */ UNIV_INTERN buf_page_t* buf_page_get_zip( /*=============*/ ulint space, /*!< in: space id */ ulint zip_size,/*!< in: compressed page size */ ulint offset);/*!< in: page number */ /********************************************************************//** This is the general function used to get access to a database page. @return pointer to the block or NULL */ UNIV_INTERN buf_block_t* buf_page_get_gen( /*=============*/ ulint space, /*!< in: space id */ ulint zip_size,/*!< in: compressed page size in bytes or 0 for uncompressed pages */ ulint offset, /*!< in: page number */ ulint rw_latch,/*!< in: RW_S_LATCH, RW_X_LATCH, RW_NO_LATCH */ buf_block_t* guess, /*!< in: guessed block or NULL */ ulint mode, /*!< in: BUF_GET, BUF_GET_IF_IN_POOL, BUF_GET_NO_LATCH */ const char* file, /*!< in: file name */ ulint line, /*!< in: line where called */ mtr_t* mtr); /*!< in: mini-transaction */ /********************************************************************//** Initializes a page to the buffer buf_pool. The page is usually not read from a file even if it cannot be found in the buffer buf_pool. This is one of the functions which perform to a block a state transition NOT_USED => FILE_PAGE (the other is buf_page_get_gen). @return pointer to the block, page bufferfixed */ UNIV_INTERN buf_block_t* buf_page_create( /*============*/ ulint space, /*!< in: space id */ ulint offset, /*!< in: offset of the page within space in units of a page */ ulint zip_size,/*!< in: compressed page size, or 0 */ mtr_t* mtr); /*!< in: mini-transaction handle */ #else /* !UNIV_HOTBACKUP */ /********************************************************************//** Inits a page to the buffer buf_pool, for use in ibbackup --restore. */ UNIV_INTERN void buf_page_init_for_backup_restore( /*=============================*/ ulint space, /*!< in: space id */ ulint offset, /*!< in: offset of the page within space in units of a page */ ulint zip_size,/*!< in: compressed page size in bytes or 0 for uncompressed pages */ buf_block_t* block); /*!< in: block to init */ #endif /* !UNIV_HOTBACKUP */ #ifndef UNIV_HOTBACKUP /********************************************************************//** Releases a compressed-only page acquired with buf_page_get_zip(). */ UNIV_INLINE void buf_page_release_zip( /*=================*/ buf_page_t* bpage); /*!< in: buffer block */ /********************************************************************//** Decrements the bufferfix count of a buffer control block and releases a latch, if specified. */ UNIV_INLINE void buf_page_release( /*=============*/ buf_block_t* block, /*!< in: buffer block */ ulint rw_latch, /*!< in: RW_S_LATCH, RW_X_LATCH, RW_NO_LATCH */ mtr_t* mtr); /*!< in: mtr */ /********************************************************************//** Moves a page to the start of the buffer pool LRU list. This high-level function can be used to prevent an important page from slipping out of the buffer pool. */ UNIV_INTERN void buf_page_make_young( /*================*/ buf_page_t* bpage); /*!< in: buffer block of a file page */ /********************************************************************//** Returns TRUE if the page can be found in the buffer pool hash table. NOTE that it is possible that the page is not yet read from disk, though. @return TRUE if found in the page hash table */ UNIV_INLINE ibool buf_page_peek( /*==========*/ ulint space, /*!< in: space id */ ulint offset);/*!< in: page number */ /********************************************************************//** Resets the check_index_page_at_flush field of a page if found in the buffer pool. */ UNIV_INTERN void buf_reset_check_index_page_at_flush( /*================================*/ ulint space, /*!< in: space id */ ulint offset);/*!< in: page number */ #ifdef UNIV_DEBUG_FILE_ACCESSES /********************************************************************//** Sets file_page_was_freed TRUE if the page is found in the buffer pool. This function should be called when we free a file page and want the debug version to check that it is not accessed any more unless reallocated. @return control block if found in page hash table, otherwise NULL */ UNIV_INTERN buf_page_t* buf_page_set_file_page_was_freed( /*=============================*/ ulint space, /*!< in: space id */ ulint offset);/*!< in: page number */ /********************************************************************//** Sets file_page_was_freed FALSE if the page is found in the buffer pool. This function should be called when we free a file page and want the debug version to check that it is not accessed any more unless reallocated. @return control block if found in page hash table, otherwise NULL */ UNIV_INTERN buf_page_t* buf_page_reset_file_page_was_freed( /*===============================*/ ulint space, /*!< in: space id */ ulint offset); /*!< in: page number */ #endif /* UNIV_DEBUG_FILE_ACCESSES */ /********************************************************************//** Reads the freed_page_clock of a buffer block. @return freed_page_clock */ UNIV_INLINE ulint buf_page_get_freed_page_clock( /*==========================*/ const buf_page_t* bpage) /*!< in: block */ __attribute__((pure)); /********************************************************************//** Reads the freed_page_clock of a buffer block. @return freed_page_clock */ UNIV_INLINE ulint buf_block_get_freed_page_clock( /*===========================*/ const buf_block_t* block) /*!< in: block */ __attribute__((pure)); /********************************************************************//** Recommends a move of a block to the start of the LRU list if there is danger of dropping from the buffer pool. NOTE: does not reserve the buffer pool mutex. @return TRUE if should be made younger */ UNIV_INLINE ibool buf_page_peek_if_too_old( /*=====================*/ const buf_page_t* bpage); /*!< in: block to make younger */ /********************************************************************//** Returns the current state of is_hashed of a page. FALSE if the page is not in the pool. NOTE that this operation does not fix the page in the pool if it is found there. @return TRUE if page hash index is built in search system */ UNIV_INTERN ibool buf_page_peek_if_search_hashed( /*===========================*/ ulint space, /*!< in: space id */ ulint offset);/*!< in: page number */ /********************************************************************//** Gets the youngest modification log sequence number for a frame. Returns zero if not file page or no modification occurred yet. @return newest modification to page */ UNIV_INLINE ib_uint64_t buf_page_get_newest_modification( /*=============================*/ const buf_page_t* bpage); /*!< in: block containing the page frame */ /********************************************************************//** Increments the modify clock of a frame by 1. The caller must (1) own the buf_pool mutex and block bufferfix count has to be zero, (2) or own an x-lock on the block. */ UNIV_INLINE void buf_block_modify_clock_inc( /*=======================*/ buf_block_t* block); /*!< in: block */ /********************************************************************//** Returns the value of the modify clock. The caller must have an s-lock or x-lock on the block. @return value */ UNIV_INLINE ib_uint64_t buf_block_get_modify_clock( /*=======================*/ buf_block_t* block); /*!< in: block */ #else /* !UNIV_HOTBACKUP */ # define buf_block_modify_clock_inc(block) ((void) 0) #endif /* !UNIV_HOTBACKUP */ /********************************************************************//** Calculates a page checksum which is stored to the page when it is written to a file. Note that we must be careful to calculate the same value on 32-bit and 64-bit architectures. @return checksum */ UNIV_INTERN ulint buf_calc_page_new_checksum( /*=======================*/ const byte* page); /*!< in: buffer page */ /********************************************************************//** In versions < 4.0.14 and < 4.1.1 there was a bug that the checksum only looked at the first few bytes of the page. This calculates that old checksum. NOTE: we must first store the new formula checksum to FIL_PAGE_SPACE_OR_CHKSUM before calculating and storing this old checksum because this takes that field as an input! @return checksum */ UNIV_INTERN ulint buf_calc_page_old_checksum( /*=======================*/ const byte* page); /*!< in: buffer page */ /********************************************************************//** Checks if a page is corrupt. @return TRUE if corrupted */ UNIV_INTERN ibool buf_page_is_corrupted( /*==================*/ const byte* read_buf, /*!< in: a database page */ ulint zip_size); /*!< in: size of compressed page; 0 for uncompressed pages */ #ifndef UNIV_HOTBACKUP /**********************************************************************//** Gets the space id, page offset, and byte offset within page of a pointer pointing to a buffer frame containing a file page. */ UNIV_INLINE void buf_ptr_get_fsp_addr( /*=================*/ const void* ptr, /*!< in: pointer to a buffer frame */ ulint* space, /*!< out: space id */ fil_addr_t* addr); /*!< out: page offset and byte offset */ /**********************************************************************//** Gets the hash value of a block. This can be used in searches in the lock hash table. @return lock hash value */ UNIV_INLINE ulint buf_block_get_lock_hash_val( /*========================*/ const buf_block_t* block) /*!< in: block */ __attribute__((pure)); #ifdef UNIV_DEBUG /*********************************************************************//** Finds a block in the buffer pool that points to a given compressed page. @return buffer block pointing to the compressed page, or NULL */ UNIV_INTERN buf_block_t* buf_pool_contains_zip( /*==================*/ const void* data); /*!< in: pointer to compressed page */ #endif /* UNIV_DEBUG */ #if defined UNIV_DEBUG || defined UNIV_BUF_DEBUG /*********************************************************************//** Validates the buffer pool data structure. @return TRUE */ UNIV_INTERN ibool buf_validate(void); /*==============*/ #endif /* UNIV_DEBUG || UNIV_BUF_DEBUG */ #if defined UNIV_DEBUG_PRINT || defined UNIV_DEBUG || defined UNIV_BUF_DEBUG /*********************************************************************//** Prints info of the buffer pool data structure. */ UNIV_INTERN void buf_print(void); /*============*/ #endif /* UNIV_DEBUG_PRINT || UNIV_DEBUG || UNIV_BUF_DEBUG */ #endif /* !UNIV_HOTBACKUP */ /********************************************************************//** Prints a page to stderr. */ UNIV_INTERN void buf_page_print( /*===========*/ const byte* read_buf, /*!< in: a database page */ ulint zip_size); /*!< in: compressed page size, or 0 for uncompressed pages */ /********************************************************************//** Decompress a block. @return TRUE if successful */ UNIV_INTERN ibool buf_zip_decompress( /*===============*/ buf_block_t* block, /*!< in/out: block */ ibool check); /*!< in: TRUE=verify the page checksum */ #ifndef UNIV_HOTBACKUP #ifdef UNIV_DEBUG /*********************************************************************//** Returns the number of latched pages in the buffer pool. @return number of latched pages */ UNIV_INTERN ulint buf_get_latched_pages_number(void); /*==============================*/ #endif /* UNIV_DEBUG */ /*********************************************************************//** Returns the number of pending buf pool ios. @return number of pending I/O operations */ UNIV_INTERN ulint buf_get_n_pending_ios(void); /*=======================*/ /*********************************************************************//** Prints info of the buffer i/o. */ UNIV_INTERN void buf_print_io( /*=========*/ ib_stream_t ib_stream); /*!< in: file where to print */ /*********************************************************************//** Returns the ratio in percents of modified pages in the buffer pool / database pages in the buffer pool. @return modified page percentage ratio */ UNIV_INTERN ulint buf_get_modified_ratio_pct(void); /*============================*/ /**********************************************************************//** Refreshes the statistics used to print per-second averages. */ UNIV_INTERN void buf_refresh_io_stats(void); /*======================*/ /*********************************************************************//** Asserts that all file pages in the buffer are in a replaceable state. @return TRUE */ UNIV_INTERN ibool buf_all_freed(void); /*===============*/ /*********************************************************************//** Checks that there currently are no pending i/o-operations for the buffer pool. @return TRUE if there is no pending i/o */ UNIV_INTERN ibool buf_pool_check_no_pending_io(void); /*==============================*/ /*********************************************************************//** Invalidates the file pages in the buffer pool when an archive recovery is completed. All the file pages buffered must be in a replaceable state when this function is called: not latched and not modified. */ UNIV_INTERN void buf_pool_invalidate(void); /*=====================*/ /************************************************************************ Reset the buffer variables. */ UNIV_INTERN void buf_var_init(void); /*==============*/ #endif /* !UNIV_HOTBACKUP */ /*======================================================================== --------------------------- LOWER LEVEL ROUTINES ------------------------- =========================================================================*/ #ifdef UNIV_SYNC_DEBUG /*********************************************************************//** Adds latch level info for the rw-lock protecting the buffer frame. This should be called in the debug version after a successful latching of a page if we know the latching order level of the acquired latch. */ UNIV_INLINE void buf_block_dbg_add_level( /*====================*/ buf_block_t* block, /*!< in: buffer page where we have acquired latch */ ulint level); /*!< in: latching order level */ #else /* UNIV_SYNC_DEBUG */ # define buf_block_dbg_add_level(block, level) /* nothing */ #endif /* UNIV_SYNC_DEBUG */ /*********************************************************************//** Gets the state of a block. @return state */ UNIV_INLINE enum buf_page_state buf_page_get_state( /*===============*/ const buf_page_t* bpage); /*!< in: pointer to the control block */ /*********************************************************************//** Gets the state of a block. @return state */ UNIV_INLINE enum buf_page_state buf_block_get_state( /*================*/ const buf_block_t* block) /*!< in: pointer to the control block */ __attribute__((pure)); /*********************************************************************//** Sets the state of a block. */ UNIV_INLINE void buf_page_set_state( /*===============*/ buf_page_t* bpage, /*!< in/out: pointer to control block */ enum buf_page_state state); /*!< in: state */ /*********************************************************************//** Sets the state of a block. */ UNIV_INLINE void buf_block_set_state( /*================*/ buf_block_t* block, /*!< in/out: pointer to control block */ enum buf_page_state state); /*!< in: state */ /*********************************************************************//** Determines if a block is mapped to a tablespace. @return TRUE if mapped */ UNIV_INLINE ibool buf_page_in_file( /*=============*/ const buf_page_t* bpage) /*!< in: pointer to control block */ __attribute__((pure)); #ifndef UNIV_HOTBACKUP /*********************************************************************//** Determines if a block should be on unzip_LRU list. @return TRUE if block belongs to unzip_LRU */ UNIV_INLINE ibool buf_page_belongs_to_unzip_LRU( /*==========================*/ const buf_page_t* bpage) /*!< in: pointer to control block */ __attribute__((pure)); /*********************************************************************//** Gets the mutex of a block. @return pointer to mutex protecting bpage */ UNIV_INLINE mutex_t* buf_page_get_mutex( /*===============*/ const buf_page_t* bpage) /*!< in: pointer to control block */ __attribute__((pure)); /*********************************************************************//** Get the flush type of a page. @return flush type */ UNIV_INLINE enum buf_flush buf_page_get_flush_type( /*====================*/ const buf_page_t* bpage) /*!< in: buffer page */ __attribute__((pure)); /*********************************************************************//** Set the flush type of a page. */ UNIV_INLINE void buf_page_set_flush_type( /*====================*/ buf_page_t* bpage, /*!< in: buffer page */ enum buf_flush flush_type); /*!< in: flush type */ /*********************************************************************//** Map a block to a file page. */ UNIV_INLINE void buf_block_set_file_page( /*====================*/ buf_block_t* block, /*!< in/out: pointer to control block */ ulint space, /*!< in: tablespace id */ ulint page_no);/*!< in: page number */ /*********************************************************************//** Gets the io_fix state of a block. @return io_fix state */ UNIV_INLINE enum buf_io_fix buf_page_get_io_fix( /*================*/ const buf_page_t* bpage) /*!< in: pointer to the control block */ __attribute__((pure)); /*********************************************************************//** Gets the io_fix state of a block. @return io_fix state */ UNIV_INLINE enum buf_io_fix buf_block_get_io_fix( /*================*/ const buf_block_t* block) /*!< in: pointer to the control block */ __attribute__((pure)); /*********************************************************************//** Sets the io_fix state of a block. */ UNIV_INLINE void buf_page_set_io_fix( /*================*/ buf_page_t* bpage, /*!< in/out: control block */ enum buf_io_fix io_fix);/*!< in: io_fix state */ /*********************************************************************//** Sets the io_fix state of a block. */ UNIV_INLINE void buf_block_set_io_fix( /*=================*/ buf_block_t* block, /*!< in/out: control block */ enum buf_io_fix io_fix);/*!< in: io_fix state */ /********************************************************************//** Determine if a buffer block can be relocated in memory. The block can be dirty, but it must not be I/O-fixed or bufferfixed. */ UNIV_INLINE ibool buf_page_can_relocate( /*==================*/ const buf_page_t* bpage) /*!< control block being relocated */ __attribute__((pure)); /*********************************************************************//** Determine if a block has been flagged old. @return TRUE if old */ UNIV_INLINE ibool buf_page_is_old( /*============*/ const buf_page_t* bpage) /*!< in: control block */ __attribute__((pure)); /*********************************************************************//** Flag a block old. */ UNIV_INLINE void buf_page_set_old( /*=============*/ buf_page_t* bpage, /*!< in/out: control block */ ibool old); /*!< in: old */ /*********************************************************************//** Determine the time of first access of a block in the buffer pool. @return ut_time_ms() at the time of first access, 0 if not accessed */ UNIV_INLINE unsigned buf_page_is_accessed( /*=================*/ const buf_page_t* bpage) /*!< in: control block */ __attribute__((nonnull, pure)); /*********************************************************************//** Flag a block accessed. */ UNIV_INLINE void buf_page_set_accessed( /*==================*/ buf_page_t* bpage, /*!< in/out: control block */ ulint time_ms) /*!< in: ut_time_ms() */ __attribute__((nonnull)); /*********************************************************************//** Gets the buf_block_t handle of a buffered file block if an uncompressed page frame exists, or NULL. @return control block, or NULL */ UNIV_INLINE buf_block_t* buf_page_get_block( /*===============*/ buf_page_t* bpage) /*!< in: control block, or NULL */ __attribute__((pure)); #endif /* !UNIV_HOTBACKUP */ #ifdef UNIV_DEBUG /*********************************************************************//** Gets a pointer to the memory frame of a block. @return pointer to the frame */ UNIV_INLINE buf_frame_t* buf_block_get_frame( /*================*/ const buf_block_t* block) /*!< in: pointer to the control block */ __attribute__((pure)); #else /* UNIV_DEBUG */ # define buf_block_get_frame(block) (block)->frame #endif /* UNIV_DEBUG */ /*********************************************************************//** Gets the space id of a block. @return space id */ UNIV_INLINE ulint buf_page_get_space( /*===============*/ const buf_page_t* bpage) /*!< in: pointer to the control block */ __attribute__((pure)); /*********************************************************************//** Gets the space id of a block. @return space id */ UNIV_INLINE ulint buf_block_get_space( /*================*/ const buf_block_t* block) /*!< in: pointer to the control block */ __attribute__((pure)); /*********************************************************************//** Gets the page number of a block. @return page number */ UNIV_INLINE ulint buf_page_get_page_no( /*=================*/ const buf_page_t* bpage) /*!< in: pointer to the control block */ __attribute__((pure)); /*********************************************************************//** Gets the page number of a block. @return page number */ UNIV_INLINE ulint buf_block_get_page_no( /*==================*/ const buf_block_t* block) /*!< in: pointer to the control block */ __attribute__((pure)); /*********************************************************************//** Gets the compressed page size of a block. @return compressed page size, or 0 */ UNIV_INLINE ulint buf_page_get_zip_size( /*==================*/ const buf_page_t* bpage) /*!< in: pointer to the control block */ __attribute__((pure)); /*********************************************************************//** Gets the compressed page size of a block. @return compressed page size, or 0 */ UNIV_INLINE ulint buf_block_get_zip_size( /*===================*/ const buf_block_t* block) /*!< in: pointer to the control block */ __attribute__((pure)); /*********************************************************************//** Gets the compressed page descriptor corresponding to an uncompressed page if applicable. */ #define buf_block_get_page_zip(block) \ (UNIV_LIKELY_NULL((block)->page.zip.data) ? &(block)->page.zip : NULL) #ifndef UNIV_HOTBACKUP /*******************************************************************//** Gets the block to whose frame the pointer is pointing to. @return pointer to block, never NULL */ UNIV_INTERN buf_block_t* buf_block_align( /*============*/ const byte* ptr); /*!< in: pointer to a frame */ /********************************************************************//** Find out if a pointer belongs to a buf_block_t. It can be a pointer to the buf_block_t itself or a member of it @return TRUE if ptr belongs to a buf_block_t struct */ UNIV_INTERN ibool buf_pointer_is_block_field( /*=======================*/ const void* ptr); /*!< in: pointer not dereferenced */ /** Find out if a pointer corresponds to a buf_block_t::mutex. @param m in: mutex candidate @return TRUE if m is a buf_block_t::mutex */ #define buf_pool_is_block_mutex(m) \ buf_pointer_is_block_field((const void*)(m)) /** Find out if a pointer corresponds to a buf_block_t::lock. @param l in: rw-lock candidate @return TRUE if l is a buf_block_t::lock */ #define buf_pool_is_block_lock(l) \ buf_pointer_is_block_field((const void*)(l)) #if defined UNIV_DEBUG || defined UNIV_ZIP_DEBUG /*********************************************************************//** Gets the compressed page descriptor corresponding to an uncompressed page if applicable. @return compressed page descriptor, or NULL */ UNIV_INLINE const page_zip_des_t* buf_frame_get_page_zip( /*===================*/ const byte* ptr); /*!< in: pointer to the page */ #endif /* UNIV_DEBUG || UNIV_ZIP_DEBUG */ /********************************************************************//** Function which inits a page for read to the buffer buf_pool. If the page is (1) already in buf_pool, or (2) if we specify to read only ibuf pages and the page is not an ibuf page, or (3) if the space is deleted or being deleted, then this function does nothing. Sets the io_fix flag to BUF_IO_READ and sets a non-recursive exclusive lock on the buffer frame. The io-handler must take care that the flag is cleared and the lock released later. @return pointer to the block or NULL */ UNIV_INTERN buf_page_t* buf_page_init_for_read( /*===================*/ ulint* err, /*!< out: DB_SUCCESS or DB_TABLESPACE_DELETED */ ulint mode, /*!< in: BUF_READ_IBUF_PAGES_ONLY, ... */ ulint space, /*!< in: space id */ ulint zip_size,/*!< in: compressed page size, or 0 */ ibool unzip, /*!< in: TRUE=request uncompressed page */ ib_int64_t tablespace_version,/*!< in: prevents reading from a wrong version of the tablespace in case we have done DISCARD + IMPORT */ ulint offset);/*!< in: page number */ /********************************************************************//** Completes an asynchronous read or write request of a file page to or from the buffer pool. */ UNIV_INTERN void buf_page_io_complete( /*=================*/ buf_page_t* bpage); /*!< in: pointer to the block in question */ /********************************************************************//** Calculates a folded value of a file page address to use in the page hash table. @return the folded value */ UNIV_INLINE ulint buf_page_address_fold( /*==================*/ ulint space, /*!< in: space id */ ulint offset) /*!< in: offset of the page within space */ __attribute__((const)); /******************************************************************//** Returns the control block of a file page, NULL if not found. @return block, NULL if not found */ UNIV_INLINE buf_page_t* buf_page_hash_get( /*==============*/ ulint space, /*!< in: space id */ ulint offset);/*!< in: offset of the page within space */ /******************************************************************//** Returns the control block of a file page, NULL if not found or an uncompressed page frame does not exist. @return block, NULL if not found */ UNIV_INLINE buf_block_t* buf_block_hash_get( /*===============*/ ulint space, /*!< in: space id */ ulint offset);/*!< in: offset of the page within space */ /*********************************************************************//** Gets the current length of the free list of buffer blocks. @return length of the free list */ UNIV_INTERN ulint buf_get_free_list_len(void); /*=======================*/ #endif /* !UNIV_HOTBACKUP */ /** The common buffer control block structure for compressed and uncompressed frames */ struct buf_page_struct{ /** @name General fields None of these bit-fields must be modified without holding buf_page_get_mutex() [buf_block_struct::mutex or buf_pool_zip_mutex], since they can be stored in the same machine word. Some of these fields are additionally protected by buf_pool_mutex. */ /* @{ */ unsigned space:32; /*!< tablespace id; also protected by buf_pool_mutex. */ unsigned offset:32; /*!< page number; also protected by buf_pool_mutex. */ unsigned state:3; /*!< state of the control block; also protected by buf_pool_mutex. State transitions from BUF_BLOCK_READY_FOR_USE to BUF_BLOCK_MEMORY need not be protected by buf_page_get_mutex(). @see enum buf_page_state */ #ifndef UNIV_HOTBACKUP unsigned flush_type:2; /*!< if this block is currently being flushed to disk, this tells the flush_type. @see enum buf_flush */ unsigned io_fix:2; /*!< type of pending I/O operation; also protected by buf_pool_mutex @see enum buf_io_fix */ unsigned buf_fix_count:25;/*!< count of how manyfold this block is currently bufferfixed */ /* @} */ #endif /* !UNIV_HOTBACKUP */ page_zip_des_t zip; /*!< compressed page; zip.data (but not the data it points to) is also protected by buf_pool_mutex */ #ifndef UNIV_HOTBACKUP buf_page_t* hash; /*!< node used in chaining to buf_pool->page_hash or buf_pool->zip_hash */ #ifdef UNIV_DEBUG ibool in_page_hash; /*!< TRUE if in buf_pool->page_hash */ ibool in_zip_hash; /*!< TRUE if in buf_pool->zip_hash */ #endif /* UNIV_DEBUG */ /** @name Page flushing fields All these are protected by buf_pool_mutex. */ /* @{ */ UT_LIST_NODE_T(buf_page_t) list; /*!< based on state, this is a list node, protected only by buf_pool_mutex, in one of the following lists in buf_pool: - BUF_BLOCK_NOT_USED: free - BUF_BLOCK_FILE_PAGE: flush_list - BUF_BLOCK_ZIP_DIRTY: flush_list - BUF_BLOCK_ZIP_PAGE: zip_clean - BUF_BLOCK_ZIP_FREE: zip_free[] The contents of the list node is undefined if !in_flush_list && state == BUF_BLOCK_FILE_PAGE, or if state is one of BUF_BLOCK_MEMORY, BUF_BLOCK_REMOVE_HASH or BUF_BLOCK_READY_IN_USE. */ #ifdef UNIV_DEBUG ibool in_flush_list; /*!< TRUE if in buf_pool->flush_list; when buf_pool_mutex is free, the following should hold: in_flush_list == (state == BUF_BLOCK_FILE_PAGE || state == BUF_BLOCK_ZIP_DIRTY) */ ibool in_free_list; /*!< TRUE if in buf_pool->free; when buf_pool_mutex is free, the following should hold: in_free_list == (state == BUF_BLOCK_NOT_USED) */ #endif /* UNIV_DEBUG */ ib_uint64_t newest_modification; /*!< log sequence number of the youngest modification to this block, zero if not modified */ ib_uint64_t oldest_modification; /*!< log sequence number of the START of the log entry written of the oldest modification to this block which has not yet been flushed on disk; zero if all modifications are on disk */ /* @} */ /** @name LRU replacement algorithm fields These fields are protected by buf_pool_mutex only (not buf_pool_zip_mutex or buf_block_struct::mutex). */ /* @{ */ UT_LIST_NODE_T(buf_page_t) LRU; /*!< node of the LRU list */ #ifdef UNIV_DEBUG ibool in_LRU_list; /*!< TRUE if the page is in the LRU list; used in debugging */ #endif /* UNIV_DEBUG */ unsigned old:1; /*!< TRUE if the block is in the old blocks in buf_pool->LRU_old */ unsigned freed_page_clock:31;/*!< the value of buf_pool->freed_page_clock when this block was the last time put to the head of the LRU list; a thread is allowed to read this for heuristic purposes without holding any mutex or latch */ unsigned access_time:32; /*!< time of first access, or 0 if the block was never accessed in the buffer pool */ /* @} */ # ifdef UNIV_DEBUG_FILE_ACCESSES ibool file_page_was_freed; /*!< this is set to TRUE when fsp frees a page in buffer pool */ # endif /* UNIV_DEBUG_FILE_ACCESSES */ #endif /* !UNIV_HOTBACKUP */ }; /** The buffer control block structure */ struct buf_block_struct{ /** @name General fields */ /* @{ */ buf_page_t page; /*!< page information; this must be the first field, so that buf_pool->page_hash can point to buf_page_t or buf_block_t */ byte* frame; /*!< pointer to buffer frame which is of size UNIV_PAGE_SIZE, and aligned to an address divisible by UNIV_PAGE_SIZE */ #ifndef UNIV_HOTBACKUP UT_LIST_NODE_T(buf_block_t) unzip_LRU; /*!< node of the decompressed LRU list; a block is in the unzip_LRU list if page.state == BUF_BLOCK_FILE_PAGE and page.zip.data != NULL */ #ifdef UNIV_DEBUG ibool in_unzip_LRU_list;/*!< TRUE if the page is in the decompressed LRU list; used in debugging */ #endif /* UNIV_DEBUG */ mutex_t mutex; /*!< mutex protecting this block: state (also protected by the buffer pool mutex), io_fix, buf_fix_count, and accessed; we introduce this new mutex in InnoDB-5.1 to relieve contention on the buffer pool mutex */ rw_lock_t lock; /*!< read-write lock of the buffer frame */ unsigned lock_hash_val:32;/*!< hashed value of the page address in the record lock hash table; protected by buf_block_t::lock (or buf_block_t::mutex, buf_pool_mutex in buf_page_get_gen(), buf_page_init_for_read() and buf_page_create()) */ ibool check_index_page_at_flush; /*!< TRUE if we know that this is an index page, and want the database to check its consistency before flush; note that there may be pages in the buffer pool which are index pages, but this flag is not set because we do not keep track of all pages; NOT protected by any mutex */ /* @} */ /** @name Optimistic search field */ /* @{ */ ib_uint64_t modify_clock; /*!< this clock is incremented every time a pointer to a record on the page may become obsolete; this is used in the optimistic cursor positioning: if the modify clock has not changed, we know that the pointer is still valid; this field may be changed if the thread (1) owns the pool mutex and the page is not bufferfixed, or (2) the thread has an x-latch on the block */ /* @} */ /** @name Hash search fields (unprotected) NOTE that these fields are NOT protected by any semaphore! */ /* @{ */ ulint n_hash_helps; /*!< counter which controls building of a new hash index for the page */ ulint n_fields; /*!< recommended prefix length for hash search: number of full fields */ ulint n_bytes; /*!< recommended prefix: number of bytes in an incomplete field */ ibool left_side; /*!< TRUE or FALSE, depending on whether the leftmost record of several records with the same prefix should be indexed in the hash index */ /* @} */ /** @name Hash search fields These 6 fields may only be modified when we have an x-latch on btr_search_latch AND - we are holding an s-latch or x-latch on buf_block_struct::lock or - we know that buf_block_struct::buf_fix_count == 0. An exception to this is when we init or create a page in the buffer pool in buf0buf.c. */ /* @{ */ #if defined UNIV_AHI_DEBUG || defined UNIV_DEBUG ulint n_pointers; /*!< used in debugging: the number of pointers in the adaptive hash index pointing to this frame */ #endif /* UNIV_AHI_DEBUG || UNIV_DEBUG */ unsigned is_hashed:1; /*!< TRUE if hash index has already been built on this page; note that it does not guarantee that the index is complete, though: there may have been hash collisions, record deletions, etc. */ unsigned curr_n_fields:10;/*!< prefix length for hash indexing: number of full fields */ unsigned curr_n_bytes:15;/*!< number of bytes in hash indexing */ unsigned curr_left_side:1;/*!< TRUE or FALSE in hash indexing */ dict_index_t* index; /*!< Index for which the adaptive hash index has been created. */ /* @} */ # ifdef UNIV_SYNC_DEBUG /** @name Debug fields */ /* @{ */ rw_lock_t debug_latch; /*!< in the debug version, each thread which bufferfixes the block acquires an s-latch here; so we can use the debug utilities in sync0rw */ /* @} */ # endif #endif /* !UNIV_HOTBACKUP */ }; /** Check if a buf_block_t object is in a valid state @param block buffer block @return TRUE if valid */ #define buf_block_state_valid(block) \ (buf_block_get_state(block) >= BUF_BLOCK_NOT_USED \ && (buf_block_get_state(block) <= BUF_BLOCK_REMOVE_HASH)) #ifndef UNIV_HOTBACKUP /**********************************************************************//** Compute the hash fold value for blocks in buf_pool->zip_hash. */ /* @{ */ #define BUF_POOL_ZIP_FOLD_PTR(ptr) ((ulint) (ptr) / UNIV_PAGE_SIZE) #define BUF_POOL_ZIP_FOLD(b) BUF_POOL_ZIP_FOLD_PTR((b)->frame) #define BUF_POOL_ZIP_FOLD_BPAGE(b) BUF_POOL_ZIP_FOLD((buf_block_t*) (b)) /* @} */ /** @brief The buffer pool statistics structure. */ struct buf_pool_stat_struct{ ulint n_page_gets; /*!< number of page gets performed; also successful searches through the adaptive hash index are counted as page gets; this field is NOT protected by the buffer pool mutex */ ulint n_pages_read; /*!< number read operations */ ulint n_pages_written;/*!< number write operations */ ulint n_pages_created;/*!< number of pages created in the pool with no read */ ulint n_ra_pages_read;/*!< number of pages read in as part of read ahead */ ulint n_ra_pages_evicted;/*!< number of read ahead pages that are evicted without being accessed */ ulint n_pages_made_young; /*!< number of pages made young, in calls to buf_LRU_make_block_young() */ ulint n_pages_not_made_young; /*!< number of pages not made young because the first access was not long enough ago, in buf_page_peek_if_too_old() */ }; /** @brief The buffer pool structure. NOTE! The definition appears here only for other modules of this directory (buf) to see it. Do not use from outside! */ struct buf_pool_struct{ /** @name General fields */ /* @{ */ ulint n_chunks; /*!< number of buffer pool chunks */ buf_chunk_t* chunks; /*!< buffer pool chunks */ ulint curr_size; /*!< current pool size in pages */ hash_table_t* page_hash; /*!< hash table of buf_page_t or buf_block_t file pages, buf_page_in_file() == TRUE, indexed by (space_id, offset) */ hash_table_t* zip_hash; /*!< hash table of buf_block_t blocks whose frames are allocated to the zip buddy system, indexed by block->frame */ ulint n_pend_reads; /*!< number of pending read operations */ ulint n_pend_unzip; /*!< number of pending decompressions */ time_t last_printout_time; /*!< when buf_print_io was last time called */ buf_pool_stat_t stat; /*!< current statistics */ buf_pool_stat_t old_stat; /*!< old statistics */ /* @} */ /** @name Page flushing algorithm fields */ /* @{ */ UT_LIST_BASE_NODE_T(buf_page_t) flush_list; /*!< base node of the modified block list */ ibool init_flush[BUF_FLUSH_N_TYPES]; /*!< this is TRUE when a flush of the given type is being initialized */ ulint n_flush[BUF_FLUSH_N_TYPES]; /*!< this is the number of pending writes in the given flush type */ os_event_t no_flush[BUF_FLUSH_N_TYPES]; /*!< this is in the set state when there is no flush batch of the given type running */ ib_rbt_t* flush_rbt; /* !< a red-black tree is used exclusively during recovery to speed up insertions in the flush_list. This tree contains blocks in order of oldest_modification LSN and is kept in sync with the flush_list. Each member of the tree MUST also be on the flush_list. This tree is relevant only in recovery and is set to NULL once the recovery is over. */ ulint freed_page_clock;/*!< a sequence number used to count the number of buffer blocks removed from the end of the LRU list; NOTE that this counter may wrap around at 4 billion! A thread is allowed to read this for heuristic purposes without holding any mutex or latch */ ulint LRU_flush_ended;/*!< when an LRU flush ends for a page, this is incremented by one; this is set to zero when a buffer block is allocated */ /* @} */ /** @name LRU replacement algorithm fields */ /* @{ */ UT_LIST_BASE_NODE_T(buf_page_t) free; /*!< base node of the free block list */ UT_LIST_BASE_NODE_T(buf_page_t) LRU; /*!< base node of the LRU list */ buf_page_t* LRU_old; /*!< pointer to the about buf_LRU_old_ratio/BUF_LRU_OLD_RATIO_DIV oldest blocks in the LRU list; NULL if LRU length less than BUF_LRU_OLD_MIN_LEN; NOTE: when LRU_old != NULL, its length should always equal LRU_old_len */ ulint LRU_old_len; /*!< length of the LRU list from the block to which LRU_old points onward, including that block; see buf0lru.c for the restrictions on this value; 0 if LRU_old == NULL; NOTE: LRU_old_len must be adjusted whenever LRU_old shrinks or grows! */ UT_LIST_BASE_NODE_T(buf_block_t) unzip_LRU; /*!< base node of the unzip_LRU list */ /* @} */ /** @name Buddy allocator fields The buddy allocator is used for allocating compressed page frames and buf_page_t descriptors of blocks that exist in the buffer pool only in compressed form. */ /* @{ */ UT_LIST_BASE_NODE_T(buf_page_t) zip_clean; /*!< unmodified compressed pages */ UT_LIST_BASE_NODE_T(buf_page_t) zip_free[BUF_BUDDY_SIZES]; /*!< buddy free lists */ #if BUF_BUDDY_HIGH != UNIV_PAGE_SIZE # error "BUF_BUDDY_HIGH != UNIV_PAGE_SIZE" #endif #if BUF_BUDDY_LOW > PAGE_ZIP_MIN_SIZE # error "BUF_BUDDY_LOW > PAGE_ZIP_MIN_SIZE" #endif /* @} */ }; /** mutex protecting the buffer pool struct and control blocks, except the read-write lock in them */ extern mutex_t buf_pool_mutex; /** mutex protecting the control blocks of compressed-only pages (of type buf_page_t, not buf_block_t) */ extern mutex_t buf_pool_zip_mutex; /** @name Accessors for buf_pool_mutex. Use these instead of accessing buf_pool_mutex directly. */ /* @{ */ /** Test if buf_pool_mutex is owned. */ #define buf_pool_mutex_own() mutex_own(&buf_pool_mutex) /** Acquire the buffer pool mutex. */ #define buf_pool_mutex_enter() do { \ ut_ad(!mutex_own(&buf_pool_zip_mutex)); \ mutex_enter(&buf_pool_mutex); \ } while (0) #if defined UNIV_DEBUG || defined UNIV_BUF_DEBUG /** Flag to forbid the release of the buffer pool mutex. Protected by buf_pool_mutex. */ extern ulint buf_pool_mutex_exit_forbidden; /** Forbid the release of the buffer pool mutex. */ # define buf_pool_mutex_exit_forbid() do { \ ut_ad(buf_pool_mutex_own()); \ buf_pool_mutex_exit_forbidden++; \ } while (0) /** Allow the release of the buffer pool mutex. */ # define buf_pool_mutex_exit_allow() do { \ ut_ad(buf_pool_mutex_own()); \ ut_a(buf_pool_mutex_exit_forbidden); \ buf_pool_mutex_exit_forbidden--; \ } while (0) /** Release the buffer pool mutex. */ # define buf_pool_mutex_exit() do { \ ut_a(!buf_pool_mutex_exit_forbidden); \ mutex_exit(&buf_pool_mutex); \ } while (0) #else /** Forbid the release of the buffer pool mutex. */ # define buf_pool_mutex_exit_forbid() ((void) 0) /** Allow the release of the buffer pool mutex. */ # define buf_pool_mutex_exit_allow() ((void) 0) /** Release the buffer pool mutex. */ # define buf_pool_mutex_exit() mutex_exit(&buf_pool_mutex) #endif #endif /* !UNIV_HOTBACKUP */ /* @} */ /********************************************************************** Let us list the consistency conditions for different control block states. NOT_USED: is in free list, not in LRU list, not in flush list, nor page hash table READY_FOR_USE: is not in free list, LRU list, or flush list, nor page hash table MEMORY: is not in free list, LRU list, or flush list, nor page hash table FILE_PAGE: space and offset are defined, is in page hash table if io_fix == BUF_IO_WRITE, pool: no_flush[flush_type] is in reset state, pool: n_flush[flush_type] > 0 (1) if buf_fix_count == 0, then is in LRU list, not in free list is in flush list, if and only if oldest_modification > 0 is x-locked, if and only if io_fix == BUF_IO_READ is s-locked, if and only if io_fix == BUF_IO_WRITE (2) if buf_fix_count > 0, then is not in LRU list, not in free list is in flush list, if and only if oldest_modification > 0 if io_fix == BUF_IO_READ, is x-locked if io_fix == BUF_IO_WRITE, is s-locked State transitions: NOT_USED => READY_FOR_USE READY_FOR_USE => MEMORY READY_FOR_USE => FILE_PAGE MEMORY => NOT_USED FILE_PAGE => NOT_USED NOTE: This transition is allowed if and only if (1) buf_fix_count == 0, (2) oldest_modification == 0, and (3) io_fix == 0. */ #ifndef UNIV_NONINL #include "buf0buf.ic" #endif #endif haildb-2.3.2/include/os0thread.ic0000644000175000017500000000211011513177357017466 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/os0thread.ic The interface to the operating system process and thread control primitives Created 9/8/1995 Heikki Tuuri *******************************************************/ haildb-2.3.2/include/mem0mem.ic0000644000175000017500000004002211513177357017136 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1994, 2010, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /********************************************************************//** @file include/mem0mem.ic The memory management Created 6/8/1994 Heikki Tuuri *************************************************************************/ #include "mem0dbg.ic" /***************************************************************//** Creates a memory heap block where data can be allocated. @return own: memory heap block, NULL if did not succeed (only possible for MEM_HEAP_BTR_SEARCH type heaps) */ UNIV_INTERN mem_block_t* mem_heap_create_block( /*==================*/ mem_heap_t* heap, /*!< in: memory heap or NULL if first block should be created */ ulint n, /*!< in: number of bytes needed for user data */ ulint type, /*!< in: type of heap: MEM_HEAP_DYNAMIC or MEM_HEAP_BUFFER */ const char* file_name,/*!< in: file name where created */ ulint line); /*!< in: line where created */ /******************************************************************//** Frees a block from a memory heap. */ UNIV_INTERN void mem_heap_block_free( /*================*/ mem_heap_t* heap, /*!< in: heap */ mem_block_t* block); /*!< in: block to free */ #ifndef UNIV_HOTBACKUP /******************************************************************//** Frees the free_block field from a memory heap. */ UNIV_INTERN void mem_heap_free_block_free( /*=====================*/ mem_heap_t* heap); /*!< in: heap */ #endif /* !UNIV_HOTBACKUP */ /***************************************************************//** Adds a new block to a memory heap. @return created block, NULL if did not succeed (only possible for MEM_HEAP_BTR_SEARCH type heaps) */ UNIV_INTERN mem_block_t* mem_heap_add_block( /*===============*/ mem_heap_t* heap, /*!< in: memory heap */ ulint n); /*!< in: number of bytes user needs */ UNIV_INLINE void mem_block_set_len(mem_block_t* block, ulint len) { ut_ad(len > 0); block->len = len; } UNIV_INLINE ulint mem_block_get_len(mem_block_t* block) { return(block->len); } UNIV_INLINE void mem_block_set_type(mem_block_t* block, ulint type) { ut_ad((type == MEM_HEAP_DYNAMIC) || (type == MEM_HEAP_BUFFER) || (type == MEM_HEAP_BUFFER + MEM_HEAP_BTR_SEARCH)); block->type = type; } UNIV_INLINE ulint mem_block_get_type(mem_block_t* block) { return(block->type); } UNIV_INLINE void mem_block_set_free(mem_block_t* block, ulint free_block) { ut_ad(free_block > 0); ut_ad(free_block <= mem_block_get_len(block)); block->free = free_block; } UNIV_INLINE ulint mem_block_get_free(mem_block_t* block) { return(block->free); } UNIV_INLINE void mem_block_set_start(mem_block_t* block, ulint start) { ut_ad(start > 0); block->start = start; } UNIV_INLINE ulint mem_block_get_start(mem_block_t* block) { return(block->start); } /***************************************************************//** Allocates and zero-fills n bytes of memory from a memory heap. @return allocated, zero-filled storage */ UNIV_INLINE void* mem_heap_zalloc( /*============*/ mem_heap_t* heap, /*!< in: memory heap */ ulint n) /*!< in: number of bytes; if the heap is allowed to grow into the buffer pool, this must be <= MEM_MAX_ALLOC_IN_BUF */ { ut_ad(heap); ut_ad(!(heap->type & MEM_HEAP_BTR_SEARCH)); return(memset(mem_heap_alloc(heap, n), 0, n)); } /***************************************************************//** Allocates n bytes of memory from a memory heap. @return allocated storage, NULL if did not succeed (only possible for MEM_HEAP_BTR_SEARCH type heaps) */ UNIV_INLINE void* mem_heap_alloc( /*===========*/ mem_heap_t* heap, /*!< in: memory heap */ ulint n) /*!< in: number of bytes; if the heap is allowed to grow into the buffer pool, this must be <= MEM_MAX_ALLOC_IN_BUF */ { mem_block_t* block; void* buf; ulint free_sz; ut_ad(mem_heap_check(heap)); block = UT_LIST_GET_LAST(heap->base); ut_ad(!(block->type & MEM_HEAP_BUFFER) || (n <= MEM_MAX_ALLOC_IN_BUF)); /* Check if there is enough space in block. If not, create a new block to the heap */ if (mem_block_get_len(block) < mem_block_get_free(block) + MEM_SPACE_NEEDED(n)) { block = mem_heap_add_block(heap, n); if (block == NULL) { return(NULL); } } free_sz = mem_block_get_free(block); buf = (byte*)block + free_sz; mem_block_set_free(block, free_sz + MEM_SPACE_NEEDED(n)); #ifdef UNIV_MEM_DEBUG UNIV_MEM_ALLOC(buf, n + MEM_FIELD_HEADER_SIZE + MEM_FIELD_TRAILER_SIZE); /* In the debug version write debugging info to the field */ mem_field_init((byte*)buf, n); /* Advance buf to point at the storage which will be given to the caller */ buf = (byte*)buf + MEM_FIELD_HEADER_SIZE; #endif #ifdef UNIV_SET_MEM_TO_ZERO UNIV_MEM_ALLOC(buf, n); memset(buf, '\0', n); #endif UNIV_MEM_ALLOC(buf, n); return(buf); } /*****************************************************************//** Frees the space in a memory heap exceeding the pointer given. The pointer must have been acquired from mem_heap_get_heap_top. The first memory block of the heap is not freed. */ UNIV_INLINE void mem_heap_free_heap_top( /*===================*/ mem_heap_t* heap, /*!< in: heap from which to free */ byte* old_top)/*!< in: pointer to old top of heap */ { mem_block_t* block; mem_block_t* prev_block; #ifdef UNIV_MEM_DEBUG ibool error; ulint total_size; ulint size; #endif ut_ad(mem_heap_check(heap)); #ifdef UNIV_MEM_DEBUG /* Validate the heap and get its total allocated size */ mem_heap_validate_or_print(heap, NULL, FALSE, &error, &total_size, NULL, NULL); ut_a(!error); /* Get the size below top pointer */ mem_heap_validate_or_print(heap, old_top, FALSE, &error, &size, NULL, NULL); ut_a(!error); #endif block = UT_LIST_GET_LAST(heap->base); while (block != NULL) { if (((byte*)block + mem_block_get_free(block) >= old_top) && ((byte*)block <= old_top)) { /* Found the right block */ break; } /* Store prev_block value before freeing the current block (the current block will be erased in freeing) */ prev_block = UT_LIST_GET_PREV(list, block); mem_heap_block_free(heap, block); block = prev_block; } ut_ad(block); /* Set the free field of block */ mem_block_set_free(block, old_top - (byte*)block); #ifdef UNIV_MEM_DEBUG ut_ad(mem_block_get_start(block) <= mem_block_get_free(block)); /* In the debug version erase block from top up */ mem_erase_buf(old_top, (byte*)block + block->len - old_top); /* Update allocated memory count */ mutex_enter(&mem_hash_mutex); mem_current_allocated_memory -= (total_size - size); mutex_exit(&mem_hash_mutex); #else /* UNIV_MEM_DEBUG */ UNIV_MEM_ASSERT_W(old_top, (byte*)block + block->len - old_top); #endif /* UNIV_MEM_DEBUG */ UNIV_MEM_ALLOC(old_top, (byte*)block + block->len - old_top); /* If free == start, we may free the block if it is not the first one */ if ((heap != block) && (mem_block_get_free(block) == mem_block_get_start(block))) { mem_heap_block_free(heap, block); } } /*****************************************************************//** Empties a memory heap. The first memory block of the heap is not freed. */ UNIV_INLINE void mem_heap_empty( /*===========*/ mem_heap_t* heap) /*!< in: heap to empty */ { mem_heap_free_heap_top(heap, (byte*)heap + mem_block_get_start(heap)); #ifndef UNIV_HOTBACKUP if (heap->free_block) { mem_heap_free_block_free(heap); } #endif /* !UNIV_HOTBACKUP */ } /*****************************************************************//** Returns a pointer to the topmost element in a memory heap. The size of the element must be given. @return pointer to the topmost element */ UNIV_INLINE void* mem_heap_get_top( /*=============*/ mem_heap_t* heap, /*!< in: memory heap */ ulint n) /*!< in: size of the topmost element */ { mem_block_t* block; void* buf; ut_ad(mem_heap_check(heap)); block = UT_LIST_GET_LAST(heap->base); buf = (byte*)block + mem_block_get_free(block) - MEM_SPACE_NEEDED(n); #ifdef UNIV_MEM_DEBUG ut_ad(mem_block_get_start(block) <=(ulint)((byte*)buf - (byte*)block)); /* In the debug version, advance buf to point at the storage which was given to the caller in the allocation*/ buf = (byte*)buf + MEM_FIELD_HEADER_SIZE; /* Check that the field lengths agree */ ut_ad(n == (ulint)mem_field_header_get_len(buf)); #endif return(buf); } /*****************************************************************//** Frees the topmost element in a memory heap. The size of the element must be given. */ UNIV_INLINE void mem_heap_free_top( /*==============*/ mem_heap_t* heap, /*!< in: memory heap */ ulint n) /*!< in: size of the topmost element */ { mem_block_t* block; ut_ad(mem_heap_check(heap)); block = UT_LIST_GET_LAST(heap->base); /* Subtract the free field of block */ mem_block_set_free(block, mem_block_get_free(block) - MEM_SPACE_NEEDED(n)); UNIV_MEM_ASSERT_W((byte*) block + mem_block_get_free(block), n); #ifdef UNIV_MEM_DEBUG ut_ad(mem_block_get_start(block) <= mem_block_get_free(block)); /* In the debug version check the consistency, and erase field */ mem_field_erase((byte*)block + mem_block_get_free(block), n); #endif /* If free == start, we may free the block if it is not the first one */ if ((heap != block) && (mem_block_get_free(block) == mem_block_get_start(block))) { mem_heap_block_free(heap, block); } else { /* Avoid a bogus UNIV_MEM_ASSERT_W() warning in a subsequent invocation of mem_heap_free_top(). Originally, this was UNIV_MEM_FREE(), to catch writes to freed memory. */ UNIV_MEM_ALLOC((byte*) block + mem_block_get_free(block), n); } } /*****************************************************************//** NOTE: Use the corresponding macros instead of this function. Creates a memory heap. For debugging purposes, takes also the file name and line as argument. @return own: memory heap, NULL if did not succeed (only possible for MEM_HEAP_BTR_SEARCH type heaps) */ UNIV_INLINE mem_heap_t* mem_heap_create_func( /*=================*/ ulint n, /*!< in: desired start block size, this means that a single user buffer of size n will fit in the block, 0 creates a default size block */ ulint type, /*!< in: heap type */ const char* file_name, /*!< in: file name where created */ ulint line) /*!< in: line where created */ { mem_block_t* block; if (!n) { n = MEM_BLOCK_START_SIZE; } block = mem_heap_create_block(NULL, n, type, file_name, line); if (block == NULL) { return(NULL); } UT_LIST_INIT(block->base); /* Add the created block itself as the first block in the list */ UT_LIST_ADD_FIRST(list, block->base, block); #ifdef UNIV_MEM_DEBUG mem_hash_insert(block, file_name, line); #endif return(block); } /*****************************************************************//** NOTE: Use the corresponding macro instead of this function. Frees the space occupied by a memory heap. In the debug version erases the heap memory blocks. */ UNIV_INLINE void mem_heap_free_func( /*===============*/ mem_heap_t* heap, /*!< in, own: heap to be freed */ const char* file_name __attribute__((unused)), /*!< in: file name where freed */ ulint line __attribute__((unused))) { mem_block_t* block; mem_block_t* prev_block; ut_ad(mem_heap_check(heap)); block = UT_LIST_GET_LAST(heap->base); #ifdef UNIV_MEM_DEBUG /* In the debug version remove the heap from the hash table of heaps and check its consistency */ mem_hash_remove(heap, file_name, line); #endif #ifndef UNIV_HOTBACKUP if (heap->free_block) { mem_heap_free_block_free(heap); } #endif /* !UNIV_HOTBACKUP */ while (block != NULL) { /* Store the contents of info before freeing current block (it is erased in freeing) */ prev_block = UT_LIST_GET_PREV(list, block); mem_heap_block_free(heap, block); block = prev_block; } } /***************************************************************//** NOTE: Use the corresponding macro instead of this function. Allocates a single buffer of memory from the dynamic memory of the C compiler. Is like malloc of C. The buffer must be freed with mem_free. @return own: free storage */ UNIV_INLINE void* mem_alloc_func( /*===========*/ ulint n, /*!< in: desired number of bytes */ ulint* size, /*!< out: allocated size in bytes, or NULL */ const char* file_name, /*!< in: file name where created */ ulint line) /*!< in: line where created */ { mem_heap_t* heap; void* buf; heap = mem_heap_create_func(n, MEM_HEAP_DYNAMIC, file_name, line); /* Note that as we created the first block in the heap big enough for the buffer requested by the caller, the buffer will be in the first block and thus we can calculate the pointer to the heap from the pointer to the buffer when we free the memory buffer. */ if (UNIV_LIKELY_NULL(size)) { /* Adjust the allocation to the actual size of the memory block. */ ulint m = mem_block_get_len(heap) - mem_block_get_free(heap); #ifdef UNIV_MEM_DEBUG m -= MEM_FIELD_HEADER_SIZE + MEM_FIELD_TRAILER_SIZE; #endif /* UNIV_MEM_DEBUG */ ut_ad(m >= n); *size = n = m; } buf = mem_heap_alloc(heap, n); ut_a((byte*)heap == (byte*)buf - MEM_BLOCK_HEADER_SIZE - MEM_FIELD_HEADER_SIZE); return(buf); } /***************************************************************//** NOTE: Use the corresponding macro instead of this function. Frees a single buffer of storage from the dynamic memory of the C compiler. Similar to the free of C. */ UNIV_INLINE void mem_free_func( /*==========*/ void* ptr, /*!< in, own: buffer to be freed */ const char* file_name, /*!< in: file name where created */ ulint line) /*!< in: line where created */ { mem_heap_t* heap; heap = (mem_heap_t*)((byte*)ptr - MEM_BLOCK_HEADER_SIZE - MEM_FIELD_HEADER_SIZE); mem_heap_free_func(heap, file_name, line); } /*****************************************************************//** Returns the space in bytes occupied by a memory heap. */ UNIV_INLINE ulint mem_heap_get_size( /*==============*/ mem_heap_t* heap) /*!< in: heap */ { ulint size = 0; ut_ad(mem_heap_check(heap)); size = heap->total_size; #ifndef UNIV_HOTBACKUP if (heap->free_block) { size += UNIV_PAGE_SIZE; } #endif /* !UNIV_HOTBACKUP */ return(size); } /**********************************************************************//** Duplicates a NUL-terminated string. @return own: a copy of the string, must be deallocated with mem_free */ UNIV_INLINE char* mem_strdup( /*=======*/ const char* str) /*!< in: string to be copied */ { ulint len = strlen(str) + 1; return((char*) memcpy(mem_alloc(len), str, len)); } /**********************************************************************//** Makes a NUL-terminated copy of a nonterminated string. @return own: a copy of the string, must be deallocated with mem_free */ UNIV_INLINE char* mem_strdupl( /*========*/ const char* str, /*!< in: string to be copied */ ulint len) /*!< in: length of str, in bytes */ { char* s = (char*) mem_alloc(len + 1); s[len] = 0; return((char*) memcpy(s, str, len)); } /**********************************************************************//** Makes a NUL-terminated copy of a nonterminated string, allocated from a memory heap. @return own: a copy of the string */ UNIV_INLINE char* mem_heap_strdupl( /*=============*/ mem_heap_t* heap, /*!< in: memory heap where string is allocated */ const char* str, /*!< in: string to be copied */ ulint len) /*!< in: length of str, in bytes */ { char* s = (char*) mem_heap_alloc(heap, len + 1); s[len] = 0; return((char*) memcpy(s, str, len)); } haildb-2.3.2/include/os0thread.h0000644000175000017500000001241611513177357017334 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2010, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/os0thread.h The interface to the operating system process and thread control primitives Created 9/8/1995 Heikki Tuuri *******************************************************/ #ifndef os0thread_h #define os0thread_h #include "univ.i" #ifdef HAVE_PTHREAD_H #include #endif /* HAVE_PTHREAD_H */ #ifdef __WIN__ #include #endif /* Maximum number of threads which can be created in the program; this is also the size of the wait slot array for user threads which can wait inside InnoDB */ #define OS_THREAD_MAX_N srv_max_n_threads /* Possible fixed priorities for threads */ #define OS_THREAD_PRIORITY_NONE 100 #define OS_THREAD_PRIORITY_BACKGROUND 1 #define OS_THREAD_PRIORITY_NORMAL 2 #define OS_THREAD_PRIORITY_ABOVE_NORMAL 3 #ifdef __WIN__ typedef void* os_thread_t; typedef unsigned long os_thread_id_t; /*!< In Windows the thread id is an unsigned long int */ #else typedef pthread_t os_thread_t; typedef os_thread_t os_thread_id_t; /*!< In Unix we use the thread handle itself as the id of the thread */ #endif /* Define a function pointer type to use in a typecast */ typedef void* (*os_posix_f_t) (void*); /***************************************************************//** Compares two thread ids for equality. @return TRUE if equal */ UNIV_INTERN ibool os_thread_eq( /*=========*/ os_thread_id_t a, /*!< in: OS thread or thread id */ os_thread_id_t b); /*!< in: OS thread or thread id */ /****************************************************************//** Converts an OS thread id to a ulint. It is NOT guaranteed that the ulint is unique for the thread though! @return thread identifier as a number */ UNIV_INTERN ulint os_thread_pf( /*=========*/ os_thread_id_t a); /*!< in: OS thread identifier */ /****************************************************************//** Creates a new thread of execution. The execution starts from the function given. The start function takes a void* parameter and returns a ulint. NOTE: We count the number of threads in os_thread_exit(). A created thread should always use that to exit and not use return() to exit. @return handle to the thread */ UNIV_INTERN os_thread_t os_thread_create( /*=============*/ #ifndef __WIN__ os_posix_f_t start_f, #else ulint (*start_f)(void*), /*!< in: pointer to function from which to start */ #endif void* arg, /*!< in: argument to start function */ os_thread_id_t* thread_id); /*!< out: id of the created thread, or NULL */ /*****************************************************************//** Exits the current thread. */ UNIV_INTERN void os_thread_exit( /*===========*/ void* exit_value); /*!< in: exit value; in Windows this void* is cast as a DWORD */ /*****************************************************************//** Returns the thread identifier of current thread. @return current thread identifier */ UNIV_INTERN os_thread_id_t os_thread_get_curr_id(void); /*========================*/ /*****************************************************************//** Returns handle to the current thread. @return current thread handle */ UNIV_INTERN os_thread_t os_thread_get_curr(void); /*====================*/ /*****************************************************************//** Advises the os to give up remainder of the thread's time slice. */ UNIV_INTERN void os_thread_yield(void); /*=================*/ /*****************************************************************//** The thread sleeps at least the time given in microseconds. */ UNIV_INTERN void os_thread_sleep( /*============*/ ulint tm); /*!< in: time in microseconds */ /******************************************************************//** Gets a thread priority. @return priority */ UNIV_INTERN ulint os_thread_get_priority( /*===================*/ os_thread_t handle);/*!< in: OS handle to the thread */ /******************************************************************//** Sets a thread priority. */ UNIV_INTERN void os_thread_set_priority( /*===================*/ os_thread_t handle, /*!< in: OS handle to the thread */ ulint pri); /*!< in: priority: one of OS_PRIORITY_... */ /******************************************************************//** Gets the last operating system error code for the calling thread. @return last error on Windows, 0 otherwise */ UNIV_INTERN ulint os_thread_get_last_error(void); /*==========================*/ #ifndef UNIV_NONINL #include "os0thread.ic" #endif #endif haildb-2.3.2/include/row0umod.ic0000644000175000017500000000202111513177357017352 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1997, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/row0umod.ic Undo modify of a row Created 2/27/1997 Heikki Tuuri *******************************************************/ haildb-2.3.2/include/read0types.h0000644000175000017500000000223311513177357017517 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1997, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/read0types.h Cursor read Created 2/16/1997 Heikki Tuuri *******************************************************/ #ifndef read0types_h #define read0types_h typedef struct read_view_struct read_view_t; typedef struct cursor_view_struct cursor_view_t; #endif haildb-2.3.2/include/ibuf0ibuf.ic0000644000175000017500000002424111513177357017461 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1997, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/ibuf0ibuf.ic Insert buffer Created 7/19/1997 Heikki Tuuri *******************************************************/ #include "page0page.h" #ifdef WITH_ZIP #include "page0zip.h" #endif /* WITH_ZIP */ #ifndef UNIV_HOTBACKUP #include "buf0lru.h" /** Counter for ibuf_should_try() */ extern ulint ibuf_flush_count; /** An index page must contain at least UNIV_PAGE_SIZE / IBUF_PAGE_SIZE_PER_FREE_SPACE bytes of free space for ibuf to try to buffer inserts to this page. If there is this much of free space, the corresponding bits are set in the ibuf bitmap. */ #define IBUF_PAGE_SIZE_PER_FREE_SPACE 32 /** Insert buffer struct */ struct ibuf_struct{ ulint size; /*!< current size of the ibuf index tree, in pages */ ulint max_size; /*!< recommended maximum size of the ibuf index tree, in pages */ ulint seg_size; /*!< allocated pages of the file segment containing ibuf header and tree */ ibool empty; /*!< after an insert to the ibuf tree is performed, this is set to FALSE, and if a contract operation finds the tree empty, this is set to TRUE */ ulint free_list_len; /*!< length of the free list */ ulint height; /*!< tree height */ dict_index_t* index; /*!< insert buffer index */ ulint n_inserts; /*!< number of inserts made to the insert buffer */ ulint n_merges; /*!< number of pages merged */ ulint n_merged_recs; /*!< number of records merged */ }; /************************************************************************//** Sets the free bit of the page in the ibuf bitmap. This is done in a separate mini-transaction, hence this operation does not restrict further work to only ibuf bitmap operations, which would result if the latch to the bitmap page were kept. */ UNIV_INTERN void ibuf_set_free_bits_func( /*====================*/ buf_block_t* block, /*!< in: index page of a non-clustered index; free bit is reset if page level is 0 */ #ifdef UNIV_IBUF_DEBUG ulint max_val,/*!< in: ULINT_UNDEFINED or a maximum value which the bits must have before setting; this is for debugging */ #endif /* UNIV_IBUF_DEBUG */ ulint val); /*!< in: value to set: < 4 */ #ifdef UNIV_IBUF_DEBUG # define ibuf_set_free_bits(b,v,max) ibuf_set_free_bits_func(b,max,v) #else /* UNIV_IBUF_DEBUG */ # define ibuf_set_free_bits(b,v,max) ibuf_set_free_bits_func(b,v) #endif /* UNIV_IBUF_DEBUG */ /**********************************************************************//** A basic partial test if an insert to the insert buffer could be possible and recommended. */ UNIV_INLINE ibool ibuf_should_try( /*============*/ dict_index_t* dict_index, /*!< in: dict_index where to insert */ ulint ignore_sec_unique) /*!< in: if != 0, we should ignore UNIQUE constraint on a secondary index when we decide */ { if (ibuf_use != IBUF_USE_NONE && !dict_index_is_clust(dict_index) && (ignore_sec_unique || !dict_index_is_unique(dict_index))) { ibuf_flush_count++; if (ibuf_flush_count % 4 == 0) { buf_LRU_try_free_flushed_blocks(); } return(TRUE); } return(FALSE); } /***********************************************************************//** Checks if a page address is an ibuf bitmap page address. @return TRUE if a bitmap page */ UNIV_INLINE ibool ibuf_bitmap_page( /*=============*/ ulint zip_size,/*!< in: compressed page size in bytes; 0 for uncompressed pages */ ulint page_no)/*!< in: page number */ { ut_ad(ut_is_2pow(zip_size)); if (!zip_size) { return(UNIV_UNLIKELY((page_no & (UNIV_PAGE_SIZE - 1)) == FSP_IBUF_BITMAP_OFFSET)); } return(UNIV_UNLIKELY((page_no & (zip_size - 1)) == FSP_IBUF_BITMAP_OFFSET)); } /*********************************************************************//** Translates the free space on a page to a value in the ibuf bitmap. @return value for ibuf bitmap bits */ UNIV_INLINE ulint ibuf_index_page_calc_free_bits( /*===========================*/ ulint zip_size, /*!< in: compressed page size in bytes; 0 for uncompressed pages */ ulint max_ins_size) /*!< in: maximum insert size after reorganize for the page */ { ulint n; ut_ad(ut_is_2pow(zip_size)); ut_ad(!zip_size || zip_size > IBUF_PAGE_SIZE_PER_FREE_SPACE); ut_ad(zip_size <= UNIV_PAGE_SIZE); if (zip_size) { n = max_ins_size / (zip_size / IBUF_PAGE_SIZE_PER_FREE_SPACE); } else { n = max_ins_size / (UNIV_PAGE_SIZE / IBUF_PAGE_SIZE_PER_FREE_SPACE); } if (n == 3) { n = 2; } if (n > 3) { n = 3; } return(n); } /*********************************************************************//** Translates the ibuf free bits to the free space on a page in bytes. @return maximum insert size after reorganize for the page */ UNIV_INLINE ulint ibuf_index_page_calc_free_from_bits( /*================================*/ ulint zip_size,/*!< in: compressed page size in bytes; 0 for uncompressed pages */ ulint bits) /*!< in: value for ibuf bitmap bits */ { ut_ad(bits < 4); ut_ad(ut_is_2pow(zip_size)); ut_ad(!zip_size || zip_size > IBUF_PAGE_SIZE_PER_FREE_SPACE); ut_ad(zip_size <= UNIV_PAGE_SIZE); if (zip_size) { if (bits == 3) { return(4 * zip_size / IBUF_PAGE_SIZE_PER_FREE_SPACE); } return(bits * zip_size / IBUF_PAGE_SIZE_PER_FREE_SPACE); } if (bits == 3) { return(4 * UNIV_PAGE_SIZE / IBUF_PAGE_SIZE_PER_FREE_SPACE); } return(bits * (UNIV_PAGE_SIZE / IBUF_PAGE_SIZE_PER_FREE_SPACE)); } #ifdef WITH_ZIP /*********************************************************************//** Translates the free space on a compressed page to a value in the ibuf bitmap. @return value for ibuf bitmap bits */ UNIV_INLINE ulint ibuf_index_page_calc_free_zip( /*==========================*/ ulint zip_size, /*!< in: compressed page size in bytes */ const buf_block_t* block) /*!< in: buffer block */ { ulint max_ins_size; const page_zip_des_t* page_zip; lint zip_max_ins; ut_ad(zip_size == buf_block_get_zip_size(block)); ut_ad(zip_size); max_ins_size = page_get_max_insert_size_after_reorganize( buf_block_get_frame(block), 1); page_zip = buf_block_get_page_zip(block); zip_max_ins = page_zip_max_ins_size(page_zip, FALSE/* not clustered */); if (UNIV_UNLIKELY(zip_max_ins < 0)) { return(0); } else if (UNIV_LIKELY(max_ins_size > (ulint) zip_max_ins)) { max_ins_size = (ulint) zip_max_ins; } return(ibuf_index_page_calc_free_bits(zip_size, max_ins_size)); } #endif /* WITH_ZIP */ /*********************************************************************//** Translates the free space on a page to a value in the ibuf bitmap. @return value for ibuf bitmap bits */ UNIV_INLINE ulint ibuf_index_page_calc_free( /*======================*/ ulint zip_size,/*!< in: compressed page size in bytes; 0 for uncompressed pages */ const buf_block_t* block) /*!< in: buffer block */ { #ifdef WITH_ZIP ut_ad(zip_size == buf_block_get_zip_size(block)); if (zip_size == 0) { #endif /* WITH_ZIP */ ulint max_ins_size; max_ins_size = page_get_max_insert_size_after_reorganize( buf_block_get_frame(block), 1); return(ibuf_index_page_calc_free_bits(0, max_ins_size)); #ifdef WITH_ZIP } else { return(ibuf_index_page_calc_free_zip(zip_size, block)); } #endif /* WITH_ZIP */ } /************************************************************************//** Updates the free bits of an uncompressed page in the ibuf bitmap if there is not enough free on the page any more. This is done in a separate mini-transaction, hence this operation does not restrict further work to only ibuf bitmap operations, which would result if the latch to the bitmap page were kept. NOTE: The free bits in the insert buffer bitmap must never exceed the free space on a page. It is unsafe to increment the bits in a separately committed mini-transaction, because in crash recovery, the free bits could momentarily be set too high. It is only safe to use this function for decrementing the free bits. Should more free space become available, we must not update the free bits here, because that would break crash recovery. */ UNIV_INLINE void ibuf_update_free_bits_if_full( /*==========================*/ buf_block_t* block, /*!< in: index page to which we have added new records; the free bits are updated if the index is non-clustered and non-unique and the page level is 0, and the page becomes fuller */ ulint max_ins_size,/*!< in: value of maximum insert size with reorganize before the latest operation performed to the page */ ulint increase)/*!< in: upper limit for the additional space used in the latest operation, if known, or ULINT_UNDEFINED */ { ulint before; ulint after; ut_ad(!buf_block_get_page_zip(block)); before = ibuf_index_page_calc_free_bits(0, max_ins_size); if (max_ins_size >= increase) { #if ULINT32_UNDEFINED <= UNIV_PAGE_SIZE # error "ULINT32_UNDEFINED <= UNIV_PAGE_SIZE" #endif after = ibuf_index_page_calc_free_bits(0, max_ins_size - increase); #ifdef UNIV_IBUF_DEBUG ut_a(after <= ibuf_index_page_calc_free(0, block)); #endif } else { after = ibuf_index_page_calc_free(0, block); } if (after == 0) { /* We move the page to the front of the buffer pool LRU list: the purpose of this is to prevent those pages to which we cannot make inserts using the insert buffer from slipping out of the buffer pool */ buf_page_make_young(&block->page); } if (before > after) { ibuf_set_free_bits(block, after, before); } } #endif /* !UNIV_HOTBACKUP */ haildb-2.3.2/include/btr0sea.ic0000644000175000017500000000471011513177357017145 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /********************************************************************//** @file include/btr0sea.ic The index tree adaptive search Created 2/17/1996 Heikki Tuuri *************************************************************************/ #include "dict0mem.h" #include "btr0cur.h" #include "buf0buf.h" /*********************************************************************//** Updates the search info. */ UNIV_INTERN void btr_search_info_update_slow( /*========================*/ btr_search_t* info, /*!< in/out: search info */ btr_cur_t* cursor);/*!< in: cursor which was just positioned */ /********************************************************************//** Returns search info for an index. @return search info; search mutex reserved */ UNIV_INLINE btr_search_t* btr_search_get_info( /*================*/ dict_index_t* dict_index) /*!< in: dict_index */ { ut_ad(dict_index); return(dict_index->search_info); } /*********************************************************************//** Updates the search info. */ UNIV_INLINE void btr_search_info_update( /*===================*/ dict_index_t* dict_index, /*!< in: dict_index of the cursor */ btr_cur_t* cursor) /*!< in: cursor which was just positioned */ { btr_search_t* info; #ifdef UNIV_SYNC_DEBUG ut_ad(!rw_lock_own(&btr_search_latch, RW_LOCK_SHARED)); ut_ad(!rw_lock_own(&btr_search_latch, RW_LOCK_EX)); #endif /* UNIV_SYNC_DEBUG */ info = btr_search_get_info(dict_index); info->hash_analysis++; if (info->hash_analysis < BTR_SEARCH_HASH_ANALYSIS) { /* Do nothing */ return; } ut_ad(cursor->flag != BTR_CUR_HASH); btr_search_info_update_slow(info, cursor); } haildb-2.3.2/include/dict0boot.ic0000644000175000017500000000467111513177357017502 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/dict0boot.ic Data dictionary creation and booting Created 4/18/1996 Heikki Tuuri *******************************************************/ /**********************************************************************//** Writes the current value of the row id counter to the dictionary header file page. */ UNIV_INTERN void dict_hdr_flush_row_id(void); /*=======================*/ /**********************************************************************//** Returns a new row id. @return the new id */ UNIV_INLINE dulint dict_sys_get_new_row_id(void) /*=========================*/ { dulint id; mutex_enter(&(dict_sys->mutex)); id = dict_sys->row_id; if (0 == (ut_dulint_get_low(id) % DICT_HDR_ROW_ID_WRITE_MARGIN)) { dict_hdr_flush_row_id(); } UT_DULINT_INC(dict_sys->row_id); mutex_exit(&(dict_sys->mutex)); return(id); } /**********************************************************************//** Reads a row id from a record or other 6-byte stored form. @return row id */ UNIV_INLINE dulint dict_sys_read_row_id( /*=================*/ byte* field) /*!< in: record field */ { #if DATA_ROW_ID_LEN != 6 # error "DATA_ROW_ID_LEN != 6" #endif return(mach_read_from_6(field)); } /**********************************************************************//** Writes a row id to a record or other 6-byte stored form. */ UNIV_INLINE void dict_sys_write_row_id( /*==================*/ byte* field, /*!< in: record field */ dulint row_id) /*!< in: row id */ { #if DATA_ROW_ID_LEN != 6 # error "DATA_ROW_ID_LEN != 6" #endif mach_write_to_6(field, row_id); } haildb-2.3.2/include/row0ext.ic0000644000175000017500000000520611513177357017216 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 2006, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/row0ext.ic Caching of externally stored column prefixes Created September 2006 Marko Makela *******************************************************/ #include "rem0types.h" #include "btr0types.h" /********************************************************************//** Looks up a column prefix of an externally stored column. @return column prefix, or NULL if the column is not stored externally, or pointer to field_ref_zero if the BLOB pointer is unset */ UNIV_INLINE const byte* row_ext_lookup_ith( /*===============*/ const row_ext_t* ext, /*!< in/out: column prefix cache */ ulint i, /*!< in: index of ext->ext[] */ ulint* len) /*!< out: length of prefix, in bytes, at most REC_MAX_INDEX_COL_LEN */ { ut_ad(ext); ut_ad(len); ut_ad(i < ext->n_ext); *len = ext->len[i]; if (UNIV_UNLIKELY(*len == 0)) { /* The BLOB could not be fetched to the cache. */ return(field_ref_zero); } else { return(ext->buf + i * REC_MAX_INDEX_COL_LEN); } } /********************************************************************//** Looks up a column prefix of an externally stored column. @return column prefix, or NULL if the column is not stored externally, or pointer to field_ref_zero if the BLOB pointer is unset */ UNIV_INLINE const byte* row_ext_lookup( /*===========*/ const row_ext_t* ext, /*!< in: column prefix cache */ ulint col, /*!< in: column number in the InnoDB table object, as reported by dict_col_get_no(); NOT relative to the records in the clustered index */ ulint* len) /*!< out: length of prefix, in bytes, at most REC_MAX_INDEX_COL_LEN */ { ulint i; ut_ad(ext); ut_ad(len); for (i = 0; i < ext->n_ext; i++) { if (col == ext->ext[i]) { return(row_ext_lookup_ith(ext, i, len)); } } return(NULL); } haildb-2.3.2/include/row0prebuilt.h0000644000175000017500000001506411513177357020103 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1997, 2010, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /***************************************************** Row select prebuilt structure definition. Created 02/03/2009 Sunny Bains *******************************************************/ #ifndef row0prebuilt_h #define row0prebuilt_h #include "lock0types.h" #include "row0sel.h" /************************************************************************ Create a prebuilt struct for a user table handle. @return own: a prebuilt struct */ UNIV_INTERN row_prebuilt_t* row_prebuilt_create( /*================*/ dict_table_t* table); /*!< in: Innobase table handle */ /************************************************************************ Free a prebuilt struct for a user table handle. */ UNIV_INTERN void row_prebuilt_free( /*==============*/ row_prebuilt_t* prebuilt, /*!< in, own: prebuilt struct */ ibool dict_locked); /*!< in: TRUE if dict was locked */ /************************************************************************ Reset a prebuilt struct for a user table handle. */ UNIV_INTERN void row_prebuilt_reset( /*===============*/ row_prebuilt_t* prebuilt); /*!< in/out: prebuilt struct */ /************************************************************************* Updates the transaction pointers in query graphs stored in the prebuilt struct. */ UNIV_INTERN void row_prebuilt_update_trx( /*====================*/ row_prebuilt_t* prebuilt, /*!< in/out: prebuilt struct handle */ trx_t* trx); /*!< in: transaction handle */ #define FETCH_CACHE_SIZE 16 /* After fetching this many rows, we start caching them in fetch_cache */ #define FETCH_CACHE_THRESHOLD 4 /* Values for hint_need_to_fetch_extra_cols */ #define ROW_RETRIEVE_PRIMARY_KEY 1 #define ROW_RETRIEVE_ALL_COLS 2 #define ROW_PREBUILT_ALLOCATED 78540783 #define ROW_PREBUILT_FREED 26423527 #define ROW_PREBUILT_FETCH_MAGIC_N 465765687 /* An InnoDB cached row. */ typedef struct ib_cached_row_struct { ulint max_len; /* max len of rec if not NULL */ ulint rec_len; /* length of valid data in rec */ rec_t* rec; /* cached record, pointer into the start of the record data */ byte* ptr; /* pointer to start of record */ } ib_cached_row_t; /** Cache for rows fetched when positioning the cursor. */ typedef struct ib_row_cache_struct { mem_heap_t* heap; /* memory heap for cached rows */ ib_cached_row_t*ptr; /* a cache for fetched rows if we fetch many rows from the same cursor: it saves CPU time to fetch them in a batch. */ unsigned n_max:10; /* max size of the row cache. */ unsigned n_size:10; /* current max setting, must be <= n_max */ unsigned first:10; /* position of the first not yet fetched row in fetch_cache */ unsigned n_cached:10; /* number of not yet accessed rows in fetch_cache */ ib_cur_op_t direction; /* ROW_SEL_NEXT or ROW_SEL_PREV */ } ib_row_cache_t; /* A struct for (sometimes lazily) prebuilt structures in an Innobase table handle used within the API; these are used to save CPU time. */ struct row_prebuilt_struct { ulint magic_n; /* this magic number is set to ROW_PREBUILT_ALLOCATED when created, or ROW_PREBUILT_FREED when the struct has been freed */ unsigned sql_stat_start:1;/* TRUE when we start processing of an SQL statement: we may have to set an intention lock on the table, create a consistent read view etc. */ unsigned client_has_locked:1; /* this is set TRUE when a client calls explicit lock on this handle with a lock flag, and set FALSE when with unlocked */ unsigned clust_index_was_generated:1; /* if the user did not define a primary key, then Innobase automatically generated a clustered index where the ordering column is the row id: in this case this flag is set to TRUE */ unsigned need_to_access_clustered:1; /* if we are fetching columns through a secondary index and at least one column is not in the secondary index, then this is set to TRUE */ unsigned index_usable:1; /* caches the value of row_merge_is_index_usable(trx,index) */ unsigned simple_select:1;/* TRUE if plain select */ unsigned new_rec_locks:2;/* normally 0; if srv_locks_unsafe_for_binlog is TRUE or session is using READ COMMITTED isolation level, in a cursor search, if we set a new record lock on an index, this is incremented; this is used in releasing the locks under the cursors if we are performing an UPDATE and we determine after retrieving the row that it does not need to be locked; thus, these can be used to implement a 'mini-rollback' that releases the latest record locks */ mem_heap_t* heap; /* memory heap from which these auxiliary structures are allocated when needed */ dict_table_t* table; /* Innobase table handle */ dict_index_t* index; /* current index for a search, if any */ trx_t* trx; /* current transaction handle */ btr_pcur_t* pcur; /* persistent cursor used in selects and updates */ btr_pcur_t* clust_pcur; /* persistent cursor used in some selects and updates */ que_fork_t* sel_graph; /* dummy query graph used in selects */ dtuple_t* search_tuple; /* prebuilt dtuple used in selects */ byte row_id[DATA_ROW_ID_LEN]; /* if the clustered index was generated, the row id of the last row fetched is stored here */ dtuple_t* clust_ref; /* prebuilt dtuple used in sel/upd/del */ enum lock_mode select_lock_type;/* LOCK_NONE, LOCK_S, or LOCK_X */ mem_heap_t* old_vers_heap; /* memory heap where a previous version is built in consistent read */ ib_row_cache_t row_cache; /* rows cached by select read ahead */ int result; /* Result of the last compare in row_search_for_client(). */ ulint magic_n2; /* this should be the same as magic_n */ }; #endif /* row0prebuilt_h */ haildb-2.3.2/include/os0proc.h0000644000175000017500000000513311513177357017026 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/os0proc.h The interface to the operating system process control primitives Created 9/30/1995 Heikki Tuuri *******************************************************/ #ifndef os0proc_h #define os0proc_h #include "univ.i" /* Required for HugeTLB support. */ #ifdef UNIV_LINUX #include #include #endif typedef void* os_process_t; typedef unsigned long int os_process_id_t; extern ibool os_use_large_pages; /* Large page size. This may be a boot-time option on some platforms */ extern ulint os_large_page_size; /****************************************************************//** Converts the current process id to a number. It is not guaranteed that the number is unique. In Linux returns the 'process number' of the current thread. That number is the same as one sees in 'top', for example. In Linux the thread id is not the same as one sees in 'top'. @return process id as a number */ UNIV_INTERN ulint os_proc_get_number(void); /*====================*/ /****************************************************************//** Allocates large pages memory. @return allocated memory */ UNIV_INTERN void* os_mem_alloc_large( /*===============*/ ulint* n); /*!< in/out: number of bytes */ /****************************************************************//** Frees large pages memory. */ UNIV_INTERN void os_mem_free_large( /*==============*/ void *ptr, /*!< in: pointer returned by os_mem_alloc_large() */ ulint size); /*!< in: size returned by os_mem_alloc_large() */ /******************************************************************//** Reset the variables. */ UNIV_INTERN void os_proc_var_init(void); /*==================*/ #ifndef UNIV_NONINL #include "os0proc.ic" #endif #endif haildb-2.3.2/include/usr0sess.h0000644000175000017500000000441711513177357017234 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/usr0sess.h Sessions Created 6/25/1996 Heikki Tuuri *******************************************************/ #ifndef usr0sess_h #define usr0sess_h #include "univ.i" #include "ut0byte.h" #include "trx0types.h" #include "srv0srv.h" #include "trx0types.h" #include "usr0types.h" #include "que0types.h" #include "data0data.h" #include "rem0rec.h" /*********************************************************************//** Opens a session. @return own: session object */ UNIV_INTERN sess_t* sess_open(void); /*============*/ /*********************************************************************//** Closes a session, freeing the memory occupied by it. */ UNIV_INTERN void sess_close( /*=======*/ sess_t* sess); /*!< in, own: session object */ /** The session handle. All fields are protected by the kernel mutex */ struct sess_struct{ ulint state; /*!< state of the session */ trx_t* trx; /*!< transaction object permanently assigned for the session: the transaction instance designated by the trx id changes, but the memory structure is preserved */ UT_LIST_BASE_NODE_T(que_t) graphs; /*!< query graphs belonging to this session */ }; /* Session states */ #define SESS_ACTIVE 1 #define SESS_ERROR 2 /* session contains an error message which has not yet been communicated to the client */ #ifndef UNIV_NONINL #include "usr0sess.ic" #endif #endif haildb-2.3.2/include/lock0iter.h0000644000175000017500000000513111513177357017333 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 2007, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/lock0iter.h Lock queue iterator type and function prototypes. Created July 16, 2007 Vasil Dimov *******************************************************/ #ifndef lock0iter_h #define lock0iter_h #include "univ.i" #include "lock0types.h" typedef struct lock_queue_iterator_struct { const lock_t* current_lock; /* In case this is a record lock queue (not table lock queue) then bit_no is the record number within the heap in which the record is stored. */ ulint bit_no; } lock_queue_iterator_t; /*******************************************************************//** Initialize lock queue iterator so that it starts to iterate from "lock". bit_no specifies the record number within the heap where the record is stored. It can be undefined (ULINT_UNDEFINED) in two cases: 1. If the lock is a table lock, thus we have a table lock queue; 2. If the lock is a record lock and it is a wait lock. In this case bit_no is calculated in this function by using lock_rec_find_set_bit(). There is exactly one bit set in the bitmap of a wait lock. */ UNIV_INTERN void lock_queue_iterator_reset( /*======================*/ lock_queue_iterator_t* iter, /*!< out: iterator */ const lock_t* lock, /*!< in: lock to start from */ ulint bit_no);/*!< in: record number in the heap */ /*******************************************************************//** Gets the previous lock in the lock queue, returns NULL if there are no more locks (i.e. the current lock is the first one). The iterator is receded (if not-NULL is returned). @return previous lock or NULL */ const lock_t* lock_queue_iterator_get_prev( /*=========================*/ lock_queue_iterator_t* iter); /*!< in/out: iterator */ #endif /* lock0iter_h */ haildb-2.3.2/include/btr0sea.h0000644000175000017500000002660611513177357017011 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /********************************************************************//** @file include/btr0sea.h The index tree adaptive search Created 2/17/1996 Heikki Tuuri *************************************************************************/ #ifndef btr0sea_h #define btr0sea_h #include "univ.i" #include "rem0rec.h" #include "dict0dict.h" #include "btr0types.h" #include "mtr0mtr.h" #include "ha0ha.h" /*****************************************************************//** Creates and initializes the adaptive search system at a database start. */ UNIV_INTERN void btr_search_sys_create( /*==================*/ ulint hash_size); /*!< in: hash index hash table size */ /*****************************************************************//** Frees the adaptive search system at a database shutdown. */ UNIV_INTERN void btr_search_sys_free(void); /*=====================*/ /********************************************************************//** Disable the adaptive hash search system and empty the index. */ UNIV_INTERN void btr_search_disable(void); /*====================*/ /********************************************************************//** Enable the adaptive hash search system. */ UNIV_INTERN void btr_search_enable(void); /*====================*/ /********************************************************************//** Returns search info for an index. @return search info; search mutex reserved */ UNIV_INLINE btr_search_t* btr_search_get_info( /*================*/ dict_index_t* index); /*!< in: index */ /*****************************************************************//** Creates and initializes a search info struct. @return own: search info struct */ UNIV_INTERN btr_search_t* btr_search_info_create( /*===================*/ mem_heap_t* heap); /*!< in: heap where created */ /*****************************************************************//** Returns the value of ref_count. The value is protected by btr_search_latch. @return ref_count value. */ UNIV_INTERN ulint btr_search_info_get_ref_count( /*==========================*/ btr_search_t* info); /*!< in: search info. */ /*********************************************************************//** Updates the search info. */ UNIV_INLINE void btr_search_info_update( /*===================*/ dict_index_t* index, /*!< in: index of the cursor */ btr_cur_t* cursor);/*!< in: cursor which was just positioned */ /******************************************************************//** Tries to guess the right search position based on the hash search info of the index. Note that if mode is PAGE_CUR_LE, which is used in inserts, and the function returns TRUE, then cursor->up_match and cursor->low_match both have sensible values. @return TRUE if succeeded */ UNIV_INTERN ibool btr_search_guess_on_hash( /*=====================*/ dict_index_t* index, /*!< in: index */ btr_search_t* info, /*!< in: index search info */ const dtuple_t* tuple, /*!< in: logical record */ ulint mode, /*!< in: PAGE_CUR_L, ... */ ulint latch_mode, /*!< in: BTR_SEARCH_LEAF, ... */ btr_cur_t* cursor, /*!< out: tree cursor */ ulint has_search_latch,/*!< in: latch mode the caller currently has on btr_search_latch: RW_S_LATCH, RW_X_LATCH, or 0 */ mtr_t* mtr); /*!< in: mtr */ /********************************************************************//** Moves or deletes hash entries for moved records. If new_page is already hashed, then the hash index for page, if any, is dropped. If new_page is not hashed, and page is hashed, then a new hash index is built to new_page with the same parameters as page (this often happens when a page is split). */ UNIV_INTERN void btr_search_move_or_delete_hash_entries( /*===================================*/ buf_block_t* new_block, /*!< in: records are copied to this page */ buf_block_t* block, /*!< in: index page from which records were copied, and the copied records will be deleted from this page */ dict_index_t* index); /*!< in: record descriptor */ /********************************************************************//** Drops a page hash index. */ UNIV_INTERN void btr_search_drop_page_hash_index( /*============================*/ buf_block_t* block); /*!< in: block containing index page, s- or x-latched, or an index page for which we know that block->buf_fix_count == 0 */ /********************************************************************//** Drops a page hash index when a page is freed from a fseg to the file system. Drops possible hash index if the page happens to be in the buffer pool. */ UNIV_INTERN void btr_search_drop_page_hash_when_freed( /*=================================*/ ulint space, /*!< in: space id */ ulint zip_size, /*!< in: compressed page size in bytes or 0 for uncompressed pages */ ulint page_no); /*!< in: page number */ /********************************************************************//** Updates the page hash index when a single record is inserted on a page. */ UNIV_INTERN void btr_search_update_hash_node_on_insert( /*==================================*/ btr_cur_t* cursor);/*!< in: cursor which was positioned to the place to insert using btr_cur_search_..., and the new record has been inserted next to the cursor */ /********************************************************************//** Updates the page hash index when a single record is inserted on a page. */ UNIV_INTERN void btr_search_update_hash_on_insert( /*=============================*/ btr_cur_t* cursor);/*!< in: cursor which was positioned to the place to insert using btr_cur_search_..., and the new record has been inserted next to the cursor */ /********************************************************************//** Updates the page hash index when a single record is deleted from a page. */ UNIV_INTERN void btr_search_update_hash_on_delete( /*=============================*/ btr_cur_t* cursor);/*!< in: cursor which was positioned on the record to delete using btr_cur_search_..., the record is not yet deleted */ /********************************************************************//** Validates the search system. @return TRUE if ok */ UNIV_INTERN ibool btr_search_validate(void); /*======================*/ /********************************************************************* Reset global configuration variables. */ UNIV_INTERN void btr_search_var_init(void); /*=====================*/ /********************************************************************* Closes the adaptive search system at a database shutdown. */ UNIV_INTERN void btr_search_sys_close(void); /*======================*/ /** Flag: has the search system been enabled? Protected by btr_search_latch and btr_search_enabled_mutex. */ extern char btr_search_enabled; /** The search info struct in an index */ struct btr_search_struct{ ulint ref_count; /*!< Number of blocks in this index tree that have search index built i.e. block->index points to this index. Protected by btr_search_latch except when during initialization in btr_search_info_create(). */ /* @{ The following fields are not protected by any latch. Unfortunately, this means that they must be aligned to the machine word, i.e., they cannot be turned into bit-fields. */ buf_block_t* root_guess;/*!< the root page frame when it was last time fetched, or NULL */ ulint hash_analysis; /*!< when this exceeds BTR_SEARCH_HASH_ANALYSIS, the hash analysis starts; this is reset if no success noticed */ ibool last_hash_succ; /*!< TRUE if the last search would have succeeded, or did succeed, using the hash index; NOTE that the value here is not exact: it is not calculated for every search, and the calculation itself is not always accurate! */ ulint n_hash_potential; /*!< number of consecutive searches which would have succeeded, or did succeed, using the hash index; the range is 0 .. BTR_SEARCH_BUILD_LIMIT + 5 */ /* @} */ /*---------------------- @{ */ ulint n_fields; /*!< recommended prefix length for hash search: number of full fields */ ulint n_bytes; /*!< recommended prefix: number of bytes in an incomplete field @see BTR_PAGE_MAX_REC_SIZE */ ibool left_side; /*!< TRUE or FALSE, depending on whether the leftmost record of several records with the same prefix should be indexed in the hash index */ /*---------------------- @} */ #ifdef UNIV_SEARCH_PERF_STAT ulint n_hash_succ; /*!< number of successful hash searches thus far */ ulint n_hash_fail; /*!< number of failed hash searches */ ulint n_patt_succ; /*!< number of successful pattern searches thus far */ ulint n_searches; /*!< number of searches */ #endif /* UNIV_SEARCH_PERF_STAT */ #ifdef UNIV_DEBUG ulint magic_n; /*!< magic number @see BTR_SEARCH_MAGIC_N */ /** value of btr_search_struct::magic_n, used in assertions */ # define BTR_SEARCH_MAGIC_N 1112765 #endif /* UNIV_DEBUG */ }; /** The hash index system */ typedef struct btr_search_sys_struct btr_search_sys_t; /** The hash index system */ struct btr_search_sys_struct{ hash_table_t* hash_index; /*!< the adaptive hash index, mapping dtuple_fold values to rec_t pointers on index pages */ }; /** The adaptive hash index */ extern btr_search_sys_t* btr_search_sys; /** @brief The latch protecting the adaptive search system This latch protects the (1) hash index; (2) columns of a record to which we have a pointer in the hash index; but does NOT protect: (3) next record offset field in a record; (4) next or previous records on the same page. Bear in mind (3) and (4) when using the hash index. */ extern rw_lock_t* btr_search_latch_temp; /** The latch protecting the adaptive search system */ #define btr_search_latch (*btr_search_latch_temp) #ifdef UNIV_SEARCH_PERF_STAT /** Number of successful adaptive hash index lookups */ extern ulint btr_search_n_succ; /** Number of failed adaptive hash index lookups */ extern ulint btr_search_n_hash_fail; #endif /* UNIV_SEARCH_PERF_STAT */ /** After change in n_fields or n_bytes in info, this many rounds are waited before starting the hash analysis again: this is to save CPU time when there is no hope in building a hash index. */ #define BTR_SEARCH_HASH_ANALYSIS 17 /** Limit of consecutive searches for trying a search shortcut on the search pattern */ #define BTR_SEARCH_ON_PATTERN_LIMIT 3 /** Limit of consecutive searches for trying a search shortcut using the hash index */ #define BTR_SEARCH_ON_HASH_LIMIT 3 /** We do this many searches before trying to keep the search latch over calls from MySQL. If we notice someone waiting for the latch, we again set this much timeout. This is to reduce contention. */ #define BTR_SEA_TIMEOUT 10000 #ifndef UNIV_NONINL #include "btr0sea.ic" #endif #endif haildb-2.3.2/include/mem0dbg.ic0000644000175000017500000000734111513177357017123 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1994, 2010, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /********************************************************************//** @file include/mem0dbg.ic The memory management: the debug code. This is not an independent compilation module but is included in mem0mem.*. Created 6/8/1994 Heikki Tuuri *************************************************************************/ #ifdef UNIV_MEM_DEBUG extern ulint mem_current_allocated_memory; /******************************************************************//** Initializes an allocated memory field in the debug version. */ UNIV_INTERN void mem_field_init( /*===========*/ byte* buf, /*!< in: memory field */ ulint n); /*!< in: how many bytes the user requested */ /******************************************************************//** Erases an allocated memory field in the debug version. */ UNIV_INTERN void mem_field_erase( /*============*/ byte* buf, /*!< in: memory field */ ulint n); /*!< in: how many bytes the user requested */ /***************************************************************//** Initializes a buffer to a random combination of hex BA and BE. Used to initialize allocated memory. */ UNIV_INTERN void mem_init_buf( /*=========*/ byte* buf, /*!< in: pointer to buffer */ ulint n); /*!< in: length of buffer */ /***************************************************************//** Initializes a buffer to a random combination of hex DE and AD. Used to erase freed memory. */ UNIV_INTERN void mem_erase_buf( /*==========*/ byte* buf, /*!< in: pointer to buffer */ ulint n); /*!< in: length of buffer */ /***************************************************************//** Inserts a created memory heap to the hash table of current allocated memory heaps. Initializes the hash table when first called. */ UNIV_INTERN void mem_hash_insert( /*============*/ mem_heap_t* heap, /*!< in: the created heap */ const char* file_name, /*!< in: file name of creation */ ulint line); /*!< in: line where created */ /***************************************************************//** Removes a memory heap (which is going to be freed by the caller) from the list of live memory heaps. Returns the size of the heap in terms of how much memory in bytes was allocated for the user of the heap (not the total space occupied by the heap). Also validates the heap. NOTE: This function does not free the storage occupied by the heap itself, only the node in the list of heaps. */ UNIV_INTERN void mem_hash_remove( /*============*/ mem_heap_t* heap, /*!< in: the heap to be freed */ const char* file_name, /*!< in: file name of freeing */ ulint line); /*!< in: line where freed */ void mem_field_header_set_len(byte* field, ulint len); ulint mem_field_header_get_len(byte* field); void mem_field_header_set_check(byte* field, ulint check); ulint mem_field_header_get_check(byte* field); void mem_field_trailer_set_check(byte* field, ulint check); ulint mem_field_trailer_get_check(byte* field); #endif /* UNIV_MEM_DEBUG */ haildb-2.3.2/include/ut0rbt.h0000644000175000017500000002410411513177357016660 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 2006, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /*******************************************************************//** @file include/ut0rbt.h Red-Black tree implementation. Created 2007-03-20 Sunny Bains ************************************************************************/ #ifndef INNOBASE_UT0RBT_H #define INNOBASE_UT0RBT_H #if !defined(IB_RBT_TESTING) #include "univ.i" #include "ut0mem.h" #else #include #include #include #include #define ut_malloc malloc #define ut_free free #define ulint unsigned long #define ut_a(c) assert(c) #define ut_error assert(0) #define ibool unsigned int #define TRUE 1 #define FALSE 0 #endif /* Red black tree typedefs */ typedef struct ib_rbt_struct ib_rbt_t; typedef struct ib_rbt_node_struct ib_rbt_node_t; /* FIXME: Iterator is a better name than _bound_ */ typedef struct ib_rbt_bound_struct ib_rbt_bound_t; typedef void (*ib_rbt_print_node)(const ib_rbt_node_t* node); typedef int (*ib_rbt_compare)(const void* p1, const void* p2); /* Red black tree color types */ enum ib_rbt_color_enum { IB_RBT_RED, IB_RBT_BLACK }; typedef enum ib_rbt_color_enum ib_rbt_color_t; /* Red black tree node */ struct ib_rbt_node_struct { ib_rbt_color_t color; /* color of this node */ ib_rbt_node_t* left; /* points left child */ ib_rbt_node_t* right; /* points right child */ ib_rbt_node_t* parent; /* points parent node */ char value[1]; /* Data value */ }; /* Red black tree instance.*/ struct ib_rbt_struct { ib_rbt_node_t* nil; /* Black colored node that is used as a sentinel. This is pre-allocated too.*/ ib_rbt_node_t* root; /* Root of the tree, this is pre-allocated and the first data node is the left child.*/ ulint n_nodes; /* Total number of data nodes */ ib_rbt_compare compare; /* Fn. to use for comparison */ ulint sizeof_value; /* Sizeof the item in bytes */ }; /* The result of searching for a key in the tree, this is useful for a speedy lookup and insert if key doesn't exist.*/ struct ib_rbt_bound_struct { const ib_rbt_node_t* last; /* Last node visited */ int result; /* Result of comparing with the last non-nil node that was visited */ }; /* Size in elements (t is an rb tree instance) */ #define rbt_size(t) (t->n_nodes) /* Check whether the rb tree is empty (t is an rb tree instance) */ #define rbt_empty(t) (rbt_size(t) == 0) /* Get data value (t is the data type, n is an rb tree node instance) */ #define rbt_value(t, n) ((t*) &n->value[0]) /* Compare a key with the node value (t is tree, k is key, n is node)*/ #define rbt_compare(t, k, n) (t->compare(k, n->value)) /****************************************************************//** Free an instance of a red black tree */ UNIV_INTERN void rbt_free( /*=====*/ ib_rbt_t* tree); /*!< in: rb tree to free */ /****************************************************************//** Create an instance of a red black tree @return rb tree instance */ UNIV_INTERN ib_rbt_t* rbt_create( /*=======*/ size_t sizeof_value, /*!< in: size in bytes */ ib_rbt_compare compare); /*!< in: comparator */ /****************************************************************//** Delete a node from the red black tree, identified by key. @return TRUE if success FALSE if not found */ UNIV_INTERN ibool rbt_delete( /*=======*/ ib_rbt_t* tree, /*!< in: rb tree */ const void* key); /*!< in: key to delete */ /****************************************************************//** Remove a node from the rb tree, the node is not free'd, that is the callers responsibility. @return the deleted node with the const. */ UNIV_INTERN ib_rbt_node_t* rbt_remove_node( /*============*/ ib_rbt_t* tree, /*!< in: rb tree */ const ib_rbt_node_t* node); /*!< in: node to delete, this is a fudge and declared const because the caller has access only to const nodes.*/ /****************************************************************//** Find a matching node in the rb tree. @return node if found else return NULL */ UNIV_INTERN const ib_rbt_node_t* rbt_lookup( /*=======*/ const ib_rbt_t* tree, /*!< in: rb tree to search */ const void* key); /*!< in: key to lookup */ /****************************************************************//** Generic insert of a value in the rb tree. @return inserted node */ UNIV_INTERN const ib_rbt_node_t* rbt_insert( /*=======*/ ib_rbt_t* tree, /*!< in: rb tree */ const void* key, /*!< in: key for ordering */ const void* value); /*!< in: data that will be copied to the node.*/ /****************************************************************//** Add a new node to the tree, useful for data that is pre-sorted. @return appended node */ UNIV_INTERN const ib_rbt_node_t* rbt_add_node( /*=========*/ ib_rbt_t* tree, /*!< in: rb tree */ ib_rbt_bound_t* parent, /*!< in: parent */ const void* value); /*!< in: this value is copied to the node */ /****************************************************************//** Return the left most data node in the tree @return left most node */ UNIV_INTERN const ib_rbt_node_t* rbt_first( /*======*/ const ib_rbt_t* tree); /*!< in: rb tree */ /****************************************************************//** Return the right most data node in the tree @return right most node */ UNIV_INTERN const ib_rbt_node_t* rbt_last( /*=====*/ const ib_rbt_t* tree); /*!< in: rb tree */ /****************************************************************//** Return the next node from current. @return successor node to current that is passed in. */ UNIV_INTERN const ib_rbt_node_t* rbt_next( /*=====*/ const ib_rbt_t* tree, /*!< in: rb tree */ const ib_rbt_node_t* /*!< in: current node */ current); /****************************************************************//** Return the prev node from current. @return precedessor node to current that is passed in */ UNIV_INTERN const ib_rbt_node_t* rbt_prev( /*=====*/ const ib_rbt_t* tree, /*!< in: rb tree */ const ib_rbt_node_t* /*!< in: current node */ current); /****************************************************************//** Find the node that has the lowest key that is >= key. @return node that satisfies the lower bound constraint or NULL */ UNIV_INTERN const ib_rbt_node_t* rbt_lower_bound( /*============*/ const ib_rbt_t* tree, /*!< in: rb tree */ const void* key); /*!< in: key to search */ /****************************************************************//** Find the node that has the greatest key that is <= key. @return node that satisifies the upper bound constraint or NULL */ UNIV_INTERN const ib_rbt_node_t* rbt_upper_bound( /*============*/ const ib_rbt_t* tree, /*!< in: rb tree */ const void* key); /*!< in: key to search */ /****************************************************************//** Search for the key, a node will be retuned in parent.last, whether it was found or not. If not found then parent.last will contain the parent node for the possibly new key otherwise the matching node. @return result of last comparison */ UNIV_INTERN int rbt_search( /*=======*/ const ib_rbt_t* tree, /*!< in: rb tree */ ib_rbt_bound_t* parent, /*!< in: search bounds */ const void* key); /*!< in: key to search */ /****************************************************************//** Search for the key, a node will be retuned in parent.last, whether it was found or not. If not found then parent.last will contain the parent node for the possibly new key otherwise the matching node. @return result of last comparison */ UNIV_INTERN int rbt_search_cmp( /*===========*/ const ib_rbt_t* tree, /*!< in: rb tree */ ib_rbt_bound_t* parent, /*!< in: search bounds */ const void* key, /*!< in: key to search */ ib_rbt_compare compare); /*!< in: comparator */ /****************************************************************//** Clear the tree, deletes (and free's) all the nodes. */ UNIV_INTERN void rbt_clear( /*======*/ ib_rbt_t* tree); /*!< in: rb tree */ /****************************************************************//** Merge the node from dst into src. Return the number of nodes merged. @return no. of recs merged */ UNIV_INTERN ulint rbt_merge_uniq( /*===========*/ ib_rbt_t* dst, /*!< in: dst rb tree */ const ib_rbt_t* src); /*!< in: src rb tree */ /****************************************************************//** Merge the node from dst into src. Return the number of nodes merged. Delete the nodes from src after copying node to dst. As a side effect the duplicates will be left untouched in the src, since we don't support duplicates (yet). NOTE: src and dst must be similar, the function doesn't check for this condition (yet). @return no. of recs merged */ UNIV_INTERN ulint rbt_merge_uniq_destructive( /*=======================*/ ib_rbt_t* dst, /*!< in: dst rb tree */ ib_rbt_t* src); /*!< in: src rb tree */ /****************************************************************//** Verify the integrity of the RB tree. For debugging. 0 failure else height of tree (in count of black nodes). @return TRUE if OK FALSE if tree invalid. */ UNIV_INTERN ibool rbt_validate( /*=========*/ const ib_rbt_t* tree); /*!< in: tree to validate */ /****************************************************************//** Iterate over the tree in depth first order. */ UNIV_INTERN void rbt_print( /*======*/ const ib_rbt_t* tree, /*!< in: tree to traverse */ ib_rbt_print_node print); /*!< in: print function */ #endif /* INNOBASE_UT0RBT_H */ haildb-2.3.2/include/trx0trx.ic0000644000175000017500000000760611513177357017247 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/trx0trx.ic The transaction Created 3/26/1996 Heikki Tuuri *******************************************************/ /*************************************************************//** Starts the transaction if it is not yet started. */ UNIV_INLINE void trx_start_if_not_started( /*=====================*/ trx_t* trx) /*!< in: transaction */ { ut_ad(trx->conc_state != TRX_COMMITTED_IN_MEMORY); if (trx->conc_state == TRX_NOT_STARTED) { trx_start(trx, ULINT_UNDEFINED); } } /****************************************************************//** Retrieves the error_info field from a trx. @return the error info */ UNIV_INLINE const dict_index_t* trx_get_error_info( /*===============*/ const trx_t* trx) /*!< in: trx object */ { return(trx->error_info); } /*******************************************************************//** Retrieves transacion's id, represented as unsigned long long. @return transaction's id */ UNIV_INLINE ib_uint64_t trx_get_id( /*=======*/ const trx_t* trx) /*!< in: transaction */ { return((ib_uint64_t)ut_conv_dulint_to_longlong(trx->id)); } /*******************************************************************//** Retrieves transaction's que state in a human readable string. The string should not be free()'d or modified. @return string in the data segment */ UNIV_INLINE const char* trx_get_que_state_str( /*==================*/ const trx_t* trx) /*!< in: transaction */ { /* be sure to adjust TRX_QUE_STATE_STR_MAX_LEN if you change this */ switch (trx->que_state) { case TRX_QUE_RUNNING: return("RUNNING"); case TRX_QUE_LOCK_WAIT: return("LOCK WAIT"); case TRX_QUE_ROLLING_BACK: return("ROLLING BACK"); case TRX_QUE_COMMITTING: return("COMMITTING"); default: return("UNKNOWN"); } } /**********************************************************************//** Determine if a transaction is a dictionary operation. @return dictionary operation mode */ UNIV_INLINE enum trx_dict_op trx_get_dict_operation( /*===================*/ const trx_t* trx) /*!< in: transaction */ { enum trx_dict_op op = (enum trx_dict_op) trx->dict_operation; #ifdef UNIV_DEBUG switch (op) { case TRX_DICT_OP_NONE: case TRX_DICT_OP_TABLE: case TRX_DICT_OP_INDEX: return(op); } ut_error; #endif /* UNIV_DEBUG */ return((enum trx_dict_op) UNIV_EXPECT(op, TRX_DICT_OP_NONE)); } /**********************************************************************//** Flag a transaction a dictionary operation. */ UNIV_INLINE void trx_set_dict_operation( /*===================*/ trx_t* trx, /*!< in/out: transaction */ enum trx_dict_op op) /*!< in: operation, not TRX_DICT_OP_NONE */ { #ifdef UNIV_DEBUG enum trx_dict_op old_op = trx_get_dict_operation(trx); switch (op) { case TRX_DICT_OP_NONE: ut_error; break; case TRX_DICT_OP_TABLE: switch (old_op) { case TRX_DICT_OP_NONE: case TRX_DICT_OP_INDEX: case TRX_DICT_OP_TABLE: goto ok; } ut_error; break; case TRX_DICT_OP_INDEX: ut_ad(old_op == TRX_DICT_OP_NONE); break; } ok: #endif /* UNIV_DEBUG */ trx->dict_operation = op; } haildb-2.3.2/include/sync0rw.ic0000644000175000017500000004611511513177357017217 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2009, Innobase Oy. All Rights Reserved. Copyright (c) 2008, Google Inc. Portions of this file contain modifications contributed and copyrighted by Google, Inc. Those modifications are gratefully acknowledged and are described briefly in the InnoDB documentation. The contributions by Google are incorporated with their permission, and subject to the conditions contained in the file COPYING.Google. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/sync0rw.ic The read-write lock (for threads) Created 9/11/1995 Heikki Tuuri *******************************************************/ /******************************************************************//** Lock an rw-lock in shared mode for the current thread. If the rw-lock is locked in exclusive mode, or there is an exclusive lock request waiting, the function spins a preset time (controlled by SYNC_SPIN_ROUNDS), waiting for the lock before suspending the thread. */ UNIV_INTERN void rw_lock_s_lock_spin( /*================*/ rw_lock_t* lock, /*!< in: pointer to rw-lock */ ulint pass, /*!< in: pass value; != 0, if the lock will be passed to another thread to unlock */ const char* file_name,/*!< in: file name where lock requested */ ulint line); /*!< in: line where requested */ #ifdef UNIV_SYNC_DEBUG /******************************************************************//** Inserts the debug information for an rw-lock. */ UNIV_INTERN void rw_lock_add_debug_info( /*===================*/ rw_lock_t* lock, /*!< in: rw-lock */ ulint pass, /*!< in: pass value */ ulint lock_type, /*!< in: lock type */ const char* file_name, /*!< in: file where requested */ ulint line); /*!< in: line where requested */ /******************************************************************//** Removes a debug information struct for an rw-lock. */ UNIV_INTERN void rw_lock_remove_debug_info( /*======================*/ rw_lock_t* lock, /*!< in: rw-lock */ ulint pass, /*!< in: pass value */ ulint lock_type); /*!< in: lock type */ #endif /* UNIV_SYNC_DEBUG */ /********************************************************************//** Check if there are threads waiting for the rw-lock. @return 1 if waiters, 0 otherwise */ UNIV_INLINE ulint rw_lock_get_waiters( /*================*/ const rw_lock_t* lock) /*!< in: rw-lock */ { return(lock->waiters); } /********************************************************************//** Sets lock->waiters to 1. It is not an error if lock->waiters is already 1. On platforms where ATOMIC builtins are used this function enforces a memory barrier. */ UNIV_INLINE void rw_lock_set_waiter_flag( /*====================*/ rw_lock_t* lock) /*!< in/out: rw-lock */ { #ifdef INNODB_RW_LOCKS_USE_ATOMICS os_compare_and_swap_ulint(&lock->waiters, 0, 1); #else /* INNODB_RW_LOCKS_USE_ATOMICS */ lock->waiters = 1; #endif /* INNODB_RW_LOCKS_USE_ATOMICS */ } /********************************************************************//** Resets lock->waiters to 0. It is not an error if lock->waiters is already 0. On platforms where ATOMIC builtins are used this function enforces a memory barrier. */ UNIV_INLINE void rw_lock_reset_waiter_flag( /*======================*/ rw_lock_t* lock) /*!< in/out: rw-lock */ { #ifdef INNODB_RW_LOCKS_USE_ATOMICS os_compare_and_swap_ulint(&lock->waiters, 1, 0); #else /* INNODB_RW_LOCKS_USE_ATOMICS */ lock->waiters = 0; #endif /* INNODB_RW_LOCKS_USE_ATOMICS */ } /******************************************************************//** Returns the write-status of the lock - this function made more sense with the old rw_lock implementation. @return RW_LOCK_NOT_LOCKED, RW_LOCK_EX, RW_LOCK_WAIT_EX */ UNIV_INLINE ulint rw_lock_get_writer( /*===============*/ const rw_lock_t* lock) /*!< in: rw-lock */ { lint lock_word = lock->lock_word; if (lock_word > 0) { /* return NOT_LOCKED in s-lock state, like the writer member of the old lock implementation. */ return(RW_LOCK_NOT_LOCKED); } else if (((-lock_word) % X_LOCK_DECR) == 0) { return(RW_LOCK_EX); } else { ut_ad(lock_word > -X_LOCK_DECR); return(RW_LOCK_WAIT_EX); } } /******************************************************************//** Returns the number of readers. @return number of readers */ UNIV_INLINE ulint rw_lock_get_reader_count( /*=====================*/ const rw_lock_t* lock) /*!< in: rw-lock */ { lint lock_word = lock->lock_word; if (lock_word > 0) { /* s-locked, no x-waiters */ return(X_LOCK_DECR - lock_word); } else if (lock_word < 0 && lock_word > -X_LOCK_DECR) { /* s-locked, with x-waiters */ return((ulint)(-lock_word)); } return(0); } #ifndef INNODB_RW_LOCKS_USE_ATOMICS UNIV_INLINE mutex_t* rw_lock_get_mutex( /*==============*/ rw_lock_t* lock) { return(&(lock->mutex)); } #endif /******************************************************************//** Returns the value of writer_count for the lock. Does not reserve the lock mutex, so the caller must be sure it is not changed during the call. @return value of writer_count */ UNIV_INLINE ulint rw_lock_get_x_lock_count( /*=====================*/ const rw_lock_t* lock) /*!< in: rw-lock */ { lint lock_copy = lock->lock_word; /* If there is a reader, lock_word is not divisible by X_LOCK_DECR */ if (lock_copy > 0 || (-lock_copy) % X_LOCK_DECR != 0) { return(0); } return(((-lock_copy) / X_LOCK_DECR) + 1); } /******************************************************************//** Two different implementations for decrementing the lock_word of a rw_lock: one for systems supporting atomic operations, one for others. This does does not support recusive x-locks: they should be handled by the caller and need not be atomic since they are performed by the current lock holder. Returns true if the decrement was made, false if not. @return TRUE if decr occurs */ UNIV_INLINE ibool rw_lock_lock_word_decr( /*===================*/ rw_lock_t* lock, /*!< in/out: rw-lock */ ulint amount) /*!< in: amount to decrement */ { #ifdef INNODB_RW_LOCKS_USE_ATOMICS lint local_lock_word = lock->lock_word; while (local_lock_word > 0) { if (os_compare_and_swap_lint(&lock->lock_word, local_lock_word, local_lock_word - amount)) { return(TRUE); } local_lock_word = lock->lock_word; } return(FALSE); #else /* INNODB_RW_LOCKS_USE_ATOMICS */ ibool success = FALSE; mutex_enter(&(lock->mutex)); if (lock->lock_word > 0) { lock->lock_word -= amount; success = TRUE; } mutex_exit(&(lock->mutex)); return(success); #endif /* INNODB_RW_LOCKS_USE_ATOMICS */ } /******************************************************************//** Increments lock_word the specified amount and returns new value. @return lock->lock_word after increment */ UNIV_INLINE lint rw_lock_lock_word_incr( /*===================*/ rw_lock_t* lock, /*!< in/out: rw-lock */ ulint amount) /*!< in: amount of increment */ { #ifdef INNODB_RW_LOCKS_USE_ATOMICS return(os_atomic_increment_lint(&lock->lock_word, amount)); #else /* INNODB_RW_LOCKS_USE_ATOMICS */ lint local_lock_word; mutex_enter(&(lock->mutex)); lock->lock_word += amount; local_lock_word = lock->lock_word; mutex_exit(&(lock->mutex)); return(local_lock_word); #endif /* INNODB_RW_LOCKS_USE_ATOMICS */ } /******************************************************************//** This function sets the lock->writer_thread and lock->recursive fields. For platforms where we are using atomic builtins instead of lock->mutex it sets the lock->writer_thread field using atomics to ensure memory ordering. Note that it is assumed that the caller of this function effectively owns the lock i.e.: nobody else is allowed to modify lock->writer_thread at this point in time. The protocol is that lock->writer_thread MUST be updated BEFORE the lock->recursive flag is set. */ UNIV_INLINE void rw_lock_set_writer_id_and_recursion_flag( /*=====================================*/ rw_lock_t* lock, /*!< in/out: lock to work on */ ibool recursive) /*!< in: TRUE if recursion allowed */ { os_thread_id_t curr_thread = os_thread_get_curr_id(); #ifdef INNODB_RW_LOCKS_USE_ATOMICS os_thread_id_t local_thread; ibool success; /* Prevent Valgrind warnings about writer_thread being uninitialized. It does not matter if writer_thread is uninitialized, because we are comparing writer_thread against itself, and the operation should always succeed. */ UNIV_MEM_VALID(&lock->writer_thread, sizeof lock->writer_thread); local_thread = lock->writer_thread; success = os_compare_and_swap_thread_id( &lock->writer_thread, local_thread, curr_thread); ut_a(success); lock->recursive = recursive; #else /* INNODB_RW_LOCKS_USE_ATOMICS */ mutex_enter(&lock->mutex); lock->writer_thread = curr_thread; lock->recursive = recursive; mutex_exit(&lock->mutex); #endif /* INNODB_RW_LOCKS_USE_ATOMICS */ } /******************************************************************//** Low-level function which tries to lock an rw-lock in s-mode. Performs no spinning. @return TRUE if success */ UNIV_INLINE ibool rw_lock_s_lock_low( /*===============*/ rw_lock_t* lock, /*!< in: pointer to rw-lock */ ulint pass __attribute__((unused)), /*!< in: pass value; != 0, if the lock will be passed to another thread to unlock */ const char* file_name, /*!< in: file name where lock requested */ ulint line) /*!< in: line where requested */ { /* TODO: study performance of UNIV_LIKELY branch prediction hints. */ if (!rw_lock_lock_word_decr(lock, 1)) { /* Locking did not succeed */ return(FALSE); } #ifdef UNIV_SYNC_DEBUG rw_lock_add_debug_info(lock, pass, RW_LOCK_SHARED, file_name, line); #endif /* These debugging values are not set safely: they may be incorrect or even refer to a line that is invalid for the file name. */ lock->last_s_file_name = file_name; lock->last_s_line = line; return(TRUE); /* locking succeeded */ } /******************************************************************//** Low-level function which locks an rw-lock in s-mode when we know that it is possible and none else is currently accessing the rw-lock structure. Then we can do the locking without reserving the mutex. */ UNIV_INLINE void rw_lock_s_lock_direct( /*==================*/ rw_lock_t* lock, /*!< in/out: rw-lock */ const char* file_name, /*!< in: file name where requested */ ulint line) /*!< in: line where lock requested */ { ut_ad(lock->lock_word == X_LOCK_DECR); /* Indicate there is a new reader by decrementing lock_word */ lock->lock_word--; lock->last_s_file_name = file_name; lock->last_s_line = line; #ifdef UNIV_SYNC_DEBUG rw_lock_add_debug_info(lock, 0, RW_LOCK_SHARED, file_name, line); #endif } /******************************************************************//** Low-level function which locks an rw-lock in x-mode when we know that it is not locked and none else is currently accessing the rw-lock structure. Then we can do the locking without reserving the mutex. */ UNIV_INLINE void rw_lock_x_lock_direct( /*==================*/ rw_lock_t* lock, /*!< in/out: rw-lock */ const char* file_name, /*!< in: file name where requested */ ulint line) /*!< in: line where lock requested */ { ut_ad(rw_lock_validate(lock)); ut_ad(lock->lock_word == X_LOCK_DECR); lock->lock_word -= X_LOCK_DECR; lock->writer_thread = os_thread_get_curr_id(); lock->recursive = TRUE; lock->last_x_file_name = file_name; lock->last_x_line = line; #ifdef UNIV_SYNC_DEBUG rw_lock_add_debug_info(lock, 0, RW_LOCK_EX, file_name, line); #endif } /******************************************************************//** NOTE! Use the corresponding macro, not directly this function! Lock an rw-lock in shared mode for the current thread. If the rw-lock is locked in exclusive mode, or there is an exclusive lock request waiting, the function spins a preset time (controlled by SYNC_SPIN_ROUNDS), waiting for the lock, before suspending the thread. */ UNIV_INLINE void rw_lock_s_lock_func( /*================*/ rw_lock_t* lock, /*!< in: pointer to rw-lock */ ulint pass, /*!< in: pass value; != 0, if the lock will be passed to another thread to unlock */ const char* file_name,/*!< in: file name where lock requested */ ulint line) /*!< in: line where requested */ { /* NOTE: As we do not know the thread ids for threads which have s-locked a latch, and s-lockers will be served only after waiting x-lock requests have been fulfilled, then if this thread already owns an s-lock here, it may end up in a deadlock with another thread which requests an x-lock here. Therefore, we will forbid recursive s-locking of a latch: the following assert will warn the programmer of the possibility of this kind of a deadlock. If we want to implement safe recursive s-locking, we should keep in a list the thread ids of the threads which have s-locked a latch. This would use some CPU time. */ #ifdef UNIV_SYNC_DEBUG ut_ad(!rw_lock_own(lock, RW_LOCK_SHARED)); /* see NOTE above */ #endif /* UNIV_SYNC_DEBUG */ /* TODO: study performance of UNIV_LIKELY branch prediction hints. */ if (rw_lock_s_lock_low(lock, pass, file_name, line)) { return; /* Success */ } else { /* Did not succeed, try spin wait */ rw_lock_s_lock_spin(lock, pass, file_name, line); return; } } /******************************************************************//** NOTE! Use the corresponding macro, not directly this function! Lock an rw-lock in exclusive mode for the current thread if the lock can be obtained immediately. @return TRUE if success */ UNIV_INLINE ibool rw_lock_x_lock_func_nowait( /*=======================*/ rw_lock_t* lock, /*!< in: pointer to rw-lock */ const char* file_name,/*!< in: file name where lock requested */ ulint line) /*!< in: line where requested */ { os_thread_id_t curr_thread = os_thread_get_curr_id(); ibool success; #ifdef INNODB_RW_LOCKS_USE_ATOMICS success = os_compare_and_swap_lint(&lock->lock_word, X_LOCK_DECR, 0); #else success = FALSE; mutex_enter(&(lock->mutex)); if (lock->lock_word == X_LOCK_DECR) { lock->lock_word = 0; success = TRUE; } mutex_exit(&(lock->mutex)); #endif if (success) { rw_lock_set_writer_id_and_recursion_flag(lock, TRUE); } else if (lock->recursive && os_thread_eq(lock->writer_thread, curr_thread)) { /* Relock: this lock_word modification is safe since no other threads can modify (lock, unlock, or reserve) lock_word while there is an exclusive writer and this is the writer thread. */ lock->lock_word -= X_LOCK_DECR; ut_ad(((-lock->lock_word) % X_LOCK_DECR) == 0); } else { /* Failure */ return(FALSE); } #ifdef UNIV_SYNC_DEBUG rw_lock_add_debug_info(lock, 0, RW_LOCK_EX, file_name, line); #endif lock->last_x_file_name = file_name; lock->last_x_line = line; ut_ad(rw_lock_validate(lock)); return(TRUE); } /******************************************************************//** Releases a shared mode lock. */ UNIV_INLINE void rw_lock_s_unlock_func( /*==================*/ #ifdef UNIV_SYNC_DEBUG ulint pass, /*!< in: pass value; != 0, if the lock may have been passed to another thread to unlock */ #endif rw_lock_t* lock) /*!< in/out: rw-lock */ { ut_ad((lock->lock_word % X_LOCK_DECR) != 0); #ifdef UNIV_SYNC_DEBUG rw_lock_remove_debug_info(lock, pass, RW_LOCK_SHARED); #endif /* Increment lock_word to indicate 1 less reader */ if (rw_lock_lock_word_incr(lock, 1) == 0) { /* wait_ex waiter exists. It may not be asleep, but we signal anyway. We do not wake other waiters, because they can't exist without wait_ex waiter and wait_ex waiter goes first.*/ os_event_set(lock->wait_ex_event); sync_array_object_signalled(sync_primary_wait_array); } ut_ad(rw_lock_validate(lock)); #ifdef UNIV_SYNC_PERF_STAT rw_s_exit_count++; #endif } /******************************************************************//** Releases a shared mode lock when we know there are no waiters and none else will access the lock during the time this function is executed. */ UNIV_INLINE void rw_lock_s_unlock_direct( /*====================*/ rw_lock_t* lock) /*!< in/out: rw-lock */ { ut_ad(lock->lock_word < X_LOCK_DECR); #ifdef UNIV_SYNC_DEBUG rw_lock_remove_debug_info(lock, 0, RW_LOCK_SHARED); #endif /* Decrease reader count by incrementing lock_word */ lock->lock_word++; ut_ad(!lock->waiters); ut_ad(rw_lock_validate(lock)); #ifdef UNIV_SYNC_PERF_STAT rw_s_exit_count++; #endif } /******************************************************************//** Releases an exclusive mode lock. */ UNIV_INLINE void rw_lock_x_unlock_func( /*==================*/ #ifdef UNIV_SYNC_DEBUG ulint pass, /*!< in: pass value; != 0, if the lock may have been passed to another thread to unlock */ #endif rw_lock_t* lock) /*!< in/out: rw-lock */ { ut_ad((lock->lock_word % X_LOCK_DECR) == 0); /* lock->recursive flag also indicates if lock->writer_thread is valid or stale. If we are the last of the recursive callers then we must unset lock->recursive flag to indicate that the lock->writer_thread is now stale. Note that since we still hold the x-lock we can safely read the lock_word. */ if (lock->lock_word == 0) { /* Last caller in a possible recursive chain. */ lock->recursive = FALSE; UNIV_MEM_INVALID(&lock->writer_thread, sizeof lock->writer_thread); } #ifdef UNIV_SYNC_DEBUG rw_lock_remove_debug_info(lock, pass, RW_LOCK_EX); #endif if (rw_lock_lock_word_incr(lock, X_LOCK_DECR) == X_LOCK_DECR) { /* Lock is now free. May have to signal read/write waiters. We do not need to signal wait_ex waiters, since they cannot exist when there is a writer. */ if (lock->waiters) { rw_lock_reset_waiter_flag(lock); os_event_set(lock->event); sync_array_object_signalled(sync_primary_wait_array); } } ut_ad(rw_lock_validate(lock)); #ifdef UNIV_SYNC_PERF_STAT rw_x_exit_count++; #endif } /******************************************************************//** Releases an exclusive mode lock when we know there are no waiters, and none else will access the lock during the time this function is executed. */ UNIV_INLINE void rw_lock_x_unlock_direct( /*====================*/ rw_lock_t* lock) /*!< in/out: rw-lock */ { /* Reset the exclusive lock if this thread no longer has an x-mode lock */ ut_ad((lock->lock_word % X_LOCK_DECR) == 0); #ifdef UNIV_SYNC_DEBUG rw_lock_remove_debug_info(lock, 0, RW_LOCK_EX); #endif if (lock->lock_word == 0) { lock->recursive = FALSE; UNIV_MEM_INVALID(&lock->writer_thread, sizeof lock->writer_thread); } lock->lock_word += X_LOCK_DECR; ut_ad(!lock->waiters); ut_ad(rw_lock_validate(lock)); #ifdef UNIV_SYNC_PERF_STAT rw_x_exit_count++; #endif } haildb-2.3.2/include/trx0rseg.h0000644000175000017500000001747611513177357017234 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2010, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/trx0rseg.h Rollback segment Created 3/26/1996 Heikki Tuuri *******************************************************/ #ifndef trx0rseg_h #define trx0rseg_h #include "univ.i" #include "trx0types.h" #include "trx0sys.h" /******************************************************************//** Gets a rollback segment header. @return rollback segment header, page x-latched */ UNIV_INLINE trx_rsegf_t* trx_rsegf_get( /*==========*/ ulint space, /*!< in: space where placed */ ulint zip_size, /*!< in: compressed page size in bytes or 0 for uncompressed pages */ ulint page_no, /*!< in: page number of the header */ mtr_t* mtr); /*!< in: mtr */ /******************************************************************//** Gets a newly created rollback segment header. @return rollback segment header, page x-latched */ UNIV_INLINE trx_rsegf_t* trx_rsegf_get_new( /*==============*/ ulint space, /*!< in: space where placed */ ulint zip_size, /*!< in: compressed page size in bytes or 0 for uncompressed pages */ ulint page_no, /*!< in: page number of the header */ mtr_t* mtr); /*!< in: mtr */ /***************************************************************//** Gets the file page number of the nth undo log slot. @return page number of the undo log segment */ UNIV_INLINE ulint trx_rsegf_get_nth_undo( /*===================*/ trx_rsegf_t* rsegf, /*!< in: rollback segment header */ ulint n, /*!< in: index of slot */ mtr_t* mtr); /*!< in: mtr */ /***************************************************************//** Sets the file page number of the nth undo log slot. */ UNIV_INLINE void trx_rsegf_set_nth_undo( /*===================*/ trx_rsegf_t* rsegf, /*!< in: rollback segment header */ ulint n, /*!< in: index of slot */ ulint page_no,/*!< in: page number of the undo log segment */ mtr_t* mtr); /*!< in: mtr */ /****************************************************************//** Looks for a free slot for an undo log segment. @return slot index or ULINT_UNDEFINED if not found */ UNIV_INLINE ulint trx_rsegf_undo_find_free( /*=====================*/ trx_rsegf_t* rsegf, /*!< in: rollback segment header */ mtr_t* mtr); /*!< in: mtr */ /******************************************************************//** Looks for a rollback segment, based on the rollback segment id. @return rollback segment */ UNIV_INTERN trx_rseg_t* trx_rseg_get_on_id( /*===============*/ ulint id); /*!< in: rollback segment id */ /****************************************************************//** Creates a rollback segment header. This function is called only when a new rollback segment is created in the database. @return page number of the created segment, FIL_NULL if fail */ UNIV_INTERN ulint trx_rseg_header_create( /*===================*/ ulint space, /*!< in: space id */ ulint zip_size, /*!< in: compressed page size in bytes or 0 for uncompressed pages */ ulint max_size, /*!< in: max size in pages */ ulint* slot_no, /*!< out: rseg id == slot number in trx sys */ mtr_t* mtr); /*!< in: mtr */ /*********************************************************************//** Creates the memory copies for rollback segments and initializes the rseg list and array in trx_sys at a database startup. */ UNIV_INTERN void trx_rseg_list_and_array_init( /*=========================*/ ib_recovery_t recovery, /*!< in: recovery flag */ trx_sysf_t* sys_header, /*!< in: trx system header */ mtr_t* mtr); /*!< in: mtr */ /*************************************************************************//** Free's an instance of the rollback segment in memory. */ UNIV_INTERN void trx_rseg_mem_free( /*==============*/ trx_rseg_t* rseg); /*!< in, own: instance to free */ /* Number of undo log slots in a rollback segment file copy */ #define TRX_RSEG_N_SLOTS (UNIV_PAGE_SIZE / 16) /* Maximum number of transactions supported by a single rollback segment */ #define TRX_RSEG_MAX_N_TRXS (TRX_RSEG_N_SLOTS / 2) /* The rollback segment memory object */ struct trx_rseg_struct{ /*--------------------------------------------------------*/ ulint id; /*!< rollback segment id == the index of its slot in the trx system file copy */ mutex_t mutex; /*!< mutex protecting the fields in this struct except id; NOTE that the latching order must always be kernel mutex -> rseg mutex */ ulint space; /*!< space where the rollback segment is header is placed */ ulint zip_size;/* compressed page size of space in bytes, or 0 for uncompressed spaces */ ulint page_no;/* page number of the rollback segment header */ ulint max_size;/* maximum allowed size in pages */ ulint curr_size;/* current size in pages */ /*--------------------------------------------------------*/ /* Fields for update undo logs */ UT_LIST_BASE_NODE_T(trx_undo_t) update_undo_list; /* List of update undo logs */ UT_LIST_BASE_NODE_T(trx_undo_t) update_undo_cached; /* List of update undo log segments cached for fast reuse */ /*--------------------------------------------------------*/ /* Fields for insert undo logs */ UT_LIST_BASE_NODE_T(trx_undo_t) insert_undo_list; /* List of insert undo logs */ UT_LIST_BASE_NODE_T(trx_undo_t) insert_undo_cached; /* List of insert undo log segments cached for fast reuse */ /*--------------------------------------------------------*/ ulint last_page_no; /*!< Page number of the last not yet purged log header in the history list; FIL_NULL if all list purged */ ulint last_offset; /*!< Byte offset of the last not yet purged log header */ trx_id_t last_trx_no; /*!< Transaction number of the last not yet purged log */ ibool last_del_marks; /*!< TRUE if the last not yet purged log needs purging */ /*--------------------------------------------------------*/ UT_LIST_NODE_T(trx_rseg_t) rseg_list; /* the list of the rollback segment memory objects */ }; /* Undo log segment slot in a rollback segment header */ /*-------------------------------------------------------------*/ #define TRX_RSEG_SLOT_PAGE_NO 0 /* Page number of the header page of an undo log segment */ /*-------------------------------------------------------------*/ /* Slot size */ #define TRX_RSEG_SLOT_SIZE 4 /* The offset of the rollback segment header on its page */ #define TRX_RSEG FSEG_PAGE_DATA /* Transaction rollback segment header */ /*-------------------------------------------------------------*/ #define TRX_RSEG_MAX_SIZE 0 /* Maximum allowed size for rollback segment in pages */ #define TRX_RSEG_HISTORY_SIZE 4 /* Number of file pages occupied by the logs in the history list */ #define TRX_RSEG_HISTORY 8 /* The update undo logs for committed transactions */ #define TRX_RSEG_FSEG_HEADER (8 + FLST_BASE_NODE_SIZE) /* Header for the file segment where this page is placed */ #define TRX_RSEG_UNDO_SLOTS (8 + FLST_BASE_NODE_SIZE + FSEG_HEADER_SIZE) /* Undo log segment slots */ /*-------------------------------------------------------------*/ #ifndef UNIV_NONINL #include "trx0rseg.ic" #endif #endif haildb-2.3.2/include/ut0byte.h0000644000175000017500000001646511513177357017047 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1994, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /******************************************************************//** @file include/ut0byte.h Utilities for byte operations Created 1/20/1994 Heikki Tuuri ***********************************************************************/ #ifndef ut0byte_h #define ut0byte_h #include "univ.i" /** Pair of ulint integers. */ typedef struct dulint_struct dulint; /** Type definition for a 64-bit unsigned integer, which works also in 32-bit machines. NOTE! Access the fields only with the accessor functions. This definition appears here only for the compiler to know the size of a dulint. */ struct dulint_struct{ ulint high; /*!< most significant 32 bits */ ulint low; /*!< least significant 32 bits */ }; /** Zero value for a dulint */ extern const dulint ut_dulint_zero; /** Maximum value for a dulint */ extern const dulint ut_dulint_max; /*******************************************************//** Creates a 64-bit dulint out of two ulints. @return created dulint */ UNIV_INLINE dulint ut_dulint_create( /*=============*/ ulint high, /*!< in: high-order 32 bits */ ulint low); /*!< in: low-order 32 bits */ /*******************************************************//** Gets the high-order 32 bits of a dulint. @return 32 bits in ulint */ UNIV_INLINE ulint ut_dulint_get_high( /*===============*/ dulint d); /*!< in: dulint */ /*******************************************************//** Gets the low-order 32 bits of a dulint. @return 32 bits in ulint */ UNIV_INLINE ulint ut_dulint_get_low( /*==============*/ dulint d); /*!< in: dulint */ /*******************************************************//** Converts a dulint (a struct of 2 ulints) to ib_int64_t, which is a 64-bit integer type. @return value in ib_int64_t type */ UNIV_INLINE ib_int64_t ut_conv_dulint_to_longlong( /*=======================*/ dulint d); /*!< in: dulint */ /*******************************************************//** Tests if a dulint is zero. @return TRUE if zero */ UNIV_INLINE ibool ut_dulint_is_zero( /*==============*/ dulint a); /*!< in: dulint */ /*******************************************************//** Compares two dulints. @return -1 if a < b, 0 if a == b, 1 if a > b */ UNIV_INLINE int ut_dulint_cmp( /*==========*/ dulint a, /*!< in: dulint */ dulint b); /*!< in: dulint */ /*******************************************************//** Adds a ulint to a dulint. @return sum a + b */ UNIV_INLINE dulint ut_dulint_add( /*==========*/ dulint a, /*!< in: dulint */ ulint b); /*!< in: ulint */ /*******************************************************//** Subtracts a ulint from a dulint. @return a - b */ UNIV_INLINE dulint ut_dulint_subtract( /*===============*/ dulint a, /*!< in: dulint */ ulint b); /*!< in: ulint, b <= a */ /********************************************************//** Rounds a dulint downward to a multiple of a power of 2. @return rounded value */ UNIV_INLINE dulint ut_dulint_align_down( /*=================*/ dulint n, /*!< in: number to be rounded */ ulint align_no); /*!< in: align by this number which must be a power of 2 */ /********************************************************//** Rounds a dulint upward to a multiple of a power of 2. @return rounded value */ UNIV_INLINE dulint ut_dulint_align_up( /*===============*/ dulint n, /*!< in: number to be rounded */ ulint align_no); /*!< in: align by this number which must be a power of 2 */ /********************************************************//** Rounds a dulint downward to a multiple of a power of 2. @return rounded value */ UNIV_INLINE ib_uint64_t ut_uint64_align_down( /*=================*/ ib_uint64_t n, /*!< in: number to be rounded */ ulint align_no); /*!< in: align by this number which must be a power of 2 */ /********************************************************//** Rounds ib_uint64_t upward to a multiple of a power of 2. @return rounded value */ UNIV_INLINE ib_uint64_t ut_uint64_align_up( /*===============*/ ib_uint64_t n, /*!< in: number to be rounded */ ulint align_no); /*!< in: align by this number which must be a power of 2 */ /*******************************************************//** Increments a dulint variable by 1. */ #define UT_DULINT_INC(D)\ {\ if ((D).low == 0xFFFFFFFFUL) {\ (D).high = (D).high + 1;\ (D).low = 0;\ } else {\ (D).low = (D).low + 1;\ }\ } /*******************************************************//** Tests if two dulints are equal. */ #define UT_DULINT_EQ(D1, D2) (((D1).low == (D2).low)\ && ((D1).high == (D2).high)) #ifdef notdefined /************************************************************//** Sort function for dulint arrays. */ UNIV_INTERN void ut_dulint_sort( /*===========*/ dulint* arr, /*!< in/out: array to be sorted */ dulint* aux_arr,/*!< in/out: auxiliary array (same size as arr) */ ulint low, /*!< in: low bound of sort interval, inclusive */ ulint high); /*!< in: high bound of sort interval, noninclusive */ #endif /* notdefined */ /*********************************************************//** The following function rounds up a pointer to the nearest aligned address. @return aligned pointer */ UNIV_INLINE void* ut_align( /*=====*/ const void* ptr, /*!< in: pointer */ ulint align_no); /*!< in: align by this number */ /*********************************************************//** The following function rounds down a pointer to the nearest aligned address. @return aligned pointer */ UNIV_INLINE void* ut_align_down( /*==========*/ const void* ptr, /*!< in: pointer */ ulint align_no) /*!< in: align by this number */ __attribute__((const)); /*********************************************************//** The following function computes the offset of a pointer from the nearest aligned address. @return distance from aligned pointer */ UNIV_INLINE ulint ut_align_offset( /*============*/ const void* ptr, /*!< in: pointer */ ulint align_no) /*!< in: align by this number */ __attribute__((const)); /*****************************************************************//** Gets the nth bit of a ulint. @return TRUE if nth bit is 1; 0th bit is defined to be the least significant */ UNIV_INLINE ibool ut_bit_get_nth( /*===========*/ ulint a, /*!< in: ulint */ ulint n); /*!< in: nth bit requested */ /*****************************************************************//** Sets the nth bit of a ulint. @return the ulint with the bit set as requested */ UNIV_INLINE ulint ut_bit_set_nth( /*===========*/ ulint a, /*!< in: ulint */ ulint n, /*!< in: nth bit requested */ ibool val); /*!< in: value for the bit to set */ #ifndef UNIV_NONINL #include "ut0byte.ic" #endif #endif haildb-2.3.2/include/sync0rw.h0000644000175000017500000005511011513177357017046 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2010, Innobase Oy. All Rights Reserved. Copyright (c) 2008, Google Inc. Portions of this file contain modifications contributed and copyrighted by Google, Inc. Those modifications are gratefully acknowledged and are described briefly in the InnoDB documentation. The contributions by Google are incorporated with their permission, and subject to the conditions contained in the file COPYING.Google. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/sync0rw.h The read-write lock (for threads, not for database transactions) Created 9/11/1995 Heikki Tuuri *******************************************************/ #ifndef sync0rw_h #define sync0rw_h #include "univ.i" #ifndef UNIV_HOTBACKUP #include "ut0lst.h" #include "sync0sync.h" #include "os0sync.h" #endif /* !UNIV_HOTBACKUP */ /* Latch types; these are used also in btr0btr.h: keep the numerical values smaller than 30 and the order of the numerical values like below! */ #define RW_S_LATCH 1 #define RW_X_LATCH 2 #define RW_NO_LATCH 3 #ifndef UNIV_HOTBACKUP /* We decrement lock_word by this amount for each x_lock. It is also the start value for the lock_word, meaning that it limits the maximum number of concurrent read locks before the rw_lock breaks. The current value of 0x00100000 allows 1,048,575 concurrent readers and 2047 recursive writers.*/ #define X_LOCK_DECR 0x00100000 typedef struct rw_lock_struct rw_lock_t; #ifdef UNIV_SYNC_DEBUG typedef struct rw_lock_debug_struct rw_lock_debug_t; #endif /* UNIV_SYNC_DEBUG */ typedef UT_LIST_BASE_NODE_T(rw_lock_t) rw_lock_list_t; extern rw_lock_list_t rw_lock_list; extern mutex_t rw_lock_list_mutex; #ifdef UNIV_SYNC_DEBUG /* The global mutex which protects debug info lists of all rw-locks. To modify the debug info list of an rw-lock, this mutex has to be acquired in addition to the mutex protecting the lock. */ extern mutex_t rw_lock_debug_mutex; extern os_event_t rw_lock_debug_event; /*!< If deadlock detection does not get immediately the mutex it may wait for this event */ extern ibool rw_lock_debug_waiters; /*!< This is set to TRUE, if there may be waiters for the event */ #endif /* UNIV_SYNC_DEBUG */ /** number of spin waits on rw-latches, resulted during exclusive (write) locks */ extern ib_int64_t rw_s_spin_wait_count; /** number of spin loop rounds on rw-latches, resulted during exclusive (write) locks */ extern ib_int64_t rw_s_spin_round_count; /** number of unlocks (that unlock shared locks), set only when UNIV_SYNC_PERF_STAT is defined */ extern ib_int64_t rw_s_exit_count; /** number of OS waits on rw-latches, resulted during shared (read) locks */ extern ib_int64_t rw_s_os_wait_count; /** number of spin waits on rw-latches, resulted during shared (read) locks */ extern ib_int64_t rw_x_spin_wait_count; /** number of spin loop rounds on rw-latches, resulted during shared (read) locks */ extern ib_int64_t rw_x_spin_round_count; /** number of OS waits on rw-latches, resulted during exclusive (write) locks */ extern ib_int64_t rw_x_os_wait_count; /** number of unlocks (that unlock exclusive locks), set only when UNIV_SYNC_PERF_STAT is defined */ extern ib_int64_t rw_x_exit_count; /******************************************************************//** Creates, or rather, initializes an rw-lock object in a specified memory location (which must be appropriately aligned). The rw-lock is initialized to the non-locked state. Explicit freeing of the rw-lock with rw_lock_free is necessary only if the memory block containing it is freed. */ #ifdef UNIV_DEBUG # ifdef UNIV_SYNC_DEBUG # define rw_lock_create(L, level) \ rw_lock_create_func((L), (level), #L, __FILE__, __LINE__) # else /* UNIV_SYNC_DEBUG */ # define rw_lock_create(L, level) \ rw_lock_create_func((L), #L, __FILE__, __LINE__) # endif /* UNIV_SYNC_DEBUG */ #else /* UNIV_DEBUG */ # define rw_lock_create(L, level) \ rw_lock_create_func((L), __FILE__, __LINE__) #endif /* UNIV_DEBUG */ /******************************************************************//** Creates, or rather, initializes an rw-lock object in a specified memory location (which must be appropriately aligned). The rw-lock is initialized to the non-locked state. Explicit freeing of the rw-lock with rw_lock_free is necessary only if the memory block containing it is freed. */ UNIV_INTERN void rw_lock_create_func( /*================*/ rw_lock_t* lock, /*!< in: pointer to memory */ #ifdef UNIV_DEBUG # ifdef UNIV_SYNC_DEBUG ulint level, /*!< in: level */ # endif /* UNIV_SYNC_DEBUG */ const char* cmutex_name, /*!< in: mutex name */ #endif /* UNIV_DEBUG */ const char* cfile_name, /*!< in: file name where created */ ulint cline); /*!< in: file line where created */ /******************************************************************//** Calling this function is obligatory only if the memory buffer containing the rw-lock is freed. Removes an rw-lock object from the global list. The rw-lock is checked to be in the non-locked state. */ UNIV_INTERN void rw_lock_free( /*=========*/ rw_lock_t* lock); /*!< in: rw-lock */ #ifdef UNIV_DEBUG /******************************************************************//** Checks that the rw-lock has been initialized and that there are no simultaneous shared and exclusive locks. @return TRUE */ UNIV_INTERN ibool rw_lock_validate( /*=============*/ rw_lock_t* lock); /*!< in: rw-lock */ #endif /* UNIV_DEBUG */ /**************************************************************//** NOTE! The following macros should be used in rw s-locking, not the corresponding function. */ #define rw_lock_s_lock(M) rw_lock_s_lock_func(\ (M), 0, __FILE__, __LINE__) /**************************************************************//** NOTE! The following macros should be used in rw s-locking, not the corresponding function. */ #define rw_lock_s_lock_gen(M, P) rw_lock_s_lock_func(\ (M), (P), __FILE__, __LINE__) /**************************************************************//** NOTE! The following macros should be used in rw s-locking, not the corresponding function. */ #define rw_lock_s_lock_nowait(M, F, L) rw_lock_s_lock_low(\ (M), 0, (F), (L)) /******************************************************************//** Low-level function which tries to lock an rw-lock in s-mode. Performs no spinning. @return TRUE if success */ UNIV_INLINE ibool rw_lock_s_lock_low( /*===============*/ rw_lock_t* lock, /*!< in: pointer to rw-lock */ ulint pass __attribute__((unused)), /*!< in: pass value; != 0, if the lock will be passed to another thread to unlock */ const char* file_name, /*!< in: file name where lock requested */ ulint line); /*!< in: line where requested */ /******************************************************************//** NOTE! Use the corresponding macro, not directly this function, except if you supply the file name and line number. Lock an rw-lock in shared mode for the current thread. If the rw-lock is locked in exclusive mode, or there is an exclusive lock request waiting, the function spins a preset time (controlled by SYNC_SPIN_ROUNDS), waiting for the lock, before suspending the thread. */ UNIV_INLINE void rw_lock_s_lock_func( /*================*/ rw_lock_t* lock, /*!< in: pointer to rw-lock */ ulint pass, /*!< in: pass value; != 0, if the lock will be passed to another thread to unlock */ const char* file_name,/*!< in: file name where lock requested */ ulint line); /*!< in: line where requested */ /******************************************************************//** NOTE! Use the corresponding macro, not directly this function! Lock an rw-lock in exclusive mode for the current thread if the lock can be obtained immediately. @return TRUE if success */ UNIV_INLINE ibool rw_lock_x_lock_func_nowait( /*=======================*/ rw_lock_t* lock, /*!< in: pointer to rw-lock */ const char* file_name,/*!< in: file name where lock requested */ ulint line); /*!< in: line where requested */ /******************************************************************//** Releases a shared mode lock. */ UNIV_INLINE void rw_lock_s_unlock_func( /*==================*/ #ifdef UNIV_SYNC_DEBUG ulint pass, /*!< in: pass value; != 0, if the lock may have been passed to another thread to unlock */ #endif rw_lock_t* lock); /*!< in/out: rw-lock */ #ifdef UNIV_SYNC_DEBUG # define rw_lock_s_unlock_gen(L, P) rw_lock_s_unlock_func(P, L) #else # define rw_lock_s_unlock_gen(L, P) rw_lock_s_unlock_func(L) #endif /*******************************************************************//** Releases a shared mode lock. */ #define rw_lock_s_unlock(L) rw_lock_s_unlock_gen(L, 0) /**************************************************************//** NOTE! The following macro should be used in rw x-locking, not the corresponding function. */ #define rw_lock_x_lock(M) rw_lock_x_lock_func(\ (M), 0, __FILE__, __LINE__) /**************************************************************//** NOTE! The following macro should be used in rw x-locking, not the corresponding function. */ #define rw_lock_x_lock_gen(M, P) rw_lock_x_lock_func(\ (M), (P), __FILE__, __LINE__) /**************************************************************//** NOTE! The following macros should be used in rw x-locking, not the corresponding function. */ #define rw_lock_x_lock_nowait(M) rw_lock_x_lock_func_nowait(\ (M), __FILE__, __LINE__) /******************************************************************//** NOTE! Use the corresponding macro, not directly this function! Lock an rw-lock in exclusive mode for the current thread. If the rw-lock is locked in shared or exclusive mode, or there is an exclusive lock request waiting, the function spins a preset time (controlled by SYNC_SPIN_ROUNDS), waiting for the lock, before suspending the thread. If the same thread has an x-lock on the rw-lock, locking succeed, with the following exception: if pass != 0, only a single x-lock may be taken on the lock. NOTE: If the same thread has an s-lock, locking does not succeed! */ UNIV_INTERN void rw_lock_x_lock_func( /*================*/ rw_lock_t* lock, /*!< in: pointer to rw-lock */ ulint pass, /*!< in: pass value; != 0, if the lock will be passed to another thread to unlock */ const char* file_name,/*!< in: file name where lock requested */ ulint line); /*!< in: line where requested */ /******************************************************************//** Releases an exclusive mode lock. */ UNIV_INLINE void rw_lock_x_unlock_func( /*==================*/ #ifdef UNIV_SYNC_DEBUG ulint pass, /*!< in: pass value; != 0, if the lock may have been passed to another thread to unlock */ #endif rw_lock_t* lock); /*!< in/out: rw-lock */ #ifdef UNIV_SYNC_DEBUG # define rw_lock_x_unlock_gen(L, P) rw_lock_x_unlock_func(P, L) #else # define rw_lock_x_unlock_gen(L, P) rw_lock_x_unlock_func(L) #endif /*******************************************************************//** Releases an exclusive mode lock. */ #define rw_lock_x_unlock(L) rw_lock_x_unlock_gen(L, 0) /******************************************************************//** Low-level function which locks an rw-lock in s-mode when we know that it is possible and none else is currently accessing the rw-lock structure. Then we can do the locking without reserving the mutex. */ UNIV_INLINE void rw_lock_s_lock_direct( /*==================*/ rw_lock_t* lock, /*!< in/out: rw-lock */ const char* file_name, /*!< in: file name where requested */ ulint line); /*!< in: line where lock requested */ /******************************************************************//** Low-level function which locks an rw-lock in x-mode when we know that it is not locked and none else is currently accessing the rw-lock structure. Then we can do the locking without reserving the mutex. */ UNIV_INLINE void rw_lock_x_lock_direct( /*==================*/ rw_lock_t* lock, /*!< in/out: rw-lock */ const char* file_name, /*!< in: file name where requested */ ulint line); /*!< in: line where lock requested */ /******************************************************************//** This function is used in the insert buffer to move the ownership of an x-latch on a buffer frame to the current thread. The x-latch was set by the buffer read operation and it protected the buffer frame while the read was done. The ownership is moved because we want that the current thread is able to acquire a second x-latch which is stored in an mtr. This, in turn, is needed to pass the debug checks of index page operations. */ UNIV_INTERN void rw_lock_x_lock_move_ownership( /*==========================*/ rw_lock_t* lock); /*!< in: lock which was x-locked in the buffer read */ /******************************************************************//** Releases a shared mode lock when we know there are no waiters and none else will access the lock during the time this function is executed. */ UNIV_INLINE void rw_lock_s_unlock_direct( /*====================*/ rw_lock_t* lock); /*!< in/out: rw-lock */ /******************************************************************//** Releases an exclusive mode lock when we know there are no waiters, and none else will access the lock durint the time this function is executed. */ UNIV_INLINE void rw_lock_x_unlock_direct( /*====================*/ rw_lock_t* lock); /*!< in/out: rw-lock */ /******************************************************************//** Returns the value of writer_count for the lock. Does not reserve the lock mutex, so the caller must be sure it is not changed during the call. @return value of writer_count */ UNIV_INLINE ulint rw_lock_get_x_lock_count( /*=====================*/ const rw_lock_t* lock); /*!< in: rw-lock */ /********************************************************************//** Check if there are threads waiting for the rw-lock. @return 1 if waiters, 0 otherwise */ UNIV_INLINE ulint rw_lock_get_waiters( /*================*/ const rw_lock_t* lock); /*!< in: rw-lock */ /******************************************************************//** Returns the write-status of the lock - this function made more sense with the old rw_lock implementation. @return RW_LOCK_NOT_LOCKED, RW_LOCK_EX, RW_LOCK_WAIT_EX */ UNIV_INLINE ulint rw_lock_get_writer( /*===============*/ const rw_lock_t* lock); /*!< in: rw-lock */ /******************************************************************//** Returns the number of readers. @return number of readers */ UNIV_INLINE ulint rw_lock_get_reader_count( /*=====================*/ const rw_lock_t* lock); /*!< in: rw-lock */ /******************************************************************//** Decrements lock_word the specified amount if it is greater than 0. This is used by both s_lock and x_lock operations. @return TRUE if decr occurs */ UNIV_INLINE ibool rw_lock_lock_word_decr( /*===================*/ rw_lock_t* lock, /*!< in/out: rw-lock */ ulint amount); /*!< in: amount to decrement */ /******************************************************************//** Increments lock_word the specified amount and returns new value. @return lock->lock_word after increment */ UNIV_INLINE lint rw_lock_lock_word_incr( /*===================*/ rw_lock_t* lock, /*!< in/out: rw-lock */ ulint amount); /*!< in: amount to increment */ /******************************************************************//** This function sets the lock->writer_thread and lock->recursive fields. For platforms where we are using atomic builtins instead of lock->mutex it sets the lock->writer_thread field using atomics to ensure memory ordering. Note that it is assumed that the caller of this function effectively owns the lock i.e.: nobody else is allowed to modify lock->writer_thread at this point in time. The protocol is that lock->writer_thread MUST be updated BEFORE the lock->recursive flag is set. */ UNIV_INLINE void rw_lock_set_writer_id_and_recursion_flag( /*=====================================*/ rw_lock_t* lock, /*!< in/out: lock to work on */ ibool recursive); /*!< in: TRUE if recursion allowed */ #ifdef UNIV_SYNC_DEBUG /******************************************************************//** Checks if the thread has locked the rw-lock in the specified mode, with the pass value == 0. */ UNIV_INTERN ibool rw_lock_own( /*========*/ rw_lock_t* lock, /*!< in: rw-lock */ ulint lock_type) /*!< in: lock type: RW_LOCK_SHARED, RW_LOCK_EX */ __attribute__((warn_unused_result)); #endif /* UNIV_SYNC_DEBUG */ /******************************************************************//** Checks if somebody has locked the rw-lock in the specified mode. */ UNIV_INTERN ibool rw_lock_is_locked( /*==============*/ rw_lock_t* lock, /*!< in: rw-lock */ ulint lock_type); /*!< in: lock type: RW_LOCK_SHARED, RW_LOCK_EX */ #ifdef UNIV_SYNC_DEBUG /***************************************************************//** Prints debug info of an rw-lock. */ UNIV_INTERN void rw_lock_print( /*==========*/ rw_lock_t* lock); /*!< in: rw-lock */ /***************************************************************//** Prints debug info of currently locked rw-locks. */ UNIV_INTERN void rw_lock_list_print_info( /*====================*/ ib_stream_t ib_stream); /*!< in: stream where to print */ /***************************************************************//** Returns the number of currently locked rw-locks. Works only in the debug version. @return number of locked rw-locks */ UNIV_INTERN ulint rw_lock_n_locked(void); /*==================*/ /*#####################################################################*/ /******************************************************************//** Acquires the debug mutex. We cannot use the mutex defined in sync0sync, because the debug mutex is also acquired in sync0arr while holding the OS mutex protecting the sync array, and the ordinary mutex_enter might recursively call routines in sync0arr, leading to a deadlock on the OS mutex. */ UNIV_INTERN void rw_lock_debug_mutex_enter(void); /*==========================*/ /******************************************************************//** Releases the debug mutex. */ UNIV_INTERN void rw_lock_debug_mutex_exit(void); /*==========================*/ /*********************************************************************//** Prints info of a debug struct. */ UNIV_INTERN void rw_lock_debug_print( /*================*/ rw_lock_debug_t* info); /*!< in: debug struct */ #endif /* UNIV_SYNC_DEBUG */ /********************************************************************** Reset the variables. */ UNIV_INTERN void rw_lock_var_init(void); /*==================*/ /* NOTE! The structure appears here only for the compiler to know its size. Do not use its fields directly! */ /** The structure used in the spin lock implementation of a read-write lock. Several threads may have a shared lock simultaneously in this lock, but only one writer may have an exclusive lock, in which case no shared locks are allowed. To prevent starving of a writer blocked by readers, a writer may queue for x-lock by decrementing lock_word: no new readers will be let in while the thread waits for readers to exit. */ struct rw_lock_struct { volatile lint lock_word; /*!< Holds the state of the lock. */ volatile ulint waiters;/*!< 1: there are waiters */ volatile ibool recursive;/*!< Default value FALSE which means the lock is non-recursive. The value is typically set to TRUE making normal rw_locks recursive. In case of asynchronous IO, when a non-zero value of 'pass' is passed then we keep the lock non-recursive. This flag also tells us about the state of writer_thread field. If this flag is set then writer_thread MUST contain the thread id of the current x-holder or wait-x thread. This flag must be reset in x_unlock functions before incrementing the lock_word */ volatile os_thread_id_t writer_thread; /*!< Thread id of writer thread. Is only guaranteed to have sane and non-stale value iff recursive flag is set. */ os_event_t event; /*!< Used by sync0arr.c for thread queueing */ os_event_t wait_ex_event; /*!< Event for next-writer to wait on. A thread must decrement lock_word before waiting. */ #ifndef INNODB_RW_LOCKS_USE_ATOMICS mutex_t mutex; /*!< The mutex protecting rw_lock_struct */ #endif /* INNODB_RW_LOCKS_USE_ATOMICS */ UT_LIST_NODE_T(rw_lock_t) list; /*!< All allocated rw locks are put into a list */ #ifdef UNIV_SYNC_DEBUG UT_LIST_BASE_NODE_T(rw_lock_debug_t) debug_list; /*!< In the debug version: pointer to the debug info list of the lock */ ulint level; /*!< Level in the global latching order. */ #endif /* UNIV_SYNC_DEBUG */ ulint count_os_wait; /*!< Count of os_waits. May not be accurate */ const char* cfile_name;/*!< File name where lock created */ /* last s-lock file/line is not guaranteed to be correct */ const char* last_s_file_name;/*!< File name where last s-locked */ const char* last_x_file_name;/*!< File name where last x-locked */ ibool writer_is_wait_ex; /*!< This is TRUE if the writer field is RW_LOCK_WAIT_EX; this field is located far from the memory update hotspot fields which are at the start of this struct, thus we can peek this field without causing much memory bus traffic */ unsigned cline:14; /*!< Line where created */ unsigned last_s_line:14; /*!< Line number where last time s-locked */ unsigned last_x_line:14; /*!< Line number where last time x-locked */ ulint magic_n; /*!< RW_LOCK_MAGIC_N */ }; /** Value of rw_lock_struct::magic_n */ #define RW_LOCK_MAGIC_N 22643 #ifdef UNIV_SYNC_DEBUG /** The structure for storing debug info of an rw-lock */ struct rw_lock_debug_struct { os_thread_id_t thread_id; /*!< The thread id of the thread which locked the rw-lock */ ulint pass; /*!< Pass value given in the lock operation */ ulint lock_type; /*!< Type of the lock: RW_LOCK_EX, RW_LOCK_SHARED, RW_LOCK_WAIT_EX */ const char* file_name;/*!< File name where the lock was obtained */ ulint line; /*!< Line where the rw-lock was locked */ UT_LIST_NODE_T(rw_lock_debug_t) list; /*!< Debug structs are linked in a two-way list */ }; #endif /* UNIV_SYNC_DEBUG */ #ifndef UNIV_NONINL #include "sync0rw.ic" #endif #endif /* !UNIV_HOTBACKUP */ #endif haildb-2.3.2/include/dict0load.h0000644000175000017500000001101011513177357017273 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/dict0load.h Loads to the memory cache database object definitions from dictionary tables Created 4/24/1996 Heikki Tuuri *******************************************************/ #ifndef dict0load_h #define dict0load_h #include "univ.i" #include "dict0types.h" #include "ut0byte.h" #include "mem0mem.h" #include "srv0srv.h" /********************************************************************//** In a crash recovery we already have all the tablespace objects created. This function compares the space id information in the InnoDB data dictionary to what we already read with fil_load_single_table_tablespaces(). In a normal startup, we create the tablespace objects for every table in InnoDB's data dictionary, if the corresponding .ibd file exists. We also scan the biggest space id, and store it to fil_system. */ UNIV_INTERN void dict_check_tablespaces_and_store_max_id( /*====================================*/ ibool in_crash_recovery); /*!< in: are we doing a crash recovery */ /********************************************************************//** Finds the first table name in the given database. @return own: table name, NULL if does not exist; the caller must free the memory in the string! */ UNIV_INTERN char* dict_get_first_table_name_in_db( /*============================*/ const char* name); /*!< in: database name which ends to '/' */ /********************************************************************//** Loads a table definition and also all its index definitions, and also the cluster definition if the table is a member in a cluster. Also loads all foreign key constraints where the foreign key is in the table or where a foreign key references columns in this table. @return table, NULL if does not exist; if the table is stored in an .ibd file, but the file does not exist, then we set the ibd_file_missing flag TRUE in the table object we return */ UNIV_INTERN dict_table_t* dict_load_table( /*============*/ ib_recovery_t recovery,/*!< in: recovery flag */ const char* name); /*!< in: table name in the databasename/tablename format */ /***********************************************************************//** Loads a table object based on the table id. @return table; NULL if table does not exist */ UNIV_INTERN dict_table_t* dict_load_table_on_id( /*==================*/ ib_recovery_t recovery,/*!< in: recovery flag */ dulint table_id); /*!< in: table id */ /********************************************************************//** This function is called when the database is booted. Loads system table index definitions except for the clustered index which is added to the dictionary cache at booting before calling this function. */ UNIV_INTERN void dict_load_sys_table( /*================*/ dict_table_t* table); /*!< in: system table */ /***********************************************************************//** Loads foreign key constraints where the table is either the foreign key holder or where the table is referenced by a foreign key. Adds these constraints to the data dictionary. Note that we know that the dictionary cache already contains all constraints where the other relevant table is already in the dictionary cache. @return DB_SUCCESS or error code */ UNIV_INTERN ulint dict_load_foreigns( /*===============*/ const char* table_name, /*!< in: table name */ ibool check_charsets);/*!< in: TRUE=check charsets compatibility */ /********************************************************************//** Prints to the standard output information on all tables found in the data dictionary system table. */ UNIV_INTERN void dict_print(void); /*============*/ #ifndef UNIV_NONINL #include "dict0load.ic" #endif #endif haildb-2.3.2/include/pars0types.h0000644000175000017500000000374711513177357017564 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1998, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/pars0types.h SQL parser global types Created 1/11/1998 Heikki Tuuri *******************************************************/ #ifndef pars0types_h #define pars0types_h typedef struct pars_info_struct pars_info_t; typedef struct pars_user_func_struct pars_user_func_t; typedef struct pars_bound_lit_struct pars_bound_lit_t; typedef struct pars_bound_id_struct pars_bound_id_t; typedef struct sym_node_struct sym_node_t; typedef struct sym_tab_struct sym_tab_t; typedef struct pars_res_word_struct pars_res_word_t; typedef struct func_node_struct func_node_t; typedef struct order_node_struct order_node_t; typedef struct proc_node_struct proc_node_t; typedef struct elsif_node_struct elsif_node_t; typedef struct if_node_struct if_node_t; typedef struct while_node_struct while_node_t; typedef struct for_node_struct for_node_t; typedef struct exit_node_struct exit_node_t; typedef struct return_node_struct return_node_t; typedef struct assign_node_struct assign_node_t; typedef struct col_assign_node_struct col_assign_node_t; typedef UT_LIST_BASE_NODE_T(sym_node_t) sym_node_list_t; #endif haildb-2.3.2/include/sync0arr.ic0000644000175000017500000000206711513177357017351 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/sync0arr.ic The wait array for synchronization primitives Inline code Created 9/5/1995 Heikki Tuuri *******************************************************/ haildb-2.3.2/include/api0misc.h0000644000175000017500000000413111513177357017143 0ustar00pcrewspcrews00000000000000/****************************************************** Interface between Innobase and client. This file contains the functions that don't have a proper home yet. (c) 2008 Oracle Corpn./Innobase Oy *******************************************************/ #include "univ.i" #include "os0file.h" #include "que0que.h" #include "trx0trx.h" /************************************************************************** Determines if the currently running transaction has been interrupted. @return TRUE if interrupted */ UNIV_INTERN ibool trx_is_interrupted( /*===============*/ const trx_t* trx); /*!< in: transaction */ /********************************************************************** Create a temporary file using the OS specific function. */ UNIV_INTERN int ib_create_tempfile( /*===============*/ const char* filename); /*!< in: temp filename prefix */ /******************************************************************** Handles user errors and lock waits detected by the database engine. @return TRUE if it was a lock wait and we should continue running the query thread */ UNIV_INTERN ibool ib_handle_errors( /*=============*/ enum db_err* new_err, /*!< out: possible new error encountered in lock wait, or if no new error, the value of trx->error_state at the entry of this function */ trx_t* trx, /*!< in: transaction */ que_thr_t* thr, /*!< in: query thread */ trx_savept_t* savept); /*!< in: savepoint or NULL */ /************************************************************************* Sets a lock on a table. @return error code or DB_SUCCESS */ UNIV_INTERN enum db_err ib_trx_lock_table_with_retry( /*=========================*/ trx_t* trx, /*!< in/out: transaction */ dict_table_t* table, /*!< in: table to lock */ enum lock_mode mode); /*!< in: lock mode */ /************************************************************************* Updates the table modification counter and calculates new estimates for table and index statistics if necessary. */ UNIV_INTERN void ib_update_statistics_if_needed( /*===========================*/ dict_table_t* table); /*!< in/out: table */ haildb-2.3.2/include/trx0roll.ic0000644000175000017500000000264411513177357017377 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/trx0roll.ic Transaction rollback Created 3/26/1996 Heikki Tuuri *******************************************************/ /*******************************************************************//** Returns pointer to nth element in an undo number array. @return pointer to the nth element */ UNIV_INLINE trx_undo_inf_t* trx_undo_arr_get_nth_info( /*======================*/ trx_undo_arr_t* arr, /*!< in: undo number array */ ulint n) /*!< in: position */ { ut_ad(arr); ut_ad(n < arr->n_cells); return(arr->infos + n); } haildb-2.3.2/include/mem0mem.h0000644000175000017500000003432111513177357016777 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1994, 2010, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/mem0mem.h The memory management Created 6/9/1994 Heikki Tuuri *******************************************************/ #ifndef mem0mem_h #define mem0mem_h #include "univ.i" #include "ut0mem.h" #include "ut0byte.h" #include "ut0rnd.h" #ifndef UNIV_HOTBACKUP # include "sync0sync.h" #endif /* UNIV_HOTBACKUP */ #include "ut0lst.h" #include "mach0data.h" /* -------------------- MEMORY HEAPS ----------------------------- */ /* The info structure stored at the beginning of a heap block */ typedef struct mem_block_info_struct mem_block_info_t; /* A block of a memory heap consists of the info structure followed by an area of memory */ typedef mem_block_info_t mem_block_t; /* A memory heap is a nonempty linear list of memory blocks */ typedef mem_block_t mem_heap_t; /* Types of allocation for memory heaps: DYNAMIC means allocation from the dynamic memory pool of the C compiler, BUFFER means allocation from the buffer pool; the latter method is used for very big heaps */ #define MEM_HEAP_DYNAMIC 0 /* the most common type */ #define MEM_HEAP_BUFFER 1 #define MEM_HEAP_BTR_SEARCH 2 /* this flag can optionally be ORed to MEM_HEAP_BUFFER, in which case heap->free_block is used in some cases for memory allocations, and if it's NULL, the memory allocation functions can return NULL. */ /* The following start size is used for the first block in the memory heap if the size is not specified, i.e., 0 is given as the parameter in the call of create. The standard size is the maximum (payload) size of the blocks used for allocations of small buffers. */ #define MEM_BLOCK_START_SIZE 64 #define MEM_BLOCK_STANDARD_SIZE \ (UNIV_PAGE_SIZE >= 16384 ? 8000 : MEM_MAX_ALLOC_IN_BUF) /* If a memory heap is allowed to grow into the buffer pool, the following is the maximum size for a single allocated buffer: */ #define MEM_MAX_ALLOC_IN_BUF (UNIV_PAGE_SIZE - 200) /******************************************************************//** Initializes the memory system. */ UNIV_INTERN void mem_init( /*=====*/ ulint size); /*!< in: common pool size in bytes */ /******************************************************************//** Closes the memory system. */ UNIV_INTERN void mem_close(void); /*===========*/ /**************************************************************//** Use this macro instead of the corresponding function! Macro for memory heap creation. */ #define mem_heap_create(N) mem_heap_create_func(\ (N), MEM_HEAP_DYNAMIC, __FILE__, __LINE__) /**************************************************************//** Use this macro instead of the corresponding function! Macro for memory heap creation. */ #define mem_heap_create_in_buffer(N) mem_heap_create_func(\ (N), MEM_HEAP_BUFFER, __FILE__, __LINE__) /**************************************************************//** Use this macro instead of the corresponding function! Macro for memory heap creation. */ #define mem_heap_create_in_btr_search(N) mem_heap_create_func(\ (N), MEM_HEAP_BTR_SEARCH | MEM_HEAP_BUFFER,\ __FILE__, __LINE__) /**************************************************************//** Use this macro instead of the corresponding function! Macro for memory heap freeing. */ #define mem_heap_free(heap) mem_heap_free_func(\ (heap), __FILE__, __LINE__) /*****************************************************************//** NOTE: Use the corresponding macros instead of this function. Creates a memory heap. For debugging purposes, takes also the file name and line as arguments. @return own: memory heap, NULL if did not succeed (only possible for MEM_HEAP_BTR_SEARCH type heaps) */ UNIV_INLINE mem_heap_t* mem_heap_create_func( /*=================*/ ulint n, /*!< in: desired start block size, this means that a single user buffer of size n will fit in the block, 0 creates a default size block */ ulint type, /*!< in: heap type */ const char* file_name, /*!< in: file name where created */ ulint line); /*!< in: line where created */ /*****************************************************************//** NOTE: Use the corresponding macro instead of this function. Frees the space occupied by a memory heap. In the debug version erases the heap memory blocks. */ UNIV_INLINE void mem_heap_free_func( /*===============*/ mem_heap_t* heap, /*!< in, own: heap to be freed */ const char* file_name, /*!< in: file name where freed */ ulint line); /*!< in: line where freed */ /***************************************************************//** Allocates and zero-fills n bytes of memory from a memory heap. @return allocated, zero-filled storage */ UNIV_INLINE void* mem_heap_zalloc( /*============*/ mem_heap_t* heap, /*!< in: memory heap */ ulint n); /*!< in: number of bytes; if the heap is allowed to grow into the buffer pool, this must be <= MEM_MAX_ALLOC_IN_BUF */ /***************************************************************//** Allocates n bytes of memory from a memory heap. @return allocated storage, NULL if did not succeed (only possible for MEM_HEAP_BTR_SEARCH type heaps) */ UNIV_INLINE void* mem_heap_alloc( /*===========*/ mem_heap_t* heap, /*!< in: memory heap */ ulint n); /*!< in: number of bytes; if the heap is allowed to grow into the buffer pool, this must be <= MEM_MAX_ALLOC_IN_BUF */ /*****************************************************************//** Frees the space in a memory heap exceeding the pointer given. The pointer must have been acquired from mem_heap_get_heap_top. The first memory block of the heap is not freed. */ UNIV_INLINE void mem_heap_free_heap_top( /*===================*/ mem_heap_t* heap, /*!< in: heap from which to free */ byte* old_top);/*!< in: pointer to old top of heap */ /*****************************************************************//** Empties a memory heap. The first memory block of the heap is not freed. */ UNIV_INLINE void mem_heap_empty( /*===========*/ mem_heap_t* heap); /*!< in: heap to empty */ /*****************************************************************//** Returns a pointer to the topmost element in a memory heap. The size of the element must be given. @return pointer to the topmost element */ UNIV_INLINE void* mem_heap_get_top( /*=============*/ mem_heap_t* heap, /*!< in: memory heap */ ulint n); /*!< in: size of the topmost element */ /*****************************************************************//** Frees the topmost element in a memory heap. The size of the element must be given. */ UNIV_INLINE void mem_heap_free_top( /*==============*/ mem_heap_t* heap, /*!< in: memory heap */ ulint n); /*!< in: size of the topmost element */ /*****************************************************************//** Returns the space in bytes occupied by a memory heap. */ UNIV_INLINE ulint mem_heap_get_size( /*==============*/ mem_heap_t* heap); /*!< in: heap */ /**************************************************************//** Use this macro instead of the corresponding function! Macro for memory buffer allocation */ #define mem_zalloc(N) memset(mem_alloc(N), 0, (N)); #define mem_alloc(N) mem_alloc_func((N), NULL, __FILE__, __LINE__) #define mem_alloc2(N,S) mem_alloc_func((N), (S), __FILE__, __LINE__) /***************************************************************//** NOTE: Use the corresponding macro instead of this function. Allocates a single buffer of memory from the dynamic memory of the C compiler. Is like malloc of C. The buffer must be freed with mem_free. @return own: free storage */ UNIV_INLINE void* mem_alloc_func( /*===========*/ ulint n, /*!< in: requested size in bytes */ ulint* size, /*!< out: allocated size in bytes, or NULL */ const char* file_name, /*!< in: file name where created */ ulint line); /*!< in: line where created */ /**************************************************************//** Use this macro instead of the corresponding function! Macro for memory buffer freeing */ #define mem_free(PTR) mem_free_func((PTR), __FILE__, __LINE__) /***************************************************************//** NOTE: Use the corresponding macro instead of this function. Frees a single buffer of storage from the dynamic memory of C compiler. Similar to free of C. */ UNIV_INLINE void mem_free_func( /*==========*/ void* ptr, /*!< in, own: buffer to be freed */ const char* file_name, /*!< in: file name where created */ ulint line); /*!< in: line where created */ /**********************************************************************//** Duplicates a NUL-terminated string. @return own: a copy of the string, must be deallocated with mem_free */ UNIV_INLINE char* mem_strdup( /*=======*/ const char* str); /*!< in: string to be copied */ /**********************************************************************//** Makes a NUL-terminated copy of a nonterminated string. @return own: a copy of the string, must be deallocated with mem_free */ UNIV_INLINE char* mem_strdupl( /*========*/ const char* str, /*!< in: string to be copied */ ulint len); /*!< in: length of str, in bytes */ /**********************************************************************//** Duplicates a NUL-terminated string, allocated from a memory heap. @return own: a copy of the string */ UNIV_INTERN char* mem_heap_strdup( /*============*/ mem_heap_t* heap, /*!< in: memory heap where string is allocated */ const char* str); /*!< in: string to be copied */ /**********************************************************************//** Makes a NUL-terminated copy of a nonterminated string, allocated from a memory heap. @return own: a copy of the string */ UNIV_INLINE char* mem_heap_strdupl( /*=============*/ mem_heap_t* heap, /*!< in: memory heap where string is allocated */ const char* str, /*!< in: string to be copied */ ulint len); /*!< in: length of str, in bytes */ /**********************************************************************//** Concatenate two strings and return the result, using a memory heap. @return own: the result */ UNIV_INTERN char* mem_heap_strcat( /*============*/ mem_heap_t* heap, /*!< in: memory heap where string is allocated */ const char* s1, /*!< in: string 1 */ const char* s2); /*!< in: string 2 */ /**********************************************************************//** Duplicate a block of data, allocated from a memory heap. @return own: a copy of the data */ UNIV_INTERN void* mem_heap_dup( /*=========*/ mem_heap_t* heap, /*!< in: memory heap where copy is allocated */ const void* data, /*!< in: data to be copied */ ulint len); /*!< in: length of data, in bytes */ /****************************************************************//** A simple (s)printf replacement that dynamically allocates the space for the formatted string from the given heap. This supports a very limited set of the printf syntax: types 's' and 'u' and length modifier 'l' (which is required for the 'u' type). @return heap-allocated formatted string */ UNIV_INTERN char* mem_heap_printf( /*============*/ mem_heap_t* heap, /*!< in: memory heap */ const char* format, /*!< in: format string */ ...) __attribute__ ((format (printf, 2, 3))); #ifdef UNIV_DEBUG /******************************************************************//** Goes through the list of all allocated mem blocks, checks their magic numbers, and reports possible corruption. */ UNIV_INTERN void mem_heap_verify( /*============*/ const mem_heap_t* heap);/*!< in: heap to verify */ #endif /*#######################################################################*/ /* The info header of a block in a memory heap */ struct mem_block_info_struct { ulint magic_n;/* magic number for debugging */ char file_name[8];/* file name where the mem heap was created */ ulint line; /*!< line number where the mem heap was created */ UT_LIST_BASE_NODE_T(mem_block_t) base; /* In the first block in the the list this is the base node of the list of blocks; in subsequent blocks this is undefined */ UT_LIST_NODE_T(mem_block_t) list; /* This contains pointers to next and prev in the list. The first block allocated to the heap is also the first block in this list, though it also contains the base node of the list. */ ulint len; /*!< physical length of this block in bytes */ ulint total_size; /* physical length in bytes of all blocks in the heap. This is defined only in the base node and is set to ULINT_UNDEFINED in others. */ ulint type; /*!< type of heap: MEM_HEAP_DYNAMIC, or MEM_HEAP_BUF possibly ORed to MEM_HEAP_BTR_SEARCH */ ulint free; /*!< offset in bytes of the first free position for user data in the block */ ulint start; /*!< the value of the struct field 'free' at the creation of the block */ #ifndef UNIV_HOTBACKUP void* free_block; /* if the MEM_HEAP_BTR_SEARCH bit is set in type, and this is the heap root, this can contain an allocated buffer frame, which can be appended as a free block to the heap, if we need more space; otherwise, this is NULL */ void* buf_block; /* if this block has been allocated from the buffer pool, this contains the buf_block_t handle; otherwise, this is NULL */ #endif /* !UNIV_HOTBACKUP */ #ifdef UNIV_DEBUG UT_LIST_NODE_T(mem_block_t) mem_block_list; /* List of all mem blocks allocated; protected by the mem_comm_pool mutex */ #endif }; #define MEM_BLOCK_MAGIC_N 764741555 #define MEM_FREED_BLOCK_MAGIC_N 547711122 /* Header size for a memory heap block */ #define MEM_BLOCK_HEADER_SIZE ut_calc_align(sizeof(mem_block_info_t),\ UNIV_MEM_ALIGNMENT) #include "mem0dbg.h" #ifndef UNIV_NONINL #include "mem0mem.ic" #endif #endif haildb-2.3.2/include/trx0rec.ic0000644000175000017500000000652411513177357017201 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/trx0rec.ic Transaction undo log record Created 3/26/1996 Heikki Tuuri *******************************************************/ #ifndef UNIV_HOTBACKUP /**********************************************************************//** Reads from an undo log record the record type. @return record type */ UNIV_INLINE ulint trx_undo_rec_get_type( /*==================*/ const trx_undo_rec_t* undo_rec) /*!< in: undo log record */ { return(mach_read_from_1(undo_rec + 2) & (TRX_UNDO_CMPL_INFO_MULT - 1)); } /**********************************************************************//** Reads from an undo log record the record compiler info. @return compiler info */ UNIV_INLINE ulint trx_undo_rec_get_cmpl_info( /*=======================*/ const trx_undo_rec_t* undo_rec) /*!< in: undo log record */ { return(mach_read_from_1(undo_rec + 2) / TRX_UNDO_CMPL_INFO_MULT); } /**********************************************************************//** Returns TRUE if an undo log record contains an extern storage field. @return TRUE if extern */ UNIV_INLINE ibool trx_undo_rec_get_extern_storage( /*============================*/ const trx_undo_rec_t* undo_rec) /*!< in: undo log record */ { if (mach_read_from_1(undo_rec + 2) & TRX_UNDO_UPD_EXTERN) { return(TRUE); } return(FALSE); } /**********************************************************************//** Reads the undo log record number. @return undo no */ UNIV_INLINE undo_no_t trx_undo_rec_get_undo_no( /*=====================*/ const trx_undo_rec_t* undo_rec) /*!< in: undo log record */ { const byte* ptr; ptr = undo_rec + 3; return(mach_dulint_read_much_compressed(ptr)); } /**********************************************************************//** Returns the start of the undo record data area. @return offset to the data area */ UNIV_INLINE ulint trx_undo_rec_get_offset( /*====================*/ undo_no_t undo_no) /*!< in: undo no read from node */ { return (3 + mach_dulint_get_much_compressed_size(undo_no)); } /***********************************************************************//** Copies the undo record to the heap. @return own: copy of undo log record */ UNIV_INLINE trx_undo_rec_t* trx_undo_rec_copy( /*==============*/ const trx_undo_rec_t* undo_rec, /*!< in: undo log record */ mem_heap_t* heap) /*!< in: heap where copied */ { ulint len; len = mach_read_from_2(undo_rec) - ut_align_offset(undo_rec, UNIV_PAGE_SIZE); return(mem_heap_dup(heap, undo_rec, len)); } #endif /* !UNIV_HOTBACKUP */ haildb-2.3.2/include/lock0lock.h0000644000175000017500000007442411513177357017333 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2010, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/lock0lock.h The transaction lock system Created 5/7/1996 Heikki Tuuri *******************************************************/ #ifndef lock0lock_h #define lock0lock_h #include "univ.i" #include "buf0types.h" #include "trx0types.h" #include "mtr0types.h" #include "rem0types.h" #include "dict0types.h" #include "que0types.h" #include "lock0types.h" #include "read0types.h" #include "hash0hash.h" #include "ut0vec.h" #ifdef UNIV_DEBUG extern ibool lock_print_waits; #endif /* UNIV_DEBUG */ /* Buffer for storing information about the most recent deadlock error */ extern ib_stream_t lock_latest_err_stream; /*********************************************************************//** Gets the size of a lock struct. @return size in bytes */ UNIV_INTERN ulint lock_get_size(void); /*===============*/ /*********************************************************************//** Creates the lock system at database start. */ UNIV_INTERN void lock_sys_create( /*============*/ ulint n_cells); /*!< in: number of slots in lock hash table */ /*********************************************************************//** Closes the lock system at database shutdown. */ UNIV_INTERN void lock_sys_close(void); /*================*/ /*********************************************************************//** Checks if some transaction has an implicit x-lock on a record in a clustered index. @return transaction which has the x-lock, or NULL */ UNIV_INLINE trx_t* lock_clust_rec_some_has_impl( /*=========================*/ const rec_t* rec, /*!< in: user record */ dict_index_t* index, /*!< in: clustered index */ const ulint* offsets);/*!< in: rec_get_offsets(rec, index) */ /*********************************************************************//** Gets the heap_no of the smallest user record on a page. @return heap_no of smallest user record, or PAGE_HEAP_NO_SUPREMUM */ UNIV_INLINE ulint lock_get_min_heap_no( /*=================*/ const buf_block_t* block); /*!< in: buffer block */ /*************************************************************//** Updates the lock table when we have reorganized a page. NOTE: we copy also the locks set on the infimum of the page; the infimum may carry locks if an update of a record is occurring on the page, and its locks were temporarily stored on the infimum. */ UNIV_INTERN void lock_move_reorganize_page( /*======================*/ const buf_block_t* block, /*!< in: old index page, now reorganized */ const buf_block_t* oblock);/*!< in: copy of the old, not reorganized page */ /*************************************************************//** Moves the explicit locks on user records to another page if a record list end is moved to another page. */ UNIV_INTERN void lock_move_rec_list_end( /*===================*/ const buf_block_t* new_block, /*!< in: index page to move to */ const buf_block_t* block, /*!< in: index page */ const rec_t* rec); /*!< in: record on page: this is the first record moved */ /*************************************************************//** Moves the explicit locks on user records to another page if a record list start is moved to another page. */ UNIV_INTERN void lock_move_rec_list_start( /*=====================*/ const buf_block_t* new_block, /*!< in: index page to move to */ const buf_block_t* block, /*!< in: index page */ const rec_t* rec, /*!< in: record on page: this is the first record NOT copied */ const rec_t* old_end); /*!< in: old previous-to-last record on new_page before the records were copied */ /*************************************************************//** Updates the lock table when a page is split to the right. */ UNIV_INTERN void lock_update_split_right( /*====================*/ const buf_block_t* right_block, /*!< in: right page */ const buf_block_t* left_block); /*!< in: left page */ /*************************************************************//** Updates the lock table when a page is merged to the right. */ UNIV_INTERN void lock_update_merge_right( /*====================*/ const buf_block_t* right_block, /*!< in: right page to which merged */ const rec_t* orig_succ, /*!< in: original successor of infimum on the right page before merge */ const buf_block_t* left_block); /*!< in: merged index page which will be discarded */ /*************************************************************//** Updates the lock table when the root page is copied to another in btr_root_raise_and_insert. Note that we leave lock structs on the root page, even though they do not make sense on other than leaf pages: the reason is that in a pessimistic update the infimum record of the root page will act as a dummy carrier of the locks of the record to be updated. */ UNIV_INTERN void lock_update_root_raise( /*===================*/ const buf_block_t* block, /*!< in: index page to which copied */ const buf_block_t* root); /*!< in: root page */ /*************************************************************//** Updates the lock table when a page is copied to another and the original page is removed from the chain of leaf pages, except if page is the root! */ UNIV_INTERN void lock_update_copy_and_discard( /*=========================*/ const buf_block_t* new_block, /*!< in: index page to which copied */ const buf_block_t* block); /*!< in: index page; NOT the root! */ /*************************************************************//** Updates the lock table when a page is split to the left. */ UNIV_INTERN void lock_update_split_left( /*===================*/ const buf_block_t* right_block, /*!< in: right page */ const buf_block_t* left_block); /*!< in: left page */ /*************************************************************//** Updates the lock table when a page is merged to the left. */ UNIV_INTERN void lock_update_merge_left( /*===================*/ const buf_block_t* left_block, /*!< in: left page to which merged */ const rec_t* orig_pred, /*!< in: original predecessor of supremum on the left page before merge */ const buf_block_t* right_block); /*!< in: merged index page which will be discarded */ /*************************************************************//** Resets the original locks on heir and replaces them with gap type locks inherited from rec. */ UNIV_INTERN void lock_rec_reset_and_inherit_gap_locks( /*=================================*/ const buf_block_t* heir_block, /*!< in: block containing the record which inherits */ const buf_block_t* block, /*!< in: block containing the record from which inherited; does NOT reset the locks on this record */ ulint heir_heap_no, /*!< in: heap_no of the inheriting record */ ulint heap_no); /*!< in: heap_no of the donating record */ /*************************************************************//** Updates the lock table when a page is discarded. */ UNIV_INTERN void lock_update_discard( /*================*/ const buf_block_t* heir_block, /*!< in: index page which will inherit the locks */ ulint heir_heap_no, /*!< in: heap_no of the record which will inherit the locks */ const buf_block_t* block); /*!< in: index page which will be discarded */ /*************************************************************//** Updates the lock table when a new user record is inserted. */ UNIV_INTERN void lock_update_insert( /*===============*/ const buf_block_t* block, /*!< in: buffer block containing rec */ const rec_t* rec); /*!< in: the inserted record */ /*************************************************************//** Updates the lock table when a record is removed. */ UNIV_INTERN void lock_update_delete( /*===============*/ const buf_block_t* block, /*!< in: buffer block containing rec */ const rec_t* rec); /*!< in: the record to be removed */ /*********************************************************************//** Stores on the page infimum record the explicit locks of another record. This function is used to store the lock state of a record when it is updated and the size of the record changes in the update. The record is in such an update moved, perhaps to another page. The infimum record acts as a dummy carrier record, taking care of lock releases while the actual record is being moved. */ UNIV_INTERN void lock_rec_store_on_page_infimum( /*===========================*/ const buf_block_t* block, /*!< in: buffer block containing rec */ const rec_t* rec); /*!< in: record whose lock state is stored on the infimum record of the same page; lock bits are reset on the record */ /*********************************************************************//** Restores the state of explicit lock requests on a single record, where the state was stored on the infimum of the page. */ UNIV_INTERN void lock_rec_restore_from_page_infimum( /*===============================*/ const buf_block_t* block, /*!< in: buffer block containing rec */ const rec_t* rec, /*!< in: record whose lock state is restored */ const buf_block_t* donator);/*!< in: page (rec is not necessarily on this page) whose infimum stored the lock state; lock bits are reset on the infimum */ /*********************************************************************//** Returns TRUE if there are explicit record locks on a page. @return TRUE if there are explicit record locks on the page */ UNIV_INTERN ibool lock_rec_expl_exist_on_page( /*========================*/ ulint space, /*!< in: space id */ ulint page_no);/*!< in: page number */ /*********************************************************************//** Checks if locks of other transactions prevent an immediate insert of a record. If they do, first tests if the query thread should anyway be suspended for some reason; if not, then puts the transaction and the query thread to the lock wait state and inserts a waiting request for a gap x-lock to the lock queue. @return DB_SUCCESS, DB_LOCK_WAIT, DB_DEADLOCK, or DB_QUE_THR_SUSPENDED */ UNIV_INTERN ulint lock_rec_insert_check_and_lock( /*===========================*/ ulint flags, /*!< in: if BTR_NO_LOCKING_FLAG bit is set, does nothing */ const rec_t* rec, /*!< in: record after which to insert */ buf_block_t* block, /*!< in/out: buffer block of rec */ dict_index_t* index, /*!< in: index */ que_thr_t* thr, /*!< in: query thread */ mtr_t* mtr, /*!< in/out: mini-transaction */ ibool* inherit);/*!< out: set to TRUE if the new inserted record maybe should inherit LOCK_GAP type locks from the successor record */ /*********************************************************************//** Checks if locks of other transactions prevent an immediate modify (update, delete mark, or delete unmark) of a clustered index record. If they do, first tests if the query thread should anyway be suspended for some reason; if not, then puts the transaction and the query thread to the lock wait state and inserts a waiting request for a record x-lock to the lock queue. @return DB_SUCCESS, DB_LOCK_WAIT, DB_DEADLOCK, or DB_QUE_THR_SUSPENDED */ UNIV_INTERN ulint lock_clust_rec_modify_check_and_lock( /*=================================*/ ulint flags, /*!< in: if BTR_NO_LOCKING_FLAG bit is set, does nothing */ const buf_block_t* block, /*!< in: buffer block of rec */ const rec_t* rec, /*!< in: record which should be modified */ dict_index_t* index, /*!< in: clustered index */ const ulint* offsets,/*!< in: rec_get_offsets(rec, index) */ que_thr_t* thr); /*!< in: query thread */ /*********************************************************************//** Checks if locks of other transactions prevent an immediate modify (delete mark or delete unmark) of a secondary index record. @return DB_SUCCESS, DB_LOCK_WAIT, DB_DEADLOCK, or DB_QUE_THR_SUSPENDED */ UNIV_INTERN ulint lock_sec_rec_modify_check_and_lock( /*===============================*/ ulint flags, /*!< in: if BTR_NO_LOCKING_FLAG bit is set, does nothing */ buf_block_t* block, /*!< in/out: buffer block of rec */ const rec_t* rec, /*!< in: record which should be modified; NOTE: as this is a secondary index, we always have to modify the clustered index record first: see the comment below */ dict_index_t* index, /*!< in: secondary index */ que_thr_t* thr, /*!< in: query thread */ mtr_t* mtr); /*!< in/out: mini-transaction */ /*********************************************************************//** Like the counterpart for a clustered index below, but now we read a secondary index record. @return DB_SUCCESS, DB_LOCK_WAIT, DB_DEADLOCK, or DB_QUE_THR_SUSPENDED */ UNIV_INTERN ulint lock_sec_rec_read_check_and_lock( /*=============================*/ ulint flags, /*!< in: if BTR_NO_LOCKING_FLAG bit is set, does nothing */ const buf_block_t* block, /*!< in: buffer block of rec */ const rec_t* rec, /*!< in: user record or page supremum record which should be read or passed over by a read cursor */ dict_index_t* index, /*!< in: secondary index */ const ulint* offsets,/*!< in: rec_get_offsets(rec, index) */ enum lock_mode mode, /*!< in: mode of the lock which the read cursor should set on records: LOCK_S or LOCK_X; the latter is possible in SELECT FOR UPDATE */ ulint gap_mode,/*!< in: LOCK_ORDINARY, LOCK_GAP, or LOCK_REC_NOT_GAP */ que_thr_t* thr); /*!< in: query thread */ /*********************************************************************//** Checks if locks of other transactions prevent an immediate read, or passing over by a read cursor, of a clustered index record. If they do, first tests if the query thread should anyway be suspended for some reason; if not, then puts the transaction and the query thread to the lock wait state and inserts a waiting request for a record lock to the lock queue. Sets the requested mode lock on the record. @return DB_SUCCESS, DB_LOCK_WAIT, DB_DEADLOCK, or DB_QUE_THR_SUSPENDED */ UNIV_INTERN ulint lock_clust_rec_read_check_and_lock( /*===============================*/ ulint flags, /*!< in: if BTR_NO_LOCKING_FLAG bit is set, does nothing */ const buf_block_t* block, /*!< in: buffer block of rec */ const rec_t* rec, /*!< in: user record or page supremum record which should be read or passed over by a read cursor */ dict_index_t* index, /*!< in: clustered index */ const ulint* offsets,/*!< in: rec_get_offsets(rec, index) */ enum lock_mode mode, /*!< in: mode of the lock which the read cursor should set on records: LOCK_S or LOCK_X; the latter is possible in SELECT FOR UPDATE */ ulint gap_mode,/*!< in: LOCK_ORDINARY, LOCK_GAP, or LOCK_REC_NOT_GAP */ que_thr_t* thr); /*!< in: query thread */ /*********************************************************************//** Checks if locks of other transactions prevent an immediate read, or passing over by a read cursor, of a clustered index record. If they do, first tests if the query thread should anyway be suspended for some reason; if not, then puts the transaction and the query thread to the lock wait state and inserts a waiting request for a record lock to the lock queue. Sets the requested mode lock on the record. This is an alternative version of lock_clust_rec_read_check_and_lock() that does not require the parameter "offsets". @return DB_SUCCESS, DB_LOCK_WAIT, DB_DEADLOCK, or DB_QUE_THR_SUSPENDED */ UNIV_INTERN ulint lock_clust_rec_read_check_and_lock_alt( /*===================================*/ ulint flags, /*!< in: if BTR_NO_LOCKING_FLAG bit is set, does nothing */ const buf_block_t* block, /*!< in: buffer block of rec */ const rec_t* rec, /*!< in: user record or page supremum record which should be read or passed over by a read cursor */ dict_index_t* index, /*!< in: clustered index */ enum lock_mode mode, /*!< in: mode of the lock which the read cursor should set on records: LOCK_S or LOCK_X; the latter is possible in SELECT FOR UPDATE */ ulint gap_mode,/*!< in: LOCK_ORDINARY, LOCK_GAP, or LOCK_REC_NOT_GAP */ que_thr_t* thr); /*!< in: query thread */ /*********************************************************************//** Checks that a record is seen in a consistent read. @return TRUE if sees, or FALSE if an earlier version of the record should be retrieved */ UNIV_INTERN ibool lock_clust_rec_cons_read_sees( /*==========================*/ const rec_t* rec, /*!< in: user record which should be read or passed over by a read cursor */ dict_index_t* index, /*!< in: clustered index */ const ulint* offsets,/*!< in: rec_get_offsets(rec, index) */ read_view_t* view); /*!< in: consistent read view */ /*********************************************************************//** Checks that a non-clustered index record is seen in a consistent read. NOTE that a non-clustered index page contains so little information on its modifications that also in the case FALSE, the present version of rec may be the right, but we must check this from the clustered index record. @return TRUE if certainly sees, or FALSE if an earlier version of the clustered index record might be needed */ UNIV_INTERN ulint lock_sec_rec_cons_read_sees( /*========================*/ const rec_t* rec, /*!< in: user record which should be read or passed over by a read cursor */ const read_view_t* view); /*!< in: consistent read view */ /*********************************************************************//** Locks the specified database table in the mode given. If the lock cannot be granted immediately, the query thread is put to wait. @return DB_SUCCESS, DB_LOCK_WAIT, DB_DEADLOCK, or DB_QUE_THR_SUSPENDED */ UNIV_INTERN ulint lock_table( /*=======*/ ulint flags, /*!< in: if BTR_NO_LOCKING_FLAG bit is set, does nothing */ dict_table_t* table, /*!< in: database table in dictionary cache */ enum lock_mode mode, /*!< in: lock mode */ que_thr_t* thr); /*!< in: query thread */ /*************************************************************//** Removes a granted record lock of a transaction from the queue and grants locks to other transactions waiting in the queue if they now are entitled to a lock. */ UNIV_INTERN void lock_rec_unlock( /*============*/ trx_t* trx, /*!< in: transaction that has set a record lock */ const buf_block_t* block, /*!< in: buffer block containing rec */ const rec_t* rec, /*!< in: record */ enum lock_mode lock_mode);/*!< in: LOCK_S or LOCK_X */ /*********************************************************************//** Releases transaction locks, and releases possible other transactions waiting because of these locks. */ UNIV_INTERN void lock_release_off_kernel( /*====================*/ trx_t* trx); /*!< in: transaction */ /*********************************************************************//** Cancels a waiting lock request and releases possible other transactions waiting behind it. */ UNIV_INTERN void lock_cancel_waiting_and_release( /*============================*/ lock_t* lock); /*!< in: waiting lock request */ /*********************************************************************//** Removes locks on a table to be dropped or truncated. If remove_also_table_sx_locks is TRUE then table-level S and X locks are also removed in addition to other table-level and record-level locks. No lock, that is going to be removed, is allowed to be a wait lock. */ UNIV_INTERN void lock_remove_all_on_table( /*=====================*/ dict_table_t* table, /*!< in: table to be dropped or truncated */ ibool remove_also_table_sx_locks);/*!< in: also removes table S and X locks */ /*********************************************************************//** Calculates the fold value of a page file address: used in inserting or searching for a lock in the hash table. @return folded value */ UNIV_INLINE ulint lock_rec_fold( /*==========*/ ulint space, /*!< in: space */ ulint page_no)/*!< in: page number */ __attribute__((const)); /*********************************************************************//** Calculates the hash value of a page file address: used in inserting or searching for a lock in the hash table. @return hashed value */ UNIV_INLINE ulint lock_rec_hash( /*==========*/ ulint space, /*!< in: space */ ulint page_no);/*!< in: page number */ /**********************************************************************//** Looks for a set bit in a record lock bitmap. Returns ULINT_UNDEFINED, if none found. @return bit index == heap number of the record, or ULINT_UNDEFINED if none found */ UNIV_INTERN ulint lock_rec_find_set_bit( /*==================*/ const lock_t* lock); /*!< in: record lock with at least one bit set */ /*********************************************************************//** Gets the source table of an ALTER TABLE transaction. The table must be covered by an IX or IS table lock. @return the source table of transaction, if it is covered by an IX or IS table lock; dest if there is no source table, and NULL if the transaction is locking more than two tables or an inconsistency is found */ UNIV_INTERN dict_table_t* lock_get_src_table( /*===============*/ trx_t* trx, /*!< in: transaction */ dict_table_t* dest, /*!< in: destination of ALTER TABLE */ enum lock_mode* mode); /*!< out: lock mode of the source table */ /*********************************************************************//** Determine if the given table is exclusively "owned" by the given transaction, i.e., transaction holds LOCK_IX and possibly LOCK_AUTO_INC on the table. @return TRUE if table is only locked by trx, with LOCK_IX, and possibly LOCK_AUTO_INC */ UNIV_INTERN ibool lock_is_table_exclusive( /*====================*/ dict_table_t* table, /*!< in: table */ trx_t* trx); /*!< in: transaction */ /*********************************************************************//** Checks if a lock request lock1 has to wait for request lock2. @return TRUE if lock1 has to wait for lock2 to be removed */ UNIV_INTERN ibool lock_has_to_wait( /*=============*/ const lock_t* lock1, /*!< in: waiting lock */ const lock_t* lock2); /*!< in: another lock; NOTE that it is assumed that this has a lock bit set on the same record as in lock1 if the locks are record locks */ /*********************************************************************//** Checks that a transaction id is sensible, i.e., not in the future. @return TRUE if ok */ UNIV_INTERN ibool lock_check_trx_id_sanity( /*=====================*/ trx_id_t trx_id, /*!< in: trx id */ const rec_t* rec, /*!< in: user record */ dict_index_t* index, /*!< in: clustered index */ const ulint* offsets, /*!< in: rec_get_offsets(rec, index) */ ibool has_kernel_mutex);/*!< in: TRUE if the caller owns the kernel mutex */ /*********************************************************************//** Prints info of a table lock. */ UNIV_INTERN void lock_table_print( /*=============*/ ib_stream_t ib_stream, /*!< in: stream where to print */ const lock_t* lock); /*!< in: table type lock */ /*********************************************************************//** Prints info of a record lock. */ UNIV_INTERN void lock_rec_print( /*===========*/ ib_stream_t ib_stream, /*!< in: stream where to print */ const lock_t* lock); /*!< in: record type lock */ /*********************************************************************//** Prints info of locks for all transactions. @return FALSE if not able to obtain kernel mutex and exits without printing info */ UNIV_INTERN ibool lock_print_info_summary( /*====================*/ ib_stream_t ib_stream, /*!< in: stream where to print */ ibool nowait); /*!< in: whether to wait for the kernel mutex */ /************************************************************************* >>>>>>> .merge-right.r6467 Prints info of locks for each transaction. */ UNIV_INTERN void lock_print_info_all_transactions( /*=============================*/ ib_stream_t ib_stream); /*!< in: stream where to print */ /*********************************************************************//** Return approximate number or record locks (bits set in the bitmap) for this transaction. Since delete-marked records may be removed, the record count will not be precise. */ UNIV_INTERN ulint lock_number_of_rows_locked( /*=======================*/ trx_t* trx); /*!< in: transaction */ /*******************************************************************//** Gets the type of a lock. Non-inline version for using outside of the lock module. @return LOCK_TABLE or LOCK_REC */ UNIV_INTERN ulint lock_get_type( /*==========*/ const lock_t* lock); /*!< in: lock */ /*******************************************************************//** Gets the id of the transaction owning a lock. @return transaction id */ UNIV_INTERN ib_uint64_t lock_get_trx_id( /*============*/ const lock_t* lock); /*!< in: lock */ /*******************************************************************//** Gets the mode of a lock in a human readable string. The string should not be free()'d or modified. @return lock mode */ UNIV_INTERN const char* lock_get_mode_str( /*==============*/ const lock_t* lock); /*!< in: lock */ /*******************************************************************//** Gets the type of a lock in a human readable string. The string should not be free()'d or modified. @return lock type */ UNIV_INTERN const char* lock_get_type_str( /*==============*/ const lock_t* lock); /*!< in: lock */ /*******************************************************************//** Gets the id of the table on which the lock is. @return id of the table */ UNIV_INTERN ib_uint64_t lock_get_table_id( /*==============*/ const lock_t* lock); /*!< in: lock */ /*******************************************************************//** Gets the name of the table on which the lock is. The string should not be free()'d or modified. @return name of the table */ UNIV_INTERN const char* lock_get_table_name( /*================*/ const lock_t* lock); /*!< in: lock */ /*******************************************************************//** For a record lock, gets the index on which the lock is. @return index */ UNIV_INTERN const dict_index_t* lock_rec_get_index( /*===============*/ const lock_t* lock); /*!< in: lock */ /*******************************************************************//** For a record lock, gets the name of the index on which the lock is. The string should not be free()'d or modified. @return name of the index */ UNIV_INTERN const char* lock_rec_get_index_name( /*====================*/ const lock_t* lock); /*!< in: lock */ /*******************************************************************//** For a record lock, gets the tablespace number on which the lock is. @return tablespace number */ UNIV_INTERN ulint lock_rec_get_space_id( /*==================*/ const lock_t* lock); /*!< in: lock */ /*******************************************************************//** For a record lock, gets the page number on which the lock is. @return page number */ UNIV_INTERN ulint lock_rec_get_page_no( /*=================*/ const lock_t* lock); /*!< in: lock */ /***********************************************************************//** Reset the lock variables. */ UNIV_INTERN void lock_var_init(void); /*===============*/ /***********************************************************************//** Closes the lock system at database shutdown. */ UNIV_INTERN void lock_sys_close(void); /*================*/ /** Lock modes and types */ /* @{ */ #define LOCK_MODE_MASK 0xFUL /*!< mask used to extract mode from the type_mode field in a lock */ /** Lock types */ /* @{ */ #define LOCK_TABLE 16 /*!< table lock */ #define LOCK_REC 32 /*!< record lock */ #define LOCK_TYPE_MASK 0xF0UL /*!< mask used to extract lock type from the type_mode field in a lock */ #if LOCK_MODE_MASK & LOCK_TYPE_MASK # error "LOCK_MODE_MASK & LOCK_TYPE_MASK" #endif #define LOCK_WAIT 256 /*!< Waiting lock flag; when set, it means that the lock has not yet been granted, it is just waiting for its turn in the wait queue */ /* Precise modes */ #define LOCK_ORDINARY 0 /*!< this flag denotes an ordinary next-key lock in contrast to LOCK_GAP or LOCK_REC_NOT_GAP */ #define LOCK_GAP 512 /*!< when this bit is set, it means that the lock holds only on the gap before the record; for instance, an x-lock on the gap does not give permission to modify the record on which the bit is set; locks of this type are created when records are removed from the index chain of records */ #define LOCK_REC_NOT_GAP 1024 /*!< this bit means that the lock is only on the index record and does NOT block inserts to the gap before the index record; this is used in the case when we retrieve a record with a unique key, and is also used in locking plain SELECTs (not part of UPDATE or DELETE) when the user has set the READ COMMITTED isolation level */ #define LOCK_INSERT_INTENTION 2048 /*!< this bit is set when we place a waiting gap type record lock request in order to let an insert of an index record to wait until there are no conflicting locks by other transactions on the gap; note that this flag remains set when the waiting lock is granted, or if the lock is inherited to a neighboring record */ #if (LOCK_WAIT|LOCK_GAP|LOCK_REC_NOT_GAP|LOCK_INSERT_INTENTION)&LOCK_MODE_MASK # error #endif #if (LOCK_WAIT|LOCK_GAP|LOCK_REC_NOT_GAP|LOCK_INSERT_INTENTION)&LOCK_TYPE_MASK # error #endif /* @} */ /** Lock operation struct */ typedef struct lock_op_struct lock_op_t; /** Lock operation struct */ struct lock_op_struct{ dict_table_t* table; /*!< table to be locked */ enum lock_mode mode; /*!< lock mode */ }; /** The lock system struct */ struct lock_sys_struct{ hash_table_t* rec_hash; /*!< hash table of the record locks */ }; /** The lock system */ extern lock_sys_t* lock_sys; #ifndef UNIV_NONINL #include "lock0lock.ic" #endif #endif haildb-2.3.2/include/dict0boot.h0000644000175000017500000001231411513177357017327 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2010, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/dict0boot.h Data dictionary creation and booting Created 4/18/1996 Heikki Tuuri *******************************************************/ #ifndef dict0boot_h #define dict0boot_h #include "univ.i" #include "mtr0mtr.h" #include "mtr0log.h" #include "ut0byte.h" #include "buf0buf.h" #include "fsp0fsp.h" #include "dict0dict.h" typedef byte dict_hdr_t; /**********************************************************************//** Gets a pointer to the dictionary header and x-latches its page. @return pointer to the dictionary header, page x-latched */ UNIV_INTERN dict_hdr_t* dict_hdr_get( /*=========*/ mtr_t* mtr); /*!< in: mtr */ /**********************************************************************//** Returns a new row, table, index, or tree id. @return the new id */ UNIV_INTERN dulint dict_hdr_get_new_id( /*================*/ ulint type); /*!< in: DICT_HDR_ROW_ID, ... */ /**********************************************************************//** Returns a new row id. @return the new id */ UNIV_INLINE dulint dict_sys_get_new_row_id(void); /*=========================*/ /**********************************************************************//** Reads a row id from a record or other 6-byte stored form. @return row id */ UNIV_INLINE dulint dict_sys_read_row_id( /*=================*/ byte* field); /*!< in: record field */ /**********************************************************************//** Writes a row id to a record or other 6-byte stored form. */ UNIV_INLINE void dict_sys_write_row_id( /*==================*/ byte* field, /*!< in: record field */ dulint row_id);/*!< in: row id */ /*****************************************************************//** Initializes the data dictionary memory structures when the database is started. This function is also called when the data dictionary is created. */ UNIV_INTERN void dict_boot(void); /*===========*/ /*****************************************************************//** Creates and initializes the data dictionary at the database creation. */ UNIV_INTERN void dict_create(void); /*=============*/ /* Space id and page no where the dictionary header resides */ #define DICT_HDR_SPACE 0 /* the SYSTEM tablespace */ #define DICT_HDR_PAGE_NO FSP_DICT_HDR_PAGE_NO /* The ids for the basic system tables and their indexes */ #define DICT_TABLES_ID ut_dulint_create(0, 1) #define DICT_COLUMNS_ID ut_dulint_create(0, 2) #define DICT_INDEXES_ID ut_dulint_create(0, 3) #define DICT_FIELDS_ID ut_dulint_create(0, 4) /* The following is a secondary index on SYS_TABLES */ #define DICT_TABLE_IDS_ID ut_dulint_create(0, 5) #define DICT_HDR_FIRST_ID 10 /* the ids for tables etc. start from this number, except for basic system tables and their above defined indexes; ibuf tables and indexes are assigned as the id the number DICT_IBUF_ID_MIN plus the space id */ #define DICT_IBUF_ID_MIN ut_dulint_create(0xFFFFFFFFUL, 0) /* The offset of the dictionary header on the page */ #define DICT_HDR FSEG_PAGE_DATA /*-------------------------------------------------------------*/ /* Dictionary header offsets */ #define DICT_HDR_ROW_ID 0 /* The latest assigned row id */ #define DICT_HDR_TABLE_ID 8 /* The latest assigned table id */ #define DICT_HDR_INDEX_ID 16 /* The latest assigned index id */ #define DICT_HDR_MIX_ID 24 /* Obsolete, always 0. */ #define DICT_HDR_TABLES 32 /* Root of the table index tree */ #define DICT_HDR_TABLE_IDS 36 /* Root of the table index tree */ #define DICT_HDR_COLUMNS 40 /* Root of the column index tree */ #define DICT_HDR_INDEXES 44 /* Root of the index index tree */ #define DICT_HDR_FIELDS 48 /* Root of the index field index tree */ #define DICT_HDR_FSEG_HEADER 56 /* Segment header for the tablespace segment into which the dictionary header is created */ /*-------------------------------------------------------------*/ /* The field number of the page number field in the sys_indexes table clustered index */ #define DICT_SYS_INDEXES_PAGE_NO_FIELD 8 #define DICT_SYS_INDEXES_SPACE_NO_FIELD 7 #define DICT_SYS_INDEXES_TYPE_FIELD 6 #define DICT_SYS_INDEXES_NAME_FIELD 4 /* When a row id which is zero modulo this number (which must be a power of two) is assigned, the field DICT_HDR_ROW_ID on the dictionary header page is updated */ #define DICT_HDR_ROW_ID_WRITE_MARGIN 256 #ifndef UNIV_NONINL #include "dict0boot.ic" #endif #endif haildb-2.3.2/include/univ.i0000644000175000017500000003614211513177357016427 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1994, 2010, Innobase Oy. All Rights Reserved. Copyright (c) 2008, Google Inc. Copyright (c) 2009, Sun Microsystems, Inc. Portions of this file contain modifications contributed and copyrighted by Google, Inc. Those modifications are gratefully acknowledged and are described briefly in the InnoDB documentation. The contributions by Google are incorporated with their permission, and subject to the conditions contained in the file COPYING.Google. Portions of this file contain modifications contributed and copyrighted by Sun Microsystems, Inc. Those modifications are gratefully acknowledged and are described briefly in the InnoDB documentation. The contributions by Sun Microsystems are incorporated with their permission, and subject to the conditions contained in the file COPYING.Sun_Microsystems. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /***********************************************************************//** @file include/univ.i Version control for database, common definitions, and include files Created 1/20/1994 Heikki Tuuri ****************************************************************************/ #ifndef univ_i #define univ_i /* Include the header file generated by GNU autoconf or CMake */ #include "config.h" #ifdef UNIV_HOTBACKUP #include "hb_univ.i" #endif /* UNIV_HOTBACKUP */ /* Disable GCC extensions */ #if !defined( __attribute__) && !defined(__GNUC__) #define __attribute__(X) #endif /* __attribute__ */ #if defined(_WIN32) || defined(_WIN64) # ifdef _NT_ # define __NT__ # endif #ifdef _MSC_VER #define strcasecmp _stricmp #define strncasecmp _strnicmp #endif #else /* Include to get S_I... macros defined for os0file.c */ # include # if !defined(__NETWARE__) && !defined(__WIN__) # include /* mmap() for os0proc.c */ # endif # ifdef HAVE_SCHED_H # include # endif /* We only try to do explicit inlining of functions with gcc and Sun Studio */ # if !defined(__GNUC__) && !(defined(__SUNPRO_C) || defined(__SUNPRO_CC)) # undef UNIV_MUST_NOT_INLINE /* Remove compiler warning */ # define UNIV_MUST_NOT_INLINE # endif # ifdef HAVE_PREAD # define HAVE_PWRITE # endif #endif /* #if (defined(WIN32) || ... */ /* DEBUG VERSION CONTROL ===================== */ /* The following flag will make InnoDB to initialize all memory it allocates to zero. It hides Purify warnings about reading unallocated memory unless memory is read outside the allocated blocks. */ /* #define UNIV_INIT_MEM_TO_ZERO */ /* When this macro is defined then additional test functions will be compiled. These functions live at the end of each relevant source file and have "test_" prefix. These functions are not called from anywhere in the code, they can be called from gdb using the call command. Not tested on Windows. */ /* #define UNIV_COMPILE_TEST_FUNCS */ #ifdef HAVE_purify # define UNIV_DEBUG_VALGRIND #endif /* HAVE_purify */ #if 0 #define UNIV_DEBUG_VALGRIND /* Enable extra Valgrind instrumentation */ #define UNIV_DEBUG_PRINT /* Enable the compilation of some debug print functions */ #define UNIV_AHI_DEBUG /* Enable adaptive hash index debugging without UNIV_DEBUG */ #define UNIV_BUF_DEBUG /* Enable buffer pool debugging without UNIV_DEBUG */ #define UNIV_DEBUG /* Enable ut_ad() assertions and disable UNIV_INLINE */ #define UNIV_DEBUG_LOCK_VALIDATE /* Enable ut_ad(lock_rec_validate_page()) assertions. */ #define UNIV_DEBUG_FILE_ACCESSES /* Debug .ibd file access (field file_page_was_freed in buf_page_t) */ #define UNIV_LRU_DEBUG /* debug the buffer pool LRU */ #define UNIV_HASH_DEBUG /* debug HASH_ macros */ #define UNIV_LIST_DEBUG /* debug UT_LIST_ macros */ #define UNIV_LOG_LSN_DEBUG /* write LSN to the redo log; this will break redo log file compatibility, but it may be useful when debugging redo log application problems. */ #define UNIV_MEM_DEBUG /* detect memory leaks etc */ #define UNIV_IBUF_DEBUG /* debug the insert buffer */ #define UNIV_IBUF_COUNT_DEBUG /* debug the insert buffer; this limits the database to IBUF_COUNT_N_SPACES and IBUF_COUNT_N_PAGES, and the insert buffer must be empty when the database is started */ #define UNIV_SYNC_DEBUG /* debug mutex and latch operations (very slow); also UNIV_DEBUG must be defined */ #define UNIV_SEARCH_DEBUG /* debug B-tree comparisons */ #define UNIV_SYNC_PERF_STAT /* operation counts for rw-locks and mutexes */ #define UNIV_SEARCH_PERF_STAT /* statistics for the adaptive hash index */ #define UNIV_SRV_PRINT_LATCH_WAITS /* enable diagnostic output in sync0sync.c */ #define UNIV_BTR_AVOID_COPY /* when splitting B-tree nodes, do not move any records when all the records would be moved */ #define UNIV_BTR_PRINT /* enable functions for printing B-trees */ #define UNIV_ZIP_DEBUG /* extensive consistency checks for compressed pages */ #define UNIV_ZIP_COPY /* call page_zip_copy_recs() more often */ #endif #define UNIV_BTR_DEBUG /* check B-tree links */ #define UNIV_LIGHT_MEM_DEBUG /* light memory debugging */ #ifdef HAVE_purify /* The following sets all new allocated memory to zero before use: this can be used to eliminate unnecessary Purify warnings, but note that it also masks many bugs Purify could detect. For detailed Purify analysis it is best to remove the define below and look through the warnings one by one. */ #define UNIV_SET_MEM_TO_ZERO #endif /* #define UNIV_SQL_DEBUG #define UNIV_LOG_DEBUG */ /* the above option prevents forcing of log to disk at a buffer page write: it should be tested with this option off; also some ibuf tests are suppressed */ /* Linkage specifier for non-static InnoDB symbols (variables and functions) that are only referenced from within InnoDB. */ #if defined(__GNUC__) && (__GNUC__ >= 4) || defined(__INTEL_COMPILER) # define UNIV_INTERN __attribute__((visibility ("hidden"))) #else # define UNIV_INTERN #endif #if (!defined(UNIV_DEBUG) && !defined(UNIV_MUST_NOT_INLINE)) /* Definition for inline version */ #ifdef __WIN__ # define UNIV_INLINE __inline #elif defined(__SUNPRO_CC) || defined(__SUNPRO_C) # define UNIV_INLINE static inline #else # define UNIV_INLINE static __inline__ #endif #else /* If we want to compile a noninlined version we use the following macro definitions: */ #define UNIV_NONINL #define UNIV_INLINE UNIV_INTERN #endif /* UNIV_DEBUG */ #ifdef _WIN32 #define UNIV_WORD_SIZE 4 #elif defined(_WIN64) #define UNIV_WORD_SIZE 8 #else #define UNIV_WORD_SIZE SIZEOF_LONG #endif /* The following alignment is used in memory allocations in memory heap management to ensure correct alignment for doubles etc. */ #define UNIV_MEM_ALIGNMENT 8 /* The following alignment is used in aligning lints etc. */ #define UNIV_WORD_ALIGNMENT UNIV_WORD_SIZE /* DATABASE VERSION CONTROL ======================== */ /* The 2-logarithm of UNIV_PAGE_SIZE: */ #define UNIV_PAGE_SIZE_SHIFT 14 /* The universal page size of the database */ #define UNIV_PAGE_SIZE (1 << UNIV_PAGE_SIZE_SHIFT) /* Maximum number of parallel threads in a parallelized operation */ #define UNIV_MAX_PARALLELISM 32 /* UNIVERSAL TYPE DEFINITIONS ========================== */ typedef unsigned char byte; /* Define an unsigned integer type that is exactly 32 bits. */ #if SIZEOF_INT == 4 typedef int ib_int32_t; typedef unsigned int ib_uint32_t; #elif SIZEOF_LONG == 4 typedef long ib_int32_t; typedef unsigned long ib_uint32_t; #else #error "Neither int or long is 4 bytes" #endif /* Another basic type we use is unsigned long integer which should be equal to the word size of the machine, that is on a 32-bit platform 32 bits, and on a 64-bit platform 64 bits. We also give the printf format for the type as a macro ULINTPF. */ #ifdef _WIN64 typedef unsigned __int64 ulint; #define ULINTPF "%I64u" typedef __int64 lint; #else typedef unsigned long int ulint; #define ULINTPF "%lu" typedef long int lint; #endif #ifdef __WIN__ typedef __int64 ib_int64_t; typedef unsigned __int64 ib_uint64_t; #elif SIZEOF_LONG_LONG == 8 typedef long long ib_int64_t; typedef unsigned long long ib_uint64_t; #else #error "Error: InnoDB needs a 64 bit integer" #endif #ifdef __WIN__ typedef short int ib_int16_t; typedef unsigned short int ib_uint16_t; #elif SIZEOF_SHORT == 2 typedef short int ib_int16_t; typedef unsigned short int ib_uint16_t; #else #error "Error: InnoDB needs a 16 bit integer" #endif #ifndef HAVE_ULONG typedef unsigned long ulong; #define HAVE_ULONG 1 #endif /* HAVE_ULONG */ #ifndef __WIN__ #if SIZEOF_LONG != SIZEOF_CHARP #error "Error: InnoDB's ulint must be of the same size as void*" #endif #endif /* The 'undefined' value for a ulint */ #define ULINT_UNDEFINED ((ulint)(-1)) /* The undefined 32-bit unsigned integer */ #define ULINT32_UNDEFINED 0xFFFFFFFF /* Maximum value for a ulint */ #define ULINT_MAX ((ulint)(-2)) /* Maximum value for ib_uint64_t */ #define IB_UINT64_T_MAX (~(ib_uint64_t) 0) /* Maximum value for ib_uint64_t */ #define IB_ULONGLONG_MAX ((ib_uint64_t) (~0ULL)) /* This 'ibool' type is used within Innobase. Remember that different included headers may define 'bool' differently. Do not assume that 'bool' is a ulint! */ #define ibool ulint #ifndef TRUE #define TRUE 1 #define FALSE 0 #endif /* The following number as the length of a logical field means that the field has the SQL NULL as its value. NOTE that because we assume that the length of a field is a 32-bit integer when we store it, for example, to an undo log on disk, we must have also this number fit in 32 bits, also in 64-bit computers! */ #define UNIV_SQL_NULL ULINT32_UNDEFINED /* Lengths which are not UNIV_SQL_NULL, but bigger than the following number indicate that a field contains a reference to an externally stored part of the field in the tablespace. The length field then contains the sum of the following flag and the locally stored len. */ #define UNIV_EXTERN_STORAGE_FIELD (UNIV_SQL_NULL - UNIV_PAGE_SIZE) /* Some macros to improve branch prediction and reduce cache misses */ #if defined(__GNUC__) && (__GNUC__ > 2) && ! defined(__INTEL_COMPILER) /* Tell the compiler that 'expr' probably evaluates to 'constant'. */ # define UNIV_EXPECT(expr,constant) __builtin_expect(expr, constant) /* Tell the compiler that a pointer is likely to be NULL */ # define UNIV_LIKELY_NULL(ptr) __builtin_expect((ulint) ptr, 0) /* Minimize cache-miss latency by moving data at addr into a cache before it is read. */ # define UNIV_PREFETCH_R(addr) __builtin_prefetch(addr, 0, 3) /* Minimize cache-miss latency by moving data at addr into a cache before it is read or written. */ # define UNIV_PREFETCH_RW(addr) __builtin_prefetch(addr, 1, 3) /* Sun Studio includes sun_prefetch.h as of version 5.9 */ #elif (defined(__SUNPRO_C) && __SUNPRO_C >= 0x590) \ || (defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x590) # include #if __SUNPRO_C >= 0x550 # undef UNIV_INTERN # define UNIV_INTERN __hidden #endif /* __SUNPRO_C >= 0x550 */ /* Use sun_prefetch when compile with Sun Studio */ # define UNIV_EXPECT(expr,value) (expr) # define UNIV_LIKELY_NULL(expr) (expr) # define UNIV_PREFETCH_R(addr) sun_prefetch_read_many(addr) # define UNIV_PREFETCH_RW(addr) sun_prefetch_write_many(addr) #else /* Dummy versions of the macros */ # define UNIV_EXPECT(expr,value) (expr) # define UNIV_LIKELY_NULL(expr) (expr) # define UNIV_PREFETCH_R(addr) ((void) 0) # define UNIV_PREFETCH_RW(addr) ((void) 0) #endif /* Tell the compiler that cond is likely to hold */ #define UNIV_LIKELY(cond) UNIV_EXPECT(cond, TRUE) /* Tell the compiler that cond is unlikely to hold */ #define UNIV_UNLIKELY(cond) UNIV_EXPECT(cond, FALSE) /* Compile-time constant of the given array's size. */ #define UT_ARR_SIZE(a) (sizeof(a) / sizeof((a)[0])) /* The return type from a thread's start function differs between Unix and Windows, so define a typedef for it and a macro to use at the end of such functions. */ #ifdef __WIN__ typedef ulint os_thread_ret_t; #define OS_THREAD_DUMMY_RETURN return(0) #else typedef void* os_thread_ret_t; #define OS_THREAD_DUMMY_RETURN return(NULL) #endif #include #include "ut0dbg.h" #include "ut0ut.h" #include "db0err.h" #ifdef UNIV_DEBUG_VALGRIND # include # define UNIV_MEM_VALID(addr, size) VALGRIND_MAKE_MEM_DEFINED(addr, size) # define UNIV_MEM_INVALID(addr, size) VALGRIND_MAKE_MEM_UNDEFINED(addr, size) # define UNIV_MEM_FREE(addr, size) VALGRIND_MAKE_MEM_NOACCESS(addr, size) # define UNIV_MEM_ALLOC(addr, size) VALGRIND_MAKE_MEM_UNDEFINED(addr, size) # define UNIV_MEM_DESC(addr, size, b) VALGRIND_CREATE_BLOCK(addr, size, b) # define UNIV_MEM_UNDESC(b) VALGRIND_DISCARD(b) # define UNIV_MEM_ASSERT_RW(addr, size) do { \ const void* _p = (const void*) (ulint) \ VALGRIND_CHECK_MEM_IS_DEFINED(addr, size); \ if (UNIV_LIKELY_NULL(_p)) \ fprintf(stderr, "%s:%d: %p[%u] undefined at %ld\n", \ __FILE__, __LINE__, \ (const void*) (addr), (unsigned) (size), (long) \ (((const char*) _p) - ((const char*) (addr)))); \ } while (0) # define UNIV_MEM_ASSERT_W(addr, size) do { \ const void* _p = (const void*) (ulint) \ VALGRIND_CHECK_MEM_IS_ADDRESSABLE(addr, size); \ if (UNIV_LIKELY_NULL(_p)) \ fprintf(stderr, "%s:%d: %p[%u] unwritable at %ld\n", \ __FILE__, __LINE__, \ (const void*) (addr), (unsigned) (size), (long) \ (((const char*) _p) - ((const char*) (addr)))); \ } while (0) #else # define UNIV_MEM_VALID(addr, size) do {} while(0) # define UNIV_MEM_INVALID(addr, size) do {} while(0) # define UNIV_MEM_FREE(addr, size) do {} while(0) # define UNIV_MEM_ALLOC(addr, size) do {} while(0) # define UNIV_MEM_DESC(addr, size, b) do {} while(0) # define UNIV_MEM_UNDESC(b) do {} while(0) # define UNIV_MEM_ASSERT_RW(addr, size) do {} while(0) # define UNIV_MEM_ASSERT_W(addr, size) do {} while(0) #endif #define UNIV_MEM_ASSERT_AND_FREE(addr, size) do { \ UNIV_MEM_ASSERT_W(addr, size); \ UNIV_MEM_FREE(addr, size); \ } while (0) #define UNIV_MEM_ASSERT_AND_ALLOC(addr, size) do { \ UNIV_MEM_ASSERT_W(addr, size); \ UNIV_MEM_ALLOC(addr, size); \ } while (0) #define NAME_CHAR_LEN 64 #define SYSTEM_CHARSET_MBMAXLEN 3 #define NAME_LEN (NAME_CHAR_LEN*SYSTEM_CHARSET_MBMAXLEN) #define IB_FILE 1 #define IB_TMP_FILE ULINT_UNDEFINED /* An auxiliary macro to improve readability */ #if !defined __STRICT_ANSI__ && defined __GNUC__ && (__GNUC__) > 2 && !defined __INTEL_COMPILER #define STRUCT_FLD(name, value) .name = value #else #define STRUCT_FLD(name, value) value #endif #endif haildb-2.3.2/include/btr0pcur.h0000644000175000017500000005320111513177357017201 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2010, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/btr0pcur.h The index tree persistent cursor Created 2/23/1996 Heikki Tuuri *******************************************************/ #ifndef btr0pcur_h #define btr0pcur_h #include "univ.i" #include "dict0dict.h" #include "data0data.h" #include "mtr0mtr.h" #include "page0cur.h" #include "btr0cur.h" #include "btr0btr.h" #include "btr0types.h" /* Relative positions for a stored cursor position */ #define BTR_PCUR_ON 1 #define BTR_PCUR_BEFORE 2 #define BTR_PCUR_AFTER 3 /* Note that if the tree is not empty, btr_pcur_store_position does not use the following, but only uses the above three alternatives, where the position is stored relative to a specific record: this makes implementation of a scroll cursor easier */ #define BTR_PCUR_BEFORE_FIRST_IN_TREE 4 /* in an empty tree */ #define BTR_PCUR_AFTER_LAST_IN_TREE 5 /* in an empty tree */ /**************************************************************//** Allocates memory for a persistent cursor object and initializes the cursor. @return own: persistent cursor */ UNIV_INTERN btr_pcur_t* btr_pcur_create(void); /*=================*/ /**************************************************************//** Frees the memory for a persistent cursor object. */ UNIV_INTERN void btr_pcur_free( /*==========*/ btr_pcur_t* cursor); /*!< in, own: persistent cursor */ /**************************************************************//** Copies the stored position of a pcur to another pcur. */ UNIV_INTERN void btr_pcur_copy_stored_position( /*==========================*/ btr_pcur_t* pcur_receive, /*!< in: pcur which will receive the position info */ btr_pcur_t* pcur_donate); /*!< in: pcur from which the info is copied */ /**************************************************************//** Sets the old_rec_buf field to NULL. */ UNIV_INLINE void btr_pcur_init( /*==========*/ btr_pcur_t* pcur); /*!< in: persistent cursor */ /**************************************************************//** Initializes and opens a persistent cursor to an index tree. It should be closed with btr_pcur_close. */ UNIV_INLINE void btr_pcur_open_func( /*===============*/ dict_index_t* index, /*!< in: index */ const dtuple_t* tuple, /*!< in: tuple on which search done */ ulint mode, /*!< in: PAGE_CUR_L, ...; NOTE that if the search is made using a unique prefix of a record, mode should be PAGE_CUR_LE, not PAGE_CUR_GE, as the latter may end up on the previous page from the record! */ ulint latch_mode,/*!< in: BTR_SEARCH_LEAF, ... */ btr_pcur_t* cursor, /*!< in: memory buffer for persistent cursor */ const char* file, /*!< in: file name */ ulint line, /*!< in: line where called */ mtr_t* mtr); /*!< in: mtr */ #define btr_pcur_open(i,t,md,l,c,m) \ btr_pcur_open_func(i,t,md,l,c,__FILE__,__LINE__,m) /**************************************************************//** Opens an persistent cursor to an index tree without initializing the cursor. */ UNIV_INLINE void btr_pcur_open_with_no_init_func( /*============================*/ dict_index_t* index, /*!< in: index */ const dtuple_t* tuple, /*!< in: tuple on which search done */ ulint mode, /*!< in: PAGE_CUR_L, ...; NOTE that if the search is made using a unique prefix of a record, mode should be PAGE_CUR_LE, not PAGE_CUR_GE, as the latter may end up on the previous page of the record! */ ulint latch_mode,/*!< in: BTR_SEARCH_LEAF, ...; NOTE that if has_search_latch != 0 then we maybe do not acquire a latch on the cursor page, but assume that the caller uses his btr search latch to protect the record! */ btr_pcur_t* cursor, /*!< in: memory buffer for persistent cursor */ ulint has_search_latch,/*!< in: latch mode the caller currently has on btr_search_latch: RW_S_LATCH, or 0 */ const char* file, /*!< in: file name */ ulint line, /*!< in: line where called */ mtr_t* mtr); /*!< in: mtr */ #define btr_pcur_open_with_no_init(ix,t,md,l,cur,has,m) \ btr_pcur_open_with_no_init_func(ix,t,md,l,cur,has,__FILE__,__LINE__,m) /*****************************************************************//** Opens a persistent cursor at either end of an index. */ UNIV_INLINE void btr_pcur_open_at_index_side( /*========================*/ ibool from_left, /*!< in: TRUE if open to the low end, FALSE if to the high end */ dict_index_t* index, /*!< in: index */ ulint latch_mode, /*!< in: latch mode */ btr_pcur_t* pcur, /*!< in: cursor */ ibool do_init, /*!< in: TRUE if should be initialized */ mtr_t* mtr); /*!< in: mtr */ /**************************************************************//** Gets the up_match value for a pcur after a search. @return number of matched fields at the cursor or to the right if search mode was PAGE_CUR_GE, otherwise undefined */ UNIV_INLINE ulint btr_pcur_get_up_match( /*==================*/ btr_pcur_t* cursor); /*!< in: memory buffer for persistent cursor */ /**************************************************************//** Gets the low_match value for a pcur after a search. @return number of matched fields at the cursor or to the right if search mode was PAGE_CUR_LE, otherwise undefined */ UNIV_INLINE ulint btr_pcur_get_low_match( /*===================*/ btr_pcur_t* cursor); /*!< in: memory buffer for persistent cursor */ /**************************************************************//** If mode is PAGE_CUR_G or PAGE_CUR_GE, opens a persistent cursor on the first user record satisfying the search condition, in the case PAGE_CUR_L or PAGE_CUR_LE, on the last user record. If no such user record exists, then in the first case sets the cursor after last in tree, and in the latter case before first in tree. The latching mode must be BTR_SEARCH_LEAF or BTR_MODIFY_LEAF. */ UNIV_INTERN void btr_pcur_open_on_user_rec_func( /*===========================*/ dict_index_t* index, /*!< in: index */ const dtuple_t* tuple, /*!< in: tuple on which search done */ ulint mode, /*!< in: PAGE_CUR_L, ... */ ulint latch_mode, /*!< in: BTR_SEARCH_LEAF or BTR_MODIFY_LEAF */ btr_pcur_t* cursor, /*!< in: memory buffer for persistent cursor */ const char* file, /*!< in: file name */ ulint line, /*!< in: line where called */ mtr_t* mtr); /*!< in: mtr */ #define btr_pcur_open_on_user_rec(i,t,md,l,c,m) \ btr_pcur_open_on_user_rec_func(i,t,md,l,c,__FILE__,__LINE__,m) /**********************************************************************//** Positions a cursor at a randomly chosen position within a B-tree. */ UNIV_INLINE void btr_pcur_open_at_rnd_pos_func( /*==========================*/ dict_index_t* index, /*!< in: index */ ulint latch_mode, /*!< in: BTR_SEARCH_LEAF, ... */ btr_pcur_t* cursor, /*!< in/out: B-tree pcur */ const char* file, /*!< in: file name */ ulint line, /*!< in: line where called */ mtr_t* mtr); /*!< in: mtr */ #define btr_pcur_open_at_rnd_pos(i,l,c,m) \ btr_pcur_open_at_rnd_pos_func(i,l,c,__FILE__,__LINE__,m) /**************************************************************//** Frees the possible old_rec_buf buffer of a persistent cursor and sets the latch mode of the persistent cursor to BTR_NO_LATCHES. */ UNIV_INLINE void btr_pcur_close( /*===========*/ btr_pcur_t* cursor); /*!< in: persistent cursor */ /**************************************************************//** The position of the cursor is stored by taking an initial segment of the record the cursor is positioned on, before, or after, and copying it to the cursor data structure, or just setting a flag if the cursor id before the first in an EMPTY tree, or after the last in an EMPTY tree. NOTE that the page where the cursor is positioned must not be empty if the index tree is not totally empty! */ UNIV_INTERN void btr_pcur_store_position( /*====================*/ btr_pcur_t* cursor, /*!< in: persistent cursor */ mtr_t* mtr); /*!< in: mtr */ /**************************************************************//** Restores the stored position of a persistent cursor bufferfixing the page and obtaining the specified latches. If the cursor position was saved when the (1) cursor was positioned on a user record: this function restores the position to the last record LESS OR EQUAL to the stored record; (2) cursor was positioned on a page infimum record: restores the position to the last record LESS than the user record which was the successor of the page infimum; (3) cursor was positioned on the page supremum: restores to the first record GREATER than the user record which was the predecessor of the supremum. (4) cursor was positioned before the first or after the last in an empty tree: restores to before first or after the last in the tree. @return TRUE if the cursor position was stored when it was on a user record and it can be restored on a user record whose ordering fields are identical to the ones of the original user record */ UNIV_INTERN ibool btr_pcur_restore_position_func( /*===========================*/ ulint latch_mode, /*!< in: BTR_SEARCH_LEAF, ... */ btr_pcur_t* cursor, /*!< in: detached persistent cursor */ const char* file, /*!< in: file name */ ulint line, /*!< in: line where called */ mtr_t* mtr); /*!< in: mtr */ #define btr_pcur_restore_position(l,cur,mtr) \ btr_pcur_restore_position_func(l,cur,__FILE__,__LINE__,mtr) /**************************************************************//** If the latch mode of the cursor is BTR_LEAF_SEARCH or BTR_LEAF_MODIFY, releases the page latch and bufferfix reserved by the cursor. NOTE! In the case of BTR_LEAF_MODIFY, there should not exist changes made by the current mini-transaction to the data protected by the cursor latch, as then the latch must not be released until mtr_commit. */ UNIV_INTERN void btr_pcur_release_leaf( /*==================*/ btr_pcur_t* cursor, /*!< in: persistent cursor */ mtr_t* mtr); /*!< in: mtr */ /*********************************************************//** Gets the rel_pos field for a cursor whose position has been stored. @return BTR_PCUR_ON, ... */ UNIV_INLINE ulint btr_pcur_get_rel_pos( /*=================*/ const btr_pcur_t* cursor);/*!< in: persistent cursor */ /*********************************************************//** Sets the mtr field for a pcur. */ UNIV_INLINE void btr_pcur_set_mtr( /*=============*/ btr_pcur_t* cursor, /*!< in: persistent cursor */ mtr_t* mtr); /*!< in, own: mtr */ /*********************************************************//** Gets the mtr field for a pcur. @return mtr */ UNIV_INLINE mtr_t* btr_pcur_get_mtr( /*=============*/ btr_pcur_t* cursor); /*!< in: persistent cursor */ /**************************************************************//** Commits the mtr and sets the pcur latch mode to BTR_NO_LATCHES, that is, the cursor becomes detached. If there have been modifications to the page where pcur is positioned, this can be used instead of btr_pcur_release_leaf. Function btr_pcur_store_position should be used before calling this, if restoration of cursor is wanted later. */ UNIV_INLINE void btr_pcur_commit_specify_mtr( /*========================*/ btr_pcur_t* pcur, /*!< in: persistent cursor */ mtr_t* mtr); /*!< in: mtr to commit */ /**************************************************************//** Tests if a cursor is detached: that is the latch mode is BTR_NO_LATCHES. @return TRUE if detached */ UNIV_INLINE ibool btr_pcur_is_detached( /*=================*/ btr_pcur_t* pcur); /*!< in: persistent cursor */ /*********************************************************//** Moves the persistent cursor to the next record in the tree. If no records are left, the cursor stays 'after last in tree'. @return TRUE if the cursor was not after last in tree */ UNIV_INLINE ibool btr_pcur_move_to_next( /*==================*/ btr_pcur_t* cursor, /*!< in: persistent cursor; NOTE that the function may release the page latch */ mtr_t* mtr); /*!< in: mtr */ /*********************************************************//** Moves the persistent cursor to the previous record in the tree. If no records are left, the cursor stays 'before first in tree'. @return TRUE if the cursor was not before first in tree */ UNIV_INLINE ibool btr_pcur_move_to_prev( /*==================*/ btr_pcur_t* cursor, /*!< in: persistent cursor; NOTE that the function may release the page latch */ mtr_t* mtr); /*!< in: mtr */ /*********************************************************//** Moves the persistent cursor to the last record on the same page. */ UNIV_INLINE void btr_pcur_move_to_last_on_page( /*==========================*/ btr_pcur_t* cursor, /*!< in: persistent cursor */ mtr_t* mtr); /*!< in: mtr */ /*********************************************************//** Moves the persistent cursor to the next user record in the tree. If no user records are left, the cursor ends up 'after last in tree'. @return TRUE if the cursor moved forward, ending on a user record */ UNIV_INLINE ibool btr_pcur_move_to_next_user_rec( /*===========================*/ btr_pcur_t* cursor, /*!< in: persistent cursor; NOTE that the function may release the page latch */ mtr_t* mtr); /*!< in: mtr */ /*********************************************************//** Moves the persistent cursor to the prev user record in the tree. If no user records are left, the cursor ends up 'before first in tree'. @return TRUE if the cursor moved backward, ending on a user record */ UNIV_INLINE ibool btr_pcur_move_to_prev_user_rec( /*===========================*/ btr_pcur_t* cursor, /*!< in: persistent cursor; NOTE that the function may release the page latch */ mtr_t* mtr); /*!< in: mtr */ /************************************************************* Moves the persistent cursor to the first record on the next page. Releases the latch on the current page, and bufferunfixes it. Note that there must not be modifications on the current page, as then the x-latch can be released only in mtr_commit. */ UNIV_INTERN void btr_pcur_move_to_next_page( /*=======================*/ btr_pcur_t* cursor, /*!< in: persistent cursor; must be on the last record of the current page */ mtr_t* mtr); /*!< in: mtr */ /*********************************************************//** Moves the persistent cursor backward if it is on the first record of the page. Releases the latch on the current page, and bufferunfixes it. Note that to prevent a possible deadlock, the operation first stores the position of the cursor, releases the leaf latch, acquires necessary latches and restores the cursor position again before returning. The alphabetical position of the cursor is guaranteed to be sensible on return, but it may happen that the cursor is not positioned on the last record of any page, because the structure of the tree may have changed while the cursor had no latches. */ UNIV_INTERN void btr_pcur_move_backward_from_page( /*=============================*/ btr_pcur_t* cursor, /*!< in: persistent cursor, must be on the first record of the current page */ mtr_t* mtr); /*!< in: mtr */ #ifdef UNIV_DEBUG /*********************************************************//** Returns the btr cursor component of a persistent cursor. @return pointer to btr cursor component */ UNIV_INLINE btr_cur_t* btr_pcur_get_btr_cur( /*=================*/ const btr_pcur_t* cursor); /*!< in: persistent cursor */ /*********************************************************//** Returns the page cursor component of a persistent cursor. @return pointer to page cursor component */ UNIV_INLINE page_cur_t* btr_pcur_get_page_cur( /*==================*/ const btr_pcur_t* cursor); /*!< in: persistent cursor */ #else /* UNIV_DEBUG */ # define btr_pcur_get_btr_cur(cursor) (&(cursor)->btr_cur) # define btr_pcur_get_page_cur(cursor) (&(cursor)->btr_cur.page_cur) #endif /* UNIV_DEBUG */ /*********************************************************//** Returns the page of a persistent cursor. @return pointer to the page */ UNIV_INLINE page_t* btr_pcur_get_page( /*==============*/ btr_pcur_t* cursor);/*!< in: persistent cursor */ /*********************************************************//** Returns the buffer block of a persistent cursor. @return pointer to the block */ UNIV_INLINE buf_block_t* btr_pcur_get_block( /*===============*/ btr_pcur_t* cursor);/*!< in: persistent cursor */ /*********************************************************//** Returns the record of a persistent cursor. @return pointer to the record */ UNIV_INLINE rec_t* btr_pcur_get_rec( /*=============*/ btr_pcur_t* cursor);/*!< in: persistent cursor */ /*********************************************************//** Checks if the persistent cursor is on a user record. */ UNIV_INLINE ibool btr_pcur_is_on_user_rec( /*====================*/ const btr_pcur_t* cursor);/*!< in: persistent cursor */ /*********************************************************//** Checks if the persistent cursor is after the last user record on a page. */ UNIV_INLINE ibool btr_pcur_is_after_last_on_page( /*===========================*/ const btr_pcur_t* cursor);/*!< in: persistent cursor */ /*********************************************************//** Checks if the persistent cursor is before the first user record on a page. */ UNIV_INLINE ibool btr_pcur_is_before_first_on_page( /*=============================*/ const btr_pcur_t* cursor);/*!< in: persistent cursor */ /*********************************************************//** Checks if the persistent cursor is before the first user record in the index tree. */ UNIV_INLINE ibool btr_pcur_is_before_first_in_tree( /*=============================*/ btr_pcur_t* cursor, /*!< in: persistent cursor */ mtr_t* mtr); /*!< in: mtr */ /*********************************************************//** Checks if the persistent cursor is after the last user record in the index tree. */ UNIV_INLINE ibool btr_pcur_is_after_last_in_tree( /*===========================*/ btr_pcur_t* cursor, /*!< in: persistent cursor */ mtr_t* mtr); /*!< in: mtr */ /*********************************************************//** Moves the persistent cursor to the next record on the same page. */ UNIV_INLINE void btr_pcur_move_to_next_on_page( /*==========================*/ btr_pcur_t* cursor);/*!< in/out: persistent cursor */ /*********************************************************//** Moves the persistent cursor to the previous record on the same page. */ UNIV_INLINE void btr_pcur_move_to_prev_on_page( /*==========================*/ btr_pcur_t* cursor);/*!< in/out: persistent cursor */ /* The persistent B-tree cursor structure. This is used mainly for SQL selects, updates, and deletes. */ struct btr_pcur_struct{ btr_cur_t btr_cur; /*!< a B-tree cursor */ ulint latch_mode; /*!< see TODO note below! BTR_SEARCH_LEAF, BTR_MODIFY_LEAF, BTR_MODIFY_TREE, or BTR_NO_LATCHES, depending on the latching state of the page and tree where the cursor is positioned; the last value means that the cursor is not currently positioned: we say then that the cursor is detached; it can be restored to attached if the old position was stored in old_rec */ ulint old_stored; /*!< BTR_PCUR_OLD_STORED or BTR_PCUR_OLD_NOT_STORED */ rec_t* old_rec; /*!< if cursor position is stored, contains an initial segment of the latest record cursor was positioned either on, before, or after */ ulint old_n_fields; /*!< number of fields in old_rec */ ulint rel_pos; /*!< BTR_PCUR_ON, BTR_PCUR_BEFORE, or BTR_PCUR_AFTER, depending on whether cursor was on, before, or after the old_rec record */ buf_block_t* block_when_stored;/* buffer block when the position was stored */ ib_uint64_t modify_clock; /*!< the modify clock value of the buffer block when the cursor position was stored */ ulint pos_state; /*!< see TODO note below! BTR_PCUR_IS_POSITIONED, BTR_PCUR_WAS_POSITIONED, BTR_PCUR_NOT_POSITIONED */ ulint search_mode; /*!< PAGE_CUR_G, ... */ trx_t* trx_if_known; /*!< the transaction, if we know it; otherwise this field is not defined; can ONLY BE USED in error prints in fatal assertion failures! */ /*-----------------------------*/ /* NOTE that the following fields may possess dynamically allocated memory which should be freed if not needed anymore! */ mtr_t* mtr; /*!< NULL, or this field may contain a mini-transaction which holds the latch on the cursor page */ byte* old_rec_buf; /*!< NULL, or a dynamically allocated buffer for old_rec */ ulint buf_size; /*!< old_rec_buf size if old_rec_buf is not NULL */ }; #define BTR_PCUR_IS_POSITIONED 1997660512 /* TODO: currently, the state can be BTR_PCUR_IS_POSITIONED, though it really should be BTR_PCUR_WAS_POSITIONED, because we have no obligation to commit the cursor with mtr; similarly latch_mode may be out of date. This can lead to problems if btr_pcur is not used the right way; all current code should be ok. */ #define BTR_PCUR_WAS_POSITIONED 1187549791 #define BTR_PCUR_NOT_POSITIONED 1328997689 #define BTR_PCUR_OLD_STORED 908467085 #define BTR_PCUR_OLD_NOT_STORED 122766467 #ifndef UNIV_NONINL #include "btr0pcur.ic" #endif #endif haildb-2.3.2/include/thr0loc.h0000644000175000017500000000565611513177357017026 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/thr0loc.h The thread local storage Created 10/5/1995 Heikki Tuuri *******************************************************/ /* This module implements storage private to each thread, a capability useful in some situations like storing the OS handle to the current thread, or its priority. */ #ifndef thr0loc_h #define thr0loc_h #include "univ.i" #include "os0thread.h" /****************************************************************//** Initializes the thread local storage module. */ UNIV_INTERN void thr_local_init(void); /*================*/ /******************************************************************//** Close the thread local storage module. */ UNIV_INTERN void thr_local_close(void); /*=================*/ /*******************************************************************//** Creates a local storage struct for the calling new thread. */ UNIV_INTERN void thr_local_create(void); /*==================*/ /*******************************************************************//** Frees the local storage struct for the specified thread. */ UNIV_INTERN void thr_local_free( /*===========*/ os_thread_id_t id); /*!< in: thread id */ /*******************************************************************//** Gets the slot number in the thread table of a thread. @return slot number */ UNIV_INTERN ulint thr_local_get_slot_no( /*==================*/ os_thread_id_t id); /*!< in: thread id of the thread */ /*******************************************************************//** Sets in the local storage the slot number in the thread table of a thread. */ UNIV_INTERN void thr_local_set_slot_no( /*==================*/ os_thread_id_t id, /*!< in: thread id of the thread */ ulint slot_no);/*!< in: slot number */ /*******************************************************************//** Returns pointer to the 'in_ibuf' field within the current thread local storage. @return pointer to the in_ibuf field */ UNIV_INTERN ibool* thr_local_get_in_ibuf_field(void); /*=============================*/ #ifndef UNIV_NONINL #include "thr0loc.ic" #endif #endif haildb-2.3.2/include/page0cur.ic0000644000175000017500000002137511513177357017321 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1994, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /********************************************************************//** @file include/page0cur.ic The page cursor Created 10/4/1994 Heikki Tuuri *************************************************************************/ #include "page0page.h" #include "buf0types.h" #ifdef UNIV_DEBUG /*********************************************************//** Gets pointer to the page frame where the cursor is positioned. @return page */ UNIV_INLINE page_t* page_cur_get_page( /*==============*/ page_cur_t* cur) /*!< in: page cursor */ { ut_ad(cur); ut_ad(page_align(cur->rec) == cur->block->frame); return(page_align(cur->rec)); } /*********************************************************//** Gets pointer to the buffer block where the cursor is positioned. @return page */ UNIV_INLINE buf_block_t* page_cur_get_block( /*===============*/ page_cur_t* cur) /*!< in: page cursor */ { ut_ad(cur); ut_ad(page_align(cur->rec) == cur->block->frame); return(cur->block); } /*********************************************************//** Gets pointer to the page frame where the cursor is positioned. @return page */ UNIV_INLINE page_zip_des_t* page_cur_get_page_zip( /*==================*/ page_cur_t* cur) /*!< in: page cursor */ { return(buf_block_get_page_zip(page_cur_get_block(cur))); } /*********************************************************//** Gets the record where the cursor is positioned. @return record */ UNIV_INLINE rec_t* page_cur_get_rec( /*=============*/ page_cur_t* cur) /*!< in: page cursor */ { ut_ad(cur); ut_ad(page_align(cur->rec) == cur->block->frame); return(cur->rec); } #endif /* UNIV_DEBUG */ /*********************************************************//** Sets the cursor object to point before the first user record on the page. */ UNIV_INLINE void page_cur_set_before_first( /*======================*/ const buf_block_t* block, /*!< in: index page */ page_cur_t* cur) /*!< in: cursor */ { cur->block = (buf_block_t*) block; cur->rec = page_get_infimum_rec(buf_block_get_frame(cur->block)); } /*********************************************************//** Sets the cursor object to point after the last user record on the page. */ UNIV_INLINE void page_cur_set_after_last( /*====================*/ const buf_block_t* block, /*!< in: index page */ page_cur_t* cur) /*!< in: cursor */ { cur->block = (buf_block_t*) block; cur->rec = page_get_supremum_rec(buf_block_get_frame(cur->block)); } /*********************************************************//** Returns TRUE if the cursor is before first user record on page. @return TRUE if at start */ UNIV_INLINE ibool page_cur_is_before_first( /*=====================*/ const page_cur_t* cur) /*!< in: cursor */ { ut_ad(cur); ut_ad(page_align(cur->rec) == cur->block->frame); return(page_rec_is_infimum(cur->rec)); } /*********************************************************//** Returns TRUE if the cursor is after last user record. @return TRUE if at end */ UNIV_INLINE ibool page_cur_is_after_last( /*===================*/ const page_cur_t* cur) /*!< in: cursor */ { ut_ad(cur); ut_ad(page_align(cur->rec) == cur->block->frame); return(page_rec_is_supremum(cur->rec)); } /**********************************************************//** Positions the cursor on the given record. */ UNIV_INLINE void page_cur_position( /*==============*/ const rec_t* rec, /*!< in: record on a page */ const buf_block_t* block, /*!< in: buffer block containing the record */ page_cur_t* cur) /*!< out: page cursor */ { ut_ad(rec && block && cur); ut_ad(page_align(rec) == block->frame); cur->rec = (rec_t*) rec; cur->block = (buf_block_t*) block; } /**********************************************************//** Invalidates a page cursor by setting the record pointer NULL. */ UNIV_INLINE void page_cur_invalidate( /*================*/ page_cur_t* cur) /*!< out: page cursor */ { ut_ad(cur); cur->rec = NULL; cur->block = NULL; } /**********************************************************//** Moves the cursor to the next record on page. */ UNIV_INLINE void page_cur_move_to_next( /*==================*/ page_cur_t* cur) /*!< in/out: cursor; must not be after last */ { ut_ad(!page_cur_is_after_last(cur)); cur->rec = page_rec_get_next(cur->rec); } /**********************************************************//** Moves the cursor to the previous record on page. */ UNIV_INLINE void page_cur_move_to_prev( /*==================*/ page_cur_t* cur) /*!< in/out: page cursor, not before first */ { ut_ad(!page_cur_is_before_first(cur)); cur->rec = page_rec_get_prev(cur->rec); } #ifndef UNIV_HOTBACKUP /****************************************************************//** Searches the right position for a page cursor. @return number of matched fields on the left */ UNIV_INLINE ulint page_cur_search( /*============*/ const buf_block_t* block, /*!< in: buffer block */ const dict_index_t* dict_index, /*!< in: record descriptor */ const dtuple_t* tuple, /*!< in: data tuple */ ulint mode, /*!< in: PAGE_CUR_L, PAGE_CUR_LE, PAGE_CUR_G, or PAGE_CUR_GE */ page_cur_t* cursor) /*!< out: page cursor */ { ulint low_matched_fields = 0; ulint low_matched_bytes = 0; ulint up_matched_fields = 0; ulint up_matched_bytes = 0; ut_ad(dtuple_check_typed(tuple)); page_cur_search_with_match(block, dict_index, tuple, mode, &up_matched_fields, &up_matched_bytes, &low_matched_fields, &low_matched_bytes, cursor); return(low_matched_fields); } /***********************************************************//** Inserts a record next to page cursor. Returns pointer to inserted record if succeed, i.e., enough space available, NULL otherwise. The cursor stays at the same logical position, but the physical position may change if it is pointing to a compressed page that was reorganized. @return pointer to record if succeed, NULL otherwise */ UNIV_INLINE rec_t* page_cur_tuple_insert( /*==================*/ page_cur_t* cursor, /*!< in/out: a page cursor */ const dtuple_t* tuple, /*!< in: pointer to a data tuple */ dict_index_t* dict_index, /*!< in: record descriptor */ ulint n_ext, /*!< in: number of externally stored columns */ mtr_t* mtr) /*!< in: mini-transaction handle, or NULL */ { mem_heap_t* heap; ulint* offsets; ulint size = rec_get_converted_size(dict_index, tuple, n_ext); rec_t* rec; heap = mem_heap_create(size + (4 + REC_OFFS_HEADER_SIZE + dtuple_get_n_fields(tuple)) * sizeof *offsets); rec = rec_convert_dtuple_to_rec((byte*) mem_heap_alloc(heap, size), dict_index, tuple, n_ext); offsets = rec_get_offsets(rec, dict_index, NULL, ULINT_UNDEFINED, &heap); #ifdef WITH_ZIP if (buf_block_get_page_zip(cursor->block)) { rec = page_cur_insert_rec_zip(&cursor->rec, cursor->block, dict_index, rec, offsets, mtr); } else #endif /* WITH_ZIP */ { rec = page_cur_insert_rec_low(cursor->rec, dict_index, rec, offsets, mtr); } mem_heap_free(heap); return(rec); } #endif /* !UNIV_HOTBACKUP */ /***********************************************************//** Inserts a record next to page cursor. Returns pointer to inserted record if succeed, i.e., enough space available, NULL otherwise. The cursor stays at the same logical position, but the physical position may change if it is pointing to a compressed page that was reorganized. @return pointer to record if succeed, NULL otherwise */ UNIV_INLINE rec_t* page_cur_rec_insert( /*================*/ page_cur_t* cursor, /*!< in/out: a page cursor */ const rec_t* rec, /*!< in: record to insert */ dict_index_t* dict_index, /*!< in: record descriptor */ ulint* offsets,/*!< in/out: rec_get_offsets(rec, index) */ mtr_t* mtr) /*!< in: mini-transaction handle, or NULL */ { #ifdef WITH_ZIP if (buf_block_get_page_zip(cursor->block)) { return(page_cur_insert_rec_zip(&cursor->rec, cursor->block, dict_index, rec, offsets, mtr)); } else #endif /* WITH_ZIP */ { return(page_cur_insert_rec_low(cursor->rec, dict_index, rec, offsets, mtr)); } } haildb-2.3.2/include/lock0types.h0000644000175000017500000000305311513177357017535 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/lock0types.h The transaction lock system global types Created 5/7/1996 Heikki Tuuri *******************************************************/ #ifndef lock0types_h #define lock0types_h #define lock_t ib_lock_t typedef struct lock_struct lock_t; typedef struct lock_sys_struct lock_sys_t; /* Basic lock modes */ enum lock_mode { LOCK_IS = 0, /* intention shared */ LOCK_IX, /* intention exclusive */ LOCK_S, /* shared */ LOCK_X, /* exclusive */ LOCK_AUTO_INC, /* locks the auto-inc counter of a table in an exclusive mode */ LOCK_NONE, /* this is used elsewhere to note consistent read */ LOCK_NUM = LOCK_NONE/* number of lock modes */ }; #endif haildb-2.3.2/include/dict0load.ic0000644000175000017500000000211311513177357017443 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/dict0load.ic Loads to the memory cache database object definitions from dictionary tables Created 4/24/1996 Heikki Tuuri *******************************************************/ haildb-2.3.2/include/fsp0fsp.h0000644000175000017500000003357211513177357017032 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/fsp0fsp.h File space management Created 12/18/1995 Heikki Tuuri *******************************************************/ #ifndef fsp0fsp_h #define fsp0fsp_h #include "univ.i" #include "mtr0mtr.h" #include "fut0lst.h" #include "ut0byte.h" #include "page0types.h" #include "fsp0types.h" /**********************************************************************//** Initializes the file space system. */ UNIV_INTERN void fsp_init(void); /*==========*/ /**********************************************************************//** Gets the current free limit of the system tablespace. The free limit means the place of the first page which has never been put to the free list for allocation. The space above that address is initialized to zero. Sets also the global variable log_fsp_current_free_limit. @return free limit in megabytes */ UNIV_INTERN ulint fsp_header_get_free_limit(void); /*===========================*/ /**********************************************************************//** Gets the size of the system tablespace from the tablespace header. If we do not have an auto-extending data file, this should be equal to the size of the data files. If there is an auto-extending data file, this can be smaller. @return size in pages */ UNIV_INTERN ulint fsp_header_get_tablespace_size(void); /*================================*/ /**********************************************************************//** Reads the file space size stored in the header page. @return tablespace size stored in the space header */ UNIV_INTERN ulint fsp_get_size_low( /*=============*/ page_t* page); /*!< in: header page (page 0 in the tablespace) */ /**********************************************************************//** Reads the space id from the first page of a tablespace. @return space id, ULINT UNDEFINED if error */ UNIV_INTERN ulint fsp_header_get_space_id( /*====================*/ const page_t* page); /*!< in: first page of a tablespace */ /**********************************************************************//** Reads the space flags from the first page of a tablespace. @return flags */ UNIV_INTERN ulint fsp_header_get_flags( /*=================*/ const page_t* page); /*!< in: first page of a tablespace */ /**********************************************************************//** Reads the compressed page size from the first page of a tablespace. @return compressed page size in bytes, or 0 if uncompressed */ UNIV_INTERN ulint fsp_header_get_zip_size( /*====================*/ const page_t* page); /*!< in: first page of a tablespace */ /**********************************************************************//** Writes the space id and compressed page size to a tablespace header. This function is used past the buffer pool when we in fil0fil.c create a new single-table tablespace. */ UNIV_INTERN void fsp_header_init_fields( /*===================*/ page_t* page, /*!< in/out: first page in the space */ ulint space_id, /*!< in: space id */ ulint flags); /*!< in: tablespace flags (FSP_SPACE_FLAGS): 0, or table->flags if newer than COMPACT */ /**********************************************************************//** Initializes the space header of a new created space and creates also the insert buffer tree root if space == 0. */ UNIV_INTERN void fsp_header_init( /*============*/ ulint space, /*!< in: space id */ ulint size, /*!< in: current size in blocks */ mtr_t* mtr); /*!< in: mini-transaction handle */ /**********************************************************************//** Increases the space size field of a space. */ UNIV_INTERN void fsp_header_inc_size( /*================*/ ulint space, /*!< in: space id */ ulint size_inc,/*!< in: size increment in pages */ mtr_t* mtr); /*!< in: mini-transaction handle */ /**********************************************************************//** Creates a new segment. @return the block where the segment header is placed, x-latched, NULL if could not create segment because of lack of space */ UNIV_INTERN buf_block_t* fseg_create( /*========*/ ulint space, /*!< in: space id */ ulint page, /*!< in: page where the segment header is placed: if this is != 0, the page must belong to another segment, if this is 0, a new page will be allocated and it will belong to the created segment */ ulint byte_offset, /*!< in: byte offset of the created segment header on the page */ mtr_t* mtr); /*!< in: mtr */ /**********************************************************************//** Creates a new segment. @return the block where the segment header is placed, x-latched, NULL if could not create segment because of lack of space */ UNIV_INTERN buf_block_t* fseg_create_general( /*================*/ ulint space, /*!< in: space id */ ulint page, /*!< in: page where the segment header is placed: if this is != 0, the page must belong to another segment, if this is 0, a new page will be allocated and it will belong to the created segment */ ulint byte_offset, /*!< in: byte offset of the created segment header on the page */ ibool has_done_reservation, /*!< in: TRUE if the caller has already done the reservation for the pages with fsp_reserve_free_extents (at least 2 extents: one for the inode and the other for the segment) then there is no need to do the check for this individual operation */ mtr_t* mtr); /*!< in: mtr */ /**********************************************************************//** Calculates the number of pages reserved by a segment, and how many pages are currently used. @return number of reserved pages */ UNIV_INTERN ulint fseg_n_reserved_pages( /*==================*/ fseg_header_t* header, /*!< in: segment header */ ulint* used, /*!< out: number of pages used (<= reserved) */ mtr_t* mtr); /*!< in: mtr handle */ /**********************************************************************//** Allocates a single free page from a segment. This function implements the intelligent allocation strategy which tries to minimize file space fragmentation. @return the allocated page offset FIL_NULL if no page could be allocated */ UNIV_INTERN ulint fseg_alloc_free_page( /*=================*/ fseg_header_t* seg_header, /*!< in: segment header */ ulint hint, /*!< in: hint of which page would be desirable */ byte direction, /*!< in: if the new page is needed because of an index page split, and records are inserted there in order, into which direction they go alphabetically: FSP_DOWN, FSP_UP, FSP_NO_DIR */ mtr_t* mtr); /*!< in: mtr handle */ /**********************************************************************//** Allocates a single free page from a segment. This function implements the intelligent allocation strategy which tries to minimize file space fragmentation. @return allocated page offset, FIL_NULL if no page could be allocated */ UNIV_INTERN ulint fseg_alloc_free_page_general( /*=========================*/ fseg_header_t* seg_header,/*!< in: segment header */ ulint hint, /*!< in: hint of which page would be desirable */ byte direction,/*!< in: if the new page is needed because of an index page split, and records are inserted there in order, into which direction they go alphabetically: FSP_DOWN, FSP_UP, FSP_NO_DIR */ ibool has_done_reservation, /*!< in: TRUE if the caller has already done the reservation for the page with fsp_reserve_free_extents, then there is no need to do the check for this individual page */ mtr_t* mtr); /*!< in: mtr handle */ /**********************************************************************//** Reserves free pages from a tablespace. All mini-transactions which may use several pages from the tablespace should call this function beforehand and reserve enough free extents so that they certainly will be able to do their operation, like a B-tree page split, fully. Reservations must be released with function fil_space_release_free_extents! The alloc_type below has the following meaning: FSP_NORMAL means an operation which will probably result in more space usage, like an insert in a B-tree; FSP_UNDO means allocation to undo logs: if we are deleting rows, then this allocation will in the long run result in less space usage (after a purge); FSP_CLEANING means allocation done in a physical record delete (like in a purge) or other cleaning operation which will result in less space usage in the long run. We prefer the latter two types of allocation: when space is scarce, FSP_NORMAL allocations will not succeed, but the latter two allocations will succeed, if possible. The purpose is to avoid dead end where the database is full but the user cannot free any space because these freeing operations temporarily reserve some space. Single-table tablespaces whose size is < 32 pages are a special case. In this function we would liberally reserve several 64 page extents for every page split or merge in a B-tree. But we do not want to waste disk space if the table only occupies < 32 pages. That is why we apply different rules in that special case, just ensuring that there are 3 free pages available. @return TRUE if we were able to make the reservation */ UNIV_INTERN ibool fsp_reserve_free_extents( /*=====================*/ ulint* n_reserved,/*!< out: number of extents actually reserved; if we return TRUE and the tablespace size is < 64 pages, then this can be 0, otherwise it is n_ext */ ulint space, /*!< in: space id */ ulint n_ext, /*!< in: number of extents to reserve */ ulint alloc_type,/*!< in: FSP_NORMAL, FSP_UNDO, or FSP_CLEANING */ mtr_t* mtr); /*!< in: mtr */ /**********************************************************************//** This function should be used to get information on how much we still will be able to insert new data to the database without running out the tablespace. Only free extents are taken into account and we also subtract the safety margin required by the above function fsp_reserve_free_extents. @return available space in kB */ UNIV_INTERN ib_uint64_t fsp_get_available_space_in_free_extents( /*====================================*/ ulint space); /*!< in: space id */ /**********************************************************************//** Frees a single page of a segment. */ UNIV_INTERN void fseg_free_page( /*===========*/ fseg_header_t* seg_header, /*!< in: segment header */ ulint space, /*!< in: space id */ ulint page, /*!< in: page offset */ mtr_t* mtr); /*!< in: mtr handle */ /**********************************************************************//** Frees part of a segment. This function can be used to free a segment by repeatedly calling this function in different mini-transactions. Doing the freeing in a single mini-transaction might result in too big a mini-transaction. @return TRUE if freeing completed */ UNIV_INTERN ibool fseg_free_step( /*===========*/ fseg_header_t* header, /*!< in, own: segment header; NOTE: if the header resides on the first page of the frag list of the segment, this pointer becomes obsolete after the last freeing step */ mtr_t* mtr); /*!< in: mtr */ /**********************************************************************//** Frees part of a segment. Differs from fseg_free_step because this function leaves the header page unfreed. @return TRUE if freeing completed, except the header page */ UNIV_INTERN ibool fseg_free_step_not_header( /*======================*/ fseg_header_t* header, /*!< in: segment header which must reside on the first fragment page of the segment */ mtr_t* mtr); /*!< in: mtr */ /***********************************************************************//** Checks if a page address is an extent descriptor page address. @return TRUE if a descriptor page */ UNIV_INLINE ibool fsp_descr_page( /*===========*/ ulint zip_size,/*!< in: compressed page size in bytes; 0 for uncompressed pages */ ulint page_no);/*!< in: page number */ /***********************************************************//** Parses a redo log record of a file page init. @return end of log record or NULL */ UNIV_INTERN byte* fsp_parse_init_file_page( /*=====================*/ byte* ptr, /*!< in: buffer */ byte* end_ptr, /*!< in: buffer end */ buf_block_t* block); /*!< in: block or NULL */ /*******************************************************************//** Validates the file space system and its segments. @return TRUE if ok */ UNIV_INTERN ibool fsp_validate( /*=========*/ ulint space); /*!< in: space id */ /*******************************************************************//** Prints info of a file space. */ UNIV_INTERN void fsp_print( /*======*/ ulint space); /*!< in: space id */ #ifdef UNIV_DEBUG /*******************************************************************//** Validates a segment. @return TRUE if ok */ UNIV_INTERN ibool fseg_validate( /*==========*/ fseg_header_t* header, /*!< in: segment header */ mtr_t* mtr); /*!< in: mtr */ #endif /* UNIV_DEBUG */ #ifdef UNIV_BTR_PRINT /*******************************************************************//** Writes info of a segment. */ UNIV_INTERN void fseg_print( /*=======*/ fseg_header_t* header, /*!< in: segment header */ mtr_t* mtr); /*!< in: mtr */ #endif /* UNIV_BTR_PRINT */ #ifndef UNIV_NONINL #include "fsp0fsp.ic" #endif #endif haildb-2.3.2/include/row0ins.h0000644000175000017500000001265211513177357017046 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/row0ins.h Insert into a table Created 4/20/1996 Heikki Tuuri *******************************************************/ #ifndef row0ins_h #define row0ins_h #include "univ.i" #include "data0data.h" #include "que0types.h" #include "dict0types.h" #include "trx0types.h" #include "row0types.h" /***************************************************************//** Checks if foreign key constraint fails for an index entry. Sets shared locks which lock either the success or the failure of the constraint. NOTE that the caller must have a shared latch on dict_foreign_key_check_lock. @return DB_SUCCESS, DB_LOCK_WAIT, DB_NO_REFERENCED_ROW, or DB_ROW_IS_REFERENCED */ UNIV_INTERN ulint row_ins_check_foreign_constraint( /*=============================*/ ibool check_ref,/*!< in: TRUE If we want to check that the referenced table is ok, FALSE if we want to check the foreign key table */ dict_foreign_t* foreign,/*!< in: foreign constraint; NOTE that the tables mentioned in it must be in the dictionary cache if they exist at all */ dict_table_t* table, /*!< in: if check_ref is TRUE, then the foreign table, else the referenced table */ dtuple_t* entry, /*!< in: index entry for index */ que_thr_t* thr); /*!< in: query thread */ /*********************************************************************//** Creates an insert node struct. @return own: insert node struct */ UNIV_INTERN ins_node_t* row_ins_node_create( /*================*/ ib_ins_mode_t ins_type, /*!< in: INS_VALUES, ... */ dict_table_t* table, /*!< in: table where to insert */ mem_heap_t* heap); /*!< in: mem heap where created */ /*********************************************************************//** Sets a new row to insert for an INS_DIRECT node. This function is only used if we have constructed the row separately, which is a rare case; this function is quite slow. */ UNIV_INTERN void row_ins_node_set_new_row( /*=====================*/ ins_node_t* node, /*!< in: insert node */ dtuple_t* row); /*!< in: new row (or first row) for the node */ /***************************************************************//** Inserts an index entry to index. Tries first optimistic, then pessimistic descent down the tree. If the entry matches enough to a delete marked record, performs the insert by updating or delete unmarking the delete marked record. @return DB_SUCCESS, DB_LOCK_WAIT, DB_DUPLICATE_KEY, or some other error code */ UNIV_INTERN ulint row_ins_index_entry( /*================*/ dict_index_t* index, /*!< in: index */ dtuple_t* entry, /*!< in: index entry to insert */ ulint n_ext, /*!< in: number of externally stored columns */ ibool foreign,/*!< in: TRUE=check foreign key constraints */ que_thr_t* thr); /*!< in: query thread */ /***********************************************************//** Inserts a row to a table. This is a high-level function used in SQL execution graphs. @return query thread to run next or NULL */ UNIV_INTERN que_thr_t* row_ins_step( /*=========*/ que_thr_t* thr); /*!< in: query thread */ /***********************************************************//** Creates an entry template for each index of a table. */ UNIV_INTERN void row_ins_node_create_entry_list( /*=======================*/ ins_node_t* node); /*!< in: row insert node */ /* Insert node structure */ struct ins_node_struct{ que_common_t common; /*!< node type: QUE_NODE_INSERT */ ib_ins_mode_t ins_type;/* INS_VALUES, INS_SEARCHED, or INS_DIRECT */ dtuple_t* row; /*!< row to insert */ dict_table_t* table; /*!< table where to insert */ sel_node_t* select; /*!< select in searched insert */ que_node_t* values_list;/* list of expressions to evaluate and insert in an INS_VALUES insert */ ulint state; /*!< node execution state */ dict_index_t* index; /*!< NULL, or the next index where the index entry should be inserted */ dtuple_t* entry; /*!< NULL, or entry to insert in the index; after a successful insert of the entry, this should be reset to NULL */ UT_LIST_BASE_NODE_T(dtuple_t) entry_list;/* list of entries, one for each index */ byte* row_id_buf;/* buffer for the row id sys field in row */ trx_id_t trx_id; /*!< trx id or the last trx which executed the node */ byte* trx_id_buf;/* buffer for the trx id sys field in row */ mem_heap_t* entry_sys_heap; /* memory heap used as auxiliary storage; entry_list and sys fields are stored here; if this is NULL, entry list should be created and buffers for sys fields in row allocated */ ulint magic_n; }; #define INS_NODE_MAGIC_N 15849075 #ifndef UNIV_NONINL #include "row0ins.ic" #endif #endif haildb-2.3.2/include/ut0byte.ic0000644000175000017500000001750511513177357017207 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1994, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************************//** @file include/ut0byte.ic Utilities for byte operations Created 5/30/1994 Heikki Tuuri *******************************************************************/ /*******************************************************//** Creates a 64-bit dulint out of two ulints. @return created dulint */ UNIV_INLINE dulint ut_dulint_create( /*=============*/ ulint high, /*!< in: high-order 32 bits */ ulint low) /*!< in: low-order 32 bits */ { dulint res; ut_ad(high <= 0xFFFFFFFF); ut_ad(low <= 0xFFFFFFFF); res.high = high; res.low = low; return(res); } /*******************************************************//** Gets the high-order 32 bits of a dulint. @return 32 bits in ulint */ UNIV_INLINE ulint ut_dulint_get_high( /*===============*/ dulint d) /*!< in: dulint */ { return(d.high); } /*******************************************************//** Gets the low-order 32 bits of a dulint. @return 32 bits in ulint */ UNIV_INLINE ulint ut_dulint_get_low( /*==============*/ dulint d) /*!< in: dulint */ { return(d.low); } /*******************************************************//** Converts a dulint (a struct of 2 ulints) to ib_int64_t, which is a 64-bit integer type. @return value in ib_int64_t type */ UNIV_INLINE ib_int64_t ut_conv_dulint_to_longlong( /*=======================*/ dulint d) /*!< in: dulint */ { return((ib_int64_t)d.low + (((ib_int64_t)d.high) << 32)); } /*******************************************************//** Tests if a dulint is zero. @return TRUE if zero */ UNIV_INLINE ibool ut_dulint_is_zero( /*==============*/ dulint a) /*!< in: dulint */ { if ((a.low == 0) && (a.high == 0)) { return(TRUE); } return(FALSE); } /*******************************************************//** Compares two dulints. @return -1 if a < b, 0 if a == b, 1 if a > b */ UNIV_INLINE int ut_dulint_cmp( /*==========*/ dulint a, /*!< in: dulint */ dulint b) /*!< in: dulint */ { if (a.high > b.high) { return(1); } else if (a.high < b.high) { return(-1); } else if (a.low > b.low) { return(1); } else if (a.low < b.low) { return(-1); } else { return(0); } } /*******************************************************//** Adds a ulint to a dulint. @return sum a + b */ UNIV_INLINE dulint ut_dulint_add( /*==========*/ dulint a, /*!< in: dulint */ ulint b) /*!< in: ulint */ { if (0xFFFFFFFFUL - b >= a.low) { a.low += b; return(a); } a.low = a.low - (0xFFFFFFFFUL - b) - 1; a.high++; return(a); } /*******************************************************//** Subtracts a ulint from a dulint. @return a - b */ UNIV_INLINE dulint ut_dulint_subtract( /*===============*/ dulint a, /*!< in: dulint */ ulint b) /*!< in: ulint, b <= a */ { if (a.low >= b) { a.low -= b; return(a); } b -= a.low + 1; a.low = 0xFFFFFFFFUL - b; ut_ad(a.high > 0); a.high--; return(a); } /********************************************************//** Rounds a dulint downward to a multiple of a power of 2. @return rounded value */ UNIV_INLINE dulint ut_dulint_align_down( /*=================*/ dulint n, /*!< in: number to be rounded */ ulint align_no) /*!< in: align by this number which must be a power of 2 */ { ulint low, high; ut_ad(align_no > 0); ut_ad(((align_no - 1) & align_no) == 0); low = ut_dulint_get_low(n); high = ut_dulint_get_high(n); low = low & ~(align_no - 1); return(ut_dulint_create(high, low)); } /********************************************************//** Rounds a dulint upward to a multiple of a power of 2. @return rounded value */ UNIV_INLINE dulint ut_dulint_align_up( /*===============*/ dulint n, /*!< in: number to be rounded */ ulint align_no) /*!< in: align by this number which must be a power of 2 */ { return(ut_dulint_align_down(ut_dulint_add(n, align_no - 1), align_no)); } /********************************************************//** Rounds ib_uint64_t downward to a multiple of a power of 2. @return rounded value */ UNIV_INLINE ib_uint64_t ut_uint64_align_down( /*=================*/ ib_uint64_t n, /*!< in: number to be rounded */ ulint align_no) /*!< in: align by this number which must be a power of 2 */ { ut_ad(align_no > 0); ut_ad(ut_is_2pow(align_no)); return(n & ~((ib_uint64_t) align_no - 1)); } /********************************************************//** Rounds ib_uint64_t upward to a multiple of a power of 2. @return rounded value */ UNIV_INLINE ib_uint64_t ut_uint64_align_up( /*===============*/ ib_uint64_t n, /*!< in: number to be rounded */ ulint align_no) /*!< in: align by this number which must be a power of 2 */ { ib_uint64_t align_1 = (ib_uint64_t) align_no - 1; ut_ad(align_no > 0); ut_ad(ut_is_2pow(align_no)); return((n + align_1) & ~align_1); } /*********************************************************//** The following function rounds up a pointer to the nearest aligned address. @return aligned pointer */ UNIV_INLINE void* ut_align( /*=====*/ const void* ptr, /*!< in: pointer */ ulint align_no) /*!< in: align by this number */ { ut_ad(align_no > 0); ut_ad(((align_no - 1) & align_no) == 0); ut_ad(ptr); ut_ad(sizeof(void*) == sizeof(ulint)); return((void*)((((ulint)ptr) + align_no - 1) & ~(align_no - 1))); } /*********************************************************//** The following function rounds down a pointer to the nearest aligned address. @return aligned pointer */ UNIV_INLINE void* ut_align_down( /*==========*/ const void* ptr, /*!< in: pointer */ ulint align_no) /*!< in: align by this number */ { ut_ad(align_no > 0); ut_ad(((align_no - 1) & align_no) == 0); ut_ad(ptr); ut_ad(sizeof(void*) == sizeof(ulint)); return((void*)((((ulint)ptr)) & ~(align_no - 1))); } /*********************************************************//** The following function computes the offset of a pointer from the nearest aligned address. @return distance from aligned pointer */ UNIV_INLINE ulint ut_align_offset( /*============*/ const void* ptr, /*!< in: pointer */ ulint align_no) /*!< in: align by this number */ { ut_ad(align_no > 0); ut_ad(((align_no - 1) & align_no) == 0); ut_ad(ptr); ut_ad(sizeof(void*) == sizeof(ulint)); return(((ulint)ptr) & (align_no - 1)); } /*****************************************************************//** Gets the nth bit of a ulint. @return TRUE if nth bit is 1; 0th bit is defined to be the least significant */ UNIV_INLINE ibool ut_bit_get_nth( /*===========*/ ulint a, /*!< in: ulint */ ulint n) /*!< in: nth bit requested */ { ut_ad(n < 8 * sizeof(ulint)); #if TRUE != 1 # error "TRUE != 1" #endif return(1 & (a >> n)); } /*****************************************************************//** Sets the nth bit of a ulint. @return the ulint with the bit set as requested */ UNIV_INLINE ulint ut_bit_set_nth( /*===========*/ ulint a, /*!< in: ulint */ ulint n, /*!< in: nth bit requested */ ibool val) /*!< in: value for the bit to set */ { ut_ad(n < 8 * sizeof(ulint)); #if TRUE != 1 # error "TRUE != 1" #endif if (val) { return(((ulint) 1 << n) | a); } else { return(~((ulint) 1 << n) & a); } } haildb-2.3.2/include/pars0pars.ic0000644000175000017500000000201111513177357017510 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/pars0pars.ic SQL parser Created 11/19/1996 Heikki Tuuri *******************************************************/ haildb-2.3.2/include/trx0types.h0000644000175000017500000000727311513177357017432 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2010, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/trx0types.h Transaction system global type definitions Created 3/26/1996 Heikki Tuuri *******************************************************/ #ifndef trx0types_h #define trx0types_h #include "ut0byte.h" /** prepare trx_t::id for being printed via printf(3) */ #define TRX_ID_PREP_PRINTF(id) (ib_uint64_t) ut_conv_dulint_to_longlong(id) /** printf(3) format used for printing TRX_ID_PRINTF_PREP() */ #define TRX_ID_FMT "%llX" /** maximum length that a formatted trx_t::id could take, not including the terminating NUL character. */ #define TRX_ID_MAX_LEN 17 /** Memory objects */ /* @{ */ /** Transaction */ typedef struct trx_struct trx_t; /** Transaction system */ typedef struct trx_sys_struct trx_sys_t; /** Doublewrite information */ typedef struct trx_doublewrite_struct trx_doublewrite_t; /** Signal */ typedef struct trx_sig_struct trx_sig_t; /** Rollback segment */ typedef struct trx_rseg_struct trx_rseg_t; /** Transaction undo log */ typedef struct trx_undo_struct trx_undo_t; /** Array of undo numbers of undo records being rolled back or purged */ typedef struct trx_undo_arr_struct trx_undo_arr_t; /** A cell of trx_undo_arr_t */ typedef struct trx_undo_inf_struct trx_undo_inf_t; /** The control structure used in the purge operation */ typedef struct trx_purge_struct trx_purge_t; /** Rollback command node in a query graph */ typedef struct roll_node_struct roll_node_t; /** Commit command node in a query graph */ typedef struct commit_node_struct commit_node_t; /** SAVEPOINT command node in a query graph */ typedef struct trx_named_savept_struct trx_named_savept_t; /* @} */ /** Rollback contexts */ enum trx_rb_ctx { RB_NONE = 0, /*!< no rollback */ RB_NORMAL, /*!< normal rollback */ RB_RECOVERY_PURGE_REC, /*!< rolling back an incomplete transaction, in crash recovery, rolling back an INSERT that was performed by updating a delete-marked record; if the delete-marked record no longer exists in an active read view, it will be purged */ RB_RECOVERY /*!< rolling back an incomplete transaction, in crash recovery */ }; /** Transaction identifier (DB_TRX_ID, DATA_TRX_ID) */ typedef dulint trx_id_t; /** Rollback pointer (DB_ROLL_PTR, DATA_ROLL_PTR) */ typedef dulint roll_ptr_t; /** Undo number */ typedef dulint undo_no_t; /** Transaction savepoint */ typedef struct trx_savept_struct trx_savept_t; /** Transaction savepoint */ struct trx_savept_struct{ undo_no_t least_undo_no; /*!< least undo number to undo */ }; /** File objects */ /* @{ */ /** Transaction system header */ typedef byte trx_sysf_t; /** Rollback segment header */ typedef byte trx_rsegf_t; /** Undo segment header */ typedef byte trx_usegf_t; /** Undo log header */ typedef byte trx_ulogf_t; /** Undo log page header */ typedef byte trx_upagef_t; /** Undo log record */ typedef byte trx_undo_rec_t; /* @} */ #endif haildb-2.3.2/include/fut0fut.ic0000644000175000017500000000366411513177357017211 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /******************************************************************//** @file include/fut0fut.ic File-based utilities Created 12/13/1995 Heikki Tuuri ***********************************************************************/ #include "sync0rw.h" #include "buf0buf.h" /********************************************************************//** Gets a pointer to a file address and latches the page. @return pointer to a byte in a frame; the file page in the frame is bufferfixed and latched */ UNIV_INLINE byte* fut_get_ptr( /*========*/ ulint space, /*!< in: space id */ ulint zip_size,/*!< in: compressed page size in bytes or 0 for uncompressed pages */ fil_addr_t addr, /*!< in: file address */ ulint rw_latch, /*!< in: RW_S_LATCH, RW_X_LATCH */ mtr_t* mtr) /*!< in: mtr handle */ { buf_block_t* block; byte* ptr; ut_ad(addr.boffset < UNIV_PAGE_SIZE); ut_ad((rw_latch == RW_S_LATCH) || (rw_latch == RW_X_LATCH)); block = buf_page_get(space, zip_size, addr.page, rw_latch, mtr); ptr = buf_block_get_frame(block) + addr.boffset; buf_block_dbg_add_level(block, SYNC_NO_ORDER_CHECK); return(ptr); } haildb-2.3.2/include/mem0dbg.h0000644000175000017500000001002111513177357016744 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1994, 2010, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/mem0dbg.h The memory management: the debug code. This is not a compilation module, but is included in mem0mem.* ! Created 6/9/1994 Heikki Tuuri *******************************************************/ /* In the debug version each allocated field is surrounded with check fields whose sizes are given below */ #ifdef UNIV_MEM_DEBUG # ifndef UNIV_HOTBACKUP /* The mutex which protects in the debug version the hash table containing the list of live memory heaps, and also the global variables in mem0dbg.c. */ extern mutex_t mem_hash_mutex; # endif /* !UNIV_HOTBACKUP */ #define MEM_FIELD_HEADER_SIZE ut_calc_align(2 * sizeof(ulint),\ UNIV_MEM_ALIGNMENT) #define MEM_FIELD_TRAILER_SIZE sizeof(ulint) #else #define MEM_FIELD_HEADER_SIZE 0 #endif /* Space needed when allocating for a user a field of length N. The space is allocated only in multiples of UNIV_MEM_ALIGNMENT. In the debug version there are also check fields at the both ends of the field. */ #ifdef UNIV_MEM_DEBUG #define MEM_SPACE_NEEDED(N) ut_calc_align((N) + MEM_FIELD_HEADER_SIZE\ + MEM_FIELD_TRAILER_SIZE, UNIV_MEM_ALIGNMENT) #else #define MEM_SPACE_NEEDED(N) ut_calc_align((N), UNIV_MEM_ALIGNMENT) #endif #if defined UNIV_MEM_DEBUG || defined UNIV_DEBUG /***************************************************************//** Checks a memory heap for consistency and prints the contents if requested. Outputs the sum of sizes of buffers given to the user (only in the debug version), the physical size of the heap and the number of blocks in the heap. In case of error returns 0 as sizes and number of blocks. */ UNIV_INTERN void mem_heap_validate_or_print( /*=======================*/ mem_heap_t* heap, /*!< in: memory heap */ byte* top, /*!< in: calculate and validate only until this top pointer in the heap is reached, if this pointer is NULL, ignored */ ibool print, /*!< in: if TRUE, prints the contents of the heap; works only in the debug version */ ibool* error, /*!< out: TRUE if error */ ulint* us_size,/*!< out: allocated memory (for the user) in the heap, if a NULL pointer is passed as this argument, it is ignored; in the non-debug version this is always -1 */ ulint* ph_size,/*!< out: physical size of the heap, if a NULL pointer is passed as this argument, it is ignored */ ulint* n_blocks); /*!< out: number of blocks in the heap, if a NULL pointer is passed as this argument, it is ignored */ /**************************************************************//** Validates the contents of a memory heap. @return TRUE if ok */ UNIV_INTERN ibool mem_heap_validate( /*==============*/ mem_heap_t* heap); /*!< in: memory heap */ #endif /* UNIV_MEM_DEBUG || UNIV_DEBUG */ #ifdef UNIV_DEBUG /**************************************************************//** Checks that an object is a memory heap (or a block of it) @return TRUE if ok */ UNIV_INTERN ibool mem_heap_check( /*===========*/ mem_heap_t* heap); /*!< in: memory heap */ #endif /* UNIV_DEBUG */ /************************************************************//** Validates the dynamic memory @return TRUE if ok */ UNIV_INTERN ibool mem_validate(void); /*===============*/ haildb-2.3.2/include/os0file.h0000644000175000017500000007201411513177357017004 0ustar00pcrewspcrews00000000000000/*********************************************************************** Copyright (c) 1995, 2010, Innobase Oy. All Rights Reserved. Copyright (c) 2009, Percona Inc. Portions of this file contain modifications contributed and copyrighted by Percona Inc.. Those modifications are gratefully acknowledged and are described briefly in the InnoDB documentation. The contributions by Percona Inc. are incorporated with their permission, and subject to the conditions contained in the file COPYING.Percona. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ***********************************************************************/ /**************************************************//** @file include/os0file.h The interface to the operating system file io Created 10/21/1995 Heikki Tuuri *******************************************************/ #ifndef os0file_h #define os0file_h #include "univ.i" #ifndef __WIN__ #include #include #include #endif /** File node of a tablespace or the log data space */ typedef struct fil_node_struct fil_node_t; #ifdef UNIV_DO_FLUSH extern ibool os_do_not_call_flush_at_each_write; #endif /* UNIV_DO_FLUSH */ extern ibool os_has_said_disk_full; /** Flag: enable debug printout for asynchronous i/o */ extern ibool os_aio_print_debug; /** Number of pending os_file_pread() operations */ extern ulint os_file_n_pending_preads; /** Number of pending os_file_pwrite() operations */ extern ulint os_file_n_pending_pwrites; /** Number of pending read operations */ extern ulint os_n_pending_reads; /** Number of pending write operations */ extern ulint os_n_pending_writes; #ifdef __WIN__ /** We define always WIN_ASYNC_IO, and check at run-time whether the OS actually supports it: Win 95 does not, NT does. */ #define WIN_ASYNC_IO /** Use unbuffered I/O */ #define UNIV_NON_BUFFERED_IO #endif #ifdef __WIN__ /** File handle */ #define os_file_t HANDLE /** Convert a C file descriptor to a native file handle @param fd file descriptor @return native file handle */ #define OS_FILE_FROM_FD(fd) (HANDLE) _get_osfhandle(fd) #else /** File handle */ typedef int os_file_t; /** Convert a C file descriptor to a native file handle @param fd file descriptor @return native file handle */ #define OS_FILE_FROM_FD(fd) fd #endif /** Umask for creating files */ extern ulint os_innodb_umask; /** If this flag is TRUE, then we will use the native aio of the OS (provided we compiled Innobase with it in), otherwise we will use simulated aio we build below with threads */ extern ibool os_aio_use_native_aio; /** The next value should be smaller or equal to the smallest sector size used on any disk. A log block is required to be a portion of disk which is written so that if the start and the end of a block get written to disk, then the whole block gets written. This should be true even in most cases of a crash: if this fails for a log block, then it is equivalent to a media failure in the log. */ #define OS_FILE_LOG_BLOCK_SIZE 512 /** Options for file_create @{ */ #define OS_FILE_OPEN 51 #define OS_FILE_CREATE 52 #define OS_FILE_OVERWRITE 53 #define OS_FILE_OPEN_RAW 54 #define OS_FILE_CREATE_PATH 55 #define OS_FILE_OPEN_RETRY 56 /* for os_file_create() on the first ibdata file */ #define OS_FILE_READ_ONLY 333 #define OS_FILE_READ_WRITE 444 #define OS_FILE_READ_ALLOW_DELETE 555 /* for ibbackup */ /* Options for file_create */ #define OS_FILE_AIO 61 #define OS_FILE_NORMAL 62 /* @} */ /** Types for file create @{ */ #define OS_DATA_FILE 100 #define OS_LOG_FILE 101 /* @} */ /** Error codes from os_file_get_last_error @{ */ #define OS_FILE_NOT_FOUND 71 #define OS_FILE_DISK_FULL 72 #define OS_FILE_ALREADY_EXISTS 73 #define OS_FILE_PATH_ERROR 74 #define OS_FILE_AIO_RESOURCES_RESERVED 75 /* wait for OS aio resources to become available again */ #define OS_FILE_SHARING_VIOLATION 76 #define OS_FILE_ERROR_NOT_SPECIFIED 77 #define OS_FILE_INSUFFICIENT_RESOURCE 78 #define OS_FILE_OPERATION_ABORTED 79 /* @} */ /** Types for aio operations @{ */ #define OS_FILE_READ 10 #define OS_FILE_WRITE 11 #define OS_FILE_LOG 256 /* This can be ORed to type */ /* @} */ #define OS_AIO_N_PENDING_IOS_PER_THREAD 32 /*!< Win NT does not allow more than 64 */ /** Modes for aio operations @{ */ #define OS_AIO_NORMAL 21 /*!< Normal asynchronous i/o not for ibuf pages or ibuf bitmap pages */ #define OS_AIO_IBUF 22 /*!< Asynchronous i/o for ibuf pages or ibuf bitmap pages */ #define OS_AIO_LOG 23 /*!< Asynchronous i/o for the log */ #define OS_AIO_SYNC 24 /*!< Asynchronous i/o where the calling thread will itself wait for the i/o to complete, doing also the job of the i/o-handler thread; can be used for any pages, ibuf or non-ibuf. This is used to save CPU time, as we can do with fewer thread switches. Plain synchronous i/o is not as good, because it must serialize the file seek and read or write, causing a bottleneck for parallelism. */ #define OS_AIO_SIMULATED_WAKE_LATER 512 /*!< This can be ORed to mode in the call of os_aio(...), if the caller wants to post several i/o requests in a batch, and only after that wake the i/o-handler thread; this has effect only in simulated aio */ /* @} */ #define OS_WIN31 1 /*!< Microsoft Windows 3.x */ #define OS_WIN95 2 /*!< Microsoft Windows 95 */ #define OS_WINNT 3 /*!< Microsoft Windows NT 3.x */ #define OS_WIN2000 4 /*!< Microsoft Windows 2000 */ extern ulint os_n_file_reads; extern ulint os_n_file_writes; extern ulint os_n_fsyncs; /* File types for directory entry data type */ enum os_file_type_enum{ OS_FILE_TYPE_UNKNOWN = 0, OS_FILE_TYPE_FILE, /* regular file */ OS_FILE_TYPE_DIR, /* directory */ OS_FILE_TYPE_LINK /* symbolic link */ }; typedef enum os_file_type_enum os_file_type_t; /* Maximum path string length in bytes when referring to tables with in the './databasename/tablename.ibd' path format; we can allocate at least 2 buffers of this size from the thread stack; that is why this should not be made much bigger than 4000 bytes */ #define OS_FILE_MAX_PATH 4000 /* Struct used in fetching information of a file in a directory */ struct os_file_stat_struct{ char name[OS_FILE_MAX_PATH]; /*!< path to a file */ os_file_type_t type; /*!< file type */ ib_int64_t size; /*!< file size */ time_t ctime; /*!< creation time */ time_t mtime; /*!< modification time */ time_t atime; /*!< access time */ }; typedef struct os_file_stat_struct os_file_stat_t; #ifdef __WIN__ typedef HANDLE os_file_dir_t; /*!< directory stream */ #else typedef DIR* os_file_dir_t; /*!< directory stream */ #endif /***********************************************************************//** Gets the operating system version. Currently works only on Windows. @return OS_WIN95, OS_WIN31, OS_WINNT, or OS_WIN2000 */ UNIV_INTERN ulint os_get_os_version(void); /*===================*/ #ifndef UNIV_HOTBACKUP /****************************************************************//** Creates the seek mutexes used in positioned reads and writes. */ UNIV_INTERN void os_io_init_simple(void); /*===================*/ /***********************************************************************//** Creates a temporary file. This function is like tmpfile(3), but the temporary file is created in the configured temporary directory. On Netware, this function is like tmpfile(3), because the C run-time library of Netware does not expose the delete-on-close flag. @return temporary file handle, or NULL on error */ UNIV_INTERN FILE* os_file_create_tmpfile(void); /*========================*/ #endif /* !UNIV_HOTBACKUP */ /***********************************************************************//** The os_file_opendir() function opens a directory stream corresponding to the directory named by the dirname argument. The directory stream is positioned at the first entry. In both Unix and Windows we automatically skip the '.' and '..' items at the start of the directory listing. @return directory stream, NULL if error */ UNIV_INTERN os_file_dir_t os_file_opendir( /*============*/ const char* dirname, /*!< in: directory name; it must not contain a trailing '\' or '/' */ ibool error_is_fatal);/*!< in: TRUE if we should treat an error as a fatal error; if we try to open symlinks then we do not wish a fatal error if it happens not to be a directory */ /***********************************************************************//** Closes a directory stream. @return 0 if success, -1 if failure */ UNIV_INTERN int os_file_closedir( /*=============*/ os_file_dir_t dir); /*!< in: directory stream */ /***********************************************************************//** This function returns information of the next file in the directory. We jump over the '.' and '..' entries in the directory. @return 0 if ok, -1 if error, 1 if at the end of the directory */ UNIV_INTERN int os_file_readdir_next_file( /*======================*/ const char* dirname,/*!< in: directory name or path */ os_file_dir_t dir, /*!< in: directory stream */ os_file_stat_t* info); /*!< in/out: buffer where the info is returned */ /*****************************************************************//** This function attempts to create a directory named pathname. The new directory gets default permissions. On Unix, the permissions are (0770 & ~umask). If the directory exists already, nothing is done and the call succeeds, unless the fail_if_exists arguments is true. @return TRUE if call succeeds, FALSE on error */ UNIV_INTERN ibool os_file_create_directory( /*=====================*/ const char* pathname, /*!< in: directory name as null-terminated string */ ibool fail_if_exists);/*!< in: if TRUE, pre-existing directory is treated as an error. */ /****************************************************************//** A simple function to open or create a file. @return own: handle to the file, not defined if error, error number can be retrieved with os_file_get_last_error */ UNIV_INTERN os_file_t os_file_create_simple( /*==================*/ const char* name, /*!< in: name of the file or path as a null-terminated string */ ulint create_mode,/*!< in: OS_FILE_OPEN if an existing file is opened (if does not exist, error), or OS_FILE_CREATE if a new file is created (if exists, error), or OS_FILE_CREATE_PATH if new file (if exists, error) and subdirectories along its path are created (if needed)*/ ulint access_type,/*!< in: OS_FILE_READ_ONLY or OS_FILE_READ_WRITE */ ibool* success);/*!< out: TRUE if succeed, FALSE if error */ /****************************************************************//** A simple function to open or create a file. @return own: handle to the file, not defined if error, error number can be retrieved with os_file_get_last_error */ UNIV_INTERN os_file_t os_file_create_simple_no_error_handling( /*====================================*/ const char* name, /*!< in: name of the file or path as a null-terminated string */ ulint create_mode,/*!< in: OS_FILE_OPEN if an existing file is opened (if does not exist, error), or OS_FILE_CREATE if a new file is created (if exists, error) */ ulint access_type,/*!< in: OS_FILE_READ_ONLY, OS_FILE_READ_WRITE, or OS_FILE_READ_ALLOW_DELETE; the last option is used by a backup program reading the file */ ibool* success);/*!< out: TRUE if succeed, FALSE if error */ /****************************************************************//** Tries to disable OS caching on an opened file descriptor. */ UNIV_INTERN void os_file_set_nocache( /*================*/ int fd, /*!< in: file descriptor to alter */ const char* file_name, /*!< in: file name, used in the diagnostic message */ const char* operation_name);/*!< in: "open" or "create"; used in the diagnostic message */ /****************************************************************//** Opens an existing file or creates a new. @return own: handle to the file, not defined if error, error number can be retrieved with os_file_get_last_error */ UNIV_INTERN os_file_t os_file_create( /*===========*/ const char* name, /*!< in: name of the file or path as a null-terminated string */ ulint create_mode,/*!< in: OS_FILE_OPEN if an existing file is opened (if does not exist, error), or OS_FILE_CREATE if a new file is created (if exists, error), OS_FILE_OVERWRITE if a new file is created or an old overwritten; OS_FILE_OPEN_RAW, if a raw device or disk partition should be opened */ ulint purpose,/*!< in: OS_FILE_AIO, if asynchronous, non-buffered i/o is desired, OS_FILE_NORMAL, if any normal file; NOTE that it also depends on type, os_aio_.. and srv_.. variables whether we really use async i/o or unbuffered i/o: look in the function source code for the exact rules */ ulint type, /*!< in: OS_DATA_FILE or OS_LOG_FILE */ ibool* success);/*!< out: TRUE if succeed, FALSE if error */ /***********************************************************************//** Deletes a file. The file has to be closed before calling this. @return TRUE if success */ UNIV_INTERN ibool os_file_delete( /*===========*/ const char* name); /*!< in: file path as a null-terminated string */ /***********************************************************************//** Deletes a file if it exists. The file has to be closed before calling this. @return TRUE if success */ UNIV_INTERN ibool os_file_delete_if_exists( /*=====================*/ const char* name); /*!< in: file path as a null-terminated string */ /***********************************************************************//** Renames a file (can also move it to another directory). It is safest that the file is closed before calling this function. @return TRUE if success */ UNIV_INTERN ibool os_file_rename( /*===========*/ const char* oldpath, /*!< in: old file path as a null-terminated string */ const char* newpath); /*!< in: new file path */ /***********************************************************************//** Closes a file handle. In case of error, error number can be retrieved with os_file_get_last_error. @return TRUE if success */ UNIV_INTERN ibool os_file_close( /*==========*/ os_file_t file); /*!< in, own: handle to a file */ #ifdef UNIV_HOTBACKUP /***********************************************************************//** Closes a file handle. @return TRUE if success */ UNIV_INTERN ibool os_file_close_no_error_handling( /*============================*/ os_file_t file); /*!< in, own: handle to a file */ #endif /* UNIV_HOTBACKUP */ /***********************************************************************//** Gets a file size. @return TRUE if success */ UNIV_INTERN ibool os_file_get_size( /*=============*/ os_file_t file, /*!< in: handle to a file */ ulint* size, /*!< out: least significant 32 bits of file size */ ulint* size_high);/*!< out: most significant 32 bits of size */ /***********************************************************************//** Gets file size as a 64-bit integer ib_int64_t. @return size in bytes, -1 if error */ UNIV_INTERN ib_int64_t os_file_get_size_as_iblonglong( /*===========================*/ os_file_t file); /*!< in: handle to a file */ /***********************************************************************//** Write the specified number of zeros to a newly created file. @return TRUE if success */ UNIV_INTERN ibool os_file_set_size( /*=============*/ const char* name, /*!< in: name of the file or path as a null-terminated string */ os_file_t file, /*!< in: handle to a file */ ulint size, /*!< in: least significant 32 bits of file size */ ulint size_high);/*!< in: most significant 32 bits of size */ /***********************************************************************//** Truncates a file at its current position. @return TRUE if success */ UNIV_INTERN ibool os_file_set_eof( /*============*/ FILE* file); /*!< in: file to be truncated */ /***********************************************************************//** Flushes the write buffers of a given file to the disk. @return TRUE if success */ UNIV_INTERN ibool os_file_flush( /*==========*/ os_file_t file); /*!< in, own: handle to a file */ /***********************************************************************//** Retrieves the last error number if an error occurs in a file io function. The number should be retrieved before any other OS calls (because they may overwrite the error number). If the number is not known to this program, the OS error number + 100 is returned. @return error number, or OS error number + 100 */ UNIV_INTERN ulint os_file_get_last_error( /*===================*/ ibool report_all_errors); /*!< in: TRUE if we want an error message printed of all errors */ /*******************************************************************//** Requests a synchronous read operation. @return TRUE if request was successful, FALSE if fail */ UNIV_INTERN ibool os_file_read( /*=========*/ os_file_t file, /*!< in: handle to a file */ void* buf, /*!< in: buffer where to read */ ulint offset, /*!< in: least significant 32 bits of file offset where to read */ ulint offset_high,/*!< in: most significant 32 bits of offset */ ulint n); /*!< in: number of bytes to read */ /*******************************************************************//** Rewind file to its start, read at most size - 1 bytes from it to str, and NUL-terminate str. All errors are silently ignored. This function is mostly meant to be used with temporary files. */ UNIV_INTERN void os_file_read_string( /*================*/ FILE* file, /*!< in: file to read from */ char* str, /*!< in: buffer where to read */ ulint size); /*!< in: size of buffer */ /*******************************************************************//** Requests a synchronous positioned read operation. This function does not do any error handling. In case of error it returns FALSE. @return TRUE if request was successful, FALSE if fail */ UNIV_INTERN ibool os_file_read_no_error_handling( /*===========================*/ os_file_t file, /*!< in: handle to a file */ void* buf, /*!< in: buffer where to read */ ulint offset, /*!< in: least significant 32 bits of file offset where to read */ ulint offset_high,/*!< in: most significant 32 bits of offset */ ulint n); /*!< in: number of bytes to read */ /*******************************************************************//** Requests a synchronous write operation. @return TRUE if request was successful, FALSE if fail */ UNIV_INTERN ibool os_file_write( /*==========*/ const char* name, /*!< in: name of the file or path as a null-terminated string */ os_file_t file, /*!< in: handle to a file */ const void* buf, /*!< in: buffer from which to write */ ulint offset, /*!< in: least significant 32 bits of file offset where to write */ ulint offset_high,/*!< in: most significant 32 bits of offset */ ulint n); /*!< in: number of bytes to write */ /*******************************************************************//** Check the existence and type of the given file. @return TRUE if call succeeded */ UNIV_INTERN ibool os_file_status( /*===========*/ const char* path, /*!< in: pathname of the file */ ibool* exists, /*!< out: TRUE if file exists */ os_file_type_t* type); /*!< out: type of the file (if it exists) */ /****************************************************************//** The function os_file_dirname returns a directory component of a null-terminated pathname string. In the usual case, dirname returns the string up to, but not including, the final '/', and basename is the component following the final '/'. Trailing '/' charac­ ters are not counted as part of the pathname. If path does not contain a slash, dirname returns the string ".". Concatenating the string returned by dirname, a "/", and the basename yields a complete pathname. The return value is a copy of the directory component of the pathname. The copy is allocated from heap. It is the caller responsibility to free it after it is no longer needed. The following list of examples (taken from SUSv2) shows the strings returned by dirname and basename for different paths: path dirname basename "/usr/lib" "/usr" "lib" "/usr/" "/" "usr" "usr" "." "usr" "/" "/" "/" "." "." "." ".." "." ".." @return own: directory component of the pathname */ UNIV_INTERN char* os_file_dirname( /*============*/ const char* path); /*!< in: pathname */ /****************************************************************//** Creates all missing subdirectories along the given path. @return TRUE if call succeeded FALSE otherwise */ UNIV_INTERN ibool os_file_create_subdirs_if_needed( /*=============================*/ const char* path); /*!< in: path name */ /*********************************************************************** Initializes the asynchronous io system. Creates one array each for ibuf and log i/o. Also creates one array each for read and write where each array is divided logically into n_read_segs and n_write_segs respectively. The caller must create an i/o handler thread for each segment in these arrays. This function also creates the sync array. No i/o handler thread needs to be created for that */ UNIV_INTERN void os_aio_init( /*========*/ ulint n_per_seg, /*type) # define dfield_get_data(field) ((field)->data) #endif /* UNIV_DEBUG */ /*********************************************************************//** Sets the type struct of SQL data field. */ UNIV_INLINE void dfield_set_type( /*============*/ dfield_t* field, /*!< in: SQL data field */ dtype_t* type); /*!< in: pointer to data type struct */ /*********************************************************************//** Gets length of field data. @return length of data; UNIV_SQL_NULL if SQL null data */ UNIV_INLINE ulint dfield_get_len( /*===========*/ const dfield_t* field); /*!< in: field */ /*********************************************************************//** Sets length in a field. */ UNIV_INLINE void dfield_set_len( /*===========*/ dfield_t* field, /*!< in: field */ ulint len); /*!< in: length or UNIV_SQL_NULL */ /*********************************************************************//** Determines if a field is SQL NULL @return nonzero if SQL null data */ UNIV_INLINE ulint dfield_is_null( /*===========*/ const dfield_t* field); /*!< in: field */ /*********************************************************************//** Determines if a field is externally stored @return nonzero if externally stored */ UNIV_INLINE ulint dfield_is_ext( /*==========*/ const dfield_t* field); /*!< in: field */ /*********************************************************************//** Sets the "external storage" flag */ UNIV_INLINE void dfield_set_ext( /*===========*/ dfield_t* field); /*!< in/out: field */ /*********************************************************************//** Sets pointer to the data and length in a field. */ UNIV_INLINE void dfield_set_data( /*============*/ dfield_t* field, /*!< in: field */ const void* data, /*!< in: data */ ulint len); /*!< in: length or UNIV_SQL_NULL */ /*********************************************************************//** Sets a data field to SQL NULL. */ UNIV_INLINE void dfield_set_null( /*============*/ dfield_t* field); /*!< in/out: field */ /**********************************************************************//** Writes an SQL null field full of zeros. */ UNIV_INLINE void data_write_sql_null( /*================*/ byte* data, /*!< in: pointer to a buffer of size len */ ulint len); /*!< in: SQL null size in bytes */ /*********************************************************************//** Copies the data and len fields. */ UNIV_INLINE void dfield_copy_data( /*=============*/ dfield_t* field1, /*!< out: field to copy to */ const dfield_t* field2);/*!< in: field to copy from */ /*********************************************************************//** Copies a data field to another. */ UNIV_INLINE void dfield_copy( /*========*/ dfield_t* field1, /*!< out: field to copy to */ const dfield_t* field2);/*!< in: field to copy from */ /*********************************************************************//** Copies the data pointed to by a data field. */ UNIV_INLINE void dfield_dup( /*=======*/ dfield_t* field, /*!< in/out: data field */ mem_heap_t* heap); /*!< in: memory heap where allocated */ /*********************************************************************//** Tests if data length and content is equal for two dfields. @return TRUE if equal */ UNIV_INLINE ibool dfield_datas_are_binary_equal( /*==========================*/ const dfield_t* field1, /*!< in: field */ const dfield_t* field2);/*!< in: field */ /*********************************************************************//** Tests if dfield data length and content is equal to the given. @return TRUE if equal */ UNIV_INTERN ibool dfield_data_is_binary_equal( /*========================*/ const dfield_t* field, /*!< in: field */ ulint len, /*!< in: data length or UNIV_SQL_NULL */ const byte* data); /*!< in: data */ /*********************************************************************//** Gets number of fields in a data tuple. @return number of fields */ UNIV_INLINE ulint dtuple_get_n_fields( /*================*/ const dtuple_t* tuple); /*!< in: tuple */ #ifdef UNIV_DEBUG /*********************************************************************//** Gets nth field of a tuple. @return nth field */ UNIV_INLINE dfield_t* dtuple_get_nth_field( /*=================*/ const dtuple_t* tuple, /*!< in: tuple */ ulint n); /*!< in: index of field */ #else /* UNIV_DEBUG */ # define dtuple_get_nth_field(tuple, n) ((tuple)->fields + (n)) #endif /* UNIV_DEBUG */ /*********************************************************************//** Gets info bits in a data tuple. @return info bits */ UNIV_INLINE ulint dtuple_get_info_bits( /*=================*/ const dtuple_t* tuple); /*!< in: tuple */ /*********************************************************************//** Sets info bits in a data tuple. */ UNIV_INLINE void dtuple_set_info_bits( /*=================*/ dtuple_t* tuple, /*!< in: tuple */ ulint info_bits); /*!< in: info bits */ /*********************************************************************//** Gets number of fields used in record comparisons. @return number of fields used in comparisons in rem0cmp.* */ UNIV_INLINE ulint dtuple_get_n_fields_cmp( /*====================*/ const dtuple_t* tuple); /*!< in: tuple */ /*********************************************************************//** Gets number of fields used in record comparisons. */ UNIV_INLINE void dtuple_set_n_fields_cmp( /*====================*/ dtuple_t* tuple, /*!< in: tuple */ ulint n_fields_cmp); /*!< in: number of fields used in comparisons in rem0cmp.* */ /**********************************************************//** Creates a data tuple to a memory heap. The default value for number of fields used in record comparisons for this tuple is n_fields. @return own: created tuple */ UNIV_INLINE dtuple_t* dtuple_create( /*==========*/ mem_heap_t* heap, /*!< in: memory heap where the tuple is created */ ulint n_fields); /*!< in: number of fields */ /**********************************************************//** Wrap data fields in a tuple. The default value for number of fields used in record comparisons for this tuple is n_fields. @return data tuple */ UNIV_INLINE const dtuple_t* dtuple_from_fields( /*===============*/ dtuple_t* tuple, /*!< in: storage for data tuple */ const dfield_t* fields, /*!< in: fields */ ulint n_fields); /*!< in: number of fields */ /*********************************************************************//** Sets number of fields used in a tuple. Normally this is set in dtuple_create, but if you want later to set it smaller, you can use this. */ UNIV_INTERN void dtuple_set_n_fields( /*================*/ dtuple_t* tuple, /*!< in: tuple */ ulint n_fields); /*!< in: number of fields */ /*********************************************************************//** Copies a data tuple to another. This is a shallow copy; if a deep copy is desired, dfield_dup() will have to be invoked on each field. @return own: copy of tuple */ UNIV_INLINE dtuple_t* dtuple_copy( /*========*/ const dtuple_t* tuple, /*!< in: tuple to copy from */ mem_heap_t* heap); /*!< in: memory heap where the tuple is created */ /**********************************************************//** The following function returns the sum of data lengths of a tuple. The space occupied by the field structs or the tuple struct is not counted. @return sum of data lens */ UNIV_INLINE ulint dtuple_get_data_size( /*=================*/ const dtuple_t* tuple, /*!< in: typed data tuple */ ulint comp); /*!< in: nonzero=ROW_FORMAT=COMPACT */ /*********************************************************************//** Computes the number of externally stored fields in a data tuple. @return number of fields */ UNIV_INLINE ulint dtuple_get_n_ext( /*=============*/ const dtuple_t* tuple); /*!< in: tuple */ /************************************************************//** Compare two data tuples, respecting the collation of character fields. @return 1, 0 , -1 if tuple1 is greater, equal, less, respectively, than tuple2 */ UNIV_INTERN int dtuple_coll_cmp( /*============*/ void* cmp_ctx,/*!< in: client compare context */ const dtuple_t* tuple1, /*!< in: tuple 1 */ const dtuple_t* tuple2);/*!< in: tuple 2 */ /************************************************************//** Folds a prefix given as the number of fields of a tuple. @return the folded value */ UNIV_INLINE ulint dtuple_fold( /*========*/ const dtuple_t* tuple, /*!< in: the tuple */ ulint n_fields,/*!< in: number of complete fields to fold */ ulint n_bytes,/*!< in: number of bytes to fold in an incomplete last field */ dulint tree_id)/*!< in: index tree id */ __attribute__((pure)); /*******************************************************************//** Sets types of fields binary in a tuple. */ UNIV_INLINE void dtuple_set_types_binary( /*====================*/ dtuple_t* tuple, /*!< in: data tuple */ ulint n); /*!< in: number of fields to set */ /**********************************************************************//** Checks if a dtuple contains an SQL null value. @return TRUE if some field is SQL null */ UNIV_INLINE ibool dtuple_contains_null( /*=================*/ const dtuple_t* tuple); /*!< in: dtuple */ /**********************************************************//** Checks that a data field is typed. Asserts an error if not. @return TRUE if ok */ UNIV_INTERN ibool dfield_check_typed( /*===============*/ const dfield_t* field); /*!< in: data field */ /**********************************************************//** Checks that a data tuple is typed. Asserts an error if not. @return TRUE if ok */ UNIV_INTERN ibool dtuple_check_typed( /*===============*/ const dtuple_t* tuple); /*!< in: tuple */ /**********************************************************//** Checks that a data tuple is typed. @return TRUE if ok */ UNIV_INTERN ibool dtuple_check_typed_no_assert( /*=========================*/ const dtuple_t* tuple); /*!< in: tuple */ #ifdef UNIV_DEBUG /**********************************************************//** Validates the consistency of a tuple which must be complete, i.e, all fields must have been set. @return TRUE if ok */ UNIV_INTERN ibool dtuple_validate( /*============*/ const dtuple_t* tuple); /*!< in: tuple */ #endif /* UNIV_DEBUG */ /*************************************************************//** Pretty prints a dfield value according to its data type. */ UNIV_INTERN void dfield_print( /*=========*/ const dfield_t* dfield);/*!< in: dfield */ /*************************************************************//** Pretty prints a dfield value according to its data type. Also the hex string is printed if a string contains non-printable characters. */ UNIV_INTERN void dfield_print_also_hex( /*==================*/ const dfield_t* dfield); /*!< in: dfield */ /**********************************************************//** The following function prints the contents of a tuple. */ UNIV_INTERN void dtuple_print( /*=========*/ ib_stream_t ib_stream, /*!< in: output stream */ const dtuple_t* tuple); /*!< in: tuple */ /**************************************************************//** Moves parts of long fields in entry to the big record vector so that the size of tuple drops below the maximum record size allowed in the database. Moves data only from those fields which are not necessary to determine uniquely the insertion place of the tuple in the index. @return own: created big record vector, NULL if we are not able to shorten the entry enough, i.e., if there are too many fixed-length or short fields in entry or the index is clustered */ UNIV_INTERN big_rec_t* dtuple_convert_big_rec( /*===================*/ dict_index_t* index, /*!< in: index */ dtuple_t* entry, /*!< in/out: index entry */ ulint* n_ext); /*!< in/out: number of externally stored columns */ /**************************************************************//** Puts back to entry the data stored in vector. Note that to ensure the fields in entry can accommodate the data, vector must have been created from entry with dtuple_convert_big_rec. */ UNIV_INTERN void dtuple_convert_back_big_rec( /*========================*/ dict_index_t* index, /*!< in: index */ dtuple_t* entry, /*!< in: entry whose data was put to vector */ big_rec_t* vector);/*!< in, own: big rec vector; it is freed in this function */ /**************************************************************//** Frees the memory in a big rec vector. */ UNIV_INLINE void dtuple_big_rec_free( /*================*/ big_rec_t* vector);/*!< in, own: big rec vector; it is freed in this function */ /************************************************************************* Reset dfield variables. */ UNIV_INTERN void dfield_var_init(void); /*=================*/ /*######################################################################*/ /** Structure for an SQL data field */ struct dfield_struct{ void* data; /*!< pointer to data */ unsigned ext:1; /*!< TRUE=externally stored, FALSE=local */ unsigned len:32; /*!< data length; UNIV_SQL_NULL if SQL null */ dtype_t type; /*!< type of data */ }; /** Structure for an SQL data tuple of fields (logical record) */ struct dtuple_struct { ulint info_bits; /*!< info bits of an index record: the default is 0; this field is used if an index record is built from a data tuple */ ulint n_fields; /*!< number of fields in dtuple */ ulint n_fields_cmp; /*!< number of fields which should be used in comparison services of rem0cmp.*; the index search is performed by comparing only these fields, others are ignored; the default value in dtuple creation is the same value as n_fields */ dfield_t* fields; /*!< fields */ UT_LIST_NODE_T(dtuple_t) tuple_list; /*!< data tuples can be linked into a list using this field */ #ifdef UNIV_DEBUG ulint magic_n; /*!< magic number, used in debug assertions */ /** Value of dtuple_struct::magic_n */ # define DATA_TUPLE_MAGIC_N 65478679 #endif /* UNIV_DEBUG */ }; /** A slot for a field in a big rec vector */ typedef struct big_rec_field_struct big_rec_field_t; /** A slot for a field in a big rec vector */ struct big_rec_field_struct { ulint field_no; /*!< field number in record */ ulint len; /*!< stored data length, in bytes */ const void* data; /*!< stored data */ }; /** Storage format for overflow data in a big record, that is, a clustered index record which needs external storage of data fields */ struct big_rec_struct { mem_heap_t* heap; /*!< memory heap from which allocated */ ulint n_fields; /*!< number of stored fields */ big_rec_field_t*fields; /*!< stored fields */ }; #ifndef UNIV_NONINL #include "data0data.ic" #endif #endif haildb-2.3.2/include/srv0srv.ic0000644000175000017500000000201711513177357017230 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/srv0srv.ic Server main program Created 10/4/1995 Heikki Tuuri *******************************************************/ haildb-2.3.2/include/row0merge.h0000644000175000017500000001643211513177357017354 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 2005, 2010, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/row0merge.h Index build routines using a merge sort Created 13/06/2005 Jan Lindstrom *******************************************************/ #ifndef row0merge_h #define row0merge_h #include "univ.i" #include "data0data.h" #include "dict0types.h" #include "trx0types.h" #include "que0types.h" #include "mtr0mtr.h" #include "rem0types.h" #include "rem0rec.h" #include "read0types.h" #include "btr0types.h" #include "lock0types.h" #include "row0types.h" #include "srv0srv.h" /** Index field definition */ struct merge_index_field_struct { ulint prefix_len; /*!< column prefix length, or 0 if indexing the whole column */ const char* field_name; /*!< field name */ }; /** Index field definition */ typedef struct merge_index_field_struct merge_index_field_t; /** Definition of an index being created */ struct merge_index_def_struct { const char* name; /*!< index name */ ulint ind_type; /*!< 0, DICT_UNIQUE, or DICT_CLUSTERED */ ulint n_fields; /*!< number of fields in index */ merge_index_field_t* fields; /*!< field definitions */ }; /** Definition of an index being created */ typedef struct merge_index_def_struct merge_index_def_t; /*********************************************************************//** Sets an exclusive lock on a table, for the duration of creating indexes. @return error code or DB_SUCCESS */ UNIV_INTERN ulint row_merge_lock_table( /*=================*/ trx_t* trx, /*!< in/out: transaction */ dict_table_t* table, /*!< in: table to lock */ enum lock_mode mode); /*!< in: LOCK_X or LOCK_S */ /*********************************************************************//** Drop an index from the InnoDB system tables. The data dictionary must have been locked exclusively by the caller, because the transaction will not be committed. */ UNIV_INTERN void row_merge_drop_index( /*=================*/ dict_index_t* index, /*!< in: index to be removed */ dict_table_t* table, /*!< in: table */ trx_t* trx); /*!< in: transaction handle */ /*********************************************************************//** Drop those indexes which were created before an error occurred when building an index. The data dictionary must have been locked exclusively by the caller, because the transaction will not be committed. */ UNIV_INTERN void row_merge_drop_indexes( /*===================*/ trx_t* trx, /*!< in: transaction */ dict_table_t* table, /*!< in: table containing the indexes */ dict_index_t** index, /*!< in: indexes to drop */ ulint num_created); /*!< in: number of elements in index[] */ /*********************************************************************//** Drop all partially created indexes during crash recovery. */ UNIV_INTERN void row_merge_drop_temp_indexes( /*========================*/ ib_recovery_t recovery); /*!< in: recovery level setting */ /*********************************************************************//** Rename the tables in the data dictionary. The data dictionary must have been locked exclusively by the caller, because the transaction will not be committed. @return error code or DB_SUCCESS */ UNIV_INTERN ulint row_merge_rename_tables( /*====================*/ dict_table_t* old_table, /*!< in/out: old table, renamed to tmp_name */ dict_table_t* new_table, /*!< in/out: new table, renamed to old_table->name */ const char* tmp_name, /*!< in: new name for old_table */ trx_t* trx); /*!< in: transaction handle */ /*********************************************************************//** Create a temporary table for creating a primary key, using the definition of an existing table. @return table, or NULL on error */ UNIV_INTERN dict_table_t* row_merge_create_temporary_table( /*=============================*/ const char* table_name, /*!< in: new table name */ const merge_index_def_t*index_def, /*!< in: the index definition of the primary key */ const dict_table_t* table, /*!< in: old table definition */ trx_t* trx); /*!< in/out: transaction (sets error_state) */ /*********************************************************************//** Rename the temporary indexes in the dictionary to permanent ones. The data dictionary must have been locked exclusively by the caller, because the transaction will not be committed. @return DB_SUCCESS if all OK */ UNIV_INTERN ulint row_merge_rename_indexes( /*=====================*/ trx_t* trx, /*!< in/out: transaction */ dict_table_t* table); /*!< in/out: table with new indexes */ /*********************************************************************//** Create the index and load in to the dictionary. @return index, or NULL on error */ UNIV_INTERN dict_index_t* row_merge_create_index( /*===================*/ trx_t* trx, /*!< in/out: trx (sets error_state) */ dict_table_t* table, /*!< in: the index is on this table */ const merge_index_def_t*index_def); /*!< in: the index definition */ /*********************************************************************//** Check if a transaction can use an index. @return TRUE if index can be used by the transaction else FALSE */ UNIV_INTERN ibool row_merge_is_index_usable( /*======================*/ const trx_t* trx, /*!< in: transaction */ const dict_index_t* index); /*!< in: index to check */ /*********************************************************************//** If there are views that refer to the old table name then we "attach" to the new instance of the table else we drop it immediately. @return DB_SUCCESS or error code */ UNIV_INTERN ulint row_merge_drop_table( /*=================*/ trx_t* trx, /*!< in: transaction */ dict_table_t* table); /*!< in: table instance to drop */ /*********************************************************************//** Build indexes on a table by reading a clustered index, creating a temporary file containing index entries, merge sorting these index entries and inserting sorted index entries to indexes. @return DB_SUCCESS or error code */ UNIV_INTERN ulint row_merge_build_indexes( /*====================*/ trx_t* trx, /*!< in: transaction */ dict_table_t* old_table, /*!< in: table where rows are read from */ dict_table_t* new_table, /*!< in: table where indexes are created; identical to old_table unless creating a PRIMARY KEY */ dict_index_t** indexes, /*!< in: indexes to be created */ ulint n_indexes, /*!< in: size of indexes[] */ table_handle_t table); /*!< in/out: table, for reporting erroneous key value if applicable */ #endif /* row0merge.h */ haildb-2.3.2/include/log0log.h0000644000175000017500000010625711513177357017015 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2010, Innobase Oy. All Rights Reserved. Copyright (c) 2009, Google Inc. Portions of this file contain modifications contributed and copyrighted by Google, Inc. Those modifications are gratefully acknowledged and are described briefly in the InnoDB documentation. The contributions by Google are incorporated with their permission, and subject to the conditions contained in the file COPYING.Google. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/log0log.h Database log Created 12/9/1995 Heikki Tuuri *******************************************************/ #ifndef log0log_h #define log0log_h #include "univ.i" #include "ut0byte.h" #include "ut0lst.h" #ifndef UNIV_HOTBACKUP #include "sync0sync.h" #include "sync0rw.h" #endif /* !UNIV_HOTBACKUP */ #include "srv0srv.h" #include "api0api.h" /** Redo log buffer */ typedef struct log_struct log_t; /** Redo log group */ typedef struct log_group_struct log_group_t; #ifdef UNIV_DEBUG /** Flag: write to log file? */ extern ibool log_do_write; /** Flag: enable debug output when writing to the log? */ extern ibool log_debug_writes; #else /* UNIV_DEBUG */ /** Write to log */ # define log_do_write TRUE #endif /* UNIV_DEBUG */ /** Wait modes for log_write_up_to @{ */ #define LOG_NO_WAIT 91 #define LOG_WAIT_ONE_GROUP 92 #define LOG_WAIT_ALL_GROUPS 93 /* @} */ /** Maximum number of log groups in log_group_struct::checkpoint_buf */ #define LOG_MAX_N_GROUPS 32 #ifndef UNIV_HOTBACKUP /****************************************************************//** Sets the global variable log_fsp_current_free_limit. Also makes a checkpoint, so that we know that the limit has been written to a log checkpoint field on disk. */ UNIV_INTERN void log_fsp_current_free_limit_set_and_checkpoint( /*==========================================*/ ulint limit); /*!< in: limit to set */ #endif /* !UNIV_HOTBACKUP */ /*******************************************************************//** Calculates where in log files we find a specified lsn. @return log file number */ UNIV_INTERN ulint log_calc_where_lsn_is( /*==================*/ ib_int64_t* log_file_offset, /*!< out: offset in that file (including the header) */ ib_uint64_t first_header_lsn, /*!< in: first log file start lsn */ ib_uint64_t lsn, /*!< in: lsn whose position to determine */ ulint n_log_files, /*!< in: total number of log files */ ib_int64_t log_file_size); /*!< in: log file size (including the header) */ #ifndef UNIV_HOTBACKUP /*************************************************************************//** Acquire the log mutex. */ UNIV_INLINE void log_acquire(void); /*=============*/ /************************************************************//** Writes to the log the string given. The log must be released with log_release. @return end lsn of the log record, zero if did not succeed */ UNIV_INLINE ib_uint64_t log_reserve_and_write_fast( /*=======================*/ const void* str, /*!< in: string */ ulint len, /*!< in: string length */ ib_uint64_t* start_lsn);/*!< out: start lsn of the log record */ /***********************************************************************//** Releases the log mutex. */ UNIV_INLINE void log_release(void); /*=============*/ /***********************************************************************//** Checks if there is need for a log buffer flush or a new checkpoint, and does this if yes. Any database operation should call this when it has modified more than about 4 pages. NOTE that this function may only be called when the OS thread owns no synchronization objects except the dictionary mutex. */ UNIV_INLINE void log_free_check(void); /*================*/ /************************************************************//** Opens the log for log_write_low. The log must be closed with log_close and released with log_release. @return start lsn of the log record */ UNIV_INTERN ib_uint64_t log_reserve_and_open( /*=================*/ ulint len); /*!< in: length of data to be catenated */ /************************************************************//** Writes to the log the string given. It is assumed that the caller holds the log mutex. */ UNIV_INTERN void log_write_low( /*==========*/ byte* str, /*!< in: string */ ulint str_len); /*!< in: string length */ /************************************************************//** Closes the log. @return lsn */ UNIV_INTERN ib_uint64_t log_close( /*======*/ ib_recovery_t recovery); /*!< in: recovery flag */ /************************************************************//** Gets the current lsn. @return current lsn */ UNIV_INLINE ib_uint64_t log_get_lsn(void); /*=============*/ /**************************************************************** Gets the log group capacity. It is OK to read the value without holding log_sys->mutex because it is constant. @return log group capacity */ UNIV_INLINE ulint log_get_capacity(void); /*==================*/ /******************************************************//** Initializes the log. */ UNIV_INTERN void innobase_log_init(void); /*====================*/ /******************************************************************//** Inits a log group to the log system. */ UNIV_INTERN void log_group_init( /*===========*/ ulint id, /*!< in: group id */ ulint n_files, /*!< in: number of log files */ ulint file_size, /*!< in: log file size in bytes */ ulint space_id, /*!< in: space id of the file space which contains the log files of this group */ ulint archive_space_id); /*!< in: space id of the file space which contains some archived log files for this group; currently, only for the first log group this is used */ /******************************************************//** Completes an i/o to a log file. */ UNIV_INTERN void log_io_complete( /*============*/ log_group_t* group); /*!< in: log group */ /******************************************************//** This function is called, e.g., when a transaction wants to commit. It checks that the log has been written to the log file up to the last log entry written by the transaction. If there is a flush running, it waits and checks if the flush flushed enough. If not, starts a new flush. */ UNIV_INTERN void log_write_up_to( /*============*/ ib_uint64_t lsn, /*!< in: log sequence number up to which the log should be written, IB_UINT64_T_MAX if not specified */ ulint wait, /*!< in: LOG_NO_WAIT, LOG_WAIT_ONE_GROUP, or LOG_WAIT_ALL_GROUPS */ ibool flush_to_disk); /*!< in: TRUE if we want the written log also to be flushed to disk */ /****************************************************************//** Does a syncronous flush of the log buffer to disk. */ UNIV_INTERN void log_buffer_flush_to_disk(void); /*==========================*/ /****************************************************************//** This functions writes the log buffer to the log file and if 'flush' is set it forces a flush of the log file as well. This is meant to be called from background master thread only as it does not wait for the write (+ possible flush) to finish. */ UNIV_INTERN void log_buffer_sync_in_background( /*==========================*/ ibool flush); /*checkpoint_buf. */ UNIV_INTERN void log_group_read_checkpoint_info( /*===========================*/ log_group_t* group, /*!< in: log group */ ulint field); /*!< in: LOG_CHECKPOINT_1 or LOG_CHECKPOINT_2 */ /*******************************************************************//** Gets info from a checkpoint about a log group. */ UNIV_INTERN void log_checkpoint_get_nth_group_info( /*==============================*/ const byte* buf, /*!< in: buffer containing checkpoint info */ ulint n, /*!< in: nth slot */ ulint* file_no,/*!< out: archived file number */ ulint* offset);/*!< out: archived file offset */ /******************************************************//** Writes checkpoint info to groups. */ UNIV_INTERN void log_groups_write_checkpoint_info(void); /*==================================*/ /********************************************************************//** Starts an archiving operation. @return TRUE if succeed, FALSE if an archiving operation was already running */ UNIV_INTERN ibool log_archive_do( /*===========*/ ibool sync, /*!< in: TRUE if synchronous operation is desired */ ulint* n_bytes);/*!< out: archive log buffer size, 0 if nothing to archive */ /****************************************************************//** Writes the log contents to the archive up to the lsn when this function was called, and stops the archiving. When archiving is started again, the archived log file numbers start from a number one higher, so that the archiving will not write again to the archived log files which exist when this function returns. @return DB_SUCCESS or DB_ERROR */ UNIV_INTERN ulint log_archive_stop(void); /*==================*/ /****************************************************************//** Starts again archiving which has been stopped. @return DB_SUCCESS or DB_ERROR */ UNIV_INTERN ulint log_archive_start(void); /*===================*/ /****************************************************************//** >>>>>>> .merge-right.r5456 Stop archiving the log so that a gap may occur in the archived log files. @return DB_SUCCESS or DB_ERROR */ UNIV_INTERN ulint log_archive_noarchivelog(void); /*==========================*/ /****************************************************************//** Start archiving the log so that a gap may occur in the archived log files. @return DB_SUCCESS or DB_ERROR */ UNIV_INTERN ulint log_archive_archivelog(void); /*========================*/ /******************************************************//** Generates an archived log file name. */ UNIV_INTERN void log_archived_file_name_gen( /*=======================*/ char* buf, /*!< in: buffer where to write */ ulint id, /*!< in: group id */ ulint file_no);/*!< in: file number */ #else /* !UNIV_HOTBACKUP */ /******************************************************//** Writes info to a buffer of a log group when log files are created in backup restoration. */ UNIV_INTERN void log_reset_first_header_and_checkpoint( /*==================================*/ byte* hdr_buf,/*!< in: buffer which will be written to the start of the first log file */ ib_uint64_t start); /*!< in: lsn of the start of the first log file; we pretend that there is a checkpoint at start + LOG_BLOCK_HDR_SIZE */ #endif /* !UNIV_HOTBACKUP */ /********************************************************************//** Checks that there is enough free space in the log to start a new query step. Flushes the log buffer or makes a new checkpoint if necessary. NOTE: this function may only be called if the calling thread owns no synchronization objects! */ UNIV_INTERN void log_check_margins(void); /*===================*/ #ifndef UNIV_HOTBACKUP /******************************************************//** Reads a specified log segment to a buffer. */ UNIV_INTERN void log_group_read_log_seg( /*===================*/ ulint type, /*!< in: LOG_ARCHIVE or LOG_RECOVER */ byte* buf, /*!< in: buffer where to read */ log_group_t* group, /*!< in: log group */ ib_uint64_t start_lsn, /*!< in: read area start */ ib_uint64_t end_lsn); /*!< in: read area end */ /******************************************************//** Writes a buffer to a log file group. */ UNIV_INTERN void log_group_write_buf( /*================*/ log_group_t* group, /*!< in: log group */ byte* buf, /*!< in: buffer */ ulint len, /*!< in: buffer len; must be divisible by OS_FILE_LOG_BLOCK_SIZE */ ib_uint64_t start_lsn, /*!< in: start lsn of the buffer; must be divisible by OS_FILE_LOG_BLOCK_SIZE */ ulint new_data_offset);/*!< in: start offset of new data in buf: this parameter is used to decide if we have to write a new log file header */ /********************************************************//** Sets the field values in group to correspond to a given lsn. For this function to work, the values must already be correctly initialized to correspond to some lsn, for instance, a checkpoint lsn. */ UNIV_INTERN void log_group_set_fields( /*=================*/ log_group_t* group, /*!< in/out: group */ ib_uint64_t lsn); /*!< in: lsn for which the values should be set */ /******************************************************//** Calculates the data capacity of a log group, when the log file headers are not included. @return capacity in bytes */ UNIV_INTERN ulint log_group_get_capacity( /*===================*/ const log_group_t* group); /*!< in: log group */ #endif /* !UNIV_HOTBACKUP */ /************************************************************//** Gets a log block flush bit. @return TRUE if this block was the first to be written in a log flush */ UNIV_INLINE ibool log_block_get_flush_bit( /*====================*/ const byte* log_block); /*!< in: log block */ /************************************************************//** Gets a log block number stored in the header. @return log block number stored in the block header */ UNIV_INLINE ulint log_block_get_hdr_no( /*=================*/ const byte* log_block); /*!< in: log block */ /************************************************************//** Gets a log block data length. @return log block data length measured as a byte offset from the block start */ UNIV_INLINE ulint log_block_get_data_len( /*===================*/ const byte* log_block); /*!< in: log block */ /************************************************************//** Sets the log block data length. */ UNIV_INLINE void log_block_set_data_len( /*===================*/ byte* log_block, /*!< in/out: log block */ ulint len); /*!< in: data length */ /************************************************************//** Calculates the checksum for a log block. @return checksum */ UNIV_INLINE ulint log_block_calc_checksum( /*====================*/ const byte* block); /*!< in: log block */ /************************************************************//** Gets a log block checksum field value. @return checksum */ UNIV_INLINE ulint log_block_get_checksum( /*===================*/ const byte* log_block); /*!< in: log block */ /************************************************************//** Sets a log block checksum field value. */ UNIV_INLINE void log_block_set_checksum( /*===================*/ byte* log_block, /*!< in/out: log block */ ulint checksum); /*!< in: checksum */ /************************************************************//** Gets a log block first mtr log record group offset. @return first mtr log record group byte offset from the block start, 0 if none */ UNIV_INLINE ulint log_block_get_first_rec_group( /*==========================*/ const byte* log_block); /*!< in: log block */ /************************************************************//** Sets the log block first mtr log record group offset. */ UNIV_INLINE void log_block_set_first_rec_group( /*==========================*/ byte* log_block, /*!< in/out: log block */ ulint offset); /*!< in: offset, 0 if none */ /************************************************************//** Gets a log block checkpoint number field (4 lowest bytes). @return checkpoint no (4 lowest bytes) */ UNIV_INLINE ulint log_block_get_checkpoint_no( /*========================*/ const byte* log_block); /*!< in: log block */ /************************************************************//** Initializes a log block in the log buffer. */ UNIV_INLINE void log_block_init( /*===========*/ byte* log_block, /*!< in: pointer to the log buffer */ ib_uint64_t lsn); /*!< in: lsn within the log block */ /************************************************************//** Initializes a log block in the log buffer in the old, < 3.23.52 format, where there was no checksum yet. */ UNIV_INLINE void log_block_init_in_old_format( /*=========================*/ byte* log_block, /*!< in: pointer to the log buffer */ ib_uint64_t lsn); /*!< in: lsn within the log block */ /************************************************************//** Converts a lsn to a log block number. @return log block number, it is > 0 and <= 1G */ UNIV_INLINE ulint log_block_convert_lsn_to_no( /*========================*/ ib_uint64_t lsn); /*!< in: lsn of a byte within the block */ /******************************************************//** Prints info of the log. */ UNIV_INTERN void log_print( /*======*/ ib_stream_t ib_stream); /*!< in: stream where to print */ /******************************************************//** Peeks the current lsn. @return TRUE if success, FALSE if could not get the log system mutex */ UNIV_INTERN ibool log_peek_lsn( /*=========*/ ib_uint64_t* lsn); /*!< out: if returns TRUE, current lsn is here */ /**********************************************************************//** Refreshes the statistics used to print per-second averages. */ UNIV_INTERN void log_refresh_stats(void); /*===================*/ /********************************************************** Shutdown log system but doesn't release all the memory. */ UNIV_INTERN void log_shutdown(void); /*==============*/ /******************************************************************** Reset the variables. */ UNIV_INTERN void log_var_init(void); /*==============*/ /********************************************************** Free the log system data structures. */ UNIV_INTERN void log_mem_free(void); /*===============*/ extern log_t* log_sys; /* Values used as flags */ #define LOG_FLUSH 7652559 #define LOG_CHECKPOINT 78656949 #ifdef UNIV_LOG_ARCHIVE # define LOG_ARCHIVE 11122331 #endif /* UNIV_LOG_ARCHIVE */ #define LOG_RECOVER 98887331 /* The counting of lsn's starts from this value: this must be non-zero */ #define LOG_START_LSN ((ib_uint64_t) (16 * OS_FILE_LOG_BLOCK_SIZE)) #define LOG_BUFFER_SIZE (srv_log_buffer_size * UNIV_PAGE_SIZE) #define LOG_ARCHIVE_BUF_SIZE (srv_log_buffer_size * UNIV_PAGE_SIZE / 4) /* Offsets of a log block header */ #define LOG_BLOCK_HDR_NO 0 /* block number which must be > 0 and is allowed to wrap around at 2G; the highest bit is set to 1 if this is the first log block in a log flush write segment */ #define LOG_BLOCK_FLUSH_BIT_MASK 0x80000000UL /* mask used to get the highest bit in the preceding field */ #define LOG_BLOCK_HDR_DATA_LEN 4 /* number of bytes of log written to this block */ #define LOG_BLOCK_FIRST_REC_GROUP 6 /* offset of the first start of an mtr log record group in this log block, 0 if none; if the value is the same as LOG_BLOCK_HDR_DATA_LEN, it means that the first rec group has not yet been catenated to this log block, but if it will, it will start at this offset; an archive recovery can start parsing the log records starting from this offset in this log block, if value not 0 */ #define LOG_BLOCK_CHECKPOINT_NO 8 /* 4 lower bytes of the value of log_sys->next_checkpoint_no when the log block was last written to: if the block has not yet been written full, this value is only updated before a log buffer flush */ #define LOG_BLOCK_HDR_SIZE 12 /* size of the log block header in bytes */ /* Offsets of a log block trailer from the end of the block */ #define LOG_BLOCK_CHECKSUM 4 /* 4 byte checksum of the log block contents; in InnoDB versions < 3.23.52 this did not contain the checksum but the same value as .._HDR_NO */ #define LOG_BLOCK_TRL_SIZE 4 /* trailer size in bytes */ /* Offsets for a checkpoint field */ #define LOG_CHECKPOINT_NO 0 #define LOG_CHECKPOINT_LSN 8 #define LOG_CHECKPOINT_OFFSET 16 #define LOG_CHECKPOINT_LOG_BUF_SIZE 20 #define LOG_CHECKPOINT_ARCHIVED_LSN 24 #define LOG_CHECKPOINT_GROUP_ARRAY 32 /* For each value smaller than LOG_MAX_N_GROUPS the following 8 bytes: */ #define LOG_CHECKPOINT_ARCHIVED_FILE_NO 0 #define LOG_CHECKPOINT_ARCHIVED_OFFSET 4 #define LOG_CHECKPOINT_ARRAY_END (LOG_CHECKPOINT_GROUP_ARRAY\ + LOG_MAX_N_GROUPS * 8) #define LOG_CHECKPOINT_CHECKSUM_1 LOG_CHECKPOINT_ARRAY_END #define LOG_CHECKPOINT_CHECKSUM_2 (4 + LOG_CHECKPOINT_ARRAY_END) #define LOG_CHECKPOINT_FSP_FREE_LIMIT (8 + LOG_CHECKPOINT_ARRAY_END) /* current fsp free limit in tablespace 0, in units of one megabyte; this information is only used by ibbackup to decide if it can truncate unused ends of non-auto-extending data files in space 0 */ #define LOG_CHECKPOINT_FSP_MAGIC_N (12 + LOG_CHECKPOINT_ARRAY_END) /* this magic number tells if the checkpoint contains the above field: the field was added to InnoDB-3.23.50 */ #define LOG_CHECKPOINT_SIZE (16 + LOG_CHECKPOINT_ARRAY_END) #define LOG_CHECKPOINT_FSP_MAGIC_N_VAL 1441231243 /* Offsets of a log file header */ #define LOG_GROUP_ID 0 /* log group number */ #define LOG_FILE_START_LSN 4 /* lsn of the start of data in this log file */ #define LOG_FILE_NO 12 /* 4-byte archived log file number; this field is only defined in an archived log file */ #define LOG_FILE_WAS_CREATED_BY_HOT_BACKUP 16 /* a 32-byte field which contains the string 'ibbackup' and the creation time if the log file was created by ibbackup --restore; when the application is started for the first time on the restored database, it can print helpful info for the user */ #define LOG_FILE_ARCH_COMPLETED OS_FILE_LOG_BLOCK_SIZE /* this 4-byte field is TRUE when the writing of an archived log file has been completed; this field is only defined in an archived log file */ #define LOG_FILE_END_LSN (OS_FILE_LOG_BLOCK_SIZE + 4) /* lsn where the archived log file at least extends: actually the archived log file may extend to a later lsn, as long as it is within the same log block as this lsn; this field is defined only when an archived log file has been completely written */ #define LOG_CHECKPOINT_1 OS_FILE_LOG_BLOCK_SIZE /* first checkpoint field in the log header; we write alternately to the checkpoint fields when we make new checkpoints; this field is only defined in the first log file of a log group */ #define LOG_CHECKPOINT_2 (3 * OS_FILE_LOG_BLOCK_SIZE) /* second checkpoint field in the log header */ #define LOG_FILE_HDR_SIZE (4 * OS_FILE_LOG_BLOCK_SIZE) #define LOG_GROUP_OK 301 #define LOG_GROUP_CORRUPTED 302 /** Log group consists of a number of log files, each of the same size; a log group is implemented as a space in the sense of the module fil0fil. */ struct log_group_struct{ /* The following fields are protected by log_sys->mutex */ ulint id; /*!< log group id */ ulint n_files; /*!< number of files in the group */ ulint file_size; /*!< individual log file size in bytes, including the log file header */ ulint space_id; /*!< file space which implements the log group */ ulint state; /*!< LOG_GROUP_OK or LOG_GROUP_CORRUPTED */ ib_uint64_t lsn; /*!< lsn used to fix coordinates within the log group */ ulint lsn_offset; /*!< the offset of the above lsn */ ulint n_pending_writes;/*!< number of currently pending flush writes for this log group */ byte** file_header_bufs_ptr;/*!< unaligned buffers */ byte** file_header_bufs;/*!< buffers for each file header in the group */ #ifdef UNIV_LOG_ARCHIVE /*-----------------------------*/ #ifdef UNIV_LOG_ARCHIVE byte** archive_file_header_bufs_ptr;/*!< unaligned buffers */ byte** archive_file_header_bufs;/*!< buffers for each file header in the group */ #endif ulint archive_space_id;/*!< file space which implements the log group archive */ ulint archived_file_no;/*!< file number corresponding to log_sys->archived_lsn */ ulint archived_offset;/*!< file offset corresponding to log_sys->archived_lsn, 0 if we have not yet written to the archive file number archived_file_no */ ulint next_archived_file_no;/*!< during an archive write, until the write is completed, we store the next value for archived_file_no here: the write completion function then sets the new value to ..._file_no */ ulint next_archived_offset; /*!< like the preceding field */ #endif /* UNIV_LOG_ARCHIVE */ /*-----------------------------*/ ib_uint64_t scanned_lsn; /*!< used only in recovery: recovery scan succeeded up to this lsn in this log group */ byte* checkpoint_buf_ptr;/*!< unaligned checkpoint header */ byte* checkpoint_buf; /*!< checkpoint header is written from this buffer to the group */ UT_LIST_NODE_T(log_group_t) log_groups; /*!< list of log groups */ }; /** Redo log buffer */ struct log_struct{ byte pad[64]; /*!< padding to prevent other memory update hotspots from residing on the same memory cache line */ ib_uint64_t lsn; /*!< log sequence number */ ulint buf_free; /*!< first free offset within the log buffer */ #ifndef UNIV_HOTBACKUP mutex_t mutex; /*!< mutex protecting the log */ #endif /* !UNIV_HOTBACKUP */ byte* buf_ptr; /* unaligned log buffer */ byte* buf; /*!< log buffer */ ulint buf_size; /*!< log buffer size in bytes */ ulint max_buf_free; /*!< recommended maximum value of buf_free, after which the buffer is flushed */ #ifdef UNIV_DEBUG ulint old_buf_free; /*!< value of buf free when log was last time opened; only in the debug version */ ib_uint64_t old_lsn; /*!< value of lsn when log was last time opened; only in the debug version */ #endif ibool check_flush_or_checkpoint; /*!< this is set to TRUE when there may be need to flush the log buffer, or preflush buffer pool pages, or make a checkpoint; this MUST be TRUE when lsn - last_checkpoint_lsn > max_checkpoint_age; this flag is peeked at by log_free_check(), which does not reserve the log mutex */ UT_LIST_BASE_NODE_T(log_group_t) log_groups; /*!< log groups */ #ifndef UNIV_HOTBACKUP /** The fields involved in the log buffer flush @{ */ ulint buf_next_to_write;/*!< first offset in the log buffer where the byte content may not exist written to file, e.g., the start offset of a log record catenated later; this is advanced when a flush operation is completed to all the log groups */ ib_uint64_t written_to_some_lsn; /*!< first log sequence number not yet written to any log group; for this to be advanced, it is enough that the write i/o has been completed for any one log group */ ib_uint64_t written_to_all_lsn; /*!< first log sequence number not yet written to some log group; for this to be advanced, it is enough that the write i/o has been completed for all log groups. Note that since InnoDB currently has only one log group therefore this value is redundant. Also it is possible that this value falls behind the flushed_to_disk_lsn transiently. It is appropriate to use either flushed_to_disk_lsn or write_lsn which are always up-to-date and accurate. */ ib_uint64_t write_lsn; /*!< end lsn for the current running write */ ulint write_end_offset;/*!< the data in buffer has been written up to this offset when the current write ends: this field will then be copied to buf_next_to_write */ ib_uint64_t current_flush_lsn;/*!< end lsn for the current running write + flush operation */ ib_uint64_t flushed_to_disk_lsn; /*!< how far we have written the log AND flushed to disk */ ulint n_pending_writes;/*!< number of currently pending flushes or writes */ /* NOTE on the 'flush' in names of the fields below: starting from 4.0.14, we separate the write of the log file and the actual fsync() or other method to flush it to disk. The names below shhould really be 'flush_or_write'! */ os_event_t no_flush_event; /*!< this event is in the reset state when a flush or a write is running; a thread should wait for this without owning the log mutex, but NOTE that to set or reset this event, the thread MUST own the log mutex! */ ibool one_flushed; /*!< during a flush, this is first FALSE and becomes TRUE when one log group has been written or flushed */ os_event_t one_flushed_event;/*!< this event is reset when the flush or write has not yet completed for any log group; e.g., this means that a transaction has been committed when this is set; a thread should wait for this without owning the log mutex, but NOTE that to set or reset this event, the thread MUST own the log mutex! */ ulint n_log_ios; /*!< number of log i/os initiated thus far */ ulint n_log_ios_old; /*!< number of log i/o's at the previous printout */ time_t last_printout_time;/*!< when log_print was last time called */ /* @} */ /** Fields involved in checkpoints @{ */ ulint log_group_capacity; /*!< capacity of the log group; if the checkpoint age exceeds this, it is a serious error because it is possible we will then overwrite log and spoil crash recovery */ ulint max_modified_age_async; /*!< when this recommended value for lsn - buf_pool_get_oldest_modification() is exceeded, we start an asynchronous preflush of pool pages */ ulint max_modified_age_sync; /*!< when this recommended value for lsn - buf_pool_get_oldest_modification() is exceeded, we start a synchronous preflush of pool pages */ ulint adm_checkpoint_interval; /*!< administrator-specified checkpoint interval in terms of log growth in bytes; the interval actually used by the database can be smaller */ ulint max_checkpoint_age_async; /*!< when this checkpoint age is exceeded we start an asynchronous writing of a new checkpoint */ ulint max_checkpoint_age; /*!< this is the maximum allowed value for lsn - last_checkpoint_lsn when a new query step is started */ ib_uint64_t next_checkpoint_no; /*!< next checkpoint number */ ib_uint64_t last_checkpoint_lsn; /*!< latest checkpoint lsn */ ib_uint64_t next_checkpoint_lsn; /*!< next checkpoint lsn */ ulint n_pending_checkpoint_writes; /*!< number of currently pending checkpoint writes */ rw_lock_t checkpoint_lock;/*!< this latch is x-locked when a checkpoint write is running; a thread should wait for this without owning the log mutex */ #endif /* !UNIV_HOTBACKUP */ byte* checkpoint_buf_ptr;/* unaligned checkpoint header */ byte* checkpoint_buf; /*!< checkpoint header is read to this buffer */ /* @} */ #ifdef UNIV_LOG_ARCHIVE /** Fields involved in archiving @{ */ ulint archiving_state;/*!< LOG_ARCH_ON, LOG_ARCH_STOPPING LOG_ARCH_STOPPED, LOG_ARCH_OFF */ ib_uint64_t archived_lsn; /*!< archiving has advanced to this lsn */ ulint max_archived_lsn_age_async; /*!< recommended maximum age of archived_lsn, before we start asynchronous copying to the archive */ ulint max_archived_lsn_age; /*!< maximum allowed age for archived_lsn */ ib_uint64_t next_archived_lsn;/*!< during an archive write, until the write is completed, we store the next value for archived_lsn here: the write completion function then sets the new value to archived_lsn */ ulint archiving_phase;/*!< LOG_ARCHIVE_READ or LOG_ARCHIVE_WRITE */ ulint n_pending_archive_ios; /*!< number of currently pending reads or writes in archiving */ rw_lock_t archive_lock; /*!< this latch is x-locked when an archive write is running; a thread should wait for this without owning the log mutex */ ulint archive_buf_size;/*!< size of archive_buf */ byte* archive_buf; /*!< log segment is written to the archive from this buffer */ os_event_t archiving_on; /*!< if archiving has been stopped, a thread can wait for this event to become signaled */ /* @} */ #endif /* UNIV_LOG_ARCHIVE */ }; #ifdef UNIV_LOG_ARCHIVE /** Archiving state @{ */ #define LOG_ARCH_ON 71 #define LOG_ARCH_STOPPING 72 #define LOG_ARCH_STOPPING2 73 #define LOG_ARCH_STOPPED 74 #define LOG_ARCH_OFF 75 /* @} */ #endif /* UNIV_LOG_ARCHIVE */ #ifndef UNIV_NONINL #include "log0log.ic" #endif #endif haildb-2.3.2/include/read0read.h0000644000175000017500000001541011513177357017267 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1997, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/read0read.h Cursor read Created 2/16/1997 Heikki Tuuri *******************************************************/ #ifndef read0read_h #define read0read_h #include "univ.i" #include "ut0byte.h" #include "ut0lst.h" #include "trx0trx.h" #include "read0types.h" /*********************************************************************//** Opens a read view where exactly the transactions serialized before this point in time are seen in the view. @return own: read view struct */ UNIV_INTERN read_view_t* read_view_open_now( /*===============*/ trx_id_t cr_trx_id, /*!< in: trx_id of creating transaction, or ut_dulint_zero used in purge */ mem_heap_t* heap); /*!< in: memory heap from which allocated */ /*********************************************************************//** Makes a copy of the oldest existing read view, or opens a new. The view must be closed with ..._close. @return own: read view struct */ UNIV_INTERN read_view_t* read_view_oldest_copy_or_open_new( /*==============================*/ trx_id_t cr_trx_id, /*!< in: trx_id of creating transaction, or ut_dulint_zero used in purge */ mem_heap_t* heap); /*!< in: memory heap from which allocated */ /*********************************************************************//** Closes a read view. */ UNIV_INTERN void read_view_close( /*============*/ read_view_t* view); /*!< in: read view */ /*********************************************************************//** Closes a consistent read view for client. This function is called at an SQL statement end if the trx isolation level is <= TRX_ISO_READ_COMMITTED. */ UNIV_INTERN void read_view_close_for_read_committed( /*===============================*/ trx_t* trx); /*!< in: trx which has a read view */ /*********************************************************************//** Checks if a read view sees the specified transaction. @return TRUE if sees */ UNIV_INLINE ibool read_view_sees_trx_id( /*==================*/ const read_view_t* view, /*!< in: read view */ trx_id_t trx_id);/*!< in: trx id */ /*********************************************************************//** Prints a read view to stderr. */ UNIV_INTERN void read_view_print( /*============*/ const read_view_t* view); /*!< in: read view */ /*********************************************************************//** Create a consistent cursor view to be used in cursors. In this consistent read view modifications done by the creating transaction or future transactions are not visible. */ UNIV_INTERN cursor_view_t* read_cursor_view_create( /*====================*/ trx_t* cr_trx);/*!< in: trx where cursor view is created */ /*********************************************************************//** Close a given consistent cursor view and restore global read view back to a transaction read view. */ UNIV_INTERN void read_cursor_view_close( /*===================*/ trx_t* trx, /*!< in: trx */ cursor_view_t* curview); /*!< in: cursor view to be closed */ /*********************************************************************//** This function sets a given consistent cursor view to a transaction read view if given consistent cursor view is not NULL. Otherwise, function restores a global read view to a transaction read view. */ UNIV_INTERN void read_cursor_set( /*============*/ trx_t* trx, /*!< in: transaction where cursor is set */ cursor_view_t* curview);/*!< in: consistent cursor view to be set */ /** Read view lists the trx ids of those transactions for which a consistent read should not see the modifications to the database. */ struct read_view_struct{ ulint type; /*!< VIEW_NORMAL, VIEW_HIGH_GRANULARITY */ undo_no_t undo_no;/*!< ut_dulint_zero or if type is VIEW_HIGH_GRANULARITY transaction undo_no when this high-granularity consistent read view was created */ trx_id_t low_limit_no; /*!< The view does not need to see the undo logs for transactions whose transaction number is strictly smaller (<) than this value: they can be removed in purge if not needed by other views */ trx_id_t low_limit_id; /*!< The read should not see any transaction with trx id >= this value. In other words, this is the "high water mark". */ trx_id_t up_limit_id; /*!< The read should see all trx ids which are strictly smaller (<) than this value. In other words, this is the "low water mark". */ ulint n_trx_ids; /*!< Number of cells in the trx_ids array */ trx_id_t* trx_ids;/*!< Additional trx ids which the read should not see: typically, these are the active transactions at the time when the read is serialized, except the reading transaction itself; the trx ids in this array are in a descending order. These trx_ids should be between the "low" and "high" water marks, that is, up_limit_id and low_limit_id. */ trx_id_t creator_trx_id; /*!< trx id of creating transaction, or ut_dulint_zero used in purge */ UT_LIST_NODE_T(read_view_t) view_list; /*!< List of read views in trx_sys */ }; /** Read view types @{ */ #define VIEW_NORMAL 1 /*!< Normal consistent read view where transaction does not see changes made by active transactions except creating transaction. */ #define VIEW_HIGH_GRANULARITY 2 /*!< High-granularity read view where transaction does not see changes made by active transactions and own changes after a point in time when this read view was created. */ /* @} */ /** Implement InnoDB framework to support consistent read views in cursors. This struct holds both heap where consistent read view is allocated and pointer to a read view. */ struct cursor_view_struct{ mem_heap_t* heap; /*!< Memory heap for the cursor view */ read_view_t* read_view; /*!< Consistent read view of the cursor*/ ulint n_client_tables_in_use; /*!< number of Innobase tables used in the processing of this cursor */ }; #ifndef UNIV_NONINL #include "read0read.ic" #endif #endif haildb-2.3.2/include/srv0start.h0000644000175000017500000001053011513177357017406 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file include/srv0start.h Starts the Innobase database server Created 10/10/1995 Heikki Tuuri *******************************************************/ #ifndef srv0start_h #define srv0start_h #include "univ.i" #include "ut0byte.h" #include "api0api.h" /*********************************************************************//** Normalizes a directory path for Windows: converts slashes to backslashes. */ UNIV_INTERN void srv_normalize_path_for_win( /*=======================*/ char* str); /*!< in/out: null-terminated character string */ /*********************************************************************//** Reads the data files and their sizes from a character string. @return TRUE if ok, FALSE on parse error */ UNIV_INTERN ibool srv_parse_data_file_paths_and_sizes( /*================================*/ const char* str); /*!< in: the data file path string */ /*********************************************************************//** Reads log group home directories from a character string. @return TRUE if ok, FALSE on parse error */ UNIV_INTERN ibool srv_parse_log_group_home_dirs( /*==========================*/ const char* str); /*!< in: character string */ /*********************************************************************//** Frees the memory allocated by srv_parse_data_file_paths_and_sizes() and srv_parse_log_group_home_dirs(). */ UNIV_INTERN void srv_free_paths_and_sizes(void); /*==========================*/ #ifndef UNIV_HOTBACKUP /****************************************************************//** Starts Innobase and creates a new database if database files are not found and the user wants. @return DB_SUCCESS or error code */ UNIV_INTERN ib_err_t innobase_start_or_create(void); /*===========================*/ /****************************************************************//** Shuts down the Innobase database. @return DB_SUCCESS or error code */ UNIV_INTERN enum db_err innobase_shutdown( /*==============*/ ib_shutdown_t shutdown); /*!< in: shutdown flag */ /** Log sequence number at shutdown */ extern ib_uint64_t srv_shutdown_lsn; /** Log sequence number immediately after startup */ extern ib_uint64_t srv_start_lsn; #ifdef __NETWARE__ void set_panic_flag_for_netware(void); #endif #ifdef HAVE_DARWIN_THREADS /** TRUE if the F_FULLFSYNC option is available */ extern ibool srv_have_fullfsync; #endif /** TRUE if the server is being started */ extern ibool srv_is_being_started; /** TRUE if the server was successfully started */ extern ibool srv_was_started; /** TRUE if the server is being started, before rolling back any incomplete transactions */ extern ibool srv_startup_is_before_trx_rollback_phase; /** TRUE if a raw partition is in use */ extern ibool srv_start_raw_disk_in_use; /** Shutdown state */ enum srv_shutdown_state { SRV_SHUTDOWN_NONE = 0, /*!< Database running normally */ SRV_SHUTDOWN_CLEANUP, /*!< Cleaning up in logs_empty_and_mark_files_at_shutdown() */ SRV_SHUTDOWN_LAST_PHASE,/*!< Last phase after ensuring that the buffer pool can be freed: flush all file spaces and close all files */ SRV_SHUTDOWN_EXIT_THREADS/*!< Exit all threads */ }; #ifdef __WIN__ #define SRV_PATH_SEPARATOR '\\' #else #define SRV_PATH_SEPARATOR '/' #endif /** At a shutdown this value climbs from SRV_SHUTDOWN_NONE to SRV_SHUTDOWN_CLEANUP and then to SRV_SHUTDOWN_LAST_PHASE, and so on */ extern enum srv_shutdown_state srv_shutdown_state; #endif /* !UNIV_HOTBACKUP */ /** Log 'spaces' have id's >= this */ #define SRV_LOG_SPACE_FIRST_ID 0xFFFFFFF0UL #endif haildb-2.3.2/COPYING.Google0000644000175000017500000000321611513177357016113 0ustar00pcrewspcrews00000000000000Portions of this software contain modifications contributed by Google, Inc. These contributions are used with the following license: Copyright (c) 2008, Google Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. haildb-2.3.2/mach/0000755000175000017500000000000011513177437014552 5ustar00pcrewspcrews00000000000000haildb-2.3.2/mach/mach0data.c0000644000175000017500000000614611513177357016550 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1995, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /******************************************************************//** @file mach/mach0data.c Utilities for converting data from the database file to the machine format. Created 11/28/1995 Heikki Tuuri ***********************************************************************/ #include "mach0data.h" #ifdef UNIV_NONINL #include "mach0data.ic" #endif /*********************************************************//** Reads a ulint in a compressed form if the log record fully contains it. @return pointer to end of the stored field, NULL if not complete */ UNIV_INTERN byte* mach_parse_compressed( /*==================*/ byte* ptr, /*!< in: pointer to buffer from where to read */ byte* end_ptr,/*!< in: pointer to end of the buffer */ ulint* val) /*!< out: read value (< 2^32) */ { ulint flag; ut_ad(ptr && end_ptr && val); if (ptr >= end_ptr) { return(NULL); } flag = mach_read_from_1(ptr); if (flag < 0x80UL) { *val = flag; return(ptr + 1); } else if (flag < 0xC0UL) { if (end_ptr < ptr + 2) { return(NULL); } *val = mach_read_from_2(ptr) & 0x7FFFUL; return(ptr + 2); } else if (flag < 0xE0UL) { if (end_ptr < ptr + 3) { return(NULL); } *val = mach_read_from_3(ptr) & 0x3FFFFFUL; return(ptr + 3); } else if (flag < 0xF0UL) { if (end_ptr < ptr + 4) { return(NULL); } *val = mach_read_from_4(ptr) & 0x1FFFFFFFUL; return(ptr + 4); } else { ut_ad(flag == 0xF0UL); if (end_ptr < ptr + 5) { return(NULL); } *val = mach_read_from_4(ptr + 1); return(ptr + 5); } } /*********************************************************//** Reads a dulint in a compressed form if the log record fully contains it. @return pointer to end of the stored field, NULL if not complete */ UNIV_INTERN byte* mach_dulint_parse_compressed( /*=========================*/ byte* ptr, /*!< in: pointer to buffer from where to read */ byte* end_ptr,/*!< in: pointer to end of the buffer */ dulint* val) /*!< out: read value */ { ulint high; ulint low; ulint size; ut_ad(ptr && end_ptr && val); if (end_ptr < ptr + 5) { return(NULL); } high = mach_read_compressed(ptr); size = mach_get_compressed_size(high); ptr += size; if (end_ptr < ptr + 4) { return(NULL); } low = mach_read_from_4(ptr); *val = ut_dulint_create(high, low); return(ptr + 4); } haildb-2.3.2/dict/0000755000175000017500000000000011513177437014565 5ustar00pcrewspcrews00000000000000haildb-2.3.2/dict/dict0load.c0000644000175000017500000011557111513177357016607 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2010, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file dict/dict0load.c Loads to the memory cache database object definitions from dictionary tables Created 4/24/1996 Heikki Tuuri *******************************************************/ #include "dict0load.h" #ifdef UNIV_NONINL #include "dict0load.ic" #endif #include "btr0pcur.h" #include "btr0btr.h" #include "page0page.h" #include "mach0data.h" #include "dict0dict.h" #include "dict0boot.h" #include "rem0cmp.h" #include "srv0start.h" #include "srv0srv.h" /****************************************************************//** Compare the name of an index column. @return TRUE if the i'th column of index is 'name'. */ static ibool name_of_col_is( /*===========*/ const dict_table_t* table, /*!< in: table */ const dict_index_t* index, /*!< in: index */ ulint i, /*!< in: index field offset */ const char* name) /*!< in: name to compare to */ { ulint tmp = dict_col_get_no(dict_field_get_col( dict_index_get_nth_field( index, i))); return(strcmp(name, dict_table_get_col_name(table, tmp)) == 0); } /********************************************************************//** Finds the first table name in the given database. @return own: table name, NULL if does not exist; the caller must free the memory in the string! */ UNIV_INTERN char* dict_get_first_table_name_in_db( /*============================*/ const char* name) /*!< in: database name which ends in '/' */ { dict_table_t* sys_tables; btr_pcur_t pcur; dict_index_t* sys_index; dtuple_t* tuple; mem_heap_t* heap; dfield_t* dfield; const rec_t* rec; const byte* field; ulint len; mtr_t mtr; ut_ad(mutex_own(&(dict_sys->mutex))); heap = mem_heap_create(1000); mtr_start(&mtr); sys_tables = dict_table_get_low("SYS_TABLES"); sys_index = UT_LIST_GET_FIRST(sys_tables->indexes); ut_a(!dict_table_is_comp(sys_tables)); tuple = dtuple_create(heap, 1); dfield = dtuple_get_nth_field(tuple, 0); dfield_set_data(dfield, name, ut_strlen(name)); dict_index_copy_types(tuple, sys_index, 1); btr_pcur_open_on_user_rec(sys_index, tuple, PAGE_CUR_GE, BTR_SEARCH_LEAF, &pcur, &mtr); loop: rec = btr_pcur_get_rec(&pcur); if (!btr_pcur_is_on_user_rec(&pcur)) { /* Not found */ btr_pcur_close(&pcur); mtr_commit(&mtr); mem_heap_free(heap); return(NULL); } field = rec_get_nth_field_old(rec, 0, &len); if (len < strlen(name) || ut_memcmp(name, field, strlen(name)) != 0) { /* Not found */ btr_pcur_close(&pcur); mtr_commit(&mtr); mem_heap_free(heap); return(NULL); } if (!rec_get_deleted_flag(rec, 0)) { /* We found one */ char* table_name = mem_strdupl((char*) field, len); btr_pcur_close(&pcur); mtr_commit(&mtr); mem_heap_free(heap); return(table_name); } btr_pcur_move_to_next_user_rec(&pcur, &mtr); goto loop; } /********************************************************************//** Prints to the standard output information on all tables found in the data dictionary system table. */ UNIV_INTERN void dict_print(void) /*============*/ { dict_table_t* sys_tables; dict_index_t* sys_index; dict_table_t* table; btr_pcur_t pcur; const rec_t* rec; const byte* field; ulint len; mtr_t mtr; /* Enlarge the fatal semaphore wait timeout during the InnoDB table monitor printout */ mutex_enter(&kernel_mutex); srv_fatal_semaphore_wait_threshold += 7200; /* 2 hours */ mutex_exit(&kernel_mutex); mutex_enter(&(dict_sys->mutex)); mtr_start(&mtr); sys_tables = dict_table_get_low("SYS_TABLES"); sys_index = UT_LIST_GET_FIRST(sys_tables->indexes); btr_pcur_open_at_index_side(TRUE, sys_index, BTR_SEARCH_LEAF, &pcur, TRUE, &mtr); loop: btr_pcur_move_to_next_user_rec(&pcur, &mtr); rec = btr_pcur_get_rec(&pcur); if (!btr_pcur_is_on_user_rec(&pcur)) { /* end of index */ btr_pcur_close(&pcur); mtr_commit(&mtr); mutex_exit(&(dict_sys->mutex)); /* Restore the fatal semaphore wait timeout */ mutex_enter(&kernel_mutex); srv_fatal_semaphore_wait_threshold -= 7200; /* 2 hours */ mutex_exit(&kernel_mutex); return; } field = rec_get_nth_field_old(rec, 0, &len); if (!rec_get_deleted_flag(rec, 0)) { /* We found one */ char* table_name = mem_strdupl((char*) field, len); btr_pcur_store_position(&pcur, &mtr); mtr_commit(&mtr); table = dict_table_get_low(table_name); mem_free(table_name); if (table == NULL) { ib_logger(ib_stream, "InnoDB: Failed to load table "); ut_print_namel(ib_stream, (char*) field, len); ib_logger(ib_stream, "\n"); } else { /* The table definition was corrupt if there is no index */ if (dict_table_get_first_index(table)) { dict_update_statistics_low(table, TRUE); } dict_table_print_low(table); } mtr_start(&mtr); btr_pcur_restore_position(BTR_SEARCH_LEAF, &pcur, &mtr); } goto loop; } /********************************************************************//** Determine the flags of a table described in SYS_TABLES. @return compressed page size in kilobytes; or 0 if the tablespace is uncompressed, ULINT_UNDEFINED on error */ static ulint dict_sys_tables_get_flags( /*======================*/ const rec_t* rec) /*!< in: a record of SYS_TABLES */ { const byte* field; ulint len; ulint n_cols; ulint flags; field = rec_get_nth_field_old(rec, 5, &len); ut_a(len == 4); flags = mach_read_from_4(field); if (UNIV_LIKELY(flags == DICT_TABLE_ORDINARY)) { return(0); } field = rec_get_nth_field_old(rec, 4/*N_COLS*/, &len); n_cols = mach_read_from_4(field); if (UNIV_UNLIKELY(!(n_cols & 0x80000000UL))) { /* New file formats require ROW_FORMAT=COMPACT. */ return(ULINT_UNDEFINED); } switch (flags & (DICT_TF_FORMAT_MASK | DICT_TF_COMPACT)) { default: case DICT_TF_FORMAT_51 << DICT_TF_FORMAT_SHIFT: case DICT_TF_FORMAT_51 << DICT_TF_FORMAT_SHIFT | DICT_TF_COMPACT: /* flags should be DICT_TABLE_ORDINARY, or DICT_TF_FORMAT_MASK should be nonzero. */ return(ULINT_UNDEFINED); case DICT_TF_FORMAT_ZIP << DICT_TF_FORMAT_SHIFT | DICT_TF_COMPACT: #if DICT_TF_FORMAT_MAX > DICT_TF_FORMAT_ZIP # error "missing case labels for DICT_TF_FORMAT_ZIP .. DICT_TF_FORMAT_MAX" #endif /* We support this format. */ break; } if (UNIV_UNLIKELY((flags & DICT_TF_ZSSIZE_MASK) > (DICT_TF_ZSSIZE_MAX << DICT_TF_ZSSIZE_SHIFT))) { /* Unsupported compressed page size. */ return(ULINT_UNDEFINED); } if (UNIV_UNLIKELY(flags & (~0 << DICT_TF_BITS))) { /* Some unused bits are set. */ return(ULINT_UNDEFINED); } return(flags); } /********************************************************************//** In a crash recovery we already have all the tablespace objects created. This function compares the space id information in the InnoDB data dictionary to what we already read with fil_load_single_table_tablespaces(). In a normal startup, we create the tablespace objects for every table in InnoDB's data dictionary, if the corresponding .ibd file exists. We also scan the biggest space id, and store it to fil_system. */ UNIV_INTERN void dict_check_tablespaces_and_store_max_id( /*====================================*/ ibool in_crash_recovery) /*!< in: are we doing a crash recovery */ { dict_table_t* sys_tables; dict_index_t* sys_index; btr_pcur_t pcur; const rec_t* rec; ulint max_space_id = 0; mtr_t mtr; mutex_enter(&(dict_sys->mutex)); mtr_start(&mtr); sys_tables = dict_table_get_low("SYS_TABLES"); sys_index = UT_LIST_GET_FIRST(sys_tables->indexes); ut_a(!dict_table_is_comp(sys_tables)); btr_pcur_open_at_index_side(TRUE, sys_index, BTR_SEARCH_LEAF, &pcur, TRUE, &mtr); loop: btr_pcur_move_to_next_user_rec(&pcur, &mtr); rec = btr_pcur_get_rec(&pcur); if (!btr_pcur_is_on_user_rec(&pcur)) { /* end of index */ btr_pcur_close(&pcur); mtr_commit(&mtr); /* We must make the tablespace cache aware of the biggest known space id */ /* printf("Biggest space id in data dictionary %lu\n", max_space_id); */ fil_set_max_space_id_if_bigger(max_space_id); mutex_exit(&(dict_sys->mutex)); return; } if (!rec_get_deleted_flag(rec, 0)) { /* We found one */ const byte* field; ulint len; ulint space_id; ulint flags; char* name; field = rec_get_nth_field_old(rec, 0, &len); name = mem_strdupl((char*) field, len); flags = dict_sys_tables_get_flags(rec); #ifndef WITH_ZIP if (flags & DICT_TF_FORMAT_ZIP) { field = rec_get_nth_field_old(rec, 5, &len); flags = mach_read_from_4(field); ib_logger(ib_stream, "InnoDB: Error: Table "); ut_print_filename(ib_stream, name); ib_logger(ib_stream, " in InnoDB data dictionary is a compressed\n" "InnoDB: table (%lx). But InnoDB not compiled " "with support for compressed tables.\n", (ulong) flags); } else #endif /* WITH_ZIP */ if (flags == ULINT_UNDEFINED) { field = rec_get_nth_field_old(rec, 5, &len); flags = mach_read_from_4(field); ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Error: table "); ut_print_filename(ib_stream, name); ib_logger(ib_stream, "\n" "InnoDB: in InnoDB data dictionary" " has unknown type %lx.\n", (ulong) flags); goto loop; } field = rec_get_nth_field_old(rec, 9, &len); ut_a(len == 4); space_id = mach_read_from_4(field); btr_pcur_store_position(&pcur, &mtr); mtr_commit(&mtr); if (space_id == 0) { /* The system tablespace always exists. */ } else if (in_crash_recovery) { /* Check that the tablespace (the .ibd file) really exists; print a warning to the .err log if not. Do not print warnings for temporary tables. */ ibool is_temp; field = rec_get_nth_field_old(rec, 4, &len); if (0x80000000UL & mach_read_from_4(field)) { /* ROW_FORMAT=COMPACT: read the is_temp flag from SYS_TABLES.MIX_LEN. */ field = rec_get_nth_field_old(rec, 7, &len); is_temp = mach_read_from_4(field) & DICT_TF2_TEMPORARY; } else { /* For tables created with old versions of InnoDB, SYS_TABLES.MIX_LEN may contain garbage. Such tables would always be in ROW_FORMAT=REDUNDANT. Pretend that all such tables are non-temporary. That is, do not suppress error printouts about temporary tables not being found. */ is_temp = FALSE; } fil_space_for_table_exists_in_mem( space_id, name, is_temp, TRUE, !is_temp); } else { /* It is a normal database startup: create the space object and check that the .ibd file exists. */ fil_open_single_table_tablespace(FALSE, space_id, flags, name); } mem_free(name); if (space_id > max_space_id) { max_space_id = space_id; } mtr_start(&mtr); btr_pcur_restore_position(BTR_SEARCH_LEAF, &pcur, &mtr); } goto loop; } /********************************************************************//** Loads definitions for table columns. */ static void dict_load_columns( /*==============*/ dict_table_t* table, /*!< in: table */ mem_heap_t* heap) /*!< in: memory heap for temporary storage */ { dict_table_t* sys_columns; dict_index_t* sys_index; btr_pcur_t pcur; dtuple_t* tuple; dfield_t* dfield; const rec_t* rec; const byte* field; ulint len; byte* buf; char* name; ulint mtype; ulint prtype; ulint col_len; ulint i; mtr_t mtr; ut_ad(mutex_own(&(dict_sys->mutex))); mtr_start(&mtr); sys_columns = dict_table_get_low("SYS_COLUMNS"); sys_index = UT_LIST_GET_FIRST(sys_columns->indexes); ut_a(!dict_table_is_comp(sys_columns)); tuple = dtuple_create(heap, 1); dfield = dtuple_get_nth_field(tuple, 0); buf = mem_heap_alloc(heap, 8); mach_write_to_8(buf, table->id); dfield_set_data(dfield, buf, 8); dict_index_copy_types(tuple, sys_index, 1); btr_pcur_open_on_user_rec(sys_index, tuple, PAGE_CUR_GE, BTR_SEARCH_LEAF, &pcur, &mtr); for (i = 0; i + DATA_N_SYS_COLS < (ulint) table->n_cols; i++) { rec = btr_pcur_get_rec(&pcur); ut_a(btr_pcur_is_on_user_rec(&pcur)); ut_a(!rec_get_deleted_flag(rec, 0)); field = rec_get_nth_field_old(rec, 0, &len); ut_ad(len == 8); ut_a(ut_dulint_cmp(table->id, mach_read_from_8(field)) == 0); field = rec_get_nth_field_old(rec, 1, &len); ut_ad(len == 4); ut_a(i == mach_read_from_4(field)); ut_a(name_of_col_is(sys_columns, sys_index, 4, "NAME")); field = rec_get_nth_field_old(rec, 4, &len); name = mem_heap_strdupl(heap, (char*) field, len); field = rec_get_nth_field_old(rec, 5, &len); mtype = mach_read_from_4(field); field = rec_get_nth_field_old(rec, 6, &len); prtype = mach_read_from_4(field); if (dtype_get_charset_coll(prtype) == 0 && dtype_is_string_type(mtype)) { /* The table was created with < 4.1.2. */ if (dtype_is_binary_string_type(mtype, prtype)) { /* Use the binary collation for string columns of binary type. */ prtype = dtype_form_prtype( prtype, DATA_CLIENT_BINARY_CHARSET_COLL); } else { /* Use the default charset for other than binary columns. */ prtype = dtype_form_prtype( prtype, data_client_default_charset_coll); } } field = rec_get_nth_field_old(rec, 7, &len); col_len = mach_read_from_4(field); ut_a(name_of_col_is(sys_columns, sys_index, 8, "PREC")); dict_mem_table_add_col(table, heap, name, mtype, prtype, col_len); btr_pcur_move_to_next_user_rec(&pcur, &mtr); } btr_pcur_close(&pcur); mtr_commit(&mtr); } /********************************************************************//** Loads definitions for index fields. */ static void dict_load_fields( /*=============*/ dict_index_t* index, /*!< in: index whose fields to load */ mem_heap_t* heap) /*!< in: memory heap for temporary storage */ { dict_table_t* sys_fields; dict_index_t* sys_index; btr_pcur_t pcur; dtuple_t* tuple; dfield_t* dfield; ulint pos_and_prefix_len; ulint prefix_len; const rec_t* rec; const byte* field; ulint len; byte* buf; ulint i; mtr_t mtr; ut_ad(mutex_own(&(dict_sys->mutex))); mtr_start(&mtr); sys_fields = dict_table_get_low("SYS_FIELDS"); sys_index = UT_LIST_GET_FIRST(sys_fields->indexes); ut_a(!dict_table_is_comp(sys_fields)); tuple = dtuple_create(heap, 1); dfield = dtuple_get_nth_field(tuple, 0); buf = mem_heap_alloc(heap, 8); mach_write_to_8(buf, index->id); dfield_set_data(dfield, buf, 8); dict_index_copy_types(tuple, sys_index, 1); btr_pcur_open_on_user_rec(sys_index, tuple, PAGE_CUR_GE, BTR_SEARCH_LEAF, &pcur, &mtr); for (i = 0; i < index->n_fields; i++) { rec = btr_pcur_get_rec(&pcur); ut_a(btr_pcur_is_on_user_rec(&pcur)); /* There could be delete marked records in SYS_FIELDS because SYS_FIELDS.INDEX_ID can be updated by ALTER TABLE ADD INDEX. */ if (rec_get_deleted_flag(rec, 0)) { goto next_rec; } field = rec_get_nth_field_old(rec, 0, &len); ut_ad(len == 8); field = rec_get_nth_field_old(rec, 1, &len); ut_a(len == 4); /* The next field stores the field position in the index and a possible column prefix length if the index field does not contain the whole column. The storage format is like this: if there is at least one prefix field in the index, then the HIGH 2 bytes contain the field number (== i) and the low 2 bytes the prefix length for the field. Otherwise the field number (== i) is contained in the 2 LOW bytes. */ pos_and_prefix_len = mach_read_from_4(field); ut_a((pos_and_prefix_len & 0xFFFFUL) == i || (pos_and_prefix_len & 0xFFFF0000UL) == (i << 16)); if ((i == 0 && pos_and_prefix_len > 0) || (pos_and_prefix_len & 0xFFFF0000UL) > 0) { prefix_len = pos_and_prefix_len & 0xFFFFUL; } else { prefix_len = 0; } ut_a(name_of_col_is(sys_fields, sys_index, 4, "COL_NAME")); field = rec_get_nth_field_old(rec, 4, &len); dict_mem_index_add_field(index, mem_heap_strdupl(heap, (char*) field, len), prefix_len); next_rec: btr_pcur_move_to_next_user_rec(&pcur, &mtr); } btr_pcur_close(&pcur); mtr_commit(&mtr); } /********************************************************************//** Loads definitions for table indexes. Adds them to the data dictionary cache. @return DB_SUCCESS if ok, DB_CORRUPTION if corruption of dictionary table or DB_UNSUPPORTED if table has unknown index type */ static ulint dict_load_indexes( /*==============*/ dict_table_t* table, /*!< in: table */ mem_heap_t* heap) /*!< in: memory heap for temporary storage */ { dict_table_t* sys_indexes; dict_index_t* sys_index; dict_index_t* index; btr_pcur_t pcur; dtuple_t* tuple; dfield_t* dfield; const rec_t* rec; const byte* field; ulint len; ulint name_len; char* name_buf; ulint type; ulint space; ulint page_no; ulint n_fields; byte* buf; ibool is_sys_table; dulint id; mtr_t mtr; ulint error = DB_SUCCESS; ut_ad(mutex_own(&(dict_sys->mutex))); if ((ut_dulint_get_high(table->id) == 0) && (ut_dulint_get_low(table->id) < DICT_HDR_FIRST_ID)) { is_sys_table = TRUE; } else { is_sys_table = FALSE; } mtr_start(&mtr); sys_indexes = dict_table_get_low("SYS_INDEXES"); sys_index = UT_LIST_GET_FIRST(sys_indexes->indexes); ut_a(!dict_table_is_comp(sys_indexes)); tuple = dtuple_create(heap, 1); dfield = dtuple_get_nth_field(tuple, 0); buf = mem_heap_alloc(heap, 8); mach_write_to_8(buf, table->id); dfield_set_data(dfield, buf, 8); dict_index_copy_types(tuple, sys_index, 1); btr_pcur_open_on_user_rec(sys_index, tuple, PAGE_CUR_GE, BTR_SEARCH_LEAF, &pcur, &mtr); for (;;) { if (!btr_pcur_is_on_user_rec(&pcur)) { break; } rec = btr_pcur_get_rec(&pcur); field = rec_get_nth_field_old(rec, 0, &len); ut_ad(len == 8); if (ut_memcmp(buf, field, len) != 0) { break; } else if (rec_get_deleted_flag(rec, 0)) { /* Skip delete marked records */ goto next_rec; } field = rec_get_nth_field_old(rec, 1, &len); ut_ad(len == 8); id = mach_read_from_8(field); ut_a(name_of_col_is(sys_indexes, sys_index, 4, "NAME")); field = rec_get_nth_field_old(rec, 4, &name_len); name_buf = mem_heap_strdupl(heap, (char*) field, name_len); field = rec_get_nth_field_old(rec, 5, &len); n_fields = mach_read_from_4(field); field = rec_get_nth_field_old(rec, 6, &len); type = mach_read_from_4(field); field = rec_get_nth_field_old(rec, 7, &len); space = mach_read_from_4(field); ut_a(name_of_col_is(sys_indexes, sys_index, 8, "PAGE_NO")); field = rec_get_nth_field_old(rec, 8, &len); page_no = mach_read_from_4(field); /* We check for unsupported types first, so that the subsequent checks are relevant for the supported types. */ if (type & ~(DICT_CLUSTERED | DICT_UNIQUE)) { ib_logger(ib_stream, "InnoDB: Error: unknown type %lu" " of index %s of table %s\n", (ulong) type, name_buf, table->name); error = DB_UNSUPPORTED; goto func_exit; } else if (page_no == FIL_NULL) { ib_logger(ib_stream, "InnoDB: Error: trying to load index %s" " for table %s\n" "InnoDB: but the index tree has been freed!\n", name_buf, table->name); error = DB_CORRUPTION; goto func_exit; } else if ((type & DICT_CLUSTERED) == 0 && NULL == dict_table_get_first_index(table)) { ib_logger(ib_stream, "InnoDB: Error: trying to load index "); ut_print_name(ib_stream, NULL, FALSE, name_buf); ib_logger(ib_stream, " for table "); ut_print_name(ib_stream, NULL, TRUE, table->name); ib_logger(ib_stream, "\nInnoDB: but the first index" " is not clustered!\n"); error = DB_CORRUPTION; goto func_exit; } else if (is_sys_table && ((type & DICT_CLUSTERED) || ((table == dict_sys->sys_tables) && (name_len == (sizeof "ID_IND") - 1) && (0 == ut_memcmp(name_buf, "ID_IND", name_len))))) { /* The index was created in memory already at booting of the database server */ } else { index = dict_mem_index_create(table->name, name_buf, space, type, n_fields); index->id = id; dict_load_fields(index, heap); error = dict_index_add_to_cache(table, index, page_no, FALSE); /* The data dictionary tables should never contain invalid index definitions. If we ignored this error and simply did not load this index definition, the .frm file would disagree with the index definitions inside InnoDB. */ if (UNIV_UNLIKELY(error != DB_SUCCESS)) { goto func_exit; } } next_rec: btr_pcur_move_to_next_user_rec(&pcur, &mtr); } func_exit: btr_pcur_close(&pcur); mtr_commit(&mtr); return(error); } /********************************************************************//** Loads a table definition and also all its index definitions, and also the cluster definition if the table is a member in a cluster. Also loads all foreign key constraints where the foreign key is in the table or where a foreign key references columns in this table. Adds all these to the data dictionary cache. @return table, NULL if does not exist; if the table is stored in an .ibd file, but the file does not exist, then we set the ibd_file_missing flag TRUE in the table object we return */ UNIV_INTERN dict_table_t* dict_load_table( /*============*/ ib_recovery_t recovery,/*!< in: recovery flag */ const char* name) /*!< in: table name in the databasename/tablename format */ { ibool ibd_file_missing = FALSE; dict_table_t* table; dict_table_t* sys_tables; btr_pcur_t pcur; dict_index_t* sys_index; dtuple_t* tuple; mem_heap_t* heap; dfield_t* dfield; const rec_t* rec; const byte* field; ulint len; ulint space; ulint n_cols; ulint flags; ulint err; mtr_t mtr; ut_ad(mutex_own(&(dict_sys->mutex))); heap = mem_heap_create(32000); mtr_start(&mtr); sys_tables = dict_table_get_low("SYS_TABLES"); sys_index = UT_LIST_GET_FIRST(sys_tables->indexes); ut_a(!dict_table_is_comp(sys_tables)); tuple = dtuple_create(heap, 1); dfield = dtuple_get_nth_field(tuple, 0); dfield_set_data(dfield, name, ut_strlen(name)); dict_index_copy_types(tuple, sys_index, 1); btr_pcur_open_on_user_rec(sys_index, tuple, PAGE_CUR_GE, BTR_SEARCH_LEAF, &pcur, &mtr); rec = btr_pcur_get_rec(&pcur); if (!btr_pcur_is_on_user_rec(&pcur) || rec_get_deleted_flag(rec, 0)) { /* Not found */ err_exit: btr_pcur_close(&pcur); mtr_commit(&mtr); mem_heap_free(heap); return(NULL); } field = rec_get_nth_field_old(rec, 0, &len); /* Check if the table name in record is the searched one */ if (len != ut_strlen(name) || ut_memcmp(name, field, len) != 0) { goto err_exit; } ut_a(name_of_col_is(sys_tables, sys_index, 9, "SPACE")); field = rec_get_nth_field_old(rec, 9, &len); space = mach_read_from_4(field); /* Check if the tablespace exists and has the right name */ if (space != 0) { flags = dict_sys_tables_get_flags(rec); #ifndef WITH_ZIP if (flags & DICT_TF_FORMAT_ZIP) { field = rec_get_nth_field_old(rec, 5, &len); flags = mach_read_from_4(field); ib_logger(ib_stream, "InnoDB: Error: Table "); ut_print_filename(ib_stream, name); ib_logger(ib_stream, " in InnoDB data dictionary is a compressed\n" "InnoDB: table (%lx). But InnoDB not compiled " "with support for compressed tables.\n", (ulong) flags); goto err_exit; } else #endif /* WITH_ZIP */ if (UNIV_UNLIKELY(flags == ULINT_UNDEFINED)) { field = rec_get_nth_field_old(rec, 5, &len); flags = mach_read_from_4(field); ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Error: table "); ut_print_filename(ib_stream, name); ib_logger(ib_stream, "\n" "InnoDB: in InnoDB data dictionary" " has unknown type %lx.\n", (ulong) flags); goto err_exit; } } else { flags = 0; } ut_a(name_of_col_is(sys_tables, sys_index, 4, "N_COLS")); field = rec_get_nth_field_old(rec, 4, &len); n_cols = mach_read_from_4(field); /* The high-order bit of N_COLS is the "compact format" flag. For tables in that format, MIX_LEN may hold additional flags. */ if (n_cols & 0x80000000UL) { ulint flags2; flags |= DICT_TF_COMPACT; ut_a(name_of_col_is(sys_tables, sys_index, 7, "MIX_LEN")); field = rec_get_nth_field_old(rec, 7, &len); flags2 = mach_read_from_4(field); if (flags2 & (~0 << (DICT_TF2_BITS - DICT_TF2_SHIFT))) { ut_print_timestamp(ib_logger); ib_logger(ib_stream, " InnoDB: Warning: table "); ut_print_filename(ib_stream, name); ib_logger(ib_stream, "\n" "InnoDB: in InnoDB data dictionary" " has unknown flags %lx.\n", (ulong) flags2); flags2 &= ~(~0 << (DICT_TF2_BITS - DICT_TF2_SHIFT)); } flags |= flags2 << DICT_TF2_SHIFT; } /* See if the tablespace is available. */ if (space == 0) { /* The system tablespace is always available. */ } else if (!fil_space_for_table_exists_in_mem( space, name, (flags >> DICT_TF2_SHIFT) & DICT_TF2_TEMPORARY, FALSE, FALSE)) { if ((flags >> DICT_TF2_SHIFT) & DICT_TF2_TEMPORARY) { /* Do not bother to retry opening temporary tables. */ ibd_file_missing = TRUE; } else { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: error: space object of table"); ut_print_filename(ib_stream, name); ib_logger(ib_stream, ",\n" "InnoDB: space id %lu did not exist in memory." " Retrying an open.\n", (ulong) space); /* Try to open the tablespace */ if (!fil_open_single_table_tablespace( TRUE, space, flags & ~(~0 << DICT_TF_BITS), name)) { /* We failed to find a sensible tablespace file */ ibd_file_missing = TRUE; } } } table = dict_mem_table_create(name, space, n_cols & ~0x80000000UL, flags); table->ibd_file_missing = (unsigned int) ibd_file_missing; ut_a(name_of_col_is(sys_tables, sys_index, 3, "ID")); field = rec_get_nth_field_old(rec, 3, &len); table->id = mach_read_from_8(field); btr_pcur_close(&pcur); mtr_commit(&mtr); dict_load_columns(table, heap); dict_table_add_to_cache(table, heap); mem_heap_empty(heap); err = dict_load_indexes(table, heap); /* If the force recovery flag is set, we open the table irrespective of the error condition, since the user may want to dump data from the clustered index. However we load the foreign key information only if all indexes were loaded. */ if (err == DB_SUCCESS) { err = dict_load_foreigns(table->name, TRUE); } else if (recovery == IB_RECOVERY_DEFAULT) { dict_table_remove_from_cache(table); table = NULL; } #if 0 if (err != DB_SUCCESS && table != NULL) { mutex_enter(&dict_foreign_err_mutex); ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Error: could not make a foreign key" " definition to match\n" "InnoDB: the foreign key table" " or the referenced table!\n" "InnoDB: The data dictionary of InnoDB is corrupt." " You may need to drop\n" "InnoDB: and recreate the foreign key table" " or the referenced table.\n" "InnoDB: Submit a detailed bug report" " check the InnoDB website for details" "InnoDB: Latest foreign key error printout:\n%s\n", dict_foreign_err_buf); mutex_exit(&dict_foreign_err_mutex); } #endif /* 0 */ mem_heap_free(heap); return(table); } /***********************************************************************//** Loads a table object based on the table id. @return table; NULL if table does not exist */ UNIV_INTERN dict_table_t* dict_load_table_on_id( /*==================*/ ib_recovery_t recovery, /*!< in: recovery flag */ dulint table_id) /*!< in: table id */ { byte id_buf[8]; btr_pcur_t pcur; mem_heap_t* heap; dtuple_t* tuple; dfield_t* dfield; dict_index_t* sys_table_ids; dict_table_t* sys_tables; const rec_t* rec; const byte* field; ulint len; dict_table_t* table; mtr_t mtr; ut_ad(mutex_own(&(dict_sys->mutex))); /* NOTE that the operation of this function is protected by the dictionary mutex, and therefore no deadlocks can occur with other dictionary operations. */ mtr_start(&mtr); /*---------------------------------------------------*/ /* Get the secondary index based on ID for table SYS_TABLES */ sys_tables = dict_sys->sys_tables; sys_table_ids = dict_table_get_next_index( dict_table_get_first_index(sys_tables)); ut_a(!dict_table_is_comp(sys_tables)); heap = mem_heap_create(256); tuple = dtuple_create(heap, 1); dfield = dtuple_get_nth_field(tuple, 0); /* Write the table id in byte format to id_buf */ mach_write_to_8(id_buf, table_id); dfield_set_data(dfield, id_buf, 8); dict_index_copy_types(tuple, sys_table_ids, 1); btr_pcur_open_on_user_rec(sys_table_ids, tuple, PAGE_CUR_GE, BTR_SEARCH_LEAF, &pcur, &mtr); rec = btr_pcur_get_rec(&pcur); if (!btr_pcur_is_on_user_rec(&pcur) || rec_get_deleted_flag(rec, 0)) { /* Not found */ btr_pcur_close(&pcur); mtr_commit(&mtr); mem_heap_free(heap); return(NULL); } /*---------------------------------------------------*/ /* Now we have the record in the secondary index containing the table ID and NAME */ rec = btr_pcur_get_rec(&pcur); field = rec_get_nth_field_old(rec, 0, &len); ut_ad(len == 8); /* Check if the table id in record is the one searched for */ if (ut_dulint_cmp(table_id, mach_read_from_8(field)) != 0) { btr_pcur_close(&pcur); mtr_commit(&mtr); mem_heap_free(heap); return(NULL); } /* Now we get the table name from the record */ field = rec_get_nth_field_old(rec, 1, &len); /* Load the table definition to memory */ table = dict_load_table( recovery, mem_heap_strdupl(heap, (char*) field, len)); btr_pcur_close(&pcur); mtr_commit(&mtr); mem_heap_free(heap); return(table); } /********************************************************************//** This function is called when the database is booted. Loads system table index definitions except for the clustered index which is added to the dictionary cache at booting before calling this function. */ UNIV_INTERN void dict_load_sys_table( /*================*/ dict_table_t* table) /*!< in: system table */ { mem_heap_t* heap; ut_ad(mutex_own(&(dict_sys->mutex))); heap = mem_heap_create(1000); dict_load_indexes(table, heap); mem_heap_free(heap); } /********************************************************************//** Loads foreign key constraint col names (also for the referenced table). */ static void dict_load_foreign_cols( /*===================*/ const char* id, /*!< in: foreign constraint id as a null-terminated string */ dict_foreign_t* foreign)/*!< in: foreign constraint object */ { dict_table_t* sys_foreign_cols; dict_index_t* sys_index; btr_pcur_t pcur; dtuple_t* tuple; dfield_t* dfield; const rec_t* rec; const byte* field; ulint len; ulint i; mtr_t mtr; ut_ad(mutex_own(&(dict_sys->mutex))); foreign->foreign_col_names = mem_heap_alloc( foreign->heap, foreign->n_fields * sizeof(void*)); foreign->referenced_col_names = mem_heap_alloc( foreign->heap, foreign->n_fields * sizeof(void*)); mtr_start(&mtr); sys_foreign_cols = dict_table_get_low("SYS_FOREIGN_COLS"); sys_index = UT_LIST_GET_FIRST(sys_foreign_cols->indexes); ut_a(!dict_table_is_comp(sys_foreign_cols)); tuple = dtuple_create(foreign->heap, 1); dfield = dtuple_get_nth_field(tuple, 0); dfield_set_data(dfield, id, ut_strlen(id)); dict_index_copy_types(tuple, sys_index, 1); btr_pcur_open_on_user_rec(sys_index, tuple, PAGE_CUR_GE, BTR_SEARCH_LEAF, &pcur, &mtr); for (i = 0; i < foreign->n_fields; i++) { rec = btr_pcur_get_rec(&pcur); ut_a(btr_pcur_is_on_user_rec(&pcur)); ut_a(!rec_get_deleted_flag(rec, 0)); field = rec_get_nth_field_old(rec, 0, &len); ut_a(len == ut_strlen(id)); ut_a(ut_memcmp(id, field, len) == 0); field = rec_get_nth_field_old(rec, 1, &len); ut_a(len == 4); ut_a(i == mach_read_from_4(field)); field = rec_get_nth_field_old(rec, 4, &len); foreign->foreign_col_names[i] = mem_heap_strdupl( foreign->heap, (char*) field, len); field = rec_get_nth_field_old(rec, 5, &len); foreign->referenced_col_names[i] = mem_heap_strdupl( foreign->heap, (char*) field, len); btr_pcur_move_to_next_user_rec(&pcur, &mtr); } btr_pcur_close(&pcur); mtr_commit(&mtr); } /***********************************************************************//** Loads a foreign key constraint to the dictionary cache. @return DB_SUCCESS or error code */ static ulint dict_load_foreign( /*==============*/ const char* id, /*!< in: foreign constraint id as a null-terminated string */ ibool check_charsets) /*!< in: TRUE=check charset compatibility */ { dict_foreign_t* foreign; dict_table_t* sys_foreign; btr_pcur_t pcur; dict_index_t* sys_index; dtuple_t* tuple; mem_heap_t* heap2; dfield_t* dfield; const rec_t* rec; const byte* field; ulint len; ulint n_fields_and_type; mtr_t mtr; ut_ad(mutex_own(&(dict_sys->mutex))); heap2 = mem_heap_create(1000); mtr_start(&mtr); sys_foreign = dict_table_get_low("SYS_FOREIGN"); sys_index = UT_LIST_GET_FIRST(sys_foreign->indexes); ut_a(!dict_table_is_comp(sys_foreign)); tuple = dtuple_create(heap2, 1); dfield = dtuple_get_nth_field(tuple, 0); dfield_set_data(dfield, id, ut_strlen(id)); dict_index_copy_types(tuple, sys_index, 1); btr_pcur_open_on_user_rec(sys_index, tuple, PAGE_CUR_GE, BTR_SEARCH_LEAF, &pcur, &mtr); rec = btr_pcur_get_rec(&pcur); if (!btr_pcur_is_on_user_rec(&pcur) || rec_get_deleted_flag(rec, 0)) { /* Not found */ ib_logger(ib_stream, "InnoDB: Error A: cannot load foreign constraint %s\n", id); btr_pcur_close(&pcur); mtr_commit(&mtr); mem_heap_free(heap2); return(DB_ERROR); } field = rec_get_nth_field_old(rec, 0, &len); /* Check if the id in record is the searched one */ if (len != ut_strlen(id) || ut_memcmp(id, field, len) != 0) { ib_logger(ib_stream, "InnoDB: Error B: cannot load foreign constraint %s\n", id); btr_pcur_close(&pcur); mtr_commit(&mtr); mem_heap_free(heap2); return(DB_ERROR); } /* Read the table names and the number of columns associated with the constraint */ mem_heap_free(heap2); foreign = dict_mem_foreign_create(); n_fields_and_type = mach_read_from_4( rec_get_nth_field_old(rec, 5, &len)); ut_a(len == 4); /* We store the type in the bits 24..29 of n_fields_and_type. */ foreign->type = (unsigned int) (n_fields_and_type >> 24); foreign->n_fields = (unsigned int) (n_fields_and_type & 0x3FFUL); foreign->id = mem_heap_strdup(foreign->heap, id); field = rec_get_nth_field_old(rec, 3, &len); foreign->foreign_table_name = mem_heap_strdupl( foreign->heap, (char*) field, len); field = rec_get_nth_field_old(rec, 4, &len); foreign->referenced_table_name = mem_heap_strdupl( foreign->heap, (char*) field, len); btr_pcur_close(&pcur); mtr_commit(&mtr); dict_load_foreign_cols(id, foreign); /* If the foreign table is not yet in the dictionary cache, we have to load it so that we are able to make type comparisons in the next function call. */ dict_table_get_low(foreign->foreign_table_name); /* Note that there may already be a foreign constraint object in the dictionary cache for this constraint: then the following call only sets the pointers in it to point to the appropriate table and index objects and frees the newly created object foreign. Adding to the cache should always succeed since we are not creating a new foreign key constraint but loading one from the data dictionary. */ return(dict_foreign_add_to_cache(foreign, check_charsets)); } /***********************************************************************//** Loads foreign key constraints where the table is either the foreign key holder or where the table is referenced by a foreign key. Adds these constraints to the data dictionary. Note that we know that the dictionary cache already contains all constraints where the other relevant table is already in the dictionary cache. @return DB_SUCCESS or error code */ UNIV_INTERN ulint dict_load_foreigns( /*===============*/ const char* table_name, /*!< in: table name */ ibool check_charsets) /*!< in: TRUE=check charset compatibility */ { btr_pcur_t pcur; mem_heap_t* heap; dtuple_t* tuple; dfield_t* dfield; dict_index_t* sec_index; dict_table_t* sys_foreign; const rec_t* rec; const byte* field; ulint len; char* id ; ulint err; mtr_t mtr; ut_ad(mutex_own(&(dict_sys->mutex))); sys_foreign = dict_table_get_low("SYS_FOREIGN"); if (sys_foreign == NULL) { /* No foreign keys defined yet in this database */ ib_logger(ib_stream, "InnoDB: Error: no foreign key system tables" " in the database\n"); return(DB_ERROR); } ut_a(!dict_table_is_comp(sys_foreign)); mtr_start(&mtr); /* Get the secondary index based on FOR_NAME from table SYS_FOREIGN */ sec_index = dict_table_get_next_index( dict_table_get_first_index(sys_foreign)); start_load: heap = mem_heap_create(256); tuple = dtuple_create(heap, 1); dfield = dtuple_get_nth_field(tuple, 0); dfield_set_data(dfield, table_name, ut_strlen(table_name)); dict_index_copy_types(tuple, sec_index, 1); btr_pcur_open_on_user_rec(sec_index, tuple, PAGE_CUR_GE, BTR_SEARCH_LEAF, &pcur, &mtr); loop: rec = btr_pcur_get_rec(&pcur); if (!btr_pcur_is_on_user_rec(&pcur)) { /* End of index */ goto load_next_index; } /* Now we have the record in the secondary index containing a table name and a foreign constraint ID */ rec = btr_pcur_get_rec(&pcur); field = rec_get_nth_field_old(rec, 0, &len); /* Check if the table name in the record is the one searched for; the following call does the comparison in the latin1_swedish_ci charset-collation, in a case-insensitive way. */ if (0 != cmp_data_data(NULL, dfield_get_type(dfield)->mtype, dfield_get_type(dfield)->prtype, dfield_get_data(dfield), dfield_get_len(dfield), field, len)) { goto load_next_index; } /* Since table names in SYS_FOREIGN are stored in a case-insensitive order, we have to check that the table name matches also in a binary string comparison. */ /* FIXME: On Unix, allow table names that only differ in character case. */ if (0 != ut_memcmp(field, table_name, len)) { goto next_rec; } if (rec_get_deleted_flag(rec, 0)) { goto next_rec; } /* Now we get a foreign key constraint id */ field = rec_get_nth_field_old(rec, 1, &len); id = mem_heap_strdupl(heap, (char*) field, len); btr_pcur_store_position(&pcur, &mtr); mtr_commit(&mtr); /* Load the foreign constraint definition to the dictionary cache */ err = dict_load_foreign(id, check_charsets); if (err != DB_SUCCESS) { btr_pcur_close(&pcur); mem_heap_free(heap); return(err); } mtr_start(&mtr); btr_pcur_restore_position(BTR_SEARCH_LEAF, &pcur, &mtr); next_rec: btr_pcur_move_to_next_user_rec(&pcur, &mtr); goto loop; load_next_index: btr_pcur_close(&pcur); mtr_commit(&mtr); mem_heap_free(heap); sec_index = dict_table_get_next_index(sec_index); if (sec_index != NULL) { mtr_start(&mtr); goto start_load; } return(DB_SUCCESS); } haildb-2.3.2/dict/dict0boot.c0000644000175000017500000003236011513177357016625 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2010, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file dict/dict0boot.c Data dictionary creation and booting Created 4/18/1996 Heikki Tuuri *******************************************************/ #include "dict0boot.h" #ifdef UNIV_NONINL #include "dict0boot.ic" #endif #include "dict0crea.h" #include "btr0btr.h" #include "dict0load.h" #include "dict0load.h" #include "trx0trx.h" #include "srv0srv.h" #include "ibuf0ibuf.h" #include "buf0flu.h" #include "log0recv.h" #include "os0file.h" /**********************************************************************//** Gets a pointer to the dictionary header and x-latches its page. @return pointer to the dictionary header, page x-latched */ UNIV_INTERN dict_hdr_t* dict_hdr_get( /*=========*/ mtr_t* mtr) /*!< in: mtr */ { buf_block_t* block; dict_hdr_t* header; block = buf_page_get(DICT_HDR_SPACE, 0, DICT_HDR_PAGE_NO, RW_X_LATCH, mtr); header = DICT_HDR + buf_block_get_frame(block); buf_block_dbg_add_level(block, SYNC_DICT_HEADER); return(header); } /**********************************************************************//** Returns a new table, index, or tree id. @return the new id */ UNIV_INTERN dulint dict_hdr_get_new_id( /*================*/ ulint type) /*!< in: DICT_HDR_ROW_ID, ... */ { dict_hdr_t* dict_hdr; dulint id; mtr_t mtr; ut_ad((type == DICT_HDR_TABLE_ID) || (type == DICT_HDR_INDEX_ID)); mtr_start(&mtr); dict_hdr = dict_hdr_get(&mtr); id = mtr_read_dulint(dict_hdr + type, &mtr); id = ut_dulint_add(id, 1); mlog_write_dulint(dict_hdr + type, id, &mtr); mtr_commit(&mtr); return(id); } /**********************************************************************//** Writes the current value of the row id counter to the dictionary header file page. */ UNIV_INTERN void dict_hdr_flush_row_id(void) /*=======================*/ { dict_hdr_t* dict_hdr; dulint id; mtr_t mtr; ut_ad(mutex_own(&(dict_sys->mutex))); id = dict_sys->row_id; mtr_start(&mtr); dict_hdr = dict_hdr_get(&mtr); mlog_write_dulint(dict_hdr + DICT_HDR_ROW_ID, id, &mtr); mtr_commit(&mtr); } /*****************************************************************//** Creates the file page for the dictionary header. This function is called only at the database creation. @return TRUE if succeed */ static ibool dict_hdr_create( /*============*/ mtr_t* mtr) /*!< in: mtr */ { buf_block_t* block; dict_hdr_t* dict_header; ulint root_page_no; ut_ad(mtr); /* Create the dictionary header file block in a new, allocated file segment in the system tablespace */ block = fseg_create(DICT_HDR_SPACE, 0, DICT_HDR + DICT_HDR_FSEG_HEADER, mtr); ut_a(DICT_HDR_PAGE_NO == buf_block_get_page_no(block)); dict_header = dict_hdr_get(mtr); /* Start counting row, table, index, and tree ids from DICT_HDR_FIRST_ID */ mlog_write_dulint(dict_header + DICT_HDR_ROW_ID, ut_dulint_create(0, DICT_HDR_FIRST_ID), mtr); mlog_write_dulint(dict_header + DICT_HDR_TABLE_ID, ut_dulint_create(0, DICT_HDR_FIRST_ID), mtr); mlog_write_dulint(dict_header + DICT_HDR_INDEX_ID, ut_dulint_create(0, DICT_HDR_FIRST_ID), mtr); /* Obsolete, but we must initialize it to 0 anyway. */ mlog_write_dulint(dict_header + DICT_HDR_MIX_ID, ut_dulint_create(0, DICT_HDR_FIRST_ID), mtr); /* Create the B-tree roots for the clustered indexes of the basic system tables */ /*--------------------------*/ root_page_no = btr_create(DICT_CLUSTERED | DICT_UNIQUE, DICT_HDR_SPACE, 0, DICT_TABLES_ID, dict_ind_redundant, mtr); if (root_page_no == FIL_NULL) { return(FALSE); } mlog_write_ulint(dict_header + DICT_HDR_TABLES, root_page_no, MLOG_4BYTES, mtr); /*--------------------------*/ root_page_no = btr_create(DICT_UNIQUE, DICT_HDR_SPACE, 0, DICT_TABLE_IDS_ID, dict_ind_redundant, mtr); if (root_page_no == FIL_NULL) { return(FALSE); } mlog_write_ulint(dict_header + DICT_HDR_TABLE_IDS, root_page_no, MLOG_4BYTES, mtr); /*--------------------------*/ root_page_no = btr_create(DICT_CLUSTERED | DICT_UNIQUE, DICT_HDR_SPACE, 0, DICT_COLUMNS_ID, dict_ind_redundant, mtr); if (root_page_no == FIL_NULL) { return(FALSE); } mlog_write_ulint(dict_header + DICT_HDR_COLUMNS, root_page_no, MLOG_4BYTES, mtr); /*--------------------------*/ root_page_no = btr_create(DICT_CLUSTERED | DICT_UNIQUE, DICT_HDR_SPACE, 0, DICT_INDEXES_ID, dict_ind_redundant, mtr); if (root_page_no == FIL_NULL) { return(FALSE); } mlog_write_ulint(dict_header + DICT_HDR_INDEXES, root_page_no, MLOG_4BYTES, mtr); /*--------------------------*/ root_page_no = btr_create(DICT_CLUSTERED | DICT_UNIQUE, DICT_HDR_SPACE, 0, DICT_FIELDS_ID, dict_ind_redundant, mtr); if (root_page_no == FIL_NULL) { return(FALSE); } mlog_write_ulint(dict_header + DICT_HDR_FIELDS, root_page_no, MLOG_4BYTES, mtr); /*--------------------------*/ return(TRUE); } /*****************************************************************//** Initializes the data dictionary memory structures when the database is started. This function is also called when the data dictionary is created. */ UNIV_INTERN void dict_boot(void) /*===========*/ { dict_table_t* table; dict_index_t* index; dict_hdr_t* dict_hdr; mem_heap_t* heap; mtr_t mtr; ulint error; mtr_start(&mtr); /* Create the hash tables etc. */ dict_init(); heap = mem_heap_create(450); mutex_enter(&(dict_sys->mutex)); /* Get the dictionary header */ dict_hdr = dict_hdr_get(&mtr); /* Because we only write new row ids to disk-based data structure (dictionary header) when it is divisible by DICT_HDR_ROW_ID_WRITE_MARGIN, in recovery we will not recover the latest value of the row id counter. Therefore we advance the counter at the database startup to avoid overlapping values. Note that when a user after database startup first time asks for a new row id, then because the counter is now divisible by ..._MARGIN, it will immediately be updated to the disk-based header. */ dict_sys->row_id = ut_dulint_add( ut_dulint_align_up(mtr_read_dulint(dict_hdr + DICT_HDR_ROW_ID, &mtr), DICT_HDR_ROW_ID_WRITE_MARGIN), DICT_HDR_ROW_ID_WRITE_MARGIN); /* Insert into the dictionary cache the descriptions of the basic system tables */ /*-------------------------*/ table = dict_mem_table_create("SYS_TABLES", DICT_HDR_SPACE, 8, 0); dict_mem_table_add_col(table, heap, "NAME", DATA_BINARY, 0, 0); dict_mem_table_add_col(table, heap, "ID", DATA_BINARY, 0, 0); /* ROW_FORMAT = (N_COLS >> 31) ? COMPACT : REDUNDANT */ dict_mem_table_add_col(table, heap, "N_COLS", DATA_INT, 0, 4); /* TYPE is either DICT_TABLE_ORDINARY, or (TYPE & DICT_TF_COMPACT) and (TYPE & DICT_TF_FORMAT_MASK) are nonzero and TYPE = table->flags */ dict_mem_table_add_col(table, heap, "TYPE", DATA_INT, 0, 4); dict_mem_table_add_col(table, heap, "MIX_ID", DATA_BINARY, 0, 0); /* MIX_LEN may contain additional table flags when ROW_FORMAT!=REDUNDANT. Currently, these flags include DICT_TF2_TEMPORARY. */ dict_mem_table_add_col(table, heap, "MIX_LEN", DATA_INT, 0, 4); dict_mem_table_add_col(table, heap, "CLUSTER_NAME", DATA_BINARY, 0, 0); dict_mem_table_add_col(table, heap, "SPACE", DATA_INT, 0, 4); table->id = DICT_TABLES_ID; dict_table_add_to_cache(table, heap); dict_sys->sys_tables = table; mem_heap_empty(heap); index = dict_mem_index_create("SYS_TABLES", "CLUST_IND", DICT_HDR_SPACE, DICT_UNIQUE | DICT_CLUSTERED, 1); dict_mem_index_add_field(index, "NAME", 0); index->id = DICT_TABLES_ID; error = dict_index_add_to_cache(table, index, mtr_read_ulint(dict_hdr + DICT_HDR_TABLES, MLOG_4BYTES, &mtr), FALSE); ut_a(error == DB_SUCCESS); /*-------------------------*/ index = dict_mem_index_create("SYS_TABLES", "ID_IND", DICT_HDR_SPACE, DICT_UNIQUE, 1); dict_mem_index_add_field(index, "ID", 0); index->id = DICT_TABLE_IDS_ID; error = dict_index_add_to_cache(table, index, mtr_read_ulint(dict_hdr + DICT_HDR_TABLE_IDS, MLOG_4BYTES, &mtr), FALSE); ut_a(error == DB_SUCCESS); /*-------------------------*/ table = dict_mem_table_create("SYS_COLUMNS", DICT_HDR_SPACE, 7, 0); dict_mem_table_add_col(table, heap, "TABLE_ID", DATA_BINARY, 0, 0); dict_mem_table_add_col(table, heap, "POS", DATA_INT, 0, 4); dict_mem_table_add_col(table, heap, "NAME", DATA_BINARY, 0, 0); dict_mem_table_add_col(table, heap, "MTYPE", DATA_INT, 0, 4); dict_mem_table_add_col(table, heap, "PRTYPE", DATA_INT, 0, 4); dict_mem_table_add_col(table, heap, "LEN", DATA_INT, 0, 4); dict_mem_table_add_col(table, heap, "PREC", DATA_INT, 0, 4); table->id = DICT_COLUMNS_ID; dict_table_add_to_cache(table, heap); dict_sys->sys_columns = table; mem_heap_empty(heap); index = dict_mem_index_create("SYS_COLUMNS", "CLUST_IND", DICT_HDR_SPACE, DICT_UNIQUE | DICT_CLUSTERED, 2); dict_mem_index_add_field(index, "TABLE_ID", 0); dict_mem_index_add_field(index, "POS", 0); index->id = DICT_COLUMNS_ID; error = dict_index_add_to_cache(table, index, mtr_read_ulint(dict_hdr + DICT_HDR_COLUMNS, MLOG_4BYTES, &mtr), FALSE); ut_a(error == DB_SUCCESS); /*-------------------------*/ table = dict_mem_table_create("SYS_INDEXES", DICT_HDR_SPACE, 7, 0); dict_mem_table_add_col(table, heap, "TABLE_ID", DATA_BINARY, 0, 0); dict_mem_table_add_col(table, heap, "ID", DATA_BINARY, 0, 0); dict_mem_table_add_col(table, heap, "NAME", DATA_BINARY, 0, 0); dict_mem_table_add_col(table, heap, "N_FIELDS", DATA_INT, 0, 4); dict_mem_table_add_col(table, heap, "TYPE", DATA_INT, 0, 4); dict_mem_table_add_col(table, heap, "SPACE", DATA_INT, 0, 4); dict_mem_table_add_col(table, heap, "PAGE_NO", DATA_INT, 0, 4); /* The '+ 2' below comes from the fields DB_TRX_ID, DB_ROLL_PTR */ #if DICT_SYS_INDEXES_PAGE_NO_FIELD != 6 + 2 #error "DICT_SYS_INDEXES_PAGE_NO_FIELD != 6 + 2" #endif #if DICT_SYS_INDEXES_SPACE_NO_FIELD != 5 + 2 #error "DICT_SYS_INDEXES_SPACE_NO_FIELD != 5 + 2" #endif #if DICT_SYS_INDEXES_TYPE_FIELD != 4 + 2 #error "DICT_SYS_INDEXES_TYPE_FIELD != 4 + 2" #endif #if DICT_SYS_INDEXES_NAME_FIELD != 2 + 2 #error "DICT_SYS_INDEXES_NAME_FIELD != 2 + 2" #endif table->id = DICT_INDEXES_ID; dict_table_add_to_cache(table, heap); dict_sys->sys_indexes = table; mem_heap_empty(heap); index = dict_mem_index_create("SYS_INDEXES", "CLUST_IND", DICT_HDR_SPACE, DICT_UNIQUE | DICT_CLUSTERED, 2); dict_mem_index_add_field(index, "TABLE_ID", 0); dict_mem_index_add_field(index, "ID", 0); index->id = DICT_INDEXES_ID; error = dict_index_add_to_cache(table, index, mtr_read_ulint(dict_hdr + DICT_HDR_INDEXES, MLOG_4BYTES, &mtr), FALSE); ut_a(error == DB_SUCCESS); /*-------------------------*/ table = dict_mem_table_create("SYS_FIELDS", DICT_HDR_SPACE, 3, 0); dict_mem_table_add_col(table, heap, "INDEX_ID", DATA_BINARY, 0, 0); dict_mem_table_add_col(table, heap, "POS", DATA_INT, 0, 4); dict_mem_table_add_col(table, heap, "COL_NAME", DATA_BINARY, 0, 0); table->id = DICT_FIELDS_ID; dict_table_add_to_cache(table, heap); dict_sys->sys_fields = table; mem_heap_free(heap); index = dict_mem_index_create("SYS_FIELDS", "CLUST_IND", DICT_HDR_SPACE, DICT_UNIQUE | DICT_CLUSTERED, 2); dict_mem_index_add_field(index, "INDEX_ID", 0); dict_mem_index_add_field(index, "POS", 0); index->id = DICT_FIELDS_ID; error = dict_index_add_to_cache(table, index, mtr_read_ulint(dict_hdr + DICT_HDR_FIELDS, MLOG_4BYTES, &mtr), FALSE); ut_a(error == DB_SUCCESS); mtr_commit(&mtr); /*-------------------------*/ /* Initialize the insert buffer table and index for each tablespace */ ibuf_init_at_db_start(); /* Load definitions of other indexes on system tables */ dict_load_sys_table(dict_sys->sys_tables); dict_load_sys_table(dict_sys->sys_columns); dict_load_sys_table(dict_sys->sys_indexes); dict_load_sys_table(dict_sys->sys_fields); mutex_exit(&(dict_sys->mutex)); } /*****************************************************************//** Inserts the basic system table data into themselves in the database creation. */ static void dict_insert_initial_data(void) /*==========================*/ { /* Does nothing yet */ } /*****************************************************************//** Creates and initializes the data dictionary at the database creation. */ UNIV_INTERN void dict_create(void) /*=============*/ { mtr_t mtr; mtr_start(&mtr); dict_hdr_create(&mtr); mtr_commit(&mtr); dict_boot(); dict_insert_initial_data(); } haildb-2.3.2/dict/dict0mem.c0000644000175000017500000001747511513177357016452 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /******************************************************************//** @file dict/dict0mem.c Data dictionary memory object creation Created 1/8/1996 Heikki Tuuri ***********************************************************************/ #include "dict0mem.h" #ifdef UNIV_NONINL #include "dict0mem.ic" #endif #include "rem0rec.h" #include "data0type.h" #include "mach0data.h" #include "dict0dict.h" #ifndef UNIV_HOTBACKUP # include "lock0lock.h" #endif /* !UNIV_HOTBACKUP */ #define DICT_HEAP_SIZE 100 /*!< initial memory heap size when creating a table or index object */ /**********************************************************************//** Creates a table memory object. @return own: table object */ UNIV_INTERN dict_table_t* dict_mem_table_create( /*==================*/ const char* name, /*!< in: table name */ ulint space, /*!< in: space where the clustered index of the table is placed; this parameter is ignored if the table is made a member of a cluster */ ulint n_cols, /*!< in: number of columns */ ulint flags) /*!< in: table flags */ { dict_table_t* table; mem_heap_t* heap; ut_ad(name); ut_a(!(flags & (~0 << DICT_TF2_BITS))); heap = mem_heap_create(DICT_HEAP_SIZE); table = mem_heap_zalloc(heap, sizeof(dict_table_t)); table->heap = heap; table->flags = (unsigned int) flags; table->name = mem_heap_strdup(heap, name); table->space = (unsigned int) space; table->n_cols = (unsigned int) (n_cols + DATA_N_SYS_COLS); table->cols = mem_heap_alloc(heap, (n_cols + DATA_N_SYS_COLS) * sizeof(dict_col_t)); ut_d(table->magic_n = DICT_TABLE_MAGIC_N); return(table); } /****************************************************************//** Free a table memory object. */ UNIV_INTERN void dict_mem_table_free( /*================*/ dict_table_t* table) /*!< in: table */ { ut_ad(table); ut_ad(table->magic_n == DICT_TABLE_MAGIC_N); ut_d(table->cached = FALSE); mem_heap_free(table->heap); } /****************************************************************//** Append 'name' to 'col_names'. @see dict_table_t::col_names @return new column names array */ static const char* dict_add_col_name( /*==============*/ const char* col_names, /*!< in: existing column names, or NULL */ ulint cols, /*!< in: number of existing columns */ const char* name, /*!< in: new column name */ mem_heap_t* heap) /*!< in: heap */ { ulint old_len; ulint new_len; ulint total_len; char* res; ut_ad(!cols == !col_names); /* Find out length of existing array. */ if (col_names) { const char* s = col_names; ulint i; for (i = 0; i < cols; i++) { s += strlen(s) + 1; } old_len = s - col_names; } else { old_len = 0; } new_len = strlen(name) + 1; total_len = old_len + new_len; res = mem_heap_alloc(heap, total_len); if (old_len > 0) { memcpy(res, col_names, old_len); } memcpy(res + old_len, name, new_len); return(res); } /**********************************************************************//** Adds a column definition to a table. */ UNIV_INTERN void dict_mem_table_add_col( /*===================*/ dict_table_t* table, /*!< in: table */ mem_heap_t* heap, /*!< in: temporary memory heap, or NULL */ const char* name, /*!< in: column name, or NULL */ ulint mtype, /*!< in: main datatype */ ulint prtype, /*!< in: precise type */ ulint len) /*!< in: precision */ { dict_col_t* col; #ifndef UNIV_HOTBACKUP ulint mbminlen; ulint mbmaxlen; #endif /* !UNIV_HOTBACKUP */ ulint i; ut_ad(table); ut_ad(table->magic_n == DICT_TABLE_MAGIC_N); ut_ad(!heap == !name); i = table->n_def++; if (name) { if (UNIV_UNLIKELY(table->n_def == table->n_cols)) { heap = table->heap; } if (UNIV_LIKELY(i) && UNIV_UNLIKELY(!table->col_names)) { /* All preceding column names are empty. */ char* s = mem_heap_zalloc(heap, table->n_def); table->col_names = s; } table->col_names = dict_add_col_name(table->col_names, i, name, heap); } col = dict_table_get_nth_col(table, i); col->ind = (unsigned int) i; col->ord_part = 0; col->mtype = (unsigned int) mtype; col->prtype = (unsigned int) prtype; col->len = (unsigned int) len; #ifndef UNIV_HOTBACKUP dtype_get_mblen(mtype, prtype, &mbminlen, &mbmaxlen); col->mbminlen = (unsigned int) mbminlen; col->mbmaxlen = (unsigned int) mbmaxlen; #endif /* !UNIV_HOTBACKUP */ } /**********************************************************************//** Creates an index memory object. @return own: index object */ UNIV_INTERN dict_index_t* dict_mem_index_create( /*==================*/ const char* table_name, /*!< in: table name */ const char* index_name, /*!< in: index name */ ulint space, /*!< in: space where the index tree is placed, ignored if the index is of the clustered type */ ulint type, /*!< in: DICT_UNIQUE, DICT_CLUSTERED, ... ORed */ ulint n_fields) /*!< in: number of fields */ { dict_index_t* index; mem_heap_t* heap; ut_ad(table_name && index_name); heap = mem_heap_create(DICT_HEAP_SIZE); index = mem_heap_zalloc(heap, sizeof(dict_index_t)); index->heap = heap; index->type = type; #ifndef UNIV_HOTBACKUP index->space = (unsigned int) space; #endif /* !UNIV_HOTBACKUP */ index->name = mem_heap_strdup(heap, index_name); index->table_name = table_name; index->n_fields = (unsigned int) n_fields; index->fields = mem_heap_alloc(heap, 1 + n_fields * sizeof(dict_field_t)); /* The '1 +' above prevents allocation of an empty mem block */ #ifdef UNIV_DEBUG index->magic_n = DICT_INDEX_MAGIC_N; #endif /* UNIV_DEBUG */ return(index); } /**********************************************************************//** Creates and initializes a foreign constraint memory object. @return own: foreign constraint struct */ UNIV_INTERN dict_foreign_t* dict_mem_foreign_create(void) /*=========================*/ { dict_foreign_t* foreign; mem_heap_t* heap; heap = mem_heap_create(100); foreign = mem_heap_zalloc(heap, sizeof(dict_foreign_t)); foreign->heap = heap; return(foreign); } /**********************************************************************//** Adds a field definition to an index. NOTE: does not take a copy of the column name if the field is a column. The memory occupied by the column name may be released only after publishing the index. */ UNIV_INTERN void dict_mem_index_add_field( /*=====================*/ dict_index_t* index, /*!< in: index */ const char* name, /*!< in: column name */ ulint prefix_len) /*!< in: 0 or the column prefix length in a column prefix index like INDEX (textcol(25)) */ { dict_field_t* field; ut_ad(index); ut_ad(index->magic_n == DICT_INDEX_MAGIC_N); index->n_def++; field = dict_index_get_nth_field(index, index->n_def - 1); field->name = name; field->prefix_len = (unsigned int) prefix_len; } /**********************************************************************//** Frees an index memory object. */ UNIV_INTERN void dict_mem_index_free( /*================*/ dict_index_t* index) /*!< in: index */ { ut_ad(index); ut_ad(index->magic_n == DICT_INDEX_MAGIC_N); mem_heap_free(index->heap); } haildb-2.3.2/dict/dict0dict.c0000644000175000017500000037774611513177357016631 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2010, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /******************************************************************//** @file dict/dict0dict.c Data dictionary system Created 1/8/1996 Heikki Tuuri ***********************************************************************/ #include "dict0dict.h" #ifdef UNIV_NONINL #include "dict0dict.ic" #endif /** dummy index for ROW_FORMAT=REDUNDANT supremum and infimum records */ UNIV_INTERN dict_index_t* dict_ind_redundant; /** dummy index for ROW_FORMAT=COMPACT supremum and infimum records */ UNIV_INTERN dict_index_t* dict_ind_compact; #ifndef UNIV_HOTBACKUP #include "buf0buf.h" #include "data0type.h" #include "mach0data.h" #include "dict0boot.h" #include "dict0mem.h" #include "dict0crea.h" #include "trx0undo.h" #include "btr0btr.h" #include "btr0cur.h" #include "btr0sea.h" #include "page0zip.h" #include "page0page.h" #include "pars0pars.h" #include "pars0sym.h" #include "que0que.h" #include "rem0cmp.h" #include "row0merge.h" #include "srv0start.h" #include "api0ucode.h" /* ib_utf8_strcasecmp() */ #include /** the dictionary system */ UNIV_INTERN dict_sys_t* dict_sys = NULL; /** @brief the data dictionary rw-latch protecting dict_sys table create, drop, etc. reserve this in X-mode; implicit or backround operations purge, rollback, foreign key checks reserve this in S-mode; we cannot trust that the client protects implicit or background operations a table drop since the client does not know about them; therefore we need this; NOTE: a transaction which reserves this must keep book on the mode in trx_struct::dict_operation_lock_mode */ UNIV_INTERN rw_lock_t dict_operation_lock; #define DICT_HEAP_SIZE 100 /*!< initial memory heap size when creating a table or index object */ #define DICT_POOL_PER_TABLE_HASH 512 /*!< buffer pool max size per table hash table fixed size in bytes */ #define DICT_POOL_PER_VARYING 4 /*!< buffer pool max size per data dictionary varying size in bytes */ /** Identifies generated InnoDB foreign key names */ static char dict_ibfk[] = "_ibfk_"; /** array of mutexes protecting dict_index_t::stat_n_diff_key_vals[] */ #define DICT_INDEX_STAT_MUTEX_SIZE 32 UNIV_INTERN mutex_t dict_index_stat_mutex[DICT_INDEX_STAT_MUTEX_SIZE]; /*******************************************************************//** Tries to find column names for the index and sets the col field of the index. @return TRUE if the column names were found */ static ibool dict_index_find_cols( /*=================*/ dict_table_t* table, /*!< in: table */ dict_index_t* index); /*!< in: index */ /*******************************************************************//** Builds the internal dictionary cache representation for a clustered index, containing also system fields not defined by the user. @return own: the internal representation of the clustered index */ static dict_index_t* dict_index_build_internal_clust( /*============================*/ const dict_table_t* table, /*!< in: table */ dict_index_t* index); /*!< in: user representation of a clustered index */ /*******************************************************************//** Builds the internal dictionary cache representation for a non-clustered index, containing also system fields not defined by the user. @return own: the internal representation of the non-clustered index */ static dict_index_t* dict_index_build_internal_non_clust( /*================================*/ const dict_table_t* table, /*!< in: table */ dict_index_t* index); /*!< in: user representation of a non-clustered index */ /**********************************************************************//** Removes a foreign constraint struct from the dictionary cache. */ static void dict_foreign_remove_from_cache( /*===========================*/ dict_foreign_t* foreign); /*!< in, own: foreign constraint */ /**********************************************************************//** Prints a column data. */ static void dict_col_print_low( /*===============*/ const dict_table_t* table, /*!< in: table */ const dict_col_t* col); /*!< in: column */ /**********************************************************************//** Prints an index data. */ static void dict_index_print_low( /*=================*/ dict_index_t* index); /*!< in: index */ /**********************************************************************//** Prints a field data. */ static void dict_field_print_low( /*=================*/ const dict_field_t* field); /*!< in: field */ /*********************************************************************//** Frees a foreign key struct. */ static void dict_foreign_free( /*==============*/ dict_foreign_t* foreign); /*!< in, own: foreign key struct */ /* mutex protecting the foreign and unique error buffers */ UNIV_INTERN mutex_t dict_foreign_err_mutex; /******************************************************************//** Reset dict variables. */ UNIV_INTERN void dict_var_init(void) /*===============*/ { dict_sys = NULL; memset(&dict_operation_lock, 0x0, sizeof(dict_operation_lock)); memset(&dict_foreign_err_mutex, 0x0, sizeof(dict_foreign_err_mutex)); } /********************************************************************** Makes all characters in a NUL-terminated UTF-8 string lower case. */ UNIV_INTERN void dict_casedn_str( /*============*/ char* a) /*!< in/out: string to put in lower case */ { ib_utf8_casedown(a); } /********************************************************************//** Checks if the database name in two table names is the same. @return TRUE if same db name */ UNIV_INTERN ibool dict_tables_have_same_db( /*=====================*/ const char* name1, /*!< in: table name in the form dbname '/' tablename */ const char* name2) /*!< in: table name in the form dbname '/' tablename */ { for (; *name1 == *name2; name1++, name2++) { if (*name1 == '/') { return(TRUE); } ut_a(*name1); /* the names must contain '/' */ } return(FALSE); } /********************************************************************//** Return the end of table name where we have removed dbname and '/'. @return table name */ UNIV_INTERN const char* dict_remove_db_name( /*================*/ const char* name) /*!< in: table name in the form dbname '/' tablename */ { const char* s = strchr(name, '/'); ut_a(s); return(s + 1); } /********************************************************************//** Get the database name length in a table name. @return database name length */ UNIV_INTERN ulint dict_get_db_name_len( /*=================*/ const char* name) /*!< in: table name in the form dbname '/' tablename */ { const char* s; s = strchr(name, '/'); ut_a(s); return(s - name); } /********************************************************************//** Reserves the dictionary system mutex for client. */ UNIV_INTERN void dict_mutex_enter(void) /*==================*/ { mutex_enter(&(dict_sys->mutex)); } /** Get the mutex that protects index->stat_n_diff_key_vals[] */ #define GET_INDEX_STAT_MUTEX(index) \ (&dict_index_stat_mutex[ut_fold_dulint(index->id) \ % DICT_INDEX_STAT_MUTEX_SIZE]) /**********************************************************************//** Lock the appropriate mutex to protect index->stat_n_diff_key_vals[]. index->id is used to pick the right mutex and it should not change before dict_index_stat_mutex_exit() is called on this index. */ UNIV_INTERN void dict_index_stat_mutex_enter( /*========================*/ const dict_index_t* index) /*!< in: index */ { ut_ad(index != NULL); ut_ad(index->magic_n == DICT_INDEX_MAGIC_N); ut_ad(index->cached); ut_ad(!index->to_be_dropped); mutex_enter(GET_INDEX_STAT_MUTEX(index)); } /**********************************************************************//** Unlock the appropriate mutex that protects index->stat_n_diff_key_vals[]. */ UNIV_INTERN void dict_index_stat_mutex_exit( /*=======================*/ const dict_index_t* index) /*!< in: index */ { ut_ad(index != NULL); ut_ad(index->magic_n == DICT_INDEX_MAGIC_N); ut_ad(index->cached); ut_ad(!index->to_be_dropped); mutex_exit(GET_INDEX_STAT_MUTEX(index)); } /********************************************************************//** Releases the dictionary system mutex for client. */ UNIV_INTERN void dict_mutex_exit(void) /*================*/ { mutex_exit(&(dict_sys->mutex)); } /********************************************************************//** Decrements the count of open client handles to a table. */ UNIV_INTERN void dict_table_decrement_handle_count( /*==============================*/ dict_table_t* table, /*!< in/out: table */ ibool dict_locked) /*!< in: TRUE=data dictionary locked */ { if (!dict_locked) { mutex_enter(&dict_sys->mutex); } ut_ad(mutex_own(&dict_sys->mutex)); ut_a(table->n_handles_opened > 0); table->n_handles_opened--; if (!dict_locked) { mutex_exit(&dict_sys->mutex); } } /************************************************************************ Increments the count of open client handles to a table. */ UNIV_INTERN void dict_table_increment_handle_count( /*==============================*/ dict_table_t* table, /*!< in/out: table */ ibool dict_locked) /*!< in: TRUE=data dictionary locked */ { if (!dict_locked) { mutex_enter(&dict_sys->mutex); } ut_ad(mutex_own(&dict_sys->mutex)); table->n_handles_opened++; if (!dict_locked) { mutex_exit(&dict_sys->mutex); } } #endif /* !UNIV_HOTBACKUP */ /**********************************************************************//** Returns a column's name. @return column name. NOTE: not guaranteed to stay valid if table is modified in any way (columns added, etc.). */ UNIV_INTERN const char* dict_table_get_col_name( /*====================*/ const dict_table_t* table, /*!< in: table */ ulint col_nr) /*!< in: column number */ { ulint i; const char* s; ut_ad(table); ut_ad(col_nr < table->n_def); ut_ad(table->magic_n == DICT_TABLE_MAGIC_N); s = table->col_names; if (s) { for (i = 0; i < col_nr; i++) { s += strlen(s) + 1; } } return(s); } #ifndef UNIV_HOTBACKUP /************************************************************************** Returns a column's ordinal value. @return column pos. -1 if not found. NOTE: not guaranteed to stay valid if table is modified in any way (columns added, etc.). */ UNIV_INTERN int dict_table_get_col_no( /*==================*/ const dict_table_t* table, /*!< in: table */ const char* name) /*!< in: column name */ { ulint i; const char* s; ut_ad(table->magic_n == DICT_TABLE_MAGIC_N); s = table->col_names; if (s) { for (i = 0; i < table->n_def; i++, s += strlen(s) + 1) { if (strcmp(s, name) == 0) { return(i); } } } return(-1); } /**********************************************************************//** Looks for an index with the given table and index id. NOTE that we do not reserve the dictionary mutex. @return index or NULL if not found from cache */ UNIV_INTERN dict_index_t* dict_index_get_on_id_low( /*=====================*/ dict_table_t* table, /*!< in: table */ dulint id) /*!< in: index id */ { dict_index_t* dict_index; dict_index = dict_table_get_first_index(table); while (dict_index) { if (0 == ut_dulint_cmp(id, dict_index->id)) { /* Found */ return(dict_index); } dict_index = dict_table_get_next_index(dict_index); } return(NULL); } #endif /* !UNIV_HOTBACKUP */ /********************************************************************//** Looks for column n in an index. @return position in internal representation of the index; ULINT_UNDEFINED if not contained */ UNIV_INTERN ulint dict_index_get_nth_col_pos( /*=======================*/ const dict_index_t* dict_index, /*!< in: index */ ulint n) /*!< in: column number */ { const dict_field_t* field; const dict_col_t* col; ulint pos; ulint n_fields; ut_ad(dict_index); ut_ad(dict_index->magic_n == DICT_INDEX_MAGIC_N); col = dict_table_get_nth_col(dict_index->table, n); if (dict_index_is_clust(dict_index)) { return(dict_col_get_clust_pos(col, dict_index)); } n_fields = dict_index_get_n_fields(dict_index); for (pos = 0; pos < n_fields; pos++) { field = dict_index_get_nth_field(dict_index, pos); if (col == field->col && field->prefix_len == 0) { return(pos); } } return(ULINT_UNDEFINED); } #ifndef UNIV_HOTBACKUP /********************************************************************//** Returns TRUE if the index contains a column or a prefix of that column. @return TRUE if contains the column or its prefix */ UNIV_INTERN ibool dict_index_contains_col_or_prefix( /*==============================*/ const dict_index_t* dict_index, /*!< in: index */ ulint n) /*!< in: column number */ { const dict_field_t* field; const dict_col_t* col; ulint pos; ulint n_fields; ut_ad(dict_index); ut_ad(dict_index->magic_n == DICT_INDEX_MAGIC_N); if (dict_index_is_clust(dict_index)) { return(TRUE); } col = dict_table_get_nth_col(dict_index->table, n); n_fields = dict_index_get_n_fields(dict_index); for (pos = 0; pos < n_fields; pos++) { field = dict_index_get_nth_field(dict_index, pos); if (col == field->col) { return(TRUE); } } return(FALSE); } /********************************************************************//** Looks for a matching field in an index. The column has to be the same. The column in index must be complete, or must contain a prefix longer than the column in index2. That is, we must be able to construct the prefix in index2 from the prefix in index. @return position in internal representation of the index; ULINT_UNDEFINED if not contained */ UNIV_INTERN ulint dict_index_get_nth_field_pos( /*=========================*/ const dict_index_t* dict_index, /*!< in: index from which to search */ const dict_index_t* index2, /*!< in: index */ ulint n) /*!< in: field number in index2 */ { const dict_field_t* field; const dict_field_t* field2; ulint n_fields; ulint pos; ut_ad(dict_index); ut_ad(dict_index->magic_n == DICT_INDEX_MAGIC_N); field2 = dict_index_get_nth_field(index2, n); n_fields = dict_index_get_n_fields(dict_index); for (pos = 0; pos < n_fields; pos++) { field = dict_index_get_nth_field(dict_index, pos); if (field->col == field2->col && (field->prefix_len == 0 || (field->prefix_len >= field2->prefix_len && field2->prefix_len != 0))) { return(pos); } } return(ULINT_UNDEFINED); } /**********************************************************************//** Returns a table object based on table id. @return table, NULL if does not exist */ UNIV_INTERN dict_table_t* dict_table_get_on_id( /*=================*/ ib_recovery_t recovery, /*!< in: recovery flag */ dulint table_id, /*!< in: table id */ trx_t* trx) /*!< in: transaction handle */ { dict_table_t* table; if (ut_dulint_cmp(table_id, DICT_FIELDS_ID) <= 0 || trx->dict_operation_lock_mode == RW_X_LATCH) { /* It is a system table which will always exist in the table cache: we avoid acquiring the dictionary mutex, because if we are doing a rollback to handle an error in TABLE CREATE, for example, we already have the mutex! */ ut_ad(mutex_own(&(dict_sys->mutex)) || trx->dict_operation_lock_mode == RW_X_LATCH); return(dict_table_get_on_id_low(recovery, table_id)); } mutex_enter(&(dict_sys->mutex)); table = dict_table_get_on_id_low(recovery, table_id); mutex_exit(&(dict_sys->mutex)); return(table); } /********************************************************************//** Looks for column n position in the clustered index. @return position in internal representation of the clustered index */ UNIV_INTERN ulint dict_table_get_nth_col_pos( /*=======================*/ const dict_table_t* table, /*!< in: table */ ulint n) /*!< in: column number */ { return(dict_index_get_nth_col_pos(dict_table_get_first_index(table), n)); } /********************************************************************//** Checks if a column is in the ordering columns of the clustered index of a table. Column prefixes are treated like whole columns. @return TRUE if the column, or its prefix, is in the clustered key */ UNIV_INTERN ibool dict_table_col_in_clustered_key( /*============================*/ const dict_table_t* table, /*!< in: table */ ulint n) /*!< in: column number */ { const dict_index_t* dict_index; const dict_field_t* field; const dict_col_t* col; ulint pos; ulint n_fields; ut_ad(table); col = dict_table_get_nth_col(table, n); dict_index = dict_table_get_first_index(table); n_fields = dict_index_get_n_unique(dict_index); for (pos = 0; pos < n_fields; pos++) { field = dict_index_get_nth_field(dict_index, pos); if (col == field->col) { return(TRUE); } } return(FALSE); } /**********************************************************************//** Inits the data dictionary module. */ UNIV_INTERN void dict_init(void) /*===========*/ { int i; dict_sys = mem_alloc(sizeof(dict_sys_t)); mutex_create(&dict_sys->mutex, SYNC_DICT); dict_sys->table_hash = hash_create(buf_pool_get_curr_size() / (DICT_POOL_PER_TABLE_HASH * UNIV_WORD_SIZE)); dict_sys->table_id_hash = hash_create(buf_pool_get_curr_size() / (DICT_POOL_PER_TABLE_HASH * UNIV_WORD_SIZE)); dict_sys->size = 0; UT_LIST_INIT(dict_sys->table_LRU); rw_lock_create(&dict_operation_lock, SYNC_DICT_OPERATION); mutex_create(&dict_foreign_err_mutex, SYNC_ANY_LATCH); for (i = 0; i < DICT_INDEX_STAT_MUTEX_SIZE; i++) { mutex_create(&dict_index_stat_mutex[i], SYNC_INDEX_TREE); } } /**********************************************************************//** Returns a table object and optionally increment its open handle count. NOTE! This is a high-level function to be used mainly from outside the 'dict' directory. Inside this directory dict_table_get_low is usually the appropriate function. @return table, NULL if does not exist */ UNIV_INTERN dict_table_t* dict_table_get( /*===========*/ const char* table_name, /*!< in: table name */ ibool ref_count) /*!< in: whether to increment the open handle count on the table */ { dict_table_t* table; mutex_enter(&dict_sys->mutex); table = dict_table_get_low(table_name); if (ref_count && table != NULL) { dict_table_increment_handle_count(table, TRUE); } mutex_exit(&dict_sys->mutex); if (table != NULL && !table->stat_initialized) { /* If table->ibd_file_missing == TRUE, this will print an error message and return without doing anything. */ dict_update_statistics(table); } return(table); } #endif /* !UNIV_HOTBACKUP */ /**********************************************************************//** Returns a table instance based on table id. @return table, NULL if does not exist */ UNIV_INTERN dict_table_t* dict_table_get_using_id( /*====================*/ ib_recovery_t recovery, /*!< in: recovery flag */ dulint table_id, /*!< in: table id */ ibool ref_count) /*!< in: increment open handle count if TRUE */ { dict_table_t* table; ut_ad(mutex_own(&dict_sys->mutex)); table = dict_table_get_on_id_low(recovery, table_id); if (ref_count && table != NULL) { dict_table_increment_handle_count(table, TRUE); } return(table); } /************************************************************************** Adds system columns to a table object. */ UNIV_INTERN void dict_table_add_system_columns( /*==========================*/ dict_table_t* table, /*!< in/out: table */ mem_heap_t* heap) /*!< in: temporary heap */ { ut_ad(table); ut_ad(table->n_def == table->n_cols - DATA_N_SYS_COLS); ut_ad(table->magic_n == DICT_TABLE_MAGIC_N); ut_ad(!table->cached); /* NOTE: the system columns MUST be added in the following order (so that they can be indexed by the numerical value of DATA_ROW_ID, etc.) and as the last columns of the table memory object. The clustered index will not always physically contain all system columns. */ dict_mem_table_add_col(table, heap, "DB_ROW_ID", DATA_SYS, DATA_ROW_ID | DATA_NOT_NULL, DATA_ROW_ID_LEN); #if DATA_ROW_ID != 0 #error "DATA_ROW_ID != 0" #endif dict_mem_table_add_col(table, heap, "DB_TRX_ID", DATA_SYS, DATA_TRX_ID | DATA_NOT_NULL, DATA_TRX_ID_LEN); #if DATA_TRX_ID != 1 #error "DATA_TRX_ID != 1" #endif dict_mem_table_add_col(table, heap, "DB_ROLL_PTR", DATA_SYS, DATA_ROLL_PTR | DATA_NOT_NULL, DATA_ROLL_PTR_LEN); #if DATA_ROLL_PTR != 2 #error "DATA_ROLL_PTR != 2" #endif /* This check reminds that if a new system column is added to the program, it should be dealt with here */ #if DATA_N_SYS_COLS != 3 #error "DATA_N_SYS_COLS != 3" #endif } #ifndef UNIV_HOTBACKUP /**********************************************************************//** Adds a table object to the dictionary cache. */ UNIV_INTERN void dict_table_add_to_cache( /*====================*/ dict_table_t* table, /*!< in: table */ mem_heap_t* heap) /*!< in: temporary heap */ { ulint fold; ulint id_fold; ulint i; ulint row_len; /* The lower limit for what we consider a "big" row */ #define BIG_ROW_SIZE 1024 ut_ad(mutex_own(&(dict_sys->mutex))); dict_table_add_system_columns(table, heap); table->cached = TRUE; fold = ut_fold_string(table->name); id_fold = ut_fold_dulint(table->id); row_len = 0; for (i = 0; i < table->n_def; i++) { ulint col_len = dict_col_get_max_size( dict_table_get_nth_col(table, i)); row_len += col_len; /* If we have a single unbounded field, or several gigantic fields, mark the maximum row size as BIG_ROW_SIZE. */ if (row_len >= BIG_ROW_SIZE || col_len >= BIG_ROW_SIZE) { row_len = BIG_ROW_SIZE; break; } } table->big_rows = row_len >= BIG_ROW_SIZE; /* Look for a table with the same name: error if such exists */ { dict_table_t* table2; HASH_SEARCH(name_hash, dict_sys->table_hash, fold, dict_table_t*, table2, ut_ad(table2->cached), ut_strcmp(table2->name, table->name) == 0); ut_a(table2 == NULL); #ifdef UNIV_DEBUG /* Look for the same table pointer with a different name */ HASH_SEARCH_ALL(name_hash, dict_sys->table_hash, dict_table_t*, table2, ut_ad(table2->cached), table2 == table); ut_ad(table2 == NULL); #endif /* UNIV_DEBUG */ } /* Look for a table with the same id: error if such exists */ { dict_table_t* table2; HASH_SEARCH(id_hash, dict_sys->table_id_hash, id_fold, dict_table_t*, table2, ut_ad(table2->cached), ut_dulint_cmp(table2->id, table->id) == 0); ut_a(table2 == NULL); #ifdef UNIV_DEBUG /* Look for the same table pointer with a different id */ HASH_SEARCH_ALL(id_hash, dict_sys->table_id_hash, dict_table_t*, table2, ut_ad(table2->cached), table2 == table); ut_ad(table2 == NULL); #endif /* UNIV_DEBUG */ } /* Add table to hash table of tables */ HASH_INSERT(dict_table_t, name_hash, dict_sys->table_hash, fold, table); /* Add table to hash table of tables based on table id */ HASH_INSERT(dict_table_t, id_hash, dict_sys->table_id_hash, id_fold, table); /* Add table to LRU list of tables */ UT_LIST_ADD_FIRST(table_LRU, dict_sys->table_LRU, table); dict_sys->size += mem_heap_get_size(table->heap); } /**********************************************************************//** Looks for an index with the given id. NOTE that we do not reserve the dictionary mutex: this function is for emergency purposes like printing info of a corrupt database page! @return index or NULL if not found from cache */ UNIV_INTERN dict_index_t* dict_index_find_on_id_low( /*======================*/ dulint id) /*!< in: index id */ { dict_table_t* table; dict_index_t* dict_index; table = UT_LIST_GET_FIRST(dict_sys->table_LRU); while (table) { dict_index = dict_table_get_first_index(table); while (dict_index) { if (0 == ut_dulint_cmp(id, dict_index->id)) { /* Found */ return(dict_index); } dict_index = dict_table_get_next_index(dict_index); } table = UT_LIST_GET_NEXT(table_LRU, table); } return(NULL); } /**********************************************************************//** Renames a table object. @return TRUE if success */ UNIV_INTERN ibool dict_table_rename_in_cache( /*=======================*/ dict_table_t* table, /*!< in/out: table */ const char* new_name, /*!< in: new name */ ibool rename_also_foreigns)/*!< in: in ALTER TABLE we want to preserve the original table name in constraints which reference it */ { dict_foreign_t* foreign; dict_index_t* index; ulint fold; ulint old_size; const char* old_name; ut_ad(table); ut_ad(mutex_own(&(dict_sys->mutex))); old_size = mem_heap_get_size(table->heap); old_name = table->name; fold = ut_fold_string(new_name); /* Look for a table with the same name: error if such exists */ { dict_table_t* table2; HASH_SEARCH(name_hash, dict_sys->table_hash, fold, dict_table_t*, table2, ut_ad(table2->cached), (ut_strcmp(table2->name, new_name) == 0)); if (UNIV_LIKELY_NULL(table2)) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Error: dictionary cache" " already contains a table "); ut_print_name(ib_stream, NULL, TRUE, new_name); ib_logger(ib_stream, "\n" "InnoDB: cannot rename table "); ut_print_name(ib_stream, NULL, TRUE, old_name); ib_logger(ib_stream, "\n"); return(FALSE); } } /* If the table is stored in a single-table tablespace, rename the .ibd file */ if (table->space != 0) { if (table->dir_path_of_temp_table != NULL) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Error: trying to rename a" " TEMPORARY TABLE "); ut_print_name(ib_stream, NULL, TRUE, old_name); ib_logger(ib_stream, " ("); ut_print_filename(ib_stream, table->dir_path_of_temp_table); ib_logger(ib_stream, " )\n"); return(FALSE); } else if (!fil_rename_tablespace(old_name, table->space, new_name)) { return(FALSE); } } /* Remove table from the hash tables of tables */ HASH_DELETE(dict_table_t, name_hash, dict_sys->table_hash, ut_fold_string(old_name), table); table->name = mem_heap_strdup(table->heap, new_name); /* Add table to hash table of tables */ HASH_INSERT(dict_table_t, name_hash, dict_sys->table_hash, fold, table); dict_sys->size += (mem_heap_get_size(table->heap) - old_size); /* Update the table_name field in indexes */ index = dict_table_get_first_index(table); while (index != NULL) { index->table_name = table->name; index = dict_table_get_next_index(index); } if (!rename_also_foreigns) { /* In ALTER TABLE we think of the rename table operation in the direction table -> temporary table (#sql...) as dropping the table with the old name and creating a new with the new name. Thus we kind of drop the constraints from the dictionary cache here. The foreign key constraints will be inherited to the new table from the system tables through a call of dict_load_foreigns. */ /* Remove the foreign constraints from the cache */ foreign = UT_LIST_GET_LAST(table->foreign_list); while (foreign != NULL) { dict_foreign_remove_from_cache(foreign); foreign = UT_LIST_GET_LAST(table->foreign_list); } /* Reset table field in referencing constraints */ foreign = UT_LIST_GET_FIRST(table->referenced_list); while (foreign != NULL) { foreign->referenced_table = NULL; foreign->referenced_index = NULL; foreign = UT_LIST_GET_NEXT(referenced_list, foreign); } /* Make the list of referencing constraints empty */ UT_LIST_INIT(table->referenced_list); return(TRUE); } /* Update the table name fields in foreign constraints, and update also the constraint id of new format >= 4.0.18 constraints. Note that at this point we have already changed table->name to the new name. */ foreign = UT_LIST_GET_FIRST(table->foreign_list); while (foreign != NULL) { if (ut_strlen(foreign->foreign_table_name) < ut_strlen(table->name)) { /* Allocate a longer name buffer; TODO: store buf len to save memory */ foreign->foreign_table_name = mem_heap_alloc(foreign->heap, ut_strlen(table->name) + 1); } strcpy(foreign->foreign_table_name, table->name); if (strchr(foreign->id, '/')) { ulint db_len; char* old_id; /* This is a >= 4.0.18 format id */ old_id = mem_strdup(foreign->id); if (ut_strlen(foreign->id) > ut_strlen(old_name) + ((sizeof dict_ibfk) - 1) && !memcmp(foreign->id, old_name, ut_strlen(old_name)) && !memcmp(foreign->id + ut_strlen(old_name), dict_ibfk, (sizeof dict_ibfk) - 1)) { /* This is a generated >= 4.0.18 format id */ if (strlen(table->name) > strlen(old_name)) { foreign->id = mem_heap_alloc( foreign->heap, strlen(table->name) + strlen(old_id) + 1); } /* Replace the prefix 'databasename/tablename' with the new names */ strcpy(foreign->id, table->name); strcat(foreign->id, old_id + ut_strlen(old_name)); } else { /* This is a >= 4.0.18 format id where the user gave the id name */ db_len = dict_get_db_name_len(table->name) + 1; if (dict_get_db_name_len(table->name) > dict_get_db_name_len(foreign->id)) { foreign->id = mem_heap_alloc( foreign->heap, db_len + strlen(old_id) + 1); } /* Replace the database prefix in id with the one from table->name */ ut_memcpy(foreign->id, table->name, db_len); strcpy(foreign->id + db_len, dict_remove_db_name(old_id)); } mem_free(old_id); } foreign = UT_LIST_GET_NEXT(foreign_list, foreign); } foreign = UT_LIST_GET_FIRST(table->referenced_list); while (foreign != NULL) { if (ut_strlen(foreign->referenced_table_name) < ut_strlen(table->name)) { /* Allocate a longer name buffer; TODO: store buf len to save memory */ foreign->referenced_table_name = mem_heap_alloc( foreign->heap, strlen(table->name) + 1); } strcpy(foreign->referenced_table_name, table->name); foreign = UT_LIST_GET_NEXT(referenced_list, foreign); } return(TRUE); } /**********************************************************************//** Change the id of a table object in the dictionary cache. This is used in DISCARD TABLESPACE. */ UNIV_INTERN void dict_table_change_id_in_cache( /*==========================*/ dict_table_t* table, /*!< in/out: table object already in cache */ dulint new_id) /*!< in: new id to set */ { ut_ad(table); ut_ad(mutex_own(&(dict_sys->mutex))); ut_ad(table->magic_n == DICT_TABLE_MAGIC_N); /* Remove the table from the hash table of id's */ HASH_DELETE(dict_table_t, id_hash, dict_sys->table_id_hash, ut_fold_dulint(table->id), table); table->id = new_id; /* Add the table back to the hash table */ HASH_INSERT(dict_table_t, id_hash, dict_sys->table_id_hash, ut_fold_dulint(table->id), table); } /**********************************************************************//** Removes a table object from the dictionary cache. */ UNIV_INTERN void dict_table_remove_from_cache( /*=========================*/ dict_table_t* table) /*!< in, own: table */ { dict_foreign_t* foreign; dict_index_t* index; ulint size; ut_ad(table); ut_ad(mutex_own(&(dict_sys->mutex))); ut_ad(table->magic_n == DICT_TABLE_MAGIC_N); #if 0 ib_logger(ib_stream, "Removing table "); ut_print_name(ib_stream, NULL, TRUE, table->name); ib_logger(ib_stream, " (%lu) from dictionary cache\n", (ulint) table->id.low); #endif /* Remove the foreign constraints from the cache */ foreign = UT_LIST_GET_LAST(table->foreign_list); while (foreign != NULL) { dict_foreign_remove_from_cache(foreign); foreign = UT_LIST_GET_LAST(table->foreign_list); } /* Reset table field in referencing constraints */ foreign = UT_LIST_GET_FIRST(table->referenced_list); while (foreign != NULL) { foreign->referenced_table = NULL; foreign->referenced_index = NULL; foreign = UT_LIST_GET_NEXT(referenced_list, foreign); } /* Remove the indexes from the cache */ index = UT_LIST_GET_LAST(table->indexes); while (index != NULL) { dict_index_remove_from_cache(table, index); index = UT_LIST_GET_LAST(table->indexes); } /* Remove table from the hash tables of tables */ HASH_DELETE(dict_table_t, name_hash, dict_sys->table_hash, ut_fold_string(table->name), table); HASH_DELETE(dict_table_t, id_hash, dict_sys->table_id_hash, ut_fold_dulint(table->id), table); /* Remove table from LRU list of tables */ UT_LIST_REMOVE(table_LRU, dict_sys->table_LRU, table); size = mem_heap_get_size(table->heap); ut_ad(dict_sys->size >= size); dict_sys->size -= size; dict_mem_table_free(table); } /****************************************************************//** If the given column name is reserved for InnoDB system columns, return TRUE. @return TRUE if name is reserved */ UNIV_INTERN ibool dict_col_name_is_reserved( /*======================*/ const char* name) /*!< in: column name */ { /* This check reminds that if a new system column is added to the program, it should be dealt with here. */ #if DATA_N_SYS_COLS != 3 #error "DATA_N_SYS_COLS != 3" #endif static const char* reserved_names[] = { "DB_ROW_ID", "DB_TRX_ID", "DB_ROLL_PTR" }; ulint i; for (i = 0; i < UT_ARR_SIZE(reserved_names); i++) { if (strcasecmp(name, reserved_names[i]) == 0) { return(TRUE); } } return(FALSE); } /****************************************************************//** If an undo log record for this table might not fit on a single page, return TRUE. @return TRUE if the undo log record could become too big */ static ibool dict_index_too_big_for_undo( /*========================*/ const dict_table_t* table, /*!< in: table */ const dict_index_t* new_index) /*!< in: index */ { /* Make sure that all column prefixes will fit in the undo log record in trx_undo_page_report_modify() right after trx_undo_page_init(). */ ulint i; const dict_index_t* clust_index = dict_table_get_first_index(table); ulint undo_page_len = TRX_UNDO_PAGE_HDR - TRX_UNDO_PAGE_HDR_SIZE + 2 /* next record pointer */ + 1 /* type_cmpl */ + 11 /* trx->undo_no */ + 11 /* table->id */ + 1 /* rec_get_info_bits() */ + 11 /* DB_TRX_ID */ + 11 /* DB_ROLL_PTR */ + 10 + FIL_PAGE_DATA_END /* trx_undo_left() */ + 2/* pointer to previous undo log record */; if (UNIV_UNLIKELY(!clust_index)) { ut_a(dict_index_is_clust(new_index)); clust_index = new_index; } /* Add the size of the ordering columns in the clustered index. */ for (i = 0; i < clust_index->n_uniq; i++) { const dict_col_t* col = dict_index_get_nth_col(clust_index, i); /* Use the maximum output size of mach_write_compressed(), although the encoded length should always fit in 2 bytes. */ undo_page_len += 5 + dict_col_get_max_size(col); } /* Add the old values of the columns to be updated. First, the amount and the numbers of the columns. These are written by mach_write_compressed() whose maximum output length is 5 bytes. However, given that the quantities are below REC_MAX_N_FIELDS (10 bits), the maximum length is 2 bytes per item. */ undo_page_len += 2 * (dict_table_get_n_cols(table) + 1); for (i = 0; i < clust_index->n_def; i++) { const dict_col_t* col = dict_index_get_nth_col(clust_index, i); ulint max_size = dict_col_get_max_size(col); ulint fixed_size = dict_col_get_fixed_size(col, dict_table_is_comp(table)); if (fixed_size) { /* Fixed-size columns are stored locally. */ max_size = fixed_size; } else if (max_size <= BTR_EXTERN_FIELD_REF_SIZE * 2) { /* Short columns are stored locally. */ } else if (!col->ord_part) { /* See if col->ord_part would be set because of new_index. */ ulint j; for (j = 0; j < new_index->n_uniq; j++) { if (dict_index_get_nth_col( new_index, j) == col) { goto is_ord_part; } } /* This is not an ordering column in any index. Thus, it can be stored completely externally. */ max_size = BTR_EXTERN_FIELD_REF_SIZE; } else { is_ord_part: /* This is an ordering column in some index. A long enough prefix must be written to the undo log. See trx_undo_page_fetch_ext(). */ if (max_size > REC_MAX_INDEX_COL_LEN) { max_size = REC_MAX_INDEX_COL_LEN; } max_size += BTR_EXTERN_FIELD_REF_SIZE; } undo_page_len += 5 + max_size; } return(undo_page_len >= UNIV_PAGE_SIZE); } /****************************************************************//** If a record of this index might not fit on a single B-tree page, return TRUE. @return TRUE if the index record could become too big */ static ibool dict_index_too_big_for_tree( /*========================*/ const dict_table_t* table, /*!< in: table */ const dict_index_t* new_index) /*!< in: index */ { ulint zip_size; ulint comp; ulint i; /* maximum possible storage size of a record */ ulint rec_max_size; /* maximum allowed size of a record on a leaf page */ ulint page_rec_max; /* maximum allowed size of a node pointer record */ ulint page_ptr_max; comp = dict_table_is_comp(table); zip_size = dict_table_zip_size(table); if (zip_size && zip_size < UNIV_PAGE_SIZE) { /* On a compressed page, two records must fit in the uncompressed page modification log. On compressed pages with zip_size == UNIV_PAGE_SIZE, this limit will never be reached. */ ut_ad(comp); /* The maximum allowed record size is the size of an empty page, minus a byte for recoding the heap number in the page modification log. The maximum allowed node pointer size is half that. */ page_rec_max = page_zip_empty_size(new_index->n_fields, zip_size) - 1; page_ptr_max = page_rec_max / 2; /* On a compressed page, there is a two-byte entry in the dense page directory for every record. But there is no record header. */ rec_max_size = 2; } else { /* The maximum allowed record size is half a B-tree page. No additional sparse page directory entry will be generated for the first few user records. */ page_rec_max = page_get_free_space_of_empty(comp) / 2; page_ptr_max = page_rec_max; /* Each record has a header. */ rec_max_size = comp ? REC_N_NEW_EXTRA_BYTES : REC_N_OLD_EXTRA_BYTES; } if (comp) { /* Include the "null" flags in the maximum possible record size. */ rec_max_size += UT_BITS_IN_BYTES(new_index->n_nullable); } else { /* For each column, include a 2-byte offset and a "null" flag. The 1-byte format is only used in short records that do not contain externally stored columns. Such records could never exceed the page limit, even when using the 2-byte format. */ rec_max_size += 2 * new_index->n_fields; } /* Compute the maximum possible record size. */ for (i = 0; i < new_index->n_fields; i++) { const dict_field_t* field = dict_index_get_nth_field(new_index, i); const dict_col_t* col = dict_field_get_col(field); ulint field_max_size; ulint field_ext_max_size; /* In dtuple_convert_big_rec(), variable-length columns that are longer than BTR_EXTERN_FIELD_REF_SIZE * 2 may be chosen for external storage. Fixed-length columns, and all columns of secondary index records are always stored inline. */ /* Determine the maximum length of the index field. The field_ext_max_size should be computed as the worst case in rec_get_converted_size_comp() for REC_STATUS_ORDINARY records. */ field_max_size = dict_col_get_fixed_size(col, comp); if (field_max_size) { /* dict_index_add_col() should guarantee this */ ut_ad(!field->prefix_len || field->fixed_len == field->prefix_len); /* Fixed lengths are not encoded in ROW_FORMAT=COMPACT. */ field_ext_max_size = 0; goto add_field_size; } field_max_size = dict_col_get_max_size(col); field_ext_max_size = field_max_size < 256 ? 1 : 2; if (field->prefix_len) { if (field->prefix_len < field_max_size) { field_max_size = field->prefix_len; } } else if (field_max_size > BTR_EXTERN_FIELD_REF_SIZE * 2 && dict_index_is_clust(new_index)) { /* In the worst case, we have a locally stored column of BTR_EXTERN_FIELD_REF_SIZE * 2 bytes. The length can be stored in one byte. If the column were stored externally, the lengths in the clustered index page would be BTR_EXTERN_FIELD_REF_SIZE and 2. */ field_max_size = BTR_EXTERN_FIELD_REF_SIZE * 2; field_ext_max_size = 1; } if (comp) { /* Add the extra size for ROW_FORMAT=COMPACT. For ROW_FORMAT=REDUNDANT, these bytes were added to rec_max_size before this loop. */ rec_max_size += field_ext_max_size; } add_field_size: rec_max_size += field_max_size; /* Check the size limit on leaf pages. */ if (UNIV_UNLIKELY(rec_max_size >= page_rec_max)) { return(TRUE); } /* Check the size limit on non-leaf pages. Records stored in non-leaf B-tree pages consist of the unique columns of the record (the key columns of the B-tree) and a node pointer field. When we have processed the unique columns, rec_max_size equals the size of the node pointer record minus the node pointer column. */ if (i + 1 == dict_index_get_n_unique_in_tree(new_index) && rec_max_size + REC_NODE_PTR_SIZE >= page_ptr_max) { return(TRUE); } } return(FALSE); } /**********************************************************************//** Adds an index to the dictionary cache. @return DB_SUCCESS, DB_TOO_BIG_RECORD, or DB_CORRUPTION */ UNIV_INTERN ulint dict_index_add_to_cache( /*====================*/ dict_table_t* table, /*!< in: table on which the index is */ dict_index_t* index, /*!< in, own: index; NOTE! The index memory object is freed in this function! */ ulint page_no,/*!< in: root page number of the index */ ibool strict) /*!< in: TRUE=refuse to create the index if records could be too big to fit in an B-tree page */ { dict_index_t* new_index; ulint n_ord; ulint i; ut_ad(index); ut_ad(mutex_own(&(dict_sys->mutex))); ut_ad(index->n_def == index->n_fields); ut_ad(index->magic_n == DICT_INDEX_MAGIC_N); ut_ad(mem_heap_validate(index->heap)); ut_a(!dict_index_is_clust(index) || UT_LIST_GET_LEN(table->indexes) == 0); if (!dict_index_find_cols(table, index)) { dict_mem_index_free(index); return(DB_CORRUPTION); } /* Build the cache internal representation of the index, containing also the added system fields */ if (dict_index_is_clust(index)) { new_index = dict_index_build_internal_clust(table, index); } else { new_index = dict_index_build_internal_non_clust(table, index); } /* Set the n_fields value in new_index to the actual defined number of fields in the cache internal representation */ new_index->n_fields = new_index->n_def; if (strict && dict_index_too_big_for_tree(table, new_index)) { too_big: dict_mem_index_free(new_index); dict_mem_index_free(index); return(DB_TOO_BIG_RECORD); } if (UNIV_UNLIKELY(index->type & DICT_UNIVERSAL)) { n_ord = new_index->n_fields; } else { n_ord = new_index->n_uniq; } switch (dict_table_get_format(table)) { case DICT_TF_FORMAT_51: /* ROW_FORMAT=REDUNDANT and ROW_FORMAT=COMPACT store prefixes of externally stored columns locally within the record. There are no special considerations for the undo log record size. */ goto undo_size_ok; case DICT_TF_FORMAT_ZIP: /* In ROW_FORMAT=DYNAMIC and ROW_FORMAT=COMPRESSED, column prefix indexes require that prefixes of externally stored columns are written to the undo log. This may make the undo log record bigger than the record on the B-tree page. The maximum size of an undo log record is the page size. That must be checked for below. */ break; #if DICT_TF_FORMAT_ZIP != DICT_TF_FORMAT_MAX # error "DICT_TF_FORMAT_ZIP != DICT_TF_FORMAT_MAX" #endif } for (i = 0; i < n_ord; i++) { const dict_field_t* field = dict_index_get_nth_field(new_index, i); const dict_col_t* col = dict_field_get_col(field); /* In dtuple_convert_big_rec(), variable-length columns that are longer than BTR_EXTERN_FIELD_REF_SIZE * 2 may be chosen for external storage. If the column appears in an ordering column of an index, a longer prefix of REC_MAX_INDEX_COL_LEN will be copied to the undo log by trx_undo_page_report_modify() and trx_undo_page_fetch_ext(). It suffices to check the capacity of the undo log whenever new_index includes a column prefix on a column that may be stored externally. */ if (field->prefix_len /* prefix index */ && !col->ord_part /* not yet ordering column */ && !dict_col_get_fixed_size(col, TRUE) /* variable-length */ && dict_col_get_max_size(col) > BTR_EXTERN_FIELD_REF_SIZE * 2 /* long enough */) { if (dict_index_too_big_for_undo(table, new_index)) { /* An undo log record might not fit in a single page. Refuse to create this index. */ goto too_big; } break; } } undo_size_ok: /* Flag the ordering columns */ for (i = 0; i < n_ord; i++) { dict_index_get_nth_field(new_index, i)->col->ord_part = 1; } /* Add the new index as the last index for the table */ UT_LIST_ADD_LAST(indexes, table->indexes, new_index); new_index->table = table; new_index->table_name = table->name; new_index->search_info = btr_search_info_create(new_index->heap); new_index->stat_index_size = 1; new_index->stat_n_leaf_pages = 1; new_index->page = page_no; rw_lock_create(&new_index->lock, SYNC_INDEX_TREE); if (!UNIV_UNLIKELY(new_index->type & DICT_UNIVERSAL)) { new_index->stat_n_diff_key_vals = mem_heap_alloc( new_index->heap, (1 + dict_index_get_n_unique(new_index)) * sizeof(ib_int64_t)); /* Give some sensible values to stat_n_... in case we do not calculate statistics quickly enough */ for (i = 0; i <= dict_index_get_n_unique(new_index); i++) { new_index->stat_n_diff_key_vals[i] = 100; } } dict_sys->size += mem_heap_get_size(new_index->heap); dict_mem_index_free(index); return(DB_SUCCESS); } /**********************************************************************//** Removes an index from the dictionary cache. */ UNIV_INTERN void dict_index_remove_from_cache( /*=========================*/ dict_table_t* table, /*!< in/out: table */ dict_index_t* index) /*!< in, own: index */ { ulint size; ulint retries = 0; btr_search_t* info; ut_ad(table && index); ut_ad(table->magic_n == DICT_TABLE_MAGIC_N); ut_ad(index->magic_n == DICT_INDEX_MAGIC_N); ut_ad(mutex_own(&(dict_sys->mutex))); /* We always create search info whether or not adaptive hash index is enabled or not. */ info = index->search_info; ut_ad(info); /* We are not allowed to free the in-memory index struct dict_index_t until all entries in the adaptive hash index that point to any of the page belonging to his b-tree index are dropped. This is so because dropping of these entries require access to dict_index_t struct. To avoid such scenario We keep a count of number of such pages in the search_info and only free the dict_index_t struct when this count drops to zero. */ for (;;) { ulint ref_count = btr_search_info_get_ref_count(info); if (ref_count == 0 || srv_shutdown_state == SRV_SHUTDOWN_EXIT_THREADS) { break; } /* Sleep for 10ms before trying again. */ os_thread_sleep(10000); ++retries; if (retries % 500 == 0) { /* No luck after 5 seconds of wait. */ ib_logger(ib_stream, "InnoDB: Error: Waited for" " %lu secs for hash index" " ref_count (%lu) to drop" " to 0.\n" "index: \"%s\"" " table: \"%s\"\n", retries/100, ref_count, index->name, table->name); } /* To avoid a hang here we commit suicide if the ref_count doesn't drop to zero in 600 seconds. */ if (retries >= 60000) { ut_error; } } rw_lock_free(&index->lock); /* Remove the index from the list of indexes of the table */ UT_LIST_REMOVE(indexes, table->indexes, index); size = mem_heap_get_size(index->heap); ut_ad(dict_sys->size >= size); dict_sys->size -= size; dict_mem_index_free(index); } /*******************************************************************//** Tries to find column names for the index and sets the col field of the index. @return TRUE if the column names were found */ static ibool dict_index_find_cols( /*=================*/ dict_table_t* table, /*!< in: table */ dict_index_t* index) /*!< in: index */ { ulint i; ut_ad(table && index); ut_ad(table->magic_n == DICT_TABLE_MAGIC_N); ut_ad(mutex_own(&(dict_sys->mutex))); for (i = 0; i < index->n_fields; i++) { ulint j; dict_field_t* field = dict_index_get_nth_field(index, i); for (j = 0; j < table->n_cols; j++) { if (!strcmp(dict_table_get_col_name(table, j), field->name)) { field->col = dict_table_get_nth_col(table, j); goto found; } } #ifdef UNIV_DEBUG /* It is an error not to find a matching column. */ ib_logger(ib_stream, "InnoDB: Error: no matching column for "); ut_print_name(ib_stream, NULL, FALSE, field->name); ib_logger(ib_stream, " in "); dict_index_name_print(ib_stream, NULL, index); ib_logger(ib_stream, "!\n"); #endif /* UNIV_DEBUG */ return(FALSE); found: ; } return(TRUE); } #endif /* !UNIV_HOTBACKUP */ /*******************************************************************//** Adds a column to index. */ UNIV_INTERN void dict_index_add_col( /*===============*/ dict_index_t* index, /*!< in/out: index */ const dict_table_t* table, /*!< in: table */ dict_col_t* col, /*!< in: column */ ulint prefix_len) /*!< in: column prefix length */ { dict_field_t* field; const char* col_name; col_name = dict_table_get_col_name(table, dict_col_get_no(col)); dict_mem_index_add_field(index, col_name, prefix_len); field = dict_index_get_nth_field(index, index->n_def - 1); field->col = col; field->fixed_len = (unsigned int) dict_col_get_fixed_size( col, dict_table_is_comp(table)); if (prefix_len && field->fixed_len > prefix_len) { field->fixed_len = (unsigned int) prefix_len; } /* Long fixed-length fields that need external storage are treated as variable-length fields, so that the extern flag can be embedded in the length word. */ if (field->fixed_len > DICT_MAX_INDEX_COL_LEN) { field->fixed_len = 0; } #if DICT_MAX_INDEX_COL_LEN != 768 /* The comparison limit above must be constant. If it were changed, the disk format of some fixed-length columns would change, which would be a disaster. */ # error "DICT_MAX_INDEX_COL_LEN != 768" #endif if (!(col->prtype & DATA_NOT_NULL)) { index->n_nullable++; } } #ifndef UNIV_HOTBACKUP /*******************************************************************//** Copies fields contained in index2 to index1. */ static void dict_index_copy( /*============*/ dict_index_t* index1, /*!< in: index to copy to */ dict_index_t* index2, /*!< in: index to copy from */ const dict_table_t* table, /*!< in: table */ ulint start, /*!< in: first position to copy */ ulint end) /*!< in: last position to copy */ { dict_field_t* field; ulint i; /* Copy fields contained in index2 */ for (i = start; i < end; i++) { field = dict_index_get_nth_field(index2, i); dict_index_add_col(index1, table, field->col, field->prefix_len); } } /*******************************************************************//** Copies types of fields contained in index to tuple. */ UNIV_INTERN void dict_index_copy_types( /*==================*/ dtuple_t* tuple, /*!< in/out: data tuple */ const dict_index_t* index, /*!< in: index */ ulint n_fields) /*!< in: number of field types to copy */ { ulint i; if (UNIV_UNLIKELY(index->type & DICT_UNIVERSAL)) { dtuple_set_types_binary(tuple, n_fields); return; } for (i = 0; i < n_fields; i++) { const dict_field_t* ifield; dtype_t* dfield_type; ifield = dict_index_get_nth_field(index, i); dfield_type = dfield_get_type(dtuple_get_nth_field(tuple, i)); dict_col_copy_type(dict_field_get_col(ifield), dfield_type); } } /*******************************************************************//** Copies types of columns contained in table to tuple and sets all fields of the tuple to the SQL NULL value. This function should be called right after dtuple_create(). */ UNIV_INTERN void dict_table_copy_types( /*==================*/ dtuple_t* tuple, /*!< in/out: data tuple */ const dict_table_t* table) /*!< in: table */ { ulint i; for (i = 0; i < dtuple_get_n_fields(tuple); i++) { dfield_t* dfield = dtuple_get_nth_field(tuple, i); dtype_t* dtype = dfield_get_type(dfield); dfield_set_null(dfield); dict_col_copy_type(dict_table_get_nth_col(table, i), dtype); } } /*******************************************************************//** Builds the internal dictionary cache representation for a clustered index, containing also system fields not defined by the user. @return own: the internal representation of the clustered index */ static dict_index_t* dict_index_build_internal_clust( /*============================*/ const dict_table_t* table, /*!< in: table */ dict_index_t* index) /*!< in: user representation of a clustered index */ { dict_index_t* new_index; dict_field_t* field; ulint fixed_size; ulint trx_id_pos; ulint i; ibool* indexed; ut_ad(table && index); ut_ad(dict_index_is_clust(index)); ut_ad(mutex_own(&(dict_sys->mutex))); ut_ad(table->magic_n == DICT_TABLE_MAGIC_N); /* Create a new index object with certainly enough fields */ new_index = dict_mem_index_create(table->name, index->name, table->space, index->type, index->n_fields + table->n_cols); /* Copy other relevant data from the old index struct to the new struct: it inherits the values */ new_index->n_user_defined_cols = index->n_fields; new_index->id = index->id; /* Copy the fields of index */ dict_index_copy(new_index, index, table, 0, index->n_fields); if (UNIV_UNLIKELY(index->type & DICT_UNIVERSAL)) { /* No fixed number of fields determines an entry uniquely */ new_index->n_uniq = REC_MAX_N_FIELDS; } else if (dict_index_is_unique(index)) { /* Only the fields defined so far are needed to identify the index entry uniquely */ new_index->n_uniq = new_index->n_def; } else { /* Also the row id is needed to identify the entry */ new_index->n_uniq = 1 + new_index->n_def; } new_index->trx_id_offset = 0; if (!dict_index_is_ibuf(index)) { /* Add system columns, trx id first */ trx_id_pos = new_index->n_def; #if DATA_ROW_ID != 0 # error "DATA_ROW_ID != 0" #endif #if DATA_TRX_ID != 1 # error "DATA_TRX_ID != 1" #endif #if DATA_ROLL_PTR != 2 # error "DATA_ROLL_PTR != 2" #endif if (!dict_index_is_unique(index)) { dict_index_add_col(new_index, table, dict_table_get_sys_col( table, DATA_ROW_ID), 0); trx_id_pos++; } dict_index_add_col(new_index, table, dict_table_get_sys_col(table, DATA_TRX_ID), 0); dict_index_add_col(new_index, table, dict_table_get_sys_col(table, DATA_ROLL_PTR), 0); for (i = 0; i < trx_id_pos; i++) { fixed_size = dict_col_get_fixed_size( dict_index_get_nth_col(new_index, i), dict_table_is_comp(table)); if (fixed_size == 0) { new_index->trx_id_offset = 0; break; } if (dict_index_get_nth_field(new_index, i)->prefix_len > 0) { new_index->trx_id_offset = 0; break; } new_index->trx_id_offset += (unsigned int) fixed_size; } } /* Remember the table columns already contained in new_index */ indexed = mem_zalloc(table->n_cols * sizeof *indexed); /* Mark the table columns already contained in new_index */ for (i = 0; i < new_index->n_def; i++) { field = dict_index_get_nth_field(new_index, i); /* If there is only a prefix of the column in the index field, do not mark the column as contained in the index */ if (field->prefix_len == 0) { indexed[field->col->ind] = TRUE; } } /* Add to new_index non-system columns of table not yet included there */ for (i = 0; i + DATA_N_SYS_COLS < (ulint) table->n_cols; i++) { dict_col_t* col = dict_table_get_nth_col(table, i); ut_ad(col->mtype != DATA_SYS); if (!indexed[col->ind]) { dict_index_add_col(new_index, table, col, 0); } } mem_free(indexed); ut_ad(dict_index_is_ibuf(index) || (UT_LIST_GET_LEN(table->indexes) == 0)); new_index->cached = TRUE; return(new_index); } /*******************************************************************//** Builds the internal dictionary cache representation for a non-clustered index, containing also system fields not defined by the user. @return own: the internal representation of the non-clustered index */ static dict_index_t* dict_index_build_internal_non_clust( /*================================*/ const dict_table_t* table, /*!< in: table */ dict_index_t* index) /*!< in: user representation of a non-clustered index */ { dict_field_t* field; dict_index_t* new_index; dict_index_t* clust_index; ulint i; ibool* indexed; ut_ad(table && index); ut_ad(!dict_index_is_clust(index)); ut_ad(mutex_own(&(dict_sys->mutex))); ut_ad(table->magic_n == DICT_TABLE_MAGIC_N); /* The clustered index should be the first in the list of indexes */ clust_index = UT_LIST_GET_FIRST(table->indexes); ut_ad(clust_index); ut_ad(dict_index_is_clust(clust_index)); ut_ad(!(clust_index->type & DICT_UNIVERSAL)); /* Create a new index */ new_index = dict_mem_index_create( table->name, index->name, index->space, index->type, index->n_fields + 1 + clust_index->n_uniq); /* Copy other relevant data from the old index struct to the new struct: it inherits the values */ new_index->n_user_defined_cols = index->n_fields; new_index->id = index->id; /* Copy fields from index to new_index */ dict_index_copy(new_index, index, table, 0, index->n_fields); /* Remember the table columns already contained in new_index */ indexed = mem_zalloc(table->n_cols * sizeof *indexed); /* Mark the table columns already contained in new_index */ for (i = 0; i < new_index->n_def; i++) { field = dict_index_get_nth_field(new_index, i); /* If there is only a prefix of the column in the index field, do not mark the column as contained in the index */ if (field->prefix_len == 0) { indexed[field->col->ind] = TRUE; } } /* Add to new_index the columns necessary to determine the clustered index entry uniquely */ for (i = 0; i < clust_index->n_uniq; i++) { field = dict_index_get_nth_field(clust_index, i); if (!indexed[field->col->ind]) { dict_index_add_col(new_index, table, field->col, field->prefix_len); } } mem_free(indexed); if (dict_index_is_unique(index)) { new_index->n_uniq = index->n_fields; } else { new_index->n_uniq = new_index->n_def; } /* Set the n_fields value in new_index to the actual defined number of fields */ new_index->n_fields = new_index->n_def; new_index->cached = TRUE; return(new_index); } /*====================== FOREIGN KEY PROCESSING ========================*/ /*********************************************************************//** Checks if a table is referenced by foreign keys. @return TRUE if table is referenced by a foreign key */ UNIV_INTERN ibool dict_table_is_referenced_by_foreign_key( /*====================================*/ const dict_table_t* table) /*!< in: InnoDB table */ { return(UT_LIST_GET_LEN(table->referenced_list) > 0); } /*********************************************************************//** Check if the index is referenced by a foreign key, if TRUE return foreign else return NULL @return pointer to foreign key struct if index is defined for foreign key, otherwise NULL */ UNIV_INTERN dict_foreign_t* dict_table_get_referenced_constraint( /*=================================*/ dict_table_t* table, /*!< in: InnoDB table */ dict_index_t* index) /*!< in: InnoDB index */ { dict_foreign_t* foreign; ut_ad(index != NULL); ut_ad(table != NULL); for (foreign = UT_LIST_GET_FIRST(table->referenced_list); foreign; foreign = UT_LIST_GET_NEXT(referenced_list, foreign)) { if (foreign->referenced_index == index) { return(foreign); } } return(NULL); } /*********************************************************************//** Checks if a index is defined for a foreign key constraint. Index is a part of a foreign key constraint if the index is referenced by foreign key or index is a foreign key index. @return pointer to foreign key struct if index is defined for foreign key, otherwise NULL */ UNIV_INTERN dict_foreign_t* dict_table_get_foreign_constraint( /*==============================*/ dict_table_t* table, /*!< in: InnoDB table */ dict_index_t* index) /*!< in: InnoDB index */ { dict_foreign_t* foreign; ut_ad(index != NULL); ut_ad(table != NULL); for (foreign = UT_LIST_GET_FIRST(table->foreign_list); foreign; foreign = UT_LIST_GET_NEXT(foreign_list, foreign)) { if (foreign->foreign_index == index || foreign->referenced_index == index) { return(foreign); } } return(NULL); } /*********************************************************************//** Frees a foreign key struct. */ static void dict_foreign_free( /*==============*/ dict_foreign_t* foreign) /*!< in, own: foreign key struct */ { mem_heap_free(foreign->heap); } /**********************************************************************//** Removes a foreign constraint struct from the dictionary cache. */ static void dict_foreign_remove_from_cache( /*===========================*/ dict_foreign_t* foreign) /*!< in, own: foreign constraint */ { ut_ad(mutex_own(&(dict_sys->mutex))); ut_a(foreign); if (foreign->referenced_table) { UT_LIST_REMOVE(referenced_list, foreign->referenced_table->referenced_list, foreign); } if (foreign->foreign_table) { UT_LIST_REMOVE(foreign_list, foreign->foreign_table->foreign_list, foreign); } dict_foreign_free(foreign); } /**********************************************************************//** Looks for the foreign constraint from the foreign and referenced lists of a table. @return foreign constraint */ static dict_foreign_t* dict_foreign_find( /*==============*/ dict_table_t* table, /*!< in: table object */ const char* id) /*!< in: foreign constraint id */ { dict_foreign_t* foreign; ut_ad(mutex_own(&(dict_sys->mutex))); foreign = UT_LIST_GET_FIRST(table->foreign_list); while (foreign) { if (ut_strcmp(id, foreign->id) == 0) { return(foreign); } foreign = UT_LIST_GET_NEXT(foreign_list, foreign); } foreign = UT_LIST_GET_FIRST(table->referenced_list); while (foreign) { if (ut_strcmp(id, foreign->id) == 0) { return(foreign); } foreign = UT_LIST_GET_NEXT(referenced_list, foreign); } return(NULL); } /*********************************************************************//** Tries to find an index whose first fields are the columns in the array, in the same order and is not marked for deletion and is not the same as types_idx. @return matching index, NULL if not found */ static dict_index_t* dict_foreign_find_index( /*====================*/ dict_table_t* table, /*!< in: table */ const char** columns,/*!< in: array of column names */ ulint n_cols, /*!< in: number of columns */ dict_index_t* types_idx, /*!< in: NULL or an index to whose types the column types must match */ ibool check_charsets, /*!< in: whether to check charsets. only has an effect if types_idx != NULL */ ulint check_null) /*!< in: nonzero if none of the columns must be declared NOT NULL */ { dict_index_t* index; index = dict_table_get_first_index(table); while (index != NULL) { /* Ignore matches that refer to the same instance or the index is to be dropped */ if (index->to_be_dropped || types_idx == index) { goto next_rec; } else if (dict_index_get_n_fields(index) >= n_cols) { ulint i; for (i = 0; i < n_cols; i++) { dict_field_t* field; const char* col_name; field = dict_index_get_nth_field(index, i); col_name = dict_table_get_col_name( table, dict_col_get_no(field->col)); if (field->prefix_len != 0) { /* We do not accept column prefix indexes here */ break; } if (0 != ib_utf8_strcasecmp(columns[i], col_name)) { break; } if (check_null && (field->col->prtype & DATA_NOT_NULL)) { return(NULL); } if (types_idx && !cmp_cols_are_equal( dict_index_get_nth_col(index, i), dict_index_get_nth_col(types_idx, i), check_charsets)) { break; } } if (i == n_cols) { /* We found a matching index */ return(index); } } next_rec: index = dict_table_get_next_index(index); } return(NULL); } /**********************************************************************//** Find an index that is equivalent to the one passed in and is not marked for deletion. @return index equivalent to foreign->foreign_index, or NULL */ UNIV_INTERN dict_index_t* dict_foreign_find_equiv_index( /*==========================*/ dict_foreign_t* foreign)/*!< in: foreign key */ { ut_a(foreign != NULL); /* Try to find an index which contains the columns as the first fields and in the right order, and the types are the same as in foreign->foreign_index */ return(dict_foreign_find_index( foreign->foreign_table, foreign->foreign_col_names, foreign->n_fields, foreign->foreign_index, TRUE, /* check types */ FALSE/* allow columns to be NULL */)); } /**********************************************************************//** Returns an index object by matching on the name and column names and if more than one index matches return the index with the max id @return matching index, NULL if not found */ UNIV_INTERN dict_index_t* dict_table_get_index_by_max_id( /*===========================*/ dict_table_t* table, /*!< in: table */ const char* name, /*!< in: the index name to find */ const char** columns,/*!< in: array of column names */ ulint n_cols) /*!< in: number of columns */ { dict_index_t* index; dict_index_t* found; found = NULL; index = dict_table_get_first_index(table); while (index != NULL) { if (ut_strcmp(index->name, name) == 0 && dict_index_get_n_ordering_defined_by_user(index) == n_cols) { ulint i; for (i = 0; i < n_cols; i++) { dict_field_t* field; const char* col_name; field = dict_index_get_nth_field(index, i); col_name = dict_table_get_col_name( table, dict_col_get_no(field->col)); if (0 != ib_utf8_strcasecmp( columns[i], col_name)) { break; } } if (i == n_cols) { /* We found a matching index, select the index with the higher id*/ if (!found || ut_dulint_cmp(index->id, found->id) > 0) { found = index; } } } index = dict_table_get_next_index(index); } return(found); } /**********************************************************************//** Report an error in a foreign key definition. */ static void dict_foreign_error_report_low( /*==========================*/ ib_stream_t ib_stream, /*!< in: output stream */ const char* name) /*!< in: table name */ { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " Error in foreign key constraint of table %s:\n", name); } /**********************************************************************//** Report an error in a foreign key definition. */ static void dict_foreign_error_report( /*======================*/ ib_stream_t ib_stream, /*!< in: output stream */ dict_foreign_t* fk, /*!< in: foreign key constraint */ const char* msg) /*!< in: the error message */ { mutex_enter(&dict_foreign_err_mutex); dict_foreign_error_report_low(ib_stream, fk->foreign_table_name); ib_logger(ib_stream, "%s", msg); ib_logger(ib_stream, "%s Constraint:\n", msg); dict_print_info_on_foreign_key_in_create_format( ib_stream, NULL, fk, TRUE); ib_logger(ib_stream, "\n"); if (fk->foreign_index) { ib_logger(ib_stream, "The index in the foreign key in table is "); ut_print_name(ib_stream, NULL, FALSE, fk->foreign_index->name); ib_logger(ib_stream, "\n" "See InnoDB website for details\n" "for correct foreign key definition.\n"); } mutex_exit(&dict_foreign_err_mutex); } /**********************************************************************//** Adds a foreign key constraint object to the dictionary cache. May free the object if there already is an object with the same identifier in. At least one of the foreign table and the referenced table must already be in the dictionary cache! @return DB_SUCCESS or error code */ UNIV_INTERN ulint dict_foreign_add_to_cache( /*======================*/ dict_foreign_t* foreign, /*!< in, own: foreign key constraint */ ibool check_charsets) /*!< in: TRUE=check charset compatibility */ { dict_table_t* for_table; dict_table_t* ref_table; dict_foreign_t* for_in_cache = NULL; dict_index_t* index; ibool added_to_referenced_list= FALSE; ut_ad(mutex_own(&(dict_sys->mutex))); for_table = dict_table_check_if_in_cache_low( foreign->foreign_table_name); ref_table = dict_table_check_if_in_cache_low( foreign->referenced_table_name); ut_a(for_table || ref_table); if (for_table) { for_in_cache = dict_foreign_find(for_table, foreign->id); } if (!for_in_cache && ref_table) { for_in_cache = dict_foreign_find(ref_table, foreign->id); } if (for_in_cache) { /* Free the foreign object */ mem_heap_free(foreign->heap); } else { for_in_cache = foreign; } if (for_in_cache->referenced_table == NULL && ref_table) { index = dict_foreign_find_index( ref_table, for_in_cache->referenced_col_names, for_in_cache->n_fields, for_in_cache->foreign_index, check_charsets, FALSE); if (index == NULL) { dict_foreign_error_report( ib_stream, for_in_cache, "there is no index in referenced table" " which would contain\n" "the columns as the first columns," " or the data types in the\n" "referenced table do not match" " the ones in table."); if (for_in_cache == foreign) { mem_heap_free(foreign->heap); } return(DB_CANNOT_ADD_CONSTRAINT); } for_in_cache->referenced_table = ref_table; for_in_cache->referenced_index = index; UT_LIST_ADD_LAST(referenced_list, ref_table->referenced_list, for_in_cache); added_to_referenced_list = TRUE; } if (for_in_cache->foreign_table == NULL && for_table) { index = dict_foreign_find_index( for_table, for_in_cache->foreign_col_names, for_in_cache->n_fields, for_in_cache->referenced_index, check_charsets, for_in_cache->type & (DICT_FOREIGN_ON_DELETE_SET_NULL | DICT_FOREIGN_ON_UPDATE_SET_NULL)); if (index == NULL) { dict_foreign_error_report( ib_stream, for_in_cache, "there is no index in the table" " which would contain\n" "the columns as the first columns," " or the data types in the\n" "table do not match" " the ones in the referenced table\n" "or one of the ON ... SET NULL columns" " is declared NOT NULL."); if (for_in_cache == foreign) { if (added_to_referenced_list) { UT_LIST_REMOVE( referenced_list, ref_table->referenced_list, for_in_cache); } mem_heap_free(foreign->heap); } return(DB_CANNOT_ADD_CONSTRAINT); } for_in_cache->foreign_table = for_table; for_in_cache->foreign_index = index; UT_LIST_ADD_LAST(foreign_list, for_table->foreign_list, for_in_cache); } return(DB_SUCCESS); } /*********************************************************************//** Scans from pointer onwards. Stops if is at the start of a copy of 'string' where characters are compared without case sensitivity, and only outside `` or "" quotes. Stops also at NUL. @return scanned up to this */ static const char* dict_scan_to( /*=========*/ const char* ptr, /*!< in: scan from */ const char* string) /*!< in: look for this */ { char quote = '\0'; for (; *ptr; ptr++) { if (*ptr == quote) { /* Closing quote character: do not look for starting quote or the keyword. */ quote = '\0'; } else if (quote) { /* Within quotes: do nothing. */ } else if (*ptr == '`' || *ptr == '"') { /* Starting quote: remember the quote character. */ quote = *ptr; } else { /* Outside quotes: look for the keyword. */ ulint i; for (i = 0; string[i]; i++) { if (toupper((int)(unsigned char)(ptr[i])) != toupper((int)(unsigned char) (string[i]))) { goto nomatch; } } break; nomatch: ; } } return(ptr); } /*********************************************************************//** Accepts a specified string. Comparisons are case-insensitive. @return if string was accepted, the pointer is moved after that, else ptr is returned */ static const char* dict_accept( /*========*/ const charset_t*cs, /*!< in: the character set of ptr */ const char* ptr, /*!< in: scan from this */ const char* string, /*!< in: accept only this string as the next non-whitespace string */ ibool* success)/*!< out: TRUE if accepted */ { const char* old_ptr = ptr; const char* old_ptr2; *success = FALSE; while (ib_utf8_isspace(cs, *ptr)) { ptr++; } old_ptr2 = ptr; ptr = dict_scan_to(ptr, string); if (*ptr == '\0' || old_ptr2 != ptr) { return(old_ptr); } *success = TRUE; return(ptr + ut_strlen(string)); } /*********************************************************************//** Scans an id. For the lexical definition of an 'id', see the code below. Strips backquotes or double quotes from around the id. @return scanned to */ static const char* dict_scan_id( /*=========*/ const charset_t*cs, /*!< in: the character set of ptr */ const char* ptr, /*!< in: scanned to */ mem_heap_t* heap, /*!< in: heap where to allocate the id (NULL=id will not be allocated, but it will point to string near ptr) */ const char** id, /*!< out,own: the id; NULL if no id was scannable */ ibool table_id,/*!< in: TRUE=convert the allocated id as a table name; FALSE=convert to UTF-8 */ ibool accept_also_dot) /*!< in: TRUE if also a dot can appear in a non-quoted id; in a quoted id it can appear always */ { char quote = '\0'; ulint len = 0; const char* s; char* str; char* dst; *id = NULL; while (ib_utf8_isspace(cs, *ptr)) { ptr++; } if (*ptr == '\0') { return(ptr); } if (*ptr == '`' || *ptr == '"') { quote = *ptr++; } s = ptr; if (quote) { for (;;) { if (!*ptr) { /* Syntax error */ return(ptr); } if (*ptr == quote) { ptr++; if (*ptr != quote) { break; } } ptr++; len++; } } else { while (!ib_utf8_isspace(cs, *ptr) && *ptr != '(' && *ptr != ')' && (accept_also_dot || *ptr != '.') && *ptr != ',' && *ptr != '\0') { ptr++; } len = ptr - s; } if (UNIV_UNLIKELY(!heap)) { /* no heap given: id will point to source string */ *id = s; return(ptr); } if (quote) { char* d; str = d = mem_heap_alloc(heap, len + 1); while (len--) { if ((*d++ = *s++) == quote) { s++; } } *d++ = 0; len = d - str; ut_ad(*s == quote); ut_ad(s + 1 == ptr); } else { str = mem_heap_strdupl(heap, s, len); } if (!table_id) { /* Convert the identifier from connection character set to UTF-8. */ len = 3 * len + 1; *id = dst = mem_heap_alloc(heap, len); ib_utf8_convert_from_id(cs, dst, str, len); } else { /* Encode using filename-safe characters. */ len = 5 * len + 1; *id = dst = mem_heap_alloc(heap, len); ib_utf8_convert_from_table_id(cs, dst, str, len); } return(ptr); } /*********************************************************************//** Tries to scan a column name. @return scanned to */ static const char* dict_scan_col( /*==========*/ const charset_t* cs, /*!< in: the character set of ptr */ const char* ptr, /*!< in: scanned to */ ibool* success,/*!< out: TRUE if success */ dict_table_t* table, /*!< in: table in which the column is */ const dict_col_t** column, /*!< out: pointer to column if success */ mem_heap_t* heap, /*!< in: heap where to allocate */ const char** name) /*!< out,own: the column name; NULL if no name was scannable */ { ulint i; *success = FALSE; ptr = dict_scan_id(cs, ptr, heap, name, FALSE, TRUE); if (*name == NULL) { return(ptr); /* Syntax error */ } if (table == NULL) { *success = TRUE; *column = NULL; } else { for (i = 0; i < dict_table_get_n_cols(table); i++) { const char* col_name = dict_table_get_col_name( table, i); if (0 == ib_utf8_strcasecmp(col_name, *name)) { /* Found */ *success = TRUE; *column = dict_table_get_nth_col(table, i); strcpy((char*) *name, col_name); break; } } } return(ptr); } /*********************************************************************//** Scans a table name from an SQL string. @return scanned to */ static const char* dict_scan_table_name( /*=================*/ const charset_t*cs, /*!< in: the character set of ptr */ const char* ptr, /*!< in: scanned to */ dict_table_t** table, /*!< out: table object or NULL */ const char* name, /*!< in: foreign key table name */ ibool* success,/*!< out: TRUE if ok name found */ mem_heap_t* heap, /*!< in: heap where to allocate the id */ const char** ref_name)/*!< out,own: the table name; NULL if no name was scannable */ { const char* database_name = NULL; ulint database_name_len = 0; const char* table_name = NULL; ulint table_name_len; const char* scan_name; char* ref; *success = FALSE; *table = NULL; ptr = dict_scan_id(cs, ptr, heap, &scan_name, TRUE, FALSE); if (scan_name == NULL) { return(ptr); /* Syntax error */ } if (*ptr == '.') { /* We scanned the database name; scan also the table name */ ptr++; database_name = scan_name; database_name_len = strlen(database_name); ptr = dict_scan_id(cs, ptr, heap, &table_name, TRUE, FALSE); if (table_name == NULL) { return(ptr); /* Syntax error */ } } else { /* To be able to read table dumps made with InnoDB-4.0.17 or earlier, we must allow the dot separator between the database name and the table name also to appear within a quoted identifier! InnoDB used to print a constraint as: ... REFERENCES `databasename.tablename` ... starting from 4.0.18 it is ... REFERENCES `databasename`.`tablename` ... */ const char* s; for (s = scan_name; *s; s++) { if (*s == '.') { database_name = scan_name; database_name_len = s - scan_name; scan_name = ++s; break;/* to do: multiple dots? */ } } table_name = scan_name; } if (database_name == NULL) { /* Use the database name of the foreign key table */ database_name = name; database_name_len = dict_get_db_name_len(name); } table_name_len = strlen(table_name); /* Copy database_name, '/', table_name, '\0' */ ref = mem_heap_alloc(heap, database_name_len + table_name_len + 2); memcpy(ref, database_name, database_name_len); ref[database_name_len] = '/'; memcpy(ref + database_name_len + 1, table_name, table_name_len + 1); #ifndef __WIN__ if (srv_lower_case_table_names) { #endif /* !__WIN__ */ /* The table name is always put to lower case on Windows. */ ib_utf8_casedown(ref); #ifndef __WIN__ } #endif /* !__WIN__ */ *success = TRUE; *ref_name = ref; *table = dict_table_get_low(ref); return(ptr); } /*********************************************************************//** Skips one id. The id is allowed to contain also '.'. @return scanned to */ static const char* dict_skip_word( /*===========*/ const charset_t*cs, /*!< in: the character set of ptr */ const char* ptr, /*!< in: scanned to */ ibool* success)/*!< out: TRUE if success, FALSE if just spaces left in string or a syntax error */ { const char* start; *success = FALSE; ptr = dict_scan_id(cs, ptr, NULL, &start, FALSE, TRUE); if (start) { *success = TRUE; } return(ptr); } /*********************************************************************//** Removes comments from an SQL string. A comment is either (a) '#' to the end of the line, (b) '--[space]' to the end of the line, or (c) '[slash][asterisk]' till the next '[asterisk][slash]' (like the familiar C comment syntax). @return own: SQL string stripped from comments; the caller must free this with mem_free()! */ static char* dict_strip_comments( /*================*/ const char* sql_string) /*!< in: SQL string */ { char* str; const char* sptr; char* ptr; /* unclosed quote character (0 if none) */ char quote = 0; str = mem_alloc(strlen(sql_string) + 1); sptr = sql_string; ptr = str; for (;;) { scan_more: if (*sptr == '\0') { *ptr = '\0'; ut_a(ptr <= str + strlen(sql_string)); return(str); } if (*sptr == quote) { /* Closing quote character: do not look for starting quote or comments. */ quote = 0; } else if (quote) { /* Within quotes: do not look for starting quotes or comments. */ } else if (*sptr == '"' || *sptr == '`' || *sptr == '\'') { /* Starting quote: remember the quote character. */ quote = *sptr; } else if (*sptr == '#' || (sptr[0] == '-' && sptr[1] == '-' && sptr[2] == ' ')) { for (;;) { /* In Unix a newline is 0x0A while in Windows it is 0x0D followed by 0x0A */ if (*sptr == (char)0x0A || *sptr == (char)0x0D || *sptr == '\0') { goto scan_more; } sptr++; } } else if (!quote && *sptr == '/' && *(sptr + 1) == '*') { for (;;) { if (*sptr == '*' && *(sptr + 1) == '/') { sptr += 2; goto scan_more; } if (*sptr == '\0') { goto scan_more; } sptr++; } } *ptr = *sptr; ptr++; sptr++; } } /*********************************************************************//** Finds the highest [number] for foreign key constraints of the table. Looks only at the >= 4.0.18-format id's, which are of the form databasename/tablename_ibfk_[number]. @return highest number, 0 if table has no new format foreign key constraints */ static ulint dict_table_get_highest_foreign_id( /*==============================*/ dict_table_t* table) /*!< in: table in the dictionary memory cache */ { dict_foreign_t* foreign; char* endp; ulint biggest_id = 0; ulint id; ulint len; ut_a(table); len = ut_strlen(table->name); foreign = UT_LIST_GET_FIRST(table->foreign_list); while (foreign) { if (ut_strlen(foreign->id) > ((sizeof dict_ibfk) - 1) + len && 0 == ut_memcmp(foreign->id, table->name, len) && 0 == ut_memcmp(foreign->id + len, dict_ibfk, (sizeof dict_ibfk) - 1) && foreign->id[len + ((sizeof dict_ibfk) - 1)] != '0') { /* It is of the >= 4.0.18 format */ id = strtoul(foreign->id + len + ((sizeof dict_ibfk) - 1), &endp, 10); if (*endp == '\0') { ut_a(id != biggest_id); if (id > biggest_id) { biggest_id = id; } } } foreign = UT_LIST_GET_NEXT(foreign_list, foreign); } return(biggest_id); } /*********************************************************************//** Reports a simple foreign key create clause syntax error. */ static void dict_foreign_report_syntax_err( /*===========================*/ const char* name, /*!< in: table name */ const char* start_of_latest_foreign, /*!< in: start of the foreign key clause in the SQL string */ const char* ptr) /*!< in: place of the syntax error */ { mutex_enter(&dict_foreign_err_mutex); dict_foreign_error_report_low(ib_stream, name); ib_logger(ib_stream, "%s:\nSyntax error close to:\n%s\n", start_of_latest_foreign, ptr); mutex_exit(&dict_foreign_err_mutex); } /*********************************************************************//** Scans a table create SQL string and adds to the data dictionary the foreign key constraints declared in the string. This function should be called after the indexes for a table have been created. Each foreign key constraint must be accompanied with indexes in both participating tables. The indexes are allowed to contain more fields than mentioned in the constraint. @return error code or DB_SUCCESS */ static ulint dict_create_foreign_constraints_low( /*================================*/ trx_t* trx, /*!< in: transaction */ mem_heap_t* heap, /*!< in: memory heap */ const charset_t*cs, /*!< in: the character set of sql_string */ const char* sql_string, /*!< in: CREATE TABLE or ALTER TABLE statement where foreign keys are declared like: FOREIGN KEY (a, b) REFERENCES table2(c, d), table2 can be written also with the database name before it: test.table2; the default database is the database of parameter name */ const char* name, /*!< in: table full name in the normalized form database_name/table_name */ ibool reject_fks) /*!< in: if TRUE, fail with error code DB_CANNOT_ADD_CONSTRAINT if any foreign keys are found. */ { dict_table_t* table; dict_table_t* referenced_table; dict_table_t* table_to_alter; ulint highest_id_so_far = 0; dict_index_t* index; dict_foreign_t* foreign; const char* ptr = sql_string; const char* start_of_latest_foreign = sql_string; const char* constraint_name; ibool success; ulint error; const char* ptr1; const char* ptr2; ulint i; ulint j; ibool is_on_delete; ulint n_on_deletes; ulint n_on_updates; const dict_col_t*columns[500]; const char* column_names[500]; const char* referenced_table_name; ut_ad(mutex_own(&(dict_sys->mutex))); table = dict_table_get_low(name); if (table == NULL) { mutex_enter(&dict_foreign_err_mutex); dict_foreign_error_report_low(ib_stream, name); ib_logger(ib_stream, "Cannot find the table in the internal" " data dictionary of InnoDB.\n" "Create table statement:\n%s\n", sql_string); mutex_exit(&dict_foreign_err_mutex); return(DB_ERROR); } /* First check if we are actually doing an ALTER TABLE, and in that case look for the table being altered */ ptr = dict_accept(cs, ptr, "ALTER", &success); if (!success) { goto loop; } ptr = dict_accept(cs, ptr, "TABLE", &success); if (!success) { goto loop; } /* We are doing an ALTER TABLE: scan the table name we are altering */ ptr = dict_scan_table_name(cs, ptr, &table_to_alter, name, &success, heap, &referenced_table_name); if (!success) { ib_logger(ib_stream, "InnoDB: Error: could not find" " the table being ALTERED in:\n%s\n", sql_string); return(DB_ERROR); } /* Starting from 4.0.18 and 4.1.2, we generate foreign key id's in the format databasename/tablename_ibfk_[number], where [number] is local to the table; look for the highest [number] for table_to_alter, so that we can assign to new constraints higher numbers. */ /* If we are altering a temporary table, the table name after ALTER TABLE does not correspond to the internal table name, and table_to_alter is NULL. TODO: should we fix this somehow? */ if (table_to_alter == NULL) { highest_id_so_far = 0; } else { highest_id_so_far = dict_table_get_highest_foreign_id( table_to_alter); } /* Scan for foreign key declarations in a loop */ loop: /* Scan either to "CONSTRAINT" or "FOREIGN", whichever is closer */ ptr1 = dict_scan_to(ptr, "CONSTRAINT"); ptr2 = dict_scan_to(ptr, "FOREIGN"); constraint_name = NULL; if (ptr1 < ptr2) { /* The user may have specified a constraint name. Pick it so that we can store 'databasename/constraintname' as the id of of the constraint to system tables. */ ptr = ptr1; ptr = dict_accept(cs, ptr, "CONSTRAINT", &success); ut_a(success); if (!ib_utf8_isspace(cs, *ptr) && *ptr != '"' && *ptr != '`') { goto loop; } while (ib_utf8_isspace(cs, *ptr)) { ptr++; } /* read constraint name unless got "CONSTRAINT FOREIGN" */ if (ptr != ptr2) { ptr = dict_scan_id(cs, ptr, heap, &constraint_name, FALSE, FALSE); } } else { ptr = ptr2; } if (*ptr == '\0') { /* The proper way to reject foreign keys for temporary tables would be to split the lexing and syntactical analysis of foreign key clauses from the actual adding of them, so that ha_innodb.cc could first parse the SQL command, determine if there are any foreign keys, and if so, immediately reject the command if the table is a temporary one. For now, this kludge will work. */ if (reject_fks && (UT_LIST_GET_LEN(table->foreign_list) > 0)) { return(DB_CANNOT_ADD_CONSTRAINT); } /**********************************************************/ /* The following call adds the foreign key constraints to the data dictionary system tables on disk */ error = dict_create_add_foreigns_to_dictionary( highest_id_so_far, table, trx); return(error); } start_of_latest_foreign = ptr; ptr = dict_accept(cs, ptr, "FOREIGN", &success); if (!success) { goto loop; } if (!ib_utf8_isspace(cs, *ptr)) { goto loop; } ptr = dict_accept(cs, ptr, "KEY", &success); if (!success) { goto loop; } ptr = dict_accept(cs, ptr, "(", &success); if (!success) { /* Skip index id before the '('. */ ptr = dict_skip_word(cs, ptr, &success); if (!success) { dict_foreign_report_syntax_err( name, start_of_latest_foreign, ptr); return(DB_CANNOT_ADD_CONSTRAINT); } ptr = dict_accept(cs, ptr, "(", &success); if (!success) { /* We do not flag a syntax error here because in an ALTER TABLE we may also have DROP FOREIGN KEY abc */ goto loop; } } i = 0; /* Scan the columns in the first list */ col_loop1: ut_a(i < (sizeof column_names) / sizeof *column_names); ptr = dict_scan_col(cs, ptr, &success, table, columns + i, heap, column_names + i); if (!success) { mutex_enter(&dict_foreign_err_mutex); dict_foreign_error_report_low(ib_stream, name); ib_logger(ib_stream, "%s:\nCannot resolve column name close to:\n%s\n", start_of_latest_foreign, ptr); mutex_exit(&dict_foreign_err_mutex); return(DB_CANNOT_ADD_CONSTRAINT); } i++; ptr = dict_accept(cs, ptr, ",", &success); if (success) { goto col_loop1; } ptr = dict_accept(cs, ptr, ")", &success); if (!success) { dict_foreign_report_syntax_err( name, start_of_latest_foreign, ptr); return(DB_CANNOT_ADD_CONSTRAINT); } /* Try to find an index which contains the columns as the first fields and in the right order */ index = dict_foreign_find_index(table, column_names, i, NULL, TRUE, FALSE); if (!index) { mutex_enter(&dict_foreign_err_mutex); dict_foreign_error_report_low(ib_stream, name); ib_logger(ib_stream, "There is no index in table "); ut_print_name(ib_stream, NULL, TRUE, name); ib_logger(ib_stream, " where the columns appear\n" "as the first columns. Constraint:\n%s\n" "See InnoDB website for details" "for correct foreign key definition.\n", start_of_latest_foreign); mutex_exit(&dict_foreign_err_mutex); return(DB_CANNOT_ADD_CONSTRAINT); } ptr = dict_accept(cs, ptr, "REFERENCES", &success); if (!success || !ib_utf8_isspace(cs, *ptr)) { dict_foreign_report_syntax_err( name, start_of_latest_foreign, ptr); return(DB_CANNOT_ADD_CONSTRAINT); } /* Let us create a constraint struct */ foreign = dict_mem_foreign_create(); if (constraint_name) { ulint db_len; /* Catenate 'databasename/' to the constraint name specified by the user: we conceive the constraint as belonging to the same client 'database' as the table itself. We store the name to foreign->id. */ db_len = dict_get_db_name_len(table->name); foreign->id = mem_heap_alloc( foreign->heap, db_len + strlen(constraint_name) + 2); ut_memcpy(foreign->id, table->name, db_len); foreign->id[db_len] = '/'; strcpy(foreign->id + db_len + 1, constraint_name); } foreign->foreign_table = table; foreign->foreign_table_name = mem_heap_strdup(foreign->heap, table->name); foreign->foreign_index = index; foreign->n_fields = (unsigned int) i; foreign->foreign_col_names = mem_heap_alloc(foreign->heap, i * sizeof(void*)); for (i = 0; i < foreign->n_fields; i++) { foreign->foreign_col_names[i] = mem_heap_strdup( foreign->heap, dict_table_get_col_name(table, dict_col_get_no(columns[i]))); } ptr = dict_scan_table_name(cs, ptr, &referenced_table, name, &success, heap, &referenced_table_name); /* Note that referenced_table can be NULL if the user has suppressed checking of foreign key constraints! */ if (!success || (!referenced_table && trx->check_foreigns)) { dict_foreign_free(foreign); mutex_enter(&dict_foreign_err_mutex); dict_foreign_error_report_low(ib_stream, name); ib_logger(ib_stream, "%s:\nCannot resolve table name close to:\n" "%s\n", start_of_latest_foreign, ptr); mutex_exit(&dict_foreign_err_mutex); return(DB_CANNOT_ADD_CONSTRAINT); } ptr = dict_accept(cs, ptr, "(", &success); if (!success) { dict_foreign_free(foreign); dict_foreign_report_syntax_err(name, start_of_latest_foreign, ptr); return(DB_CANNOT_ADD_CONSTRAINT); } /* Scan the columns in the second list */ i = 0; col_loop2: ptr = dict_scan_col(cs, ptr, &success, referenced_table, columns + i, heap, column_names + i); i++; if (!success) { dict_foreign_free(foreign); mutex_enter(&dict_foreign_err_mutex); dict_foreign_error_report_low(ib_stream, name); ib_logger(ib_stream, "%s:\nCannot resolve column name close to:\n" "%s\n", start_of_latest_foreign, ptr); mutex_exit(&dict_foreign_err_mutex); return(DB_CANNOT_ADD_CONSTRAINT); } ptr = dict_accept(cs, ptr, ",", &success); if (success) { goto col_loop2; } ptr = dict_accept(cs, ptr, ")", &success); if (!success || foreign->n_fields != i) { dict_foreign_free(foreign); dict_foreign_report_syntax_err(name, start_of_latest_foreign, ptr); return(DB_CANNOT_ADD_CONSTRAINT); } n_on_deletes = 0; n_on_updates = 0; scan_on_conditions: /* Loop here as long as we can find ON ... conditions */ ptr = dict_accept(cs, ptr, "ON", &success); if (!success) { goto try_find_index; } ptr = dict_accept(cs, ptr, "DELETE", &success); if (!success) { ptr = dict_accept(cs, ptr, "UPDATE", &success); if (!success) { dict_foreign_free(foreign); dict_foreign_report_syntax_err( name, start_of_latest_foreign, ptr); return(DB_CANNOT_ADD_CONSTRAINT); } is_on_delete = FALSE; n_on_updates++; } else { is_on_delete = TRUE; n_on_deletes++; } ptr = dict_accept(cs, ptr, "RESTRICT", &success); if (success) { goto scan_on_conditions; } ptr = dict_accept(cs, ptr, "CASCADE", &success); if (success) { if (is_on_delete) { foreign->type |= DICT_FOREIGN_ON_DELETE_CASCADE; } else { foreign->type |= DICT_FOREIGN_ON_UPDATE_CASCADE; } goto scan_on_conditions; } ptr = dict_accept(cs, ptr, "NO", &success); if (success) { ptr = dict_accept(cs, ptr, "ACTION", &success); if (!success) { dict_foreign_free(foreign); dict_foreign_report_syntax_err( name, start_of_latest_foreign, ptr); return(DB_CANNOT_ADD_CONSTRAINT); } if (is_on_delete) { foreign->type |= DICT_FOREIGN_ON_DELETE_NO_ACTION; } else { foreign->type |= DICT_FOREIGN_ON_UPDATE_NO_ACTION; } goto scan_on_conditions; } ptr = dict_accept(cs, ptr, "SET", &success); if (!success) { dict_foreign_free(foreign); dict_foreign_report_syntax_err(name, start_of_latest_foreign, ptr); return(DB_CANNOT_ADD_CONSTRAINT); } ptr = dict_accept(cs, ptr, "NULL", &success); if (!success) { dict_foreign_free(foreign); dict_foreign_report_syntax_err(name, start_of_latest_foreign, ptr); return(DB_CANNOT_ADD_CONSTRAINT); } for (j = 0; j < foreign->n_fields; j++) { if ((dict_index_get_nth_col(foreign->foreign_index, j)->prtype) & DATA_NOT_NULL) { /* It is not sensible to define SET NULL if the column is not allowed to be NULL! */ dict_foreign_free(foreign); mutex_enter(&dict_foreign_err_mutex); dict_foreign_error_report_low(ib_stream, name); ib_logger(ib_stream, "%s:\n" "You have defined a SET NULL condition" " though some of the\n" "columns are defined as NOT NULL.\n", start_of_latest_foreign); mutex_exit(&dict_foreign_err_mutex); return(DB_CANNOT_ADD_CONSTRAINT); } } if (is_on_delete) { foreign->type |= DICT_FOREIGN_ON_DELETE_SET_NULL; } else { foreign->type |= DICT_FOREIGN_ON_UPDATE_SET_NULL; } goto scan_on_conditions; try_find_index: if (n_on_deletes > 1 || n_on_updates > 1) { /* It is an error to define more than 1 action */ dict_foreign_free(foreign); mutex_enter(&dict_foreign_err_mutex); dict_foreign_error_report_low(ib_stream, name); ib_logger(ib_stream, "%s:\n" "You have twice an ON DELETE clause" " or twice an ON UPDATE clause.\n", start_of_latest_foreign); mutex_exit(&dict_foreign_err_mutex); return(DB_CANNOT_ADD_CONSTRAINT); } /* Try to find an index which contains the columns as the first fields and in the right order, and the types are the same as in foreign->foreign_index */ if (referenced_table) { index = dict_foreign_find_index(referenced_table, column_names, i, foreign->foreign_index, TRUE, FALSE); if (!index) { dict_foreign_free(foreign); mutex_enter(&dict_foreign_err_mutex); dict_foreign_error_report_low(ib_stream, name); ib_logger(ib_stream, "%s:\n" "Cannot find an index in the" " referenced table where the\n" "referenced columns appear as the" " first columns, or column types\n" "in the table and the referenced table" " do not match for constraint.\n" "Note that the internal storage type of" " ENUM and SET changed in\n" "tables created with >= InnoDB-4.1.12," " and such columns in old tables\n" "cannot be referenced by such columns" " in new tables.\n" "See InnoDB website for details" "for correct foreign key definition.\n", start_of_latest_foreign); mutex_exit(&dict_foreign_err_mutex); return(DB_CANNOT_ADD_CONSTRAINT); } } else { ut_a(trx->check_foreigns == FALSE); index = NULL; } foreign->referenced_index = index; foreign->referenced_table = referenced_table; foreign->referenced_table_name = mem_heap_strdup(foreign->heap, referenced_table_name); foreign->referenced_col_names = mem_heap_alloc(foreign->heap, i * sizeof(void*)); for (i = 0; i < foreign->n_fields; i++) { foreign->referenced_col_names[i] = mem_heap_strdup(foreign->heap, column_names[i]); } /* We found an ok constraint definition: add to the lists */ UT_LIST_ADD_LAST(foreign_list, table->foreign_list, foreign); if (referenced_table) { UT_LIST_ADD_LAST(referenced_list, referenced_table->referenced_list, foreign); } goto loop; } /*********************************************************************//** Scans a table create SQL string and adds to the data dictionary the foreign key constraints declared in the string. This function should be called after the indexes for a table have been created. Each foreign key constraint must be accompanied with indexes in both participating tables. The indexes are allowed to contain more fields than mentioned in the constraint. @return error code or DB_SUCCESS */ UNIV_INTERN ulint dict_create_foreign_constraints( /*============================*/ trx_t* trx, /*!< in: transaction */ const char* sql_string, /*!< in: table create statement where foreign keys are declared like: FOREIGN KEY (a, b) REFERENCES table2(c, d), table2 can be written also with the database name before it: test.table2; the default database id the database of parameter name */ const char* name, /*!< in: table full name in the normalized form database_name/table_name */ ibool reject_fks) /*!< in: if TRUE, fail with error code DB_CANNOT_ADD_CONSTRAINT if any foreign keys are found. */ { const charset_t* cs; char* str; ulint err; mem_heap_t* heap; ut_a(trx); str = dict_strip_comments(sql_string); heap = mem_heap_create(10000); cs = ib_ucode_get_connection_charset(); err = dict_create_foreign_constraints_low(trx, heap, cs, str, name, reject_fks); mem_heap_free(heap); mem_free(str); return(err); } /**********************************************************************//** Parses the CONSTRAINT id's to be dropped in an ALTER TABLE statement. @return DB_SUCCESS or DB_CANNOT_DROP_CONSTRAINT if syntax error or the constraint id does not match */ UNIV_INTERN ulint dict_foreign_parse_drop_constraints( /*================================*/ mem_heap_t* heap, /*!< in: heap from which we can allocate memory */ trx_t* trx, /*!< in: transaction */ dict_table_t* table, /*!< in: table */ ulint* n, /*!< out: number of constraints to drop */ const char*** constraints_to_drop) /*!< out: id's of the constraints to drop */ { dict_foreign_t* foreign; ibool success; char* str; const char* ptr; const char* id; const charset_t*cs; ut_a(trx); cs = ib_ucode_get_connection_charset(); *n = 0; *constraints_to_drop = mem_heap_alloc(heap, 1000 * sizeof(char*)); str = dict_strip_comments(*(trx->client_query_str)); ptr = str; ut_ad(mutex_own(&(dict_sys->mutex))); loop: ptr = dict_scan_to(ptr, "DROP"); if (*ptr == '\0') { mem_free(str); return(DB_SUCCESS); } ptr = dict_accept(cs, ptr, "DROP", &success); if (!ib_utf8_isspace(cs, *ptr)) { goto loop; } ptr = dict_accept(cs, ptr, "FOREIGN", &success); if (!success || !ib_utf8_isspace(cs, *ptr)) { goto loop; } ptr = dict_accept(cs, ptr, "KEY", &success); if (!success) { goto syntax_error; } ptr = dict_scan_id(cs, ptr, heap, &id, FALSE, TRUE); if (id == NULL) { goto syntax_error; } ut_a(*n < 1000); (*constraints_to_drop)[*n] = id; (*n)++; /* Look for the given constraint id */ foreign = UT_LIST_GET_FIRST(table->foreign_list); while (foreign != NULL) { if (0 == strcmp(foreign->id, id) || (strchr(foreign->id, '/') && 0 == strcmp(id, dict_remove_db_name(foreign->id)))) { /* Found */ break; } foreign = UT_LIST_GET_NEXT(foreign_list, foreign); } if (foreign == NULL) { mutex_enter(&dict_foreign_err_mutex); ut_print_timestamp(ib_stream); ib_logger(ib_stream, " Error in dropping of a foreign key constraint" " of table "); ut_print_name(ib_stream, NULL, TRUE, table->name); ib_logger(ib_stream, ",\nin SQL command\n%s", str); ib_logger(ib_stream, "\nCannot find a constraint with the given id "); ut_print_name(ib_stream, NULL, FALSE, id); ib_logger(ib_stream, ".\n"); mutex_exit(&dict_foreign_err_mutex); mem_free(str); return(DB_CANNOT_DROP_CONSTRAINT); } goto loop; syntax_error: mutex_enter(&dict_foreign_err_mutex); ut_print_timestamp(ib_stream); ib_logger(ib_stream," Syntax error in dropping of a" " foreign key constraint of table "); ut_print_name(ib_stream, NULL, TRUE, table->name); ib_logger(ib_stream, ",\n" "close to:\n%s\n in SQL command\n%s\n", ptr, str); mutex_exit(&dict_foreign_err_mutex); mem_free(str); return(DB_CANNOT_DROP_CONSTRAINT); } /*==================== END OF FOREIGN KEY PROCESSING ====================*/ /**********************************************************************//** Returns an index object if it is found in the dictionary cache. Assumes that dict_sys->mutex is already being held. @return index, NULL if not found */ UNIV_INTERN dict_index_t* dict_index_get_if_in_cache_low( /*===========================*/ dulint index_id) /*!< in: index id */ { ut_ad(mutex_own(&(dict_sys->mutex))); return(dict_index_find_on_id_low(index_id)); } #if defined UNIV_DEBUG || defined UNIV_BUF_DEBUG /**********************************************************************//** Returns an index object if it is found in the dictionary cache. @return index, NULL if not found */ UNIV_INTERN dict_index_t* dict_index_get_if_in_cache( /*=======================*/ dulint index_id) /*!< in: index id */ { dict_index_t* index; if (dict_sys == NULL) { return(NULL); } mutex_enter(&(dict_sys->mutex)); index = dict_index_get_if_in_cache_low(index_id); mutex_exit(&(dict_sys->mutex)); return(index); } #endif /* UNIV_DEBUG || UNIV_BUF_DEBUG */ #ifdef UNIV_DEBUG /**********************************************************************//** Checks that a tuple has n_fields_cmp value in a sensible range, so that no comparison can occur with the page number field in a node pointer. @return TRUE if ok */ UNIV_INTERN ibool dict_index_check_search_tuple( /*==========================*/ const dict_index_t* index, /*!< in: index tree */ const dtuple_t* tuple) /*!< in: tuple used in a search */ { ut_a(index); ut_a(dtuple_get_n_fields_cmp(tuple) <= dict_index_get_n_unique_in_tree(index)); return(TRUE); } #endif /* UNIV_DEBUG */ /**********************************************************************//** Builds a node pointer out of a physical record and a page number. @return own: node pointer */ UNIV_INTERN dtuple_t* dict_index_build_node_ptr( /*======================*/ const dict_index_t* index, /*!< in: index */ const rec_t* rec, /*!< in: record for which to build node pointer */ ulint page_no,/*!< in: page number to put in node pointer */ mem_heap_t* heap, /*!< in: memory heap where pointer created */ ulint level) /*!< in: level of rec in tree: 0 means leaf level */ { dtuple_t* tuple; dfield_t* field; byte* buf; ulint n_unique; if (UNIV_UNLIKELY(index->type & DICT_UNIVERSAL)) { /* In a universal index tree, we take the whole record as the node pointer if the record is on the leaf level, on non-leaf levels we remove the last field, which contains the page number of the child page */ ut_a(!dict_table_is_comp(index->table)); n_unique = rec_get_n_fields_old(rec); if (level > 0) { ut_a(n_unique > 1); n_unique--; } } else { n_unique = dict_index_get_n_unique_in_tree(index); } tuple = dtuple_create(heap, n_unique + 1); /* When searching in the tree for the node pointer, we must not do comparison on the last field, the page number field, as on upper levels in the tree there may be identical node pointers with a different page number; therefore, we set the n_fields_cmp to one less: */ dtuple_set_n_fields_cmp(tuple, n_unique); dict_index_copy_types(tuple, index, n_unique); buf = mem_heap_alloc(heap, 4); mach_write_to_4(buf, page_no); field = dtuple_get_nth_field(tuple, n_unique); dfield_set_data(field, buf, 4); dtype_set(dfield_get_type(field), DATA_SYS_CHILD, DATA_NOT_NULL, 4); rec_copy_prefix_to_dtuple(tuple, rec, index, n_unique, heap); dtuple_set_info_bits(tuple, dtuple_get_info_bits(tuple) | REC_STATUS_NODE_PTR); ut_ad(dtuple_check_typed(tuple)); return(tuple); } /**********************************************************************//** Copies an initial segment of a physical record, long enough to specify an index entry uniquely. @return pointer to the prefix record */ UNIV_INTERN rec_t* dict_index_copy_rec_order_prefix( /*=============================*/ const dict_index_t* index, /*!< in: index */ const rec_t* rec, /*!< in: record for which to copy prefix */ ulint* n_fields,/*!< out: number of fields copied */ byte** buf, /*!< in/out: memory buffer for the copied prefix, or NULL */ ulint* buf_size)/*!< in/out: buffer size */ { ulint n; UNIV_PREFETCH_R(rec); if (UNIV_UNLIKELY(index->type & DICT_UNIVERSAL)) { ut_a(!dict_table_is_comp(index->table)); n = rec_get_n_fields_old(rec); } else { n = dict_index_get_n_unique_in_tree(index); } *n_fields = n; return(rec_copy_prefix_to_buf(rec, index, n, buf, buf_size)); } /**********************************************************************//** Builds a typed data tuple out of a physical record. @return own: data tuple */ UNIV_INTERN dtuple_t* dict_index_build_data_tuple( /*========================*/ dict_index_t* index, /*!< in: index tree */ rec_t* rec, /*!< in: record for which to build data tuple */ ulint n_fields,/*!< in: number of data fields */ mem_heap_t* heap) /*!< in: memory heap where tuple created */ { dtuple_t* tuple; ut_ad(dict_table_is_comp(index->table) || n_fields <= rec_get_n_fields_old(rec)); tuple = dtuple_create(heap, n_fields); dict_index_copy_types(tuple, index, n_fields); rec_copy_prefix_to_dtuple(tuple, rec, index, n_fields, heap); ut_ad(dtuple_check_typed(tuple)); return(tuple); } /*********************************************************************//** Calculates the minimum record length in an index. */ UNIV_INTERN ulint dict_index_calc_min_rec_len( /*========================*/ const dict_index_t* index) /*!< in: index */ { ulint sum = 0; ulint i; ulint comp = dict_table_is_comp(index->table); if (comp) { ulint nullable = 0; sum = REC_N_NEW_EXTRA_BYTES; for (i = 0; i < dict_index_get_n_fields(index); i++) { const dict_col_t* col = dict_index_get_nth_col(index, i); ulint size = dict_col_get_fixed_size(col, comp); sum += size; if (!size) { size = col->len; sum += size < 128 ? 1 : 2; } if (!(col->prtype & DATA_NOT_NULL)) { nullable++; } } /* round the NULL flags up to full bytes */ sum += UT_BITS_IN_BYTES(nullable); return(sum); } for (i = 0; i < dict_index_get_n_fields(index); i++) { sum += dict_col_get_fixed_size( dict_index_get_nth_col(index, i), comp); } if (sum > 127) { sum += 2 * dict_index_get_n_fields(index); } else { sum += dict_index_get_n_fields(index); } sum += REC_N_OLD_EXTRA_BYTES; return(sum); } /*********************************************************************//** Calculates new estimates for table and index statistics. The statistics are used in query optimization. */ UNIV_INTERN void dict_update_statistics_low( /*=======================*/ dict_table_t* table, /*!< in/out: table */ ibool has_dict_mutex __attribute__((unused))) /*!< in: TRUE if the caller has the dictionary mutex */ { dict_index_t* index; ulint size; ulint sum_of_index_sizes = 0; if (table->ibd_file_missing) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: cannot calculate statistics for table %s\n" "InnoDB: because the .ibd file is missing. For help," " please refer to\n" "InnoDB: InnoDB website for details\n", table->name); return; } /* If we have set a high innodb_force_recovery level, do not calculate statistics, as a badly corrupted index can cause a crash in it. */ if (srv_force_recovery >= IB_RECOVERY_NO_IBUF_MERGE) { return; } /* Find out the sizes of the indexes and how many different values for the key they approximately have */ index = dict_table_get_first_index(table); if (index == NULL) { /* Table definition is corrupt */ return; } while (index) { size = btr_get_size(index, BTR_TOTAL_SIZE); index->stat_index_size = size; sum_of_index_sizes += size; size = btr_get_size(index, BTR_N_LEAF_PAGES); if (size == 0) { /* The root node of the tree is a leaf */ size = 1; } index->stat_n_leaf_pages = size; btr_estimate_number_of_different_key_vals(index); index = dict_table_get_next_index(index); } index = dict_table_get_first_index(table); dict_index_stat_mutex_enter(index); table->stat_n_rows = index->stat_n_diff_key_vals[ dict_index_get_n_unique(index)]; dict_index_stat_mutex_exit(index); table->stat_clustered_index_size = index->stat_index_size; table->stat_sum_of_other_index_sizes = sum_of_index_sizes - index->stat_index_size; table->stat_initialized = TRUE; table->stat_modified_counter = 0; } /*********************************************************************//** Calculates new estimates for table and index statistics. The statistics are used in query optimization. */ UNIV_INTERN void dict_update_statistics( /*===================*/ dict_table_t* table) /*!< in/out: table */ { dict_update_statistics_low(table, FALSE); } /**********************************************************************//** Prints info of a foreign key constraint. */ static void dict_foreign_print_low( /*===================*/ dict_foreign_t* foreign) /*!< in: foreign key constraint */ { ulint i; ut_ad(mutex_own(&(dict_sys->mutex))); ib_logger(ib_stream, " FOREIGN KEY CONSTRAINT %s: %s (", foreign->id, foreign->foreign_table_name); for (i = 0; i < foreign->n_fields; i++) { ib_logger(ib_stream, " %s", foreign->foreign_col_names[i]); } ib_logger(ib_stream, " )\n" " REFERENCES %s (", foreign->referenced_table_name); for (i = 0; i < foreign->n_fields; i++) { ib_logger(ib_stream, " %s", foreign->referenced_col_names[i]); } ib_logger(ib_stream, " )\n"); } /**********************************************************************//** Prints a table data. */ UNIV_INTERN void dict_table_print( /*=============*/ dict_table_t* table) /*!< in: table */ { mutex_enter(&(dict_sys->mutex)); dict_table_print_low(table); mutex_exit(&(dict_sys->mutex)); } /**********************************************************************//** Prints a table data when we know the table name. */ UNIV_INTERN void dict_table_print_by_name( /*=====================*/ const char* name) /*!< in: table name */ { dict_table_t* table; mutex_enter(&(dict_sys->mutex)); table = dict_table_get_low(name); ut_a(table); dict_table_print_low(table); mutex_exit(&(dict_sys->mutex)); } /**********************************************************************//** Prints a table data. */ UNIV_INTERN void dict_table_print_low( /*=================*/ dict_table_t* table) /*!< in: table */ { dict_index_t* index; dict_foreign_t* foreign; ulint i; ut_ad(mutex_own(&(dict_sys->mutex))); dict_update_statistics_low(table, TRUE); ib_logger(ib_stream, "--------------------------------------\n" "TABLE: name %s, id %lu %lu, flags %lx, columns %lu," " indexes %lu, appr.rows %lu\n" " COLUMNS: ", table->name, (ulong) ut_dulint_get_high(table->id), (ulong) ut_dulint_get_low(table->id), (ulong) table->flags, (ulong) table->n_cols, (ulong) UT_LIST_GET_LEN(table->indexes), (ulong) table->stat_n_rows); for (i = 0; i < (ulint) table->n_cols; i++) { dict_col_print_low(table, dict_table_get_nth_col(table, i)); ib_logger("; ", ib_stream); } ib_logger(ib_stream,"\n"); index = UT_LIST_GET_FIRST(table->indexes); while (index != NULL) { dict_index_print_low(index); index = UT_LIST_GET_NEXT(indexes, index); } foreign = UT_LIST_GET_FIRST(table->foreign_list); while (foreign != NULL) { dict_foreign_print_low(foreign); foreign = UT_LIST_GET_NEXT(foreign_list, foreign); } foreign = UT_LIST_GET_FIRST(table->referenced_list); while (foreign != NULL) { dict_foreign_print_low(foreign); foreign = UT_LIST_GET_NEXT(referenced_list, foreign); } } /**********************************************************************//** Prints a column data. */ static void dict_col_print_low( /*===============*/ const dict_table_t* table, /*!< in: table */ const dict_col_t* col) /*!< in: column */ { dtype_t type; ut_ad(mutex_own(&(dict_sys->mutex))); dict_col_copy_type(col, &type); ib_logger(ib_stream, "%s: ", dict_table_get_col_name(table, dict_col_get_no(col))); dtype_print(&type); } /**********************************************************************//** Prints an index data. */ static void dict_index_print_low( /*=================*/ dict_index_t* index) /*!< in: index */ { ib_int64_t n_vals; ulint i; const char* type_string; ut_ad(mutex_own(&(dict_sys->mutex))); dict_index_stat_mutex_enter(index); if (index->n_user_defined_cols > 0) { n_vals = index->stat_n_diff_key_vals[ index->n_user_defined_cols]; } else { n_vals = index->stat_n_diff_key_vals[1]; } dict_index_stat_mutex_exit(index); if (dict_index_is_clust(index)) { type_string = "clustered index"; } else if (dict_index_is_unique(index)) { type_string = "unique index"; } else { type_string = "secondary index"; } ib_logger(ib_stream, " INDEX: name %s, id %lu %lu, fields %lu/%lu," " uniq %lu, type %lu\n" " root page %lu, appr.key vals %lu," " leaf pages %lu, size pages %lu\n" " FIELDS: ", index->name, (ulong) ut_dulint_get_high(index->id), (ulong) ut_dulint_get_low(index->id), (ulong) index->n_user_defined_cols, (ulong) index->n_fields, (ulong) index->n_uniq, (ulong) index->type, (ulong) index->page, (ulong) n_vals, (ulong) index->stat_n_leaf_pages, (ulong) index->stat_index_size); for (i = 0; i < index->n_fields; i++) { dict_field_print_low(dict_index_get_nth_field(index, i)); } ib_logger(ib_stream, "\n"); #ifdef UNIV_BTR_PRINT btr_print_size(index); btr_print_index(index, 7); #endif /* UNIV_BTR_PRINT */ } /**********************************************************************//** Prints a field data. */ static void dict_field_print_low( /*=================*/ const dict_field_t* field) /*!< in: field */ { ut_ad(mutex_own(&(dict_sys->mutex))); ib_logger(ib_stream, " %s", field->name); if (field->prefix_len != 0) { ib_logger(ib_stream, "(%lu)", (ulong) field->prefix_len); } } /**********************************************************************//** Outputs info on a foreign key of a table in a format suitable for CREATE TABLE. */ UNIV_INTERN void dict_print_info_on_foreign_key_in_create_format( /*============================================*/ ib_stream_t ib_stream, /*!< in: stream where to print */ trx_t* trx, /*!< in: transaction */ dict_foreign_t* foreign, /*!< in: foreign key constraint */ ibool add_newline) /*!< in: whether to add a newline */ { const char* stripped_id; ulint i; if (strchr(foreign->id, '/')) { /* Strip the preceding database name from the constraint id */ stripped_id = foreign->id + 1 + dict_get_db_name_len(foreign->id); } else { stripped_id = foreign->id; } ib_logger(ib_stream, ","); if (add_newline) { /* SHOW CREATE TABLE wants constraints each printed nicely on its own line, while error messages want no newlines inserted. */ ib_logger(ib_stream, "\n "); } ib_logger(ib_stream, " CONSTRAINT "); ut_print_name(ib_stream, trx, FALSE, stripped_id); ib_logger(ib_stream, " FOREIGN KEY ("); for (i = 0;;) { ut_print_name( ib_stream, trx, FALSE, foreign->foreign_col_names[i]); if (++i < foreign->n_fields) { ib_logger(ib_stream, ", "); } else { break; } } ib_logger(ib_stream, ") REFERENCES "); if (dict_tables_have_same_db(foreign->foreign_table_name, foreign->referenced_table_name)) { /* Do not print the database name of the referenced table */ ut_print_name(ib_stream, trx, TRUE, dict_remove_db_name( foreign->referenced_table_name)); } else { ut_print_name(ib_stream, trx, TRUE, foreign->referenced_table_name); } ib_logger(ib_stream, " ("); for (i = 0;;) { ut_print_name(ib_stream, trx, FALSE, foreign->referenced_col_names[i]); if (++i < foreign->n_fields) { ib_logger(ib_stream, ", "); } else { break; } } ib_logger(ib_stream, ")"); if (foreign->type & DICT_FOREIGN_ON_DELETE_CASCADE) { ib_logger(ib_stream, " ON DELETE CASCADE"); } if (foreign->type & DICT_FOREIGN_ON_DELETE_SET_NULL) { ib_logger(ib_stream, " ON DELETE SET NULL"); } if (foreign->type & DICT_FOREIGN_ON_DELETE_NO_ACTION) { ib_logger(ib_stream, " ON DELETE NO ACTION"); } if (foreign->type & DICT_FOREIGN_ON_UPDATE_CASCADE) { ib_logger(ib_stream, " ON UPDATE CASCADE"); } if (foreign->type & DICT_FOREIGN_ON_UPDATE_SET_NULL) { ib_logger(ib_stream, " ON UPDATE SET NULL"); } if (foreign->type & DICT_FOREIGN_ON_UPDATE_NO_ACTION) { ib_logger(ib_stream, " ON UPDATE NO ACTION"); } } /**********************************************************************//** Outputs info on foreign keys of a table. */ UNIV_INTERN void dict_print_info_on_foreign_keys( /*============================*/ ibool create_table_format, /*!< in: if TRUE then print in a format suitable to be inserted into a CREATE TABLE, otherwise in the format of SHOW TABLE STATUS */ ib_stream_t ib_stream,/*!< in: stream where to print */ trx_t* trx, /*!< in: transaction */ dict_table_t* table) /*!< in: table */ { dict_foreign_t* foreign; mutex_enter(&(dict_sys->mutex)); foreign = UT_LIST_GET_FIRST(table->foreign_list); if (foreign == NULL) { mutex_exit(&(dict_sys->mutex)); return; } while (foreign != NULL) { if (create_table_format) { dict_print_info_on_foreign_key_in_create_format( ib_stream, trx, foreign, TRUE); } else { ulint i; ib_logger(ib_stream, "; ("); for (i = 0; i < foreign->n_fields; i++) { if (i) { ib_logger(ib_stream, " "); } ut_print_name(ib_stream, trx, FALSE, foreign->foreign_col_names[i]); } ib_logger(ib_stream, ") REFER "); ut_print_name(ib_stream, trx, TRUE, foreign->referenced_table_name); ib_logger(ib_stream, "("); for (i = 0; i < foreign->n_fields; i++) { if (i) { ib_logger(ib_stream, " "); } ut_print_name( ib_stream, trx, FALSE, foreign->referenced_col_names[i]); } ib_logger(ib_stream, ")"); if (foreign->type == DICT_FOREIGN_ON_DELETE_CASCADE) { ib_logger(ib_stream, " ON DELETE CASCADE"); } if (foreign->type == DICT_FOREIGN_ON_DELETE_SET_NULL) { ib_logger(ib_stream, " ON DELETE SET NULL"); } if (foreign->type & DICT_FOREIGN_ON_DELETE_NO_ACTION) { ib_logger(ib_stream, " ON DELETE NO ACTION"); } if (foreign->type & DICT_FOREIGN_ON_UPDATE_CASCADE) { ib_logger(ib_stream, " ON UPDATE CASCADE"); } if (foreign->type & DICT_FOREIGN_ON_UPDATE_SET_NULL) { ib_logger(ib_stream, " ON UPDATE SET NULL"); } if (foreign->type & DICT_FOREIGN_ON_UPDATE_NO_ACTION) { ib_logger(ib_stream, " ON UPDATE NO ACTION"); } } foreign = UT_LIST_GET_NEXT(foreign_list, foreign); } mutex_exit(&(dict_sys->mutex)); } /********************************************************************//** Displays the names of the index and the table. */ UNIV_INTERN void dict_index_name_print( /*==================*/ ib_stream_t ib_stream, /*!< in: output stream */ trx_t* trx, /*!< in: transaction */ const dict_index_t* index) /*!< in: index to print */ { ib_logger(ib_stream, "index "); ut_print_name(ib_stream, trx, FALSE, index->name); ib_logger(ib_stream, " of table "); ut_print_name(ib_stream, trx, TRUE, index->table_name); } #endif /* !UNIV_HOTBACKUP */ /**********************************************************************//** Inits dict_ind_redundant and dict_ind_compact. */ UNIV_INTERN void dict_ind_init(void) /*===============*/ { dict_table_t* table; /* create dummy table and index for REDUNDANT infimum and supremum */ table = dict_mem_table_create("SYS_DUMMY1", DICT_HDR_SPACE, 1, 0); dict_mem_table_add_col(table, NULL, NULL, DATA_CHAR, DATA_ENGLISH | DATA_NOT_NULL, 8); dict_ind_redundant = dict_mem_index_create("SYS_DUMMY1", "SYS_DUMMY1", DICT_HDR_SPACE, 0, 1); dict_index_add_col(dict_ind_redundant, table, dict_table_get_nth_col(table, 0), 0); dict_ind_redundant->table = table; /* create dummy table and index for COMPACT infimum and supremum */ table = dict_mem_table_create("SYS_DUMMY2", DICT_HDR_SPACE, 1, DICT_TF_COMPACT); dict_mem_table_add_col(table, NULL, NULL, DATA_CHAR, DATA_ENGLISH | DATA_NOT_NULL, 8); dict_ind_compact = dict_mem_index_create("SYS_DUMMY2", "SYS_DUMMY2", DICT_HDR_SPACE, 0, 1); dict_index_add_col(dict_ind_compact, table, dict_table_get_nth_col(table, 0), 0); dict_ind_compact->table = table; /* avoid ut_ad(index->cached) in dict_index_get_n_unique_in_tree */ dict_ind_redundant->cached = dict_ind_compact->cached = TRUE; } /**********************************************************************//** Frees dict_ind_redundant and dict_ind_compact. */ static void dict_ind_free(void) /*===============*/ { dict_table_t* table; table = dict_ind_compact->table; dict_mem_index_free(dict_ind_compact); dict_ind_compact = NULL; dict_mem_table_free(table); table = dict_ind_redundant->table; dict_mem_index_free(dict_ind_redundant); dict_ind_redundant = NULL; dict_mem_table_free(table); } #ifndef UNIV_HOTBACKUP /**********************************************************************//** Get index by name @return index, NULL if does not exist */ UNIV_INTERN dict_index_t* dict_table_get_index_on_name( /*=========================*/ dict_table_t* table, /*!< in: table */ const char* name) /*!< in: name of the index to find */ { dict_index_t* index; index = dict_table_get_first_index(table); while (index != NULL) { if (ut_strcmp(index->name, name) == 0) { return(index); } index = dict_table_get_next_index(index); } return(NULL); } /**********************************************************************//** Replace the index passed in with another equivalent index in the tables foreign key list. */ UNIV_INTERN void dict_table_replace_index_in_foreign_list( /*=====================================*/ dict_table_t* table, /*!< in/out: table */ dict_index_t* index) /*!< in: index to be replaced */ { dict_foreign_t* foreign; for (foreign = UT_LIST_GET_FIRST(table->foreign_list); foreign; foreign = UT_LIST_GET_NEXT(foreign_list, foreign)) { if (foreign->foreign_index == index) { dict_index_t* new_index = dict_foreign_find_equiv_index(foreign); ut_a(new_index); foreign->foreign_index = new_index; } } } /**********************************************************************//** In case there is more than one index with the same name return the index with the min(id). @return index, NULL if does not exist */ UNIV_INTERN dict_index_t* dict_table_get_index_on_name_and_min_id( /*=====================================*/ dict_table_t* table, /*!< in: table */ const char* name) /*!< in: name of the index to find */ { dict_index_t* index; dict_index_t* min_index; /* Index with matching name and min(id) */ min_index = NULL; index = dict_table_get_first_index(table); while (index != NULL) { if (ut_strcmp(index->name, name) == 0) { if (!min_index || ut_dulint_cmp(index->id, min_index->id) < 0) { min_index = index; } } index = dict_table_get_next_index(index); } return(min_index); } /************************************************************************* Locks the data dictionary in shared mode from modifications, for performing foreign key check, rollback, or other operation invisible to the client. */ UNIV_INTERN void dict_freeze_data_dictionary( /*========================*/ trx_t* trx) /*!< in: transaction */ { ut_a(trx->dict_operation_lock_mode == 0); rw_lock_s_lock(&dict_operation_lock); trx->dict_operation_lock_mode = RW_S_LATCH; } /************************************************************************* Unlocks the data dictionary shared lock. */ UNIV_INTERN void dict_unfreeze_data_dictionary( /*==========================*/ trx_t* trx) /*!< in: transaction */ { ut_a(trx->dict_operation_lock_mode == RW_S_LATCH); rw_lock_s_unlock(&dict_operation_lock); trx->dict_operation_lock_mode = 0; } /************************************************************************* Locks the data dictionary exclusively for performing a table create or other data dictionary modification operation. */ UNIV_INTERN void dict_lock_data_dictionary( /*======================*/ trx_t* trx) /*!< in: transaction */ { ut_a(trx->dict_operation_lock_mode == 0 || trx->dict_operation_lock_mode == RW_X_LATCH); /* Serialize data dictionary operations with dictionary mutex: no deadlocks or lock waits can occur then in these operations */ rw_lock_x_lock(&dict_operation_lock); trx->dict_operation_lock_mode = RW_X_LATCH; mutex_enter(&(dict_sys->mutex)); } /************************************************************************* Unlocks the data dictionary exclusive lock. */ UNIV_INTERN void dict_unlock_data_dictionary( /*========================*/ trx_t* trx) /*!< in: transaction */ { ut_a(trx->dict_operation_lock_mode == RW_X_LATCH); /* Serialize data dictionary operations with dictionary mutex: no deadlocks can occur then in these operations */ mutex_exit(&(dict_sys->mutex)); rw_lock_x_unlock(&dict_operation_lock); trx->dict_operation_lock_mode = 0; } /************************************************************************** Closes the data dictionary module. */ UNIV_INTERN void dict_close(void) /*============*/ { ulint i; /* Free the hash elements. We don't remove them from the table because we are going to destroy the table anyway. */ for (i = 0; i < hash_get_n_cells(dict_sys->table_hash); i++) { dict_table_t* table; table = HASH_GET_FIRST(dict_sys->table_hash, i); while (table) { dict_table_t* prev_table = table; table = HASH_GET_NEXT(name_hash, prev_table); #ifdef UNIV_DEBUG ut_a(prev_table->magic_n == DICT_TABLE_MAGIC_N); #endif /* Acquire only because it's a pre-condition. */ mutex_enter(&dict_sys->mutex); dict_table_remove_from_cache(prev_table); mutex_exit(&dict_sys->mutex); } } hash_table_free(dict_sys->table_hash); /* The elements are the same instance as in dict_sys->table_hash, therefore we don't delete the individual elements. */ hash_table_free(dict_sys->table_id_hash); /* Acquire only because it's a pre-condition. */ mutex_enter(&dict_sys->mutex); dict_ind_free(); mutex_exit(&dict_sys->mutex); mutex_free(&dict_sys->mutex); rw_lock_free(&dict_operation_lock); memset(&dict_operation_lock, 0x0, sizeof(dict_operation_lock)); mutex_free(&dict_foreign_err_mutex); mem_free(dict_sys); dict_sys = NULL; for (i = 0; i < DICT_INDEX_STAT_MUTEX_SIZE; i++) { mutex_free(&dict_index_stat_mutex[i]); } } #endif /* !UNIV_HOTBACKUP */ haildb-2.3.2/dict/dict0crea.c0000644000175000017500000011614011513177357016573 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2010, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file dict/dict0crea.c Database object creation Created 1/8/1996 Heikki Tuuri *******************************************************/ #include "dict0crea.h" #ifdef UNIV_NONINL #include "dict0crea.ic" #endif #include "btr0pcur.h" #include "btr0btr.h" #include "page0page.h" #include "mach0data.h" #include "dict0boot.h" #include "dict0dict.h" #include "que0que.h" #include "row0ins.h" #include "pars0pars.h" #include "trx0roll.h" #include "usr0sess.h" #include "ut0vec.h" #include "log0log.h" #include "lock0lock.h" #include "ddl0ddl.h" /*****************************************************************//** Based on a table object, this function builds the entry to be inserted in the SYS_TABLES system table. @return the tuple which should be inserted */ static dtuple_t* dict_create_sys_tables_tuple( /*=========================*/ const dict_table_t* table, /*!< in: table */ mem_heap_t* heap) /*!< in: memory heap from which the memory for the built tuple is allocated */ { dict_table_t* sys_tables; dtuple_t* entry; dfield_t* dfield; byte* ptr; ut_ad(table); ut_ad(heap); sys_tables = dict_sys->sys_tables; entry = dtuple_create(heap, 8 + DATA_N_SYS_COLS); dict_table_copy_types(entry, sys_tables); /* 0: NAME -----------------------------*/ dfield = dtuple_get_nth_field(entry, 0/*NAME*/); dfield_set_data(dfield, table->name, ut_strlen(table->name)); /* 3: ID -------------------------------*/ dfield = dtuple_get_nth_field(entry, 1/*ID*/); ptr = mem_heap_alloc(heap, 8); mach_write_to_8(ptr, table->id); dfield_set_data(dfield, ptr, 8); /* 4: N_COLS ---------------------------*/ dfield = dtuple_get_nth_field(entry, 2/*N_COLS*/); #if DICT_TF_COMPACT != 1 #error #endif ptr = mem_heap_alloc(heap, 4); mach_write_to_4(ptr, table->n_def | ((table->flags & DICT_TF_COMPACT) << 31)); dfield_set_data(dfield, ptr, 4); /* 5: TYPE -----------------------------*/ dfield = dtuple_get_nth_field(entry, 3/*TYPE*/); ptr = mem_heap_alloc(heap, 4); if (table->flags & (~DICT_TF_COMPACT & ~(~0 << DICT_TF_BITS))) { ut_a(table->flags & DICT_TF_COMPACT); ut_a(dict_table_get_format(table) >= DICT_TF_FORMAT_ZIP); ut_a((table->flags & DICT_TF_ZSSIZE_MASK) <= (DICT_TF_ZSSIZE_MAX << DICT_TF_ZSSIZE_SHIFT)); ut_a(!(table->flags & (~0 << DICT_TF2_BITS))); mach_write_to_4(ptr, table->flags & ~(~0 << DICT_TF_BITS)); } else { mach_write_to_4(ptr, DICT_TABLE_ORDINARY); } dfield_set_data(dfield, ptr, 4); /* 6: MIX_ID (obsolete) ---------------------------*/ dfield = dtuple_get_nth_field(entry, 4/*MIX_ID*/); ptr = mem_heap_zalloc(heap, 8); dfield_set_data(dfield, ptr, 8); /* 7: MIX_LEN (additional flags) --------------------------*/ dfield = dtuple_get_nth_field(entry, 5/*MIX_LEN*/); ptr = mem_heap_alloc(heap, 4); mach_write_to_4(ptr, table->flags >> DICT_TF2_SHIFT); dfield_set_data(dfield, ptr, 4); /* 8: CLUSTER_NAME ---------------------*/ dfield = dtuple_get_nth_field(entry, 6/*CLUSTER_NAME*/); dfield_set_null(dfield); /* not supported */ /* 9: SPACE ----------------------------*/ dfield = dtuple_get_nth_field(entry, 7/*SPACE*/); ptr = mem_heap_alloc(heap, 4); mach_write_to_4(ptr, table->space); dfield_set_data(dfield, ptr, 4); /*----------------------------------*/ return(entry); } /*****************************************************************//** Based on a table object, this function builds the entry to be inserted in the SYS_COLUMNS system table. @return the tuple which should be inserted */ static dtuple_t* dict_create_sys_columns_tuple( /*==========================*/ const dict_table_t* table, /*!< in: table */ ulint i, /*!< in: column number */ mem_heap_t* heap) /*!< in: memory heap from which the memory for the built tuple is allocated */ { dict_table_t* sys_columns; dtuple_t* entry; const dict_col_t* column; dfield_t* dfield; byte* ptr; const char* col_name; ut_ad(table); ut_ad(heap); column = dict_table_get_nth_col(table, i); sys_columns = dict_sys->sys_columns; entry = dtuple_create(heap, 7 + DATA_N_SYS_COLS); dict_table_copy_types(entry, sys_columns); /* 0: TABLE_ID -----------------------*/ dfield = dtuple_get_nth_field(entry, 0/*TABLE_ID*/); ptr = mem_heap_alloc(heap, 8); mach_write_to_8(ptr, table->id); dfield_set_data(dfield, ptr, 8); /* 1: POS ----------------------------*/ dfield = dtuple_get_nth_field(entry, 1/*POS*/); ptr = mem_heap_alloc(heap, 4); mach_write_to_4(ptr, i); dfield_set_data(dfield, ptr, 4); /* 4: NAME ---------------------------*/ dfield = dtuple_get_nth_field(entry, 2/*NAME*/); col_name = dict_table_get_col_name(table, i); dfield_set_data(dfield, col_name, ut_strlen(col_name)); /* 5: MTYPE --------------------------*/ dfield = dtuple_get_nth_field(entry, 3/*MTYPE*/); ptr = mem_heap_alloc(heap, 4); mach_write_to_4(ptr, column->mtype); dfield_set_data(dfield, ptr, 4); /* 6: PRTYPE -------------------------*/ dfield = dtuple_get_nth_field(entry, 4/*PRTYPE*/); ptr = mem_heap_alloc(heap, 4); mach_write_to_4(ptr, column->prtype); dfield_set_data(dfield, ptr, 4); /* 7: LEN ----------------------------*/ dfield = dtuple_get_nth_field(entry, 5/*LEN*/); ptr = mem_heap_alloc(heap, 4); mach_write_to_4(ptr, column->len); dfield_set_data(dfield, ptr, 4); /* 8: PREC ---------------------------*/ dfield = dtuple_get_nth_field(entry, 6/*PREC*/); ptr = mem_heap_alloc(heap, 4); mach_write_to_4(ptr, 0/* unused */); dfield_set_data(dfield, ptr, 4); /*---------------------------------*/ return(entry); } /***************************************************************//** Builds a table definition to insert. @return DB_SUCCESS or error code */ static ulint dict_build_table_def_step( /*======================*/ que_thr_t* thr, /*!< in: query thread */ tab_node_t* node) /*!< in: table create node */ { dict_table_t* table; dtuple_t* row; ulint error; ulint flags; const char* path_or_name; ibool is_path; mtr_t mtr; ut_ad(mutex_own(&(dict_sys->mutex))); table = node->table; table->id = dict_hdr_get_new_id(DICT_HDR_TABLE_ID); thr_get_trx(thr)->table_id = table->id; if (srv_file_per_table) { /* We create a new single-table tablespace for the table. We initially let it be 4 pages: - page 0 is the fsp header and an extent descriptor page, - page 1 is an ibuf bitmap page, - page 2 is the first inode page, - page 3 will contain the root of the clustered index of the table we create here. */ ulint space = 0; /* reset to zero for the call below */ if (table->dir_path_of_temp_table) { /* We place tables created with CREATE TEMPORARY TABLE in the configured tmp dir. */ path_or_name = table->dir_path_of_temp_table; is_path = TRUE; } else { path_or_name = table->name; is_path = FALSE; } ut_ad(dict_table_get_format(table) <= DICT_TF_FORMAT_MAX); ut_ad(!dict_table_zip_size(table) || dict_table_get_format(table) >= DICT_TF_FORMAT_ZIP); flags = table->flags & ~(~0 << DICT_TF_BITS); error = fil_create_new_single_table_tablespace( &space, path_or_name, is_path, flags == DICT_TF_COMPACT ? 0 : flags, FIL_IBD_FILE_INITIAL_SIZE); table->space = (unsigned int) space; if (error != DB_SUCCESS) { return(error); } mtr_start(&mtr); fsp_header_init(table->space, FIL_IBD_FILE_INITIAL_SIZE, &mtr); mtr_commit(&mtr); } else { /* Create in the system tablespace: disallow new features */ table->flags &= (~0 << DICT_TF_BITS) | DICT_TF_COMPACT; } row = dict_create_sys_tables_tuple(table, node->heap); row_ins_node_set_new_row(node->tab_def, row); return(DB_SUCCESS); } /***************************************************************//** Builds a column definition to insert. @return DB_SUCCESS */ static ulint dict_build_col_def_step( /*====================*/ tab_node_t* node) /*!< in: table create node */ { dtuple_t* row; row = dict_create_sys_columns_tuple(node->table, node->col_no, node->heap); row_ins_node_set_new_row(node->col_def, row); return(DB_SUCCESS); } /*****************************************************************//** Based on an index object, this function builds the entry to be inserted in the SYS_INDEXES system table. @return the tuple which should be inserted */ static dtuple_t* dict_create_sys_indexes_tuple( /*==========================*/ const dict_index_t* index, /*!< in: index */ mem_heap_t* heap) /*!< in: memory heap from which the memory for the built tuple is allocated */ { dict_table_t* sys_indexes; dict_table_t* table; dtuple_t* entry; dfield_t* dfield; byte* ptr; ut_ad(mutex_own(&(dict_sys->mutex))); ut_ad(index); ut_ad(heap); sys_indexes = dict_sys->sys_indexes; table = dict_table_get_low(index->table_name); entry = dtuple_create(heap, 7 + DATA_N_SYS_COLS); dict_table_copy_types(entry, sys_indexes); /* 0: TABLE_ID -----------------------*/ dfield = dtuple_get_nth_field(entry, 0/*TABLE_ID*/); ptr = mem_heap_alloc(heap, 8); mach_write_to_8(ptr, table->id); dfield_set_data(dfield, ptr, 8); /* 1: ID ----------------------------*/ dfield = dtuple_get_nth_field(entry, 1/*ID*/); ptr = mem_heap_alloc(heap, 8); mach_write_to_8(ptr, index->id); dfield_set_data(dfield, ptr, 8); /* 4: NAME --------------------------*/ dfield = dtuple_get_nth_field(entry, 2/*NAME*/); dfield_set_data(dfield, index->name, ut_strlen(index->name)); /* 5: N_FIELDS ----------------------*/ dfield = dtuple_get_nth_field(entry, 3/*N_FIELDS*/); ptr = mem_heap_alloc(heap, 4); mach_write_to_4(ptr, index->n_fields); dfield_set_data(dfield, ptr, 4); /* 6: TYPE --------------------------*/ dfield = dtuple_get_nth_field(entry, 4/*TYPE*/); ptr = mem_heap_alloc(heap, 4); mach_write_to_4(ptr, index->type); dfield_set_data(dfield, ptr, 4); /* 7: SPACE --------------------------*/ #if DICT_SYS_INDEXES_SPACE_NO_FIELD != 7 #error "DICT_SYS_INDEXES_SPACE_NO_FIELD != 7" #endif dfield = dtuple_get_nth_field(entry, 5/*SPACE*/); ptr = mem_heap_alloc(heap, 4); mach_write_to_4(ptr, index->space); dfield_set_data(dfield, ptr, 4); /* 8: PAGE_NO --------------------------*/ #if DICT_SYS_INDEXES_PAGE_NO_FIELD != 8 #error "DICT_SYS_INDEXES_PAGE_NO_FIELD != 8" #endif dfield = dtuple_get_nth_field(entry, 6/*PAGE_NO*/); ptr = mem_heap_alloc(heap, 4); mach_write_to_4(ptr, FIL_NULL); dfield_set_data(dfield, ptr, 4); /*--------------------------------*/ return(entry); } /*****************************************************************//** Based on an index object, this function builds the entry to be inserted in the SYS_FIELDS system table. @return the tuple which should be inserted */ static dtuple_t* dict_create_sys_fields_tuple( /*=========================*/ const dict_index_t* index, /*!< in: index */ ulint i, /*!< in: field number */ mem_heap_t* heap) /*!< in: memory heap from which the memory for the built tuple is allocated */ { dict_table_t* sys_fields; dtuple_t* entry; dict_field_t* field; dfield_t* dfield; byte* ptr; ibool index_contains_column_prefix_field = FALSE; ulint j; ut_ad(index); ut_ad(heap); for (j = 0; j < index->n_fields; j++) { if (dict_index_get_nth_field(index, j)->prefix_len > 0) { index_contains_column_prefix_field = TRUE; break; } } field = dict_index_get_nth_field(index, i); sys_fields = dict_sys->sys_fields; entry = dtuple_create(heap, 3 + DATA_N_SYS_COLS); dict_table_copy_types(entry, sys_fields); /* 0: INDEX_ID -----------------------*/ dfield = dtuple_get_nth_field(entry, 0/*INDEX_ID*/); ptr = mem_heap_alloc(heap, 8); mach_write_to_8(ptr, index->id); dfield_set_data(dfield, ptr, 8); /* 1: POS + PREFIX LENGTH ----------------------------*/ dfield = dtuple_get_nth_field(entry, 1/*POS*/); ptr = mem_heap_alloc(heap, 4); if (index_contains_column_prefix_field) { /* If there are column prefix fields in the index, then we store the number of the field to the 2 HIGH bytes and the prefix length to the 2 low bytes, */ mach_write_to_4(ptr, (i << 16) + field->prefix_len); } else { /* Else we store the number of the field to the 2 LOW bytes. This is to keep the storage format compatible with InnoDB versions < 4.0.14. */ mach_write_to_4(ptr, i); } dfield_set_data(dfield, ptr, 4); /* 4: COL_NAME -------------------------*/ dfield = dtuple_get_nth_field(entry, 2/*COL_NAME*/); dfield_set_data(dfield, field->name, ut_strlen(field->name)); /*---------------------------------*/ return(entry); } /*****************************************************************//** Creates the tuple with which the index entry is searched for writing the index tree root page number, if such a tree is created. @return the tuple for search */ static dtuple_t* dict_create_search_tuple( /*=====================*/ const dtuple_t* tuple, /*!< in: the tuple inserted in the SYS_INDEXES table */ mem_heap_t* heap) /*!< in: memory heap from which the memory for the built tuple is allocated */ { dtuple_t* search_tuple; const dfield_t* field1; dfield_t* field2; ut_ad(tuple && heap); search_tuple = dtuple_create(heap, 2); field1 = dtuple_get_nth_field(tuple, 0); field2 = dtuple_get_nth_field(search_tuple, 0); dfield_copy(field2, field1); field1 = dtuple_get_nth_field(tuple, 1); field2 = dtuple_get_nth_field(search_tuple, 1); dfield_copy(field2, field1); ut_ad(dtuple_validate(search_tuple)); return(search_tuple); } /***************************************************************//** Builds an index definition row to insert. @return DB_SUCCESS or error code */ static ulint dict_build_index_def_step( /*======================*/ que_thr_t* thr, /*!< in: query thread */ ind_node_t* node) /*!< in: index create node */ { dict_table_t* table; dict_index_t* index; dtuple_t* row; trx_t* trx; ut_ad(mutex_own(&(dict_sys->mutex))); trx = thr_get_trx(thr); index = node->index; table = dict_table_get_low(index->table_name); if (table == NULL) { return(DB_TABLE_NOT_FOUND); } trx->table_id = table->id; node->table = table; ut_ad((UT_LIST_GET_LEN(table->indexes) > 0) || dict_index_is_clust(index)); index->id = dict_hdr_get_new_id(DICT_HDR_INDEX_ID); /* Inherit the space id from the table; we store all indexes of a table in the same tablespace */ index->space = table->space; node->page_no = FIL_NULL; row = dict_create_sys_indexes_tuple(index, node->heap); node->ind_row = row; row_ins_node_set_new_row(node->ind_def, row); /* Note that the index was created by this transaction. */ index->trx_id = (ib_uint64_t) ut_conv_dulint_to_longlong(trx->id); return(DB_SUCCESS); } /***************************************************************//** Builds a field definition row to insert. @return DB_SUCCESS */ static ulint dict_build_field_def_step( /*======================*/ ind_node_t* node) /*!< in: index create node */ { dict_index_t* index; dtuple_t* row; index = node->index; row = dict_create_sys_fields_tuple(index, node->field_no, node->heap); row_ins_node_set_new_row(node->field_def, row); return(DB_SUCCESS); } /***************************************************************//** Creates an index tree for the index if it is not a member of a cluster. @return DB_SUCCESS or DB_OUT_OF_FILE_SPACE */ static ulint dict_create_index_tree_step( /*========================*/ ind_node_t* node) /*!< in: index create node */ { dict_index_t* index; dict_table_t* sys_indexes; dict_table_t* table; dtuple_t* search_tuple; ulint zip_size; btr_pcur_t pcur; mtr_t mtr; ut_ad(mutex_own(&(dict_sys->mutex))); index = node->index; table = node->table; sys_indexes = dict_sys->sys_indexes; /* Run a mini-transaction in which the index tree is allocated for the index and its root address is written to the index entry in sys_indexes */ mtr_start(&mtr); search_tuple = dict_create_search_tuple(node->ind_row, node->heap); btr_pcur_open(UT_LIST_GET_FIRST(sys_indexes->indexes), search_tuple, PAGE_CUR_L, BTR_MODIFY_LEAF, &pcur, &mtr); btr_pcur_move_to_next_user_rec(&pcur, &mtr); zip_size = dict_table_zip_size(index->table); node->page_no = btr_create(index->type, index->space, zip_size, index->id, index, &mtr); /* printf("Created a new index tree in space %lu root page %lu\n", index->space, index->page_no); */ page_rec_write_index_page_no(btr_pcur_get_rec(&pcur), DICT_SYS_INDEXES_PAGE_NO_FIELD, node->page_no, &mtr); btr_pcur_close(&pcur); mtr_commit(&mtr); if (node->page_no == FIL_NULL) { return(DB_OUT_OF_FILE_SPACE); } return(DB_SUCCESS); } /*******************************************************************//** Drops the index tree associated with a row in SYS_INDEXES table. */ UNIV_INTERN void dict_drop_index_tree( /*=================*/ rec_t* rec, /*!< in/out: record in the clustered index of SYS_INDEXES table */ mtr_t* mtr) /*!< in: mtr having the latch on the record page */ { ulint root_page_no; ulint space; ulint zip_size; const byte* ptr; ulint len; ut_ad(mutex_own(&(dict_sys->mutex))); ut_a(!dict_table_is_comp(dict_sys->sys_indexes)); ptr = rec_get_nth_field_old(rec, DICT_SYS_INDEXES_PAGE_NO_FIELD, &len); ut_ad(len == 4); root_page_no = mtr_read_ulint(ptr, MLOG_4BYTES, mtr); if (root_page_no == FIL_NULL) { /* The tree has already been freed */ return; } ptr = rec_get_nth_field_old(rec, DICT_SYS_INDEXES_SPACE_NO_FIELD, &len); ut_ad(len == 4); space = mtr_read_ulint(ptr, MLOG_4BYTES, mtr); zip_size = fil_space_get_zip_size(space); if (UNIV_UNLIKELY(zip_size == ULINT_UNDEFINED)) { /* It is a single table tablespace and the .ibd file is missing: do nothing */ return; } /* We free all the pages but the root page first; this operation may span several mini-transactions */ btr_free_but_not_root(space, zip_size, root_page_no); /* Then we free the root page in the same mini-transaction where we write FIL_NULL to the appropriate field in the SYS_INDEXES record: this mini-transaction marks the B-tree totally freed */ /* printf("Dropping index tree in space %lu root page %lu\n", space, root_page_no); */ btr_free_root(space, zip_size, root_page_no, mtr); page_rec_write_index_page_no(rec, DICT_SYS_INDEXES_PAGE_NO_FIELD, FIL_NULL, mtr); } /*******************************************************************//** Truncates the index tree associated with a row in SYS_INDEXES table. @return new root page number, or FIL_NULL on failure */ UNIV_INTERN ulint dict_truncate_index_tree( /*=====================*/ dict_table_t* table, /*!< in: the table the index belongs to */ ulint space, /*!< in: 0=truncate, nonzero=create the index tree in the given tablespace */ btr_pcur_t* pcur, /*!< in/out: persistent cursor pointing to record in the clustered index of SYS_INDEXES table. The cursor may be repositioned in this call. */ mtr_t* mtr) /*!< in: mtr having the latch on the record page. The mtr may be committed and restarted in this call. */ { ulint root_page_no; ibool drop = !space; ulint zip_size; ulint type; dulint index_id; rec_t* rec; const byte* ptr; ulint len; dict_index_t* index; ut_ad(mutex_own(&(dict_sys->mutex))); ut_a(!dict_table_is_comp(dict_sys->sys_indexes)); rec = btr_pcur_get_rec(pcur); ptr = rec_get_nth_field_old(rec, DICT_SYS_INDEXES_PAGE_NO_FIELD, &len); ut_ad(len == 4); root_page_no = mtr_read_ulint(ptr, MLOG_4BYTES, mtr); if (drop && root_page_no == FIL_NULL) { /* The tree has been freed. */ ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Trying to TRUNCATE" " a missing index of table %s!\n", table->name); drop = FALSE; } ptr = rec_get_nth_field_old(rec, DICT_SYS_INDEXES_SPACE_NO_FIELD, &len); ut_ad(len == 4); if (drop) { space = mtr_read_ulint(ptr, MLOG_4BYTES, mtr); } zip_size = fil_space_get_zip_size(space); if (UNIV_UNLIKELY(zip_size == ULINT_UNDEFINED)) { /* It is a single table tablespace and the .ibd file is missing: do nothing */ ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Trying to TRUNCATE" " a missing .ibd file of table %s!\n", table->name); return(FIL_NULL); } ptr = rec_get_nth_field_old(rec, DICT_SYS_INDEXES_TYPE_FIELD, &len); ut_ad(len == 4); type = mach_read_from_4(ptr); ptr = rec_get_nth_field_old(rec, 1, &len); ut_ad(len == 8); index_id = mach_read_from_8(ptr); if (!drop) { goto create; } /* We free all the pages but the root page first; this operation may span several mini-transactions */ btr_free_but_not_root(space, zip_size, root_page_no); /* Then we free the root page in the same mini-transaction where we create the b-tree and write its new root page number to the appropriate field in the SYS_INDEXES record: this mini-transaction marks the B-tree totally truncated */ btr_page_get(space, zip_size, root_page_no, RW_X_LATCH, mtr); btr_free_root(space, zip_size, root_page_no, mtr); create: /* We will temporarily write FIL_NULL to the PAGE_NO field in SYS_INDEXES, so that the database will not get into an inconsistent state in case it crashes between the mtr_commit() below and the following mtr_commit() call. */ page_rec_write_index_page_no(rec, DICT_SYS_INDEXES_PAGE_NO_FIELD, FIL_NULL, mtr); /* We will need to commit the mini-transaction in order to avoid deadlocks in the btr_create() call, because otherwise we would be freeing and allocating pages in the same mini-transaction. */ btr_pcur_store_position(pcur, mtr); mtr_commit(mtr); mtr_start(mtr); btr_pcur_restore_position(BTR_MODIFY_LEAF, pcur, mtr); /* Find the index corresponding to this SYS_INDEXES record. */ for (index = UT_LIST_GET_FIRST(table->indexes); index; index = UT_LIST_GET_NEXT(indexes, index)) { if (!ut_dulint_cmp(index->id, index_id)) { root_page_no = btr_create(type, space, zip_size, index_id, index, mtr); index->page = (unsigned int) root_page_no; return(root_page_no); } } ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Index %lu %lu of table %s is missing\n" "InnoDB: from the data dictionary during TRUNCATE!\n", ut_dulint_get_high(index_id), ut_dulint_get_low(index_id), table->name); return(FIL_NULL); } /*********************************************************************//** Creates a table create graph. @return own: table create node */ UNIV_INTERN tab_node_t* tab_create_graph_create( /*====================*/ dict_table_t* table, /*!< in: table to create, built as a memory data structure */ mem_heap_t* heap, /*!< in: heap where created */ ibool commit) /*!< in: if TRUE commit transaction */ { tab_node_t* node; node = mem_heap_alloc(heap, sizeof(tab_node_t)); node->common.type = QUE_NODE_CREATE_TABLE; node->table = table; node->state = TABLE_BUILD_TABLE_DEF; node->heap = mem_heap_create(256); node->tab_def = row_ins_node_create(INS_DIRECT, dict_sys->sys_tables, heap); node->tab_def->common.parent = node; node->col_def = row_ins_node_create(INS_DIRECT, dict_sys->sys_columns, heap); node->col_def->common.parent = node; if (commit) { node->commit_node = commit_node_create(heap); node->commit_node->common.parent = node; } else { node->commit_node = NULL; } return(node); } /*********************************************************************//** Creates an index create graph. @return own: index create node */ UNIV_INTERN ind_node_t* ind_create_graph_create( /*====================*/ dict_index_t* index, /*!< in: index to create, built as a memory data structure */ mem_heap_t* heap, /*!< in: heap where created */ ibool commit) /*!< in: TRUE if transaction should be commit */ { ind_node_t* node; node = mem_heap_alloc(heap, sizeof(ind_node_t)); node->common.type = QUE_NODE_CREATE_INDEX; node->index = index; node->state = INDEX_BUILD_INDEX_DEF; node->page_no = FIL_NULL; node->heap = mem_heap_create(256); node->ind_def = row_ins_node_create(INS_DIRECT, dict_sys->sys_indexes, heap); node->ind_def->common.parent = node; node->field_def = row_ins_node_create(INS_DIRECT, dict_sys->sys_fields, heap); node->field_def->common.parent = node; if (commit) { node->commit_node = commit_node_create(heap); node->commit_node->common.parent = node; } else { node->commit_node = NULL; } return(node); } /***********************************************************//** Creates a table. This is a high-level function used in SQL execution graphs. @return query thread to run next or NULL */ UNIV_INTERN que_thr_t* dict_create_table_step( /*===================*/ que_thr_t* thr) /*!< in: query thread */ { tab_node_t* node; ulint err = DB_ERROR; trx_t* trx; ut_ad(thr); ut_ad(mutex_own(&(dict_sys->mutex))); trx = thr_get_trx(thr); node = thr->run_node; ut_ad(que_node_get_type(node) == QUE_NODE_CREATE_TABLE); if (thr->prev_node == que_node_get_parent(node)) { node->state = TABLE_BUILD_TABLE_DEF; } if (node->state == TABLE_BUILD_TABLE_DEF) { /* DO THE CHECKS OF THE CONSISTENCY CONSTRAINTS HERE */ err = dict_build_table_def_step(thr, node); if (err != DB_SUCCESS) { goto function_exit; } node->state = TABLE_BUILD_COL_DEF; node->col_no = 0; thr->run_node = node->tab_def; return(thr); } if (node->state == TABLE_BUILD_COL_DEF) { if (node->col_no < (node->table)->n_def) { err = dict_build_col_def_step(node); if (err != DB_SUCCESS) { goto function_exit; } node->col_no++; thr->run_node = node->col_def; return(thr); } else { node->state = TABLE_COMMIT_WORK; } } if (node->state == TABLE_COMMIT_WORK) { /* Table was correctly defined: do NOT commit the transaction (CREATE TABLE does NOT do an implicit commit of the current transaction) */ node->state = TABLE_ADD_TO_CACHE; /* thr->run_node = node->commit_node; return(thr); */ } if (node->state == TABLE_ADD_TO_CACHE) { dict_table_add_to_cache(node->table, node->heap); err = DB_SUCCESS; } function_exit: trx->error_state = err; if (err == DB_SUCCESS) { /* Ok: do nothing */ } else if (err == DB_LOCK_WAIT) { return(NULL); } else { /* SQL error detected */ return(NULL); } thr->run_node = que_node_get_parent(node); return(thr); } /***********************************************************//** Creates an index. This is a high-level function used in SQL execution graphs. @return query thread to run next or NULL */ UNIV_INTERN que_thr_t* dict_create_index_step( /*===================*/ que_thr_t* thr) /*!< in: query thread */ { ind_node_t* node; ulint err = DB_ERROR; trx_t* trx; ut_ad(thr); ut_ad(mutex_own(&(dict_sys->mutex))); trx = thr_get_trx(thr); node = thr->run_node; ut_ad(que_node_get_type(node) == QUE_NODE_CREATE_INDEX); if (thr->prev_node == que_node_get_parent(node)) { node->state = INDEX_BUILD_INDEX_DEF; } if (node->state == INDEX_BUILD_INDEX_DEF) { /* DO THE CHECKS OF THE CONSISTENCY CONSTRAINTS HERE */ err = dict_build_index_def_step(thr, node); if (err != DB_SUCCESS) { goto function_exit; } node->state = INDEX_BUILD_FIELD_DEF; node->field_no = 0; thr->run_node = node->ind_def; return(thr); } if (node->state == INDEX_BUILD_FIELD_DEF) { if (node->field_no < (node->index)->n_fields) { err = dict_build_field_def_step(node); if (err != DB_SUCCESS) { goto function_exit; } node->field_no++; thr->run_node = node->field_def; return(thr); } else { node->state = INDEX_ADD_TO_CACHE; } } if (node->state == INDEX_ADD_TO_CACHE) { dulint index_id = node->index->id; err = dict_index_add_to_cache(node->table, node->index, FIL_NULL, TRUE); node->index = dict_index_get_if_in_cache_low(index_id); ut_a(!node->index == (err != DB_SUCCESS)); if (err != DB_SUCCESS) { goto function_exit; } node->state = INDEX_CREATE_INDEX_TREE; } if (node->state == INDEX_CREATE_INDEX_TREE) { err = dict_create_index_tree_step(node); if (err != DB_SUCCESS) { dict_index_remove_from_cache(node->table, node->index); node->index = NULL; goto function_exit; } node->index->page = node->page_no; node->state = INDEX_COMMIT_WORK; } if (node->state == INDEX_COMMIT_WORK) { /* Index was correctly defined: do NOT commit the transaction (CREATE INDEX does NOT currently do an implicit commit of the current transaction) */ node->state = INDEX_CREATE_INDEX_TREE; /* thr->run_node = node->commit_node; return(thr); */ } function_exit: trx->error_state = err; if (err == DB_SUCCESS) { /* Ok: do nothing */ } else if (err == DB_LOCK_WAIT) { return(NULL); } else { /* SQL error detected */ return(NULL); } thr->run_node = que_node_get_parent(node); return(thr); } /****************************************************************//** Creates the foreign key constraints system tables inside InnoDB at database creation or database start if they are not found or are not of the right form. @return DB_SUCCESS or error code */ UNIV_INTERN ulint dict_create_or_check_foreign_constraint_tables(void) /*================================================*/ { dict_table_t* table1; dict_table_t* table2; ulint error; trx_t* trx; int started; mutex_enter(&(dict_sys->mutex)); table1 = dict_table_get_low("SYS_FOREIGN"); table2 = dict_table_get_low("SYS_FOREIGN_COLS"); if (table1 && table2 && UT_LIST_GET_LEN(table1->indexes) == 3 && UT_LIST_GET_LEN(table2->indexes) == 1) { /* Foreign constraint system tables have already been created, and they are ok */ if (!table1->cached) { dict_mem_table_free(table1); } if (!table2->cached) { dict_mem_table_free(table2); } mutex_exit(&(dict_sys->mutex)); return(DB_SUCCESS); } mutex_exit(&(dict_sys->mutex)); trx = trx_allocate_for_client(NULL); started = trx_start(trx, ULINT_UNDEFINED); ut_a(started); trx->op_info = "creating foreign key sys tables"; dict_lock_data_dictionary(trx); if (table1) { ib_logger(ib_stream, "InnoDB: dropping incompletely created" " SYS_FOREIGN table\n"); ddl_drop_table("SYS_FOREIGN", trx, TRUE); trx_commit(trx); } if (table2) { ib_logger(ib_stream, "InnoDB: dropping incompletely created" " SYS_FOREIGN_COLS table\n"); ddl_drop_table("SYS_FOREIGN_COLS", trx, TRUE); trx_commit(trx); } trx_start_if_not_started(trx); ib_logger(ib_stream, "InnoDB: Creating foreign key constraint system tables\n"); /* NOTE: in dict_load_foreigns we use the fact that there are 2 secondary indexes on SYS_FOREIGN, and they are defined just like below */ /* NOTE: when designing InnoDB's foreign key support in 2001, we made an error and made the table names and the foreign key id of type 'CHAR' (internally, really a VARCHAR). We should have made the type VARBINARY, like in other InnoDB system tables, to get a clean design. */ error = que_eval_sql(NULL, "PROCEDURE CREATE_FOREIGN_SYS_TABLES_PROC () IS\n" "BEGIN\n" "CREATE TABLE\n" "SYS_FOREIGN(ID CHAR, FOR_NAME CHAR," " REF_NAME CHAR, N_COLS INT);\n" "CREATE UNIQUE CLUSTERED INDEX ID_IND" " ON SYS_FOREIGN (ID);\n" "CREATE INDEX FOR_IND" " ON SYS_FOREIGN (FOR_NAME);\n" "CREATE INDEX REF_IND" " ON SYS_FOREIGN (REF_NAME);\n" "CREATE TABLE\n" "SYS_FOREIGN_COLS(ID CHAR, POS INT," " FOR_COL_NAME CHAR, REF_COL_NAME CHAR);\n" "CREATE UNIQUE CLUSTERED INDEX ID_IND" " ON SYS_FOREIGN_COLS (ID, POS);\n" "END;\n" , FALSE, trx); if (error != DB_SUCCESS) { ib_logger(ib_stream, "InnoDB: error %lu in creation\n", (ulong) error); ut_a(error == DB_OUT_OF_FILE_SPACE || error == DB_TOO_MANY_CONCURRENT_TRXS); ib_logger(ib_stream, "InnoDB: creation failed\n" "InnoDB: tablespace is full\n" "InnoDB: dropping incompletely created" " SYS_FOREIGN tables\n"); ddl_drop_table("SYS_FOREIGN", trx, TRUE); ddl_drop_table("SYS_FOREIGN_COLS", trx, TRUE); trx_commit(trx); error = DB_MUST_GET_MORE_FILE_SPACE; } trx_commit(trx); dict_unlock_data_dictionary(trx); trx_free_for_client(trx); if (error == DB_SUCCESS) { ib_logger(ib_stream, "InnoDB: Foreign key constraint system tables" " created\n"); } return(error); } /****************************************************************//** Evaluate the given foreign key SQL statement. @return error code or DB_SUCCESS */ static ulint dict_foreign_eval_sql( /*==================*/ pars_info_t* info, /*!< in: info struct, or NULL */ const char* sql, /*!< in: SQL string to evaluate */ dict_table_t* table, /*!< in: table */ dict_foreign_t* foreign,/*!< in: foreign */ trx_t* trx) /*!< in: transaction */ { ulint error; trx_start_if_not_started(trx); error = que_eval_sql(info, sql, FALSE, trx); if (error == DB_DUPLICATE_KEY) { mutex_enter(&dict_foreign_err_mutex); ut_print_timestamp(ib_stream); ib_logger(ib_stream, " Error in foreign key constraint creation for table "); ut_print_name(ib_stream, trx, TRUE, table->name); ib_logger(ib_stream, ".\nA foreign key constraint of name "); ut_print_name(ib_stream, trx, TRUE, foreign->id); ib_logger(ib_stream, "\nalready exists." " (Note that internally InnoDB adds 'databasename'\n" "in front of the user-defined constraint name.)\n" "Note that InnoDB's FOREIGN KEY system tables store\n" "constraint names as case-insensitive, with the\n" "standard latin1_swedish_ci collation. If you\n" "create tables or databases whose names differ only in\n" "the character case, then collisions in constraint\n" "names can occur. Workaround: name your constraints\n" "explicitly with unique names.\n"); mutex_exit(&dict_foreign_err_mutex); return(error); } if (error != DB_SUCCESS) { ib_logger(ib_stream, "InnoDB: Foreign key constraint creation failed:\n" "InnoDB: internal error number %lu\n", (ulong) error); mutex_enter(&dict_foreign_err_mutex); ut_print_timestamp(ib_stream); ib_logger(ib_stream, " Internal error in foreign key constraint creation" " for table "); ut_print_name(ib_stream, trx, TRUE, table->name); ib_logger(ib_stream, ".\n" "See the .err log in the datadir for more " "information.\n"); mutex_exit(&dict_foreign_err_mutex); return(error); } return(DB_SUCCESS); } /********************************************************************//** Add a single foreign key field definition to the data dictionary tables in the database. @return error code or DB_SUCCESS */ static ulint dict_create_add_foreign_field_to_dictionary( /*========================================*/ ulint field_nr, /*!< in: foreign field number */ dict_table_t* table, /*!< in: table */ dict_foreign_t* foreign, /*!< in: foreign */ trx_t* trx) /*!< in: transaction */ { pars_info_t* info = pars_info_create(); pars_info_add_str_literal(info, "id", foreign->id); pars_info_add_int4_literal(info, "pos", field_nr); pars_info_add_str_literal(info, "for_col_name", foreign->foreign_col_names[field_nr]); pars_info_add_str_literal(info, "ref_col_name", foreign->referenced_col_names[field_nr]); return(dict_foreign_eval_sql( info, "PROCEDURE P () IS\n" "BEGIN\n" "INSERT INTO SYS_FOREIGN_COLS VALUES" "(:id, :pos, :for_col_name, :ref_col_name);\n" "END;\n", table, foreign, trx)); } /********************************************************************//** Add a single foreign key definition to the data dictionary tables in the database. We also generate names to constraints that were not named by the user. A generated constraint has a name of the format databasename/tablename_ibfk_NUMBER, where the numbers start from 1, and are given locally for this table, that is, the number is not global, as in the old format constraints < 4.0.18 it used to be. @return error code or DB_SUCCESS */ static ulint dict_create_add_foreign_to_dictionary( /*==================================*/ ulint* id_nr, /*!< in/out: number to use in id generation; incremented if used */ dict_table_t* table, /*!< in: table */ dict_foreign_t* foreign,/*!< in: foreign */ trx_t* trx) /*!< in: transaction */ { ulint error; ulint i; pars_info_t* info = pars_info_create(); if (foreign->id == NULL) { /* Generate a new constraint id */ ulint namelen = strlen(table->name); char* id = mem_heap_alloc(foreign->heap, namelen + 20); /* no overflow if number < 1e13 */ sprintf(id, "%s_ibfk_%lu", table->name, (ulong) (*id_nr)++); foreign->id = id; } pars_info_add_str_literal(info, "id", foreign->id); pars_info_add_str_literal(info, "for_name", table->name); pars_info_add_str_literal(info, "ref_name", foreign->referenced_table_name); pars_info_add_int4_literal(info, "n_cols", foreign->n_fields + (foreign->type << 24)); error = dict_foreign_eval_sql(info, "PROCEDURE P () IS\n" "BEGIN\n" "INSERT INTO SYS_FOREIGN VALUES" "(:id, :for_name, :ref_name, :n_cols);\n" "END;\n" , table, foreign, trx); if (error != DB_SUCCESS) { return(error); } for (i = 0; i < foreign->n_fields; i++) { error = dict_create_add_foreign_field_to_dictionary( i, table, foreign, trx); if (error != DB_SUCCESS) { return(error); } } error = dict_foreign_eval_sql(NULL, "PROCEDURE P () IS\n" "BEGIN\n" "COMMIT WORK;\n" "END;\n" , table, foreign, trx); return(error); } /********************************************************************//** Adds foreign key definitions to data dictionary tables in the database. @return error code or DB_SUCCESS */ UNIV_INTERN ulint dict_create_add_foreigns_to_dictionary( /*===================================*/ ulint start_id,/*!< in: if we are actually doing ALTER TABLE ADD CONSTRAINT, we want to generate constraint numbers which are bigger than in the table so far; we number the constraints from start_id + 1 up; start_id should be set to 0 if we are creating a new table, or if the table so far has no constraints for which the name was generated here */ dict_table_t* table, /*!< in: table */ trx_t* trx) /*!< in: transaction */ { dict_foreign_t* foreign; ulint number = start_id + 1; ulint error; ut_ad(mutex_own(&(dict_sys->mutex))); if (NULL == dict_table_get_low("SYS_FOREIGN")) { ib_logger(ib_stream, "InnoDB: table SYS_FOREIGN not found" " in internal data dictionary\n"); return(DB_ERROR); } for (foreign = UT_LIST_GET_FIRST(table->foreign_list); foreign; foreign = UT_LIST_GET_NEXT(foreign_list, foreign)) { error = dict_create_add_foreign_to_dictionary(&number, table, foreign, trx); if (error != DB_SUCCESS) { return(error); } } return(DB_SUCCESS); } haildb-2.3.2/row/0000755000175000017500000000000011513177437014451 5ustar00pcrewspcrews00000000000000haildb-2.3.2/row/row0merge.c0000644000175000017500000017250111513177357016533 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 2005, 2010, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file row/row0merge.c New index creation routines using a merge sort Created 12/4/2005 Jan Lindstrom Completed by Sunny Bains and Marko Makela *******************************************************/ #include "row0merge.h" #include "row0ext.h" #include "row0row.h" #include "row0upd.h" #include "row0ins.h" #include "row0sel.h" #include "dict0dict.h" #include "dict0mem.h" #include "dict0boot.h" #include "dict0crea.h" #include "dict0load.h" #include "btr0btr.h" #include "mach0data.h" #include "trx0rseg.h" #include "trx0trx.h" #include "trx0roll.h" #include "trx0undo.h" #include "trx0purge.h" #include "trx0rec.h" #include "api0misc.h" #include "rem0cmp.h" #include "read0read.h" #include "os0file.h" #include "lock0lock.h" #include "data0data.h" #include "data0type.h" #include "pars0pars.h" #include "mem0mem.h" #include "log0log.h" #include "ut0sort.h" #include "ddl0ddl.h" #ifdef HAVE_UNISTD_H #include #endif #ifdef __WIN__ #include #endif #ifdef UNIV_DEBUG /** Set these in order ot enable debug printout. */ /* @{ */ /** Log the outcome of each row_merge_cmp() call, comparing records. */ static ibool row_merge_print_cmp; /** Log each record read from temporary file. */ static ibool row_merge_print_read; /** Log each record write to temporary file. */ static ibool row_merge_print_write; /** Log each row_merge_blocks() call, merging two blocks of records to a bigger one. */ static ibool row_merge_print_block; /** Log each block read from temporary file. */ static ibool row_merge_print_block_read; /** Log each block read from temporary file. */ static ibool row_merge_print_block_write; /* @} */ #endif /* UNIV_DEBUG */ /** @brief Block size for I/O operations in merge sort. The minimum is UNIV_PAGE_SIZE, or page_get_free_space_of_empty() rounded to a power of 2. When not creating a PRIMARY KEY that contains column prefixes, this can be set as small as UNIV_PAGE_SIZE / 2. See the comment above ut_ad(data_size < sizeof(row_merge_block_t)). */ typedef byte row_merge_block_t[1048576]; /** @brief Secondary buffer for I/O operations of merge records. This buffer is used for writing or reading a record that spans two row_merge_block_t. Thus, it must be able to hold one merge record, whose maximum size is the same as the minimum size of row_merge_block_t. */ typedef byte mrec_buf_t[UNIV_PAGE_SIZE]; /** @brief Merge record in row_merge_block_t. The format is the same as a record in ROW_FORMAT=COMPACT with the exception that the REC_N_NEW_EXTRA_BYTES are omitted. */ typedef byte mrec_t; /** Buffer for sorting in main memory. */ struct row_merge_buf_struct { mem_heap_t* heap; /*!< memory heap where allocated */ dict_index_t* index; /*!< the index the tuples belong to */ ulint total_size; /*!< total amount of data bytes */ ulint n_tuples; /*!< number of data tuples */ ulint max_tuples; /*!< maximum number of data tuples */ const dfield_t**tuples; /*!< array of pointers to arrays of fields that form the data tuples */ const dfield_t**tmp_tuples; /*!< temporary copy of tuples, for sorting */ }; /** Buffer for sorting in main memory. */ typedef struct row_merge_buf_struct row_merge_buf_t; /** Information about temporary files used in merge sort */ struct merge_file_struct { int fd; /*!< file descriptor */ ulint offset; /*!< file offset (end of file) */ ib_uint64_t n_rec; /*!< number of records in the file */ }; /** Information about temporary files used in merge sort */ typedef struct merge_file_struct merge_file_t; #ifdef UNIV_DEBUG /******************************************************//** Display a merge tuple. */ static void row_merge_tuple_print( /*==================*/ ib_stream_t ib_stream, /*!< in: output stream */ const dfield_t* entry, /*!< in: tuple to print */ ulint n_fields) /*!< in: number of fields in the tuple */ { ulint j; for (j = 0; j < n_fields; j++) { const dfield_t* field = &entry[j]; if (dfield_is_null(field)) { ib_logger(ib_stream, "\n NULL;"); } else { ulint field_len = dfield_get_len(field); ulint len = ut_min(field_len, 20); if (dfield_is_ext(field)) { ib_logger(ib_stream, "\nE"); } else { ib_logger(ib_stream, "\n "); } ut_print_buf(ib_stream, dfield_get_data(field), len); if (len != field_len) { ib_logger(ib_stream, " (total %lu bytes)", field_len); } } } ib_logger(ib_stream, "\n"); } #endif /* UNIV_DEBUG */ /******************************************************//** Allocate a sort buffer. @return own: sort buffer */ static row_merge_buf_t* row_merge_buf_create_low( /*=====================*/ mem_heap_t* heap, /*!< in: heap where allocated */ dict_index_t* index, /*!< in: secondary index */ ulint max_tuples, /*!< in: maximum number of data tuples */ ulint buf_size) /*!< in: size of the buffer, in bytes */ { row_merge_buf_t* buf; ut_ad(max_tuples > 0); ut_ad(max_tuples <= sizeof(row_merge_block_t)); ut_ad(max_tuples < buf_size); buf = mem_heap_zalloc(heap, buf_size); buf->heap = heap; buf->index = index; buf->max_tuples = max_tuples; buf->tuples = mem_heap_alloc(heap, 2 * max_tuples * sizeof *buf->tuples); buf->tmp_tuples = buf->tuples + max_tuples; return(buf); } /******************************************************//** Allocate a sort buffer. @return own: sort buffer */ static row_merge_buf_t* row_merge_buf_create( /*=================*/ dict_index_t* index) /*!< in: secondary index */ { row_merge_buf_t* buf; ulint max_tuples; ulint buf_size; mem_heap_t* heap; max_tuples = sizeof(row_merge_block_t) / ut_max(1, dict_index_get_min_size(index)); buf_size = (sizeof *buf) + (max_tuples - 1) * sizeof *buf->tuples; heap = mem_heap_create(buf_size + sizeof(row_merge_block_t)); buf = row_merge_buf_create_low(heap, index, max_tuples, buf_size); return(buf); } /******************************************************//** Empty a sort buffer. @return sort buffer */ static row_merge_buf_t* row_merge_buf_empty( /*================*/ row_merge_buf_t* buf) /*!< in,own: sort buffer */ { ulint buf_size; ulint max_tuples = buf->max_tuples; mem_heap_t* heap = buf->heap; dict_index_t* index = buf->index; buf_size = (sizeof *buf) + (max_tuples - 1) * sizeof *buf->tuples; mem_heap_empty(heap); return(row_merge_buf_create_low(heap, index, max_tuples, buf_size)); } /******************************************************//** Deallocate a sort buffer. */ static void row_merge_buf_free( /*===============*/ row_merge_buf_t* buf) /*!< in,own: sort buffer, to be freed */ { mem_heap_free(buf->heap); } /******************************************************//** Insert a data tuple into a sort buffer. @return TRUE if added, FALSE if out of space */ static ibool row_merge_buf_add( /*==============*/ row_merge_buf_t* buf, /*!< in/out: sort buffer */ const dtuple_t* row, /*!< in: row in clustered index */ const row_ext_t* ext) /*!< in: cache of externally stored column prefixes, or NULL */ { ulint i; ulint n_fields; ulint data_size; ulint extra_size; const dict_index_t* index; dfield_t* entry; dfield_t* field; if (buf->n_tuples >= buf->max_tuples) { return(FALSE); } UNIV_PREFETCH_R(row->fields); index = buf->index; n_fields = dict_index_get_n_fields(index); entry = mem_heap_alloc(buf->heap, n_fields * sizeof *entry); buf->tuples[buf->n_tuples] = entry; field = entry; data_size = 0; extra_size = UT_BITS_IN_BYTES(index->n_nullable); for (i = 0; i < n_fields; i++, field++) { const dict_field_t* ifield; const dict_col_t* col; ulint col_no; const dfield_t* row_field; ulint len; ifield = dict_index_get_nth_field(index, i); col = ifield->col; col_no = dict_col_get_no(col); row_field = dtuple_get_nth_field(row, col_no); dfield_copy(field, row_field); len = dfield_get_len(field); if (dfield_is_null(field)) { ut_ad(!(col->prtype & DATA_NOT_NULL)); continue; } else if (UNIV_LIKELY(!ext)) { } else if (dict_index_is_clust(index)) { /* Flag externally stored fields. */ const byte* buf = row_ext_lookup(ext, col_no, &len); if (UNIV_LIKELY_NULL(buf)) { ut_a(buf != field_ref_zero); if (i < dict_index_get_n_unique(index)) { dfield_set_data(field, buf, len); } else { dfield_set_ext(field); len = dfield_get_len(field); } } } else { const byte* buf = row_ext_lookup(ext, col_no, &len); if (UNIV_LIKELY_NULL(buf)) { ut_a(buf != field_ref_zero); dfield_set_data(field, buf, len); } } /* If a column prefix index, take only the prefix */ if (ifield->prefix_len) { len = dtype_get_at_most_n_mbchars( col->prtype, col->mbminlen, col->mbmaxlen, ifield->prefix_len, len, dfield_get_data(field)); dfield_set_len(field, len); } ut_ad(len <= col->len || col->mtype == DATA_BLOB); if (ifield->fixed_len) { ut_ad(len == ifield->fixed_len); ut_ad(!dfield_is_ext(field)); } else if (dfield_is_ext(field)) { extra_size += 2; } else if (len < 128 || (col->len < 256 && col->mtype != DATA_BLOB)) { extra_size++; } else { /* For variable-length columns, we look up the maximum length from the column itself. If this is a prefix index column shorter than 256 bytes, this will waste one byte. */ extra_size += 2; } data_size += len; } #ifdef UNIV_DEBUG { ulint size; ulint extra; size = rec_get_converted_size_comp(index, REC_STATUS_ORDINARY, entry, n_fields, &extra); ut_ad(data_size + extra_size + REC_N_NEW_EXTRA_BYTES == size); ut_ad(extra_size + REC_N_NEW_EXTRA_BYTES == extra); } #endif /* UNIV_DEBUG */ /* Add to the total size of the record in row_merge_block_t the encoded length of extra_size and the extra bytes (extra_size). See row_merge_buf_write() for the variable-length encoding of extra_size. */ data_size += (extra_size + 1) + ((extra_size + 1) >= 0x80); /* The following assertion may fail if row_merge_block_t is declared very small and a PRIMARY KEY is being created with many prefix columns. In that case, the record may exceed the page_zip_rec_needs_ext() limit. However, no further columns will be moved to external storage until the record is inserted to the clustered index B-tree. */ ut_ad(data_size < sizeof(row_merge_block_t)); /* Reserve one byte for the end marker of row_merge_block_t. */ if (buf->total_size + data_size >= sizeof(row_merge_block_t) - 1) { return(FALSE); } buf->total_size += data_size; buf->n_tuples++; field = entry; /* Copy the data fields. */ do { dfield_dup(field++, buf->heap); } while (--n_fields); return(TRUE); } /** Structure for reporting duplicate records. */ struct row_merge_dup_struct { const dict_index_t* index; /*!< index being sorted */ table_handle_t table; /*!< table object */ ulint n_dup; /*!< number of duplicates */ }; /** Structure for reporting duplicate records. */ typedef struct row_merge_dup_struct row_merge_dup_t; /*************************************************************//** Report a duplicate key. */ static void row_merge_dup_report( /*=================*/ row_merge_dup_t* dup, /*!< in/out: for reporting duplicates */ const dfield_t* entry) /*!< in: duplicate index entry */ { mrec_buf_t buf; const dtuple_t* tuple; dtuple_t tuple_store; const rec_t* rec; const dict_index_t* index = dup->index; ulint n_fields= dict_index_get_n_fields(index); mem_heap_t* heap = NULL; ulint offsets_[REC_OFFS_NORMAL_SIZE]; ulint* offsets; ulint n_ext; if (dup->n_dup++) { /* Only report the first duplicate record, but count all duplicate records. */ return; } rec_offs_init(offsets_); /* Convert the tuple to a record and then to client format. */ tuple = dtuple_from_fields(&tuple_store, entry, n_fields); n_ext = dict_index_is_clust(index) ? dtuple_get_n_ext(tuple) : 0; rec = rec_convert_dtuple_to_rec(buf, index, tuple, n_ext); offsets = rec_get_offsets(rec, index, offsets_, ULINT_UNDEFINED, &heap); if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } } /*************************************************************//** Compare two tuples. @return 1, 0, -1 if a is greater, equal, less, respectively, than b */ static int row_merge_tuple_cmp( /*================*/ void* cmp_ctx,/*!< in: compare context, required for BLOBs and user defined types */ ulint n_field,/*!< in: number of fields */ const dfield_t* a, /*!< in: first tuple to be compared */ const dfield_t* b, /*!< in: second tuple to be compared */ row_merge_dup_t* dup) /*!< in/out: for reporting duplicates */ { int cmp; const dfield_t* field = a; /* Compare the fields of the tuples until a difference is found or we run out of fields to compare. If !cmp at the end, the tuples are equal. */ do { cmp = cmp_dfield_dfield(cmp_ctx, a++, b++); } while (!cmp && --n_field); if (UNIV_UNLIKELY(!cmp) && UNIV_LIKELY_NULL(dup)) { /* Report a duplicate value error if the tuples are logically equal. NULL columns are logically inequal, although they are equal in the sorting order. Find out if any of the fields are NULL. */ for (b = field; b != a; b++) { if (dfield_is_null(b)) { goto func_exit; } } row_merge_dup_report(dup, field); } func_exit: return(cmp); } /** Wrapper for row_merge_tuple_sort() to inject some more context to UT_SORT_FUNCTION_BODY(). @param a array of tuples that being sorted @param b aux (work area), same size as tuples[] @param c lower bound of the sorting area, inclusive @param d upper bound of the sorting area, inclusive */ #define row_merge_tuple_sort_ctx(x,a,b,c,d) \ row_merge_tuple_sort(x,n_field,dup,a,b,c,d) /** Wrapper for row_merge_tuple_cmp() to inject some more context to UT_SORT_FUNCTION_BODY(). @param a first tuple to be compared @param b second tuple to be compared @return 1, 0, -1 if a is greater, equal, less, respectively, than b */ #define row_merge_tuple_cmp_ctx(x,a,b) row_merge_tuple_cmp(x,n_field,a,b,dup) /**********************************************************************//** Merge sort the tuple buffer in main memory. */ static void row_merge_tuple_sort( /*=================*/ void* cmp_ctx,/*!< in: compare context, required for BLOBs and user defined types */ ulint n_field,/*!< in: number of fields */ row_merge_dup_t* dup, /*!< in/out: for reporting duplicates */ const dfield_t** tuples, /*!< in/out: tuples */ const dfield_t** aux, /*!< in/out: work area */ ulint low, /*!< in: lower bound of the sorting area, inclusive */ ulint high) /*!< in: upper bound of the sorting area, exclusive */ { UT_SORT_FUNCTION_BODY(cmp_ctx, row_merge_tuple_sort_ctx, tuples, aux, low, high, row_merge_tuple_cmp_ctx); } /******************************************************//** Sort a buffer. */ static void row_merge_buf_sort( /*===============*/ row_merge_buf_t* buf, /*!< in/out: sort buffer */ row_merge_dup_t* dup) /*!< in/out: for reporting duplicates */ { row_merge_tuple_sort( buf->index->cmp_ctx, dict_index_get_n_unique(buf->index), dup, buf->tuples, buf->tmp_tuples, 0, buf->n_tuples); } /******************************************************//** Write a buffer to a block. */ static void row_merge_buf_write( /*================*/ const row_merge_buf_t* buf, /*!< in: sorted buffer */ #ifdef UNIV_DEBUG const merge_file_t* of, /*!< in: output file */ #endif /* UNIV_DEBUG */ row_merge_block_t* block) /*!< out: buffer for writing to file */ #ifndef UNIV_DEBUG # define row_merge_buf_write(buf, of, block) row_merge_buf_write(buf, block) #endif /* !UNIV_DEBUG */ { const dict_index_t* index = buf->index; ulint n_fields= dict_index_get_n_fields(index); byte* b = &(*block)[0]; ulint i; for (i = 0; i < buf->n_tuples; i++) { ulint size; ulint extra_size; const dfield_t* entry = buf->tuples[i]; size = rec_get_converted_size_comp(index, REC_STATUS_ORDINARY, entry, n_fields, &extra_size); ut_ad(size > extra_size); ut_ad(extra_size >= REC_N_NEW_EXTRA_BYTES); extra_size -= REC_N_NEW_EXTRA_BYTES; size -= REC_N_NEW_EXTRA_BYTES; /* Encode extra_size + 1 */ if (extra_size + 1 < 0x80) { *b++ = (byte) (extra_size + 1); } else { ut_ad((extra_size + 1) < 0x8000); *b++ = (byte) (0x80 | ((extra_size + 1) >> 8)); *b++ = (byte) (extra_size + 1); } ut_ad(b + size < block[1]); rec_convert_dtuple_to_rec_comp(b + extra_size, 0, index, REC_STATUS_ORDINARY, entry, n_fields); b += size; #ifdef UNIV_DEBUG if (row_merge_print_write) { ib_logger(ib_stream, "row_merge_buf_write %p,%d,%lu %lu", (void*) b, of->fd, (ulong) of->offset, (ulong) i); row_merge_tuple_print(ib_stream, entry, n_fields); } #endif /* UNIV_DEBUG */ } /* Write an "end-of-chunk" marker. */ ut_a(b < block[1]); ut_a(b == block[0] + buf->total_size); *b++ = 0; #ifdef UNIV_DEBUG_VALGRIND /* The rest of the block is uninitialized. Initialize it to avoid bogus warnings. */ memset(b, 0xff, block[1] - b); #endif /* UNIV_DEBUG_VALGRIND */ #ifdef UNIV_DEBUG if (row_merge_print_write) { ib_logger(ib_stream, "row_merge_buf_write %p,%d,%lu EOF\n", (void*) b, of->fd, (ulong) of->offset); } #endif /* UNIV_DEBUG */ } /******************************************************//** Create a memory heap and allocate space for row_merge_rec_offsets(). @return memory heap */ static mem_heap_t* row_merge_heap_create( /*==================*/ const dict_index_t* index, /*!< in: record descriptor */ ulint** offsets1, /*!< out: offsets */ ulint** offsets2) /*!< out: offsets */ { ulint i = 1 + REC_OFFS_HEADER_SIZE + dict_index_get_n_fields(index); mem_heap_t* heap = mem_heap_create(2 * i * sizeof *offsets1); *offsets1 = mem_heap_alloc(heap, i * sizeof *offsets1); *offsets2 = mem_heap_alloc(heap, i * sizeof *offsets2); (*offsets1)[0] = (*offsets2)[0] = i; (*offsets1)[1] = (*offsets2)[1] = dict_index_get_n_fields(index); return(heap); } /**********************************************************************//** Search an index object by name and column names. If several indexes match, return the index with the max id. @return matching index, NULL if not found */ static dict_index_t* row_merge_dict_table_get_index( /*===========================*/ dict_table_t* table, /*!< in: table */ const merge_index_def_t*index_def) /*!< in: index definition */ { ulint i; dict_index_t* index; const char** column_names; column_names = mem_alloc(index_def->n_fields * sizeof *column_names); for (i = 0; i < index_def->n_fields; ++i) { column_names[i] = index_def->fields[i].field_name; } index = dict_table_get_index_by_max_id( table, index_def->name, column_names, index_def->n_fields); mem_free((void*) column_names); return(index); } /********************************************************************//** Read a merge block from the file system. @return TRUE if request was successful, FALSE if fail */ static ibool row_merge_read( /*===========*/ int fd, /*!< in: file descriptor */ ulint offset, /*!< in: offset where to read */ row_merge_block_t* buf) /*!< out: data */ { ib_uint64_t ofs = ((ib_uint64_t) offset) * sizeof *buf; ibool success; #ifdef UNIV_DEBUG if (row_merge_print_block_read) { ib_logger(ib_stream, "row_merge_read fd=%d ofs=%lu\n", fd, (ulong) offset); } #endif /* UNIV_DEBUG */ success = os_file_read_no_error_handling(OS_FILE_FROM_FD(fd), buf, (ulint) (ofs & 0xFFFFFFFF), (ulint) (ofs >> 32), sizeof *buf); if (UNIV_UNLIKELY(!success)) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: failed to read merge block at %llu\n", ofs); } return(UNIV_LIKELY(success)); } /********************************************************************//** Read a merge block from the file system. @return TRUE if request was successful, FALSE if fail */ static ibool row_merge_write( /*============*/ int fd, /*!< in: file descriptor */ ulint offset, /*!< in: offset where to write */ const void* buf) /*!< in: data */ { ib_uint64_t ofs = ((ib_uint64_t) offset) * sizeof(row_merge_block_t); #ifdef UNIV_DEBUG if (row_merge_print_block_write) { ib_logger(ib_stream, "row_merge_write fd=%d ofs=%lu\n", fd, (ulong) offset); } #endif /* UNIV_DEBUG */ return(UNIV_LIKELY(os_file_write("(merge)", OS_FILE_FROM_FD(fd), buf, (ulint) (ofs & 0xFFFFFFFF), (ulint) (ofs >> 32), sizeof(row_merge_block_t)))); } /********************************************************************//** Read a merge record. @return pointer to next record, or NULL on I/O error or end of list */ static __attribute__((nonnull)) const byte* row_merge_read_rec( /*===============*/ row_merge_block_t* block, /*!< in/out: file buffer */ mrec_buf_t* buf, /*!< in/out: secondary buffer */ const byte* b, /*!< in: pointer to record */ const dict_index_t* index, /*!< in: index of the record */ int fd, /*!< in: file descriptor */ ulint* foffs, /*!< in/out: file offset */ const mrec_t** mrec, /*!< out: pointer to merge record, or NULL on end of list (non-NULL on I/O error) */ ulint* offsets)/*!< out: offsets of mrec */ { ulint extra_size; ulint data_size; ulint avail_size; ut_ad(block); ut_ad(buf); ut_ad(b >= block[0]); ut_ad(b < block[1]); ut_ad(index); ut_ad(foffs); ut_ad(mrec); ut_ad(offsets); ut_ad(*offsets == 1 + REC_OFFS_HEADER_SIZE + dict_index_get_n_fields(index)); extra_size = *b++; if (UNIV_UNLIKELY(!extra_size)) { /* End of list */ *mrec = NULL; #ifdef UNIV_DEBUG if (row_merge_print_read) { ib_logger(ib_stream, "row_merge_read %p,%p,%d,%lu EOF\n", (const void*) b, (const void*) block, fd, (ulong) *foffs); } #endif /* UNIV_DEBUG */ return(NULL); } if (extra_size >= 0x80) { /* Read another byte of extra_size. */ if (UNIV_UNLIKELY(b >= block[1])) { if (!row_merge_read(fd, ++(*foffs), block)) { err_exit: /* Signal I/O error. */ *mrec = b; return(NULL); } /* Wrap around to the beginning of the buffer. */ b = block[0]; } extra_size = (extra_size & 0x7f) << 8; extra_size |= *b++; } /* Normalize extra_size. Above, value 0 signals "end of list". */ extra_size--; /* Read the extra bytes. */ if (UNIV_UNLIKELY(b + extra_size >= block[1])) { /* The record spans two blocks. Copy the entire record to the auxiliary buffer and handle this as a special case. */ avail_size = block[1] - b; memcpy(*buf, b, avail_size); if (!row_merge_read(fd, ++(*foffs), block)) { goto err_exit; } /* Wrap around to the beginning of the buffer. */ b = block[0]; /* Copy the record. */ memcpy(*buf + avail_size, b, extra_size - avail_size); b += extra_size - avail_size; *mrec = *buf + extra_size; rec_init_offsets_comp_ordinary(*mrec, 0, index, offsets); data_size = rec_offs_data_size(offsets); /* These overflows should be impossible given that records are much smaller than either buffer, and the record starts near the beginning of each buffer. */ ut_a(extra_size + data_size < sizeof *buf); ut_a(b + data_size < block[1]); /* Copy the data bytes. */ memcpy(*buf + extra_size, b, data_size); b += data_size; goto func_exit; } *mrec = b + extra_size; rec_init_offsets_comp_ordinary(*mrec, 0, index, offsets); data_size = rec_offs_data_size(offsets); ut_ad(extra_size + data_size < sizeof *buf); b += extra_size + data_size; if (UNIV_LIKELY(b < block[1])) { /* The record fits entirely in the block. This is the normal case. */ goto func_exit; } /* The record spans two blocks. Copy it to buf. */ b -= extra_size + data_size; avail_size = block[1] - b; memcpy(*buf, b, avail_size); *mrec = *buf + extra_size; #ifdef UNIV_DEBUG /* We cannot invoke rec_offs_make_valid() here, because there are no REC_N_NEW_EXTRA_BYTES between extra_size and data_size. Similarly, rec_offs_validate() would fail, because it invokes rec_get_status(). */ offsets[2] = (ulint) *mrec; offsets[3] = (ulint) index; #endif /* UNIV_DEBUG */ if (!row_merge_read(fd, ++(*foffs), block)) { goto err_exit; } /* Wrap around to the beginning of the buffer. */ b = block[0]; /* Copy the rest of the record. */ memcpy(*buf + avail_size, b, extra_size + data_size - avail_size); b += extra_size + data_size - avail_size; func_exit: #ifdef UNIV_DEBUG if (row_merge_print_read) { ib_logger(ib_stream, "row_merge_read %p,%p,%d,%lu ", (const void*) b, (const void*) block, fd, (ulong) *foffs); rec_print_comp(ib_stream, *mrec, offsets); ib_logger(ib_stream, "\n"); } #endif /* UNIV_DEBUG */ return(b); } /********************************************************************//** Write a merge record. */ static void row_merge_write_rec_low( /*====================*/ byte* b, /*!< out: buffer */ ulint e, /*!< in: encoded extra_size */ #ifdef UNIV_DEBUG ulint size, /*!< in: total size to write */ int fd, /*!< in: file descriptor */ ulint foffs, /*!< in: file offset */ #endif /* UNIV_DEBUG */ const mrec_t* mrec, /*!< in: record to write */ const ulint* offsets)/*!< in: offsets of mrec */ #ifndef UNIV_DEBUG # define row_merge_write_rec_low(b, e, size, fd, foffs, mrec, offsets) \ row_merge_write_rec_low(b, e, mrec, offsets) #endif /* !UNIV_DEBUG */ { #ifdef UNIV_DEBUG const byte* const end = b + size; ut_ad(e == rec_offs_extra_size(offsets) + 1); if (row_merge_print_write) { ib_logger(ib_stream, "row_merge_write %p,%d,%lu ", (void*) b, fd, (ulong) foffs); rec_print_comp(ib_stream, mrec, offsets); ib_logger(ib_stream, "\n"); } #endif /* UNIV_DEBUG */ if (e < 0x80) { *b++ = (byte) e; } else { *b++ = (byte) (0x80 | (e >> 8)); *b++ = (byte) e; } memcpy(b, mrec - rec_offs_extra_size(offsets), rec_offs_size(offsets)); ut_ad(b + rec_offs_size(offsets) == end); } /********************************************************************//** Write a merge record. @return pointer to end of block, or NULL on error */ static byte* row_merge_write_rec( /*================*/ row_merge_block_t* block, /*!< in/out: file buffer */ mrec_buf_t* buf, /*!< in/out: secondary buffer */ byte* b, /*!< in: pointer to end of block */ int fd, /*!< in: file descriptor */ ulint* foffs, /*!< in/out: file offset */ const mrec_t* mrec, /*!< in: record to write */ const ulint* offsets)/*!< in: offsets of mrec */ { ulint extra_size; ulint size; ulint avail_size; ut_ad(block); ut_ad(buf); ut_ad(b >= block[0]); ut_ad(b < block[1]); ut_ad(mrec); ut_ad(foffs); ut_ad(mrec < block[0] || mrec > block[1]); ut_ad(mrec < buf[0] || mrec > buf[1]); /* Normalize extra_size. Value 0 signals "end of list". */ extra_size = rec_offs_extra_size(offsets) + 1; size = extra_size + (extra_size >= 0x80) + rec_offs_data_size(offsets); if (UNIV_UNLIKELY(b + size >= block[1])) { /* The record spans two blocks. Copy it to the temporary buffer first. */ avail_size = block[1] - b; row_merge_write_rec_low(buf[0], extra_size, size, fd, *foffs, mrec, offsets); /* Copy the head of the temporary buffer, write the completed block, and copy the tail of the record to the head of the new block. */ memcpy(b, buf[0], avail_size); if (!row_merge_write(fd, (*foffs)++, block)) { return(NULL); } UNIV_MEM_INVALID(block[0], sizeof block[0]); /* Copy the rest. */ b = block[0]; memcpy(b, buf[0] + avail_size, size - avail_size); b += size - avail_size; } else { row_merge_write_rec_low(b, extra_size, size, fd, *foffs, mrec, offsets); b += size; } return(b); } /********************************************************************//** Write an end-of-list marker. @return pointer to end of block, or NULL on error */ static byte* row_merge_write_eof( /*================*/ row_merge_block_t* block, /*!< in/out: file buffer */ byte* b, /*!< in: pointer to end of block */ int fd, /*!< in: file descriptor */ ulint* foffs) /*!< in/out: file offset */ { ut_ad(block); ut_ad(b >= block[0]); ut_ad(b < block[1]); ut_ad(foffs); #ifdef UNIV_DEBUG if (row_merge_print_write) { ib_logger(ib_stream, "row_merge_write %p,%p,%d,%lu EOF\n", (void*) b, (void*) block, fd, (ulong) *foffs); } #endif /* UNIV_DEBUG */ *b++ = 0; UNIV_MEM_ASSERT_RW(block[0], b - block[0]); UNIV_MEM_ASSERT_W(block[0], sizeof block[0]); #ifdef UNIV_DEBUG_VALGRIND /* The rest of the block is uninitialized. Initialize it to avoid bogus warnings. */ memset(b, 0xff, block[1] - b); #endif /* UNIV_DEBUG_VALGRIND */ if (!row_merge_write(fd, (*foffs)++, block)) { return(NULL); } UNIV_MEM_INVALID(block[0], sizeof block[0]); return(block[0]); } /*************************************************************//** Compare two merge records. @return 1, 0, -1 if mrec1 is greater, equal, less, respectively, than mrec2 */ static int row_merge_cmp( /*==========*/ const mrec_t* mrec1, /*!< in: first merge record to be compared */ const mrec_t* mrec2, /*!< in: second merge record to be compared */ const ulint* offsets1, /*!< in: first record offsets */ const ulint* offsets2, /*!< in: second record offsets */ const dict_index_t* index) /*!< in: index */ { int cmp; cmp = cmp_rec_rec_simple(mrec1, mrec2, offsets1, offsets2, index); #ifdef UNIV_DEBUG if (row_merge_print_cmp) { ib_logger(ib_stream, "row_merge_cmp1 "); rec_print_comp(ib_stream, mrec1, offsets1); ib_logger(ib_stream, "\nrow_merge_cmp2 "); rec_print_comp(ib_stream, mrec2, offsets2); ib_logger(ib_stream, "\nrow_merge_cmp=%d\n", cmp); } #endif /* UNIV_DEBUG */ return(cmp); } /********************************************************************//** Reads clustered index of the table and create temporary files containing the index entries for the indexes to be built. @return DB_SUCCESS or error */ static __attribute__((nonnull)) ulint row_merge_read_clustered_index( /*===========================*/ trx_t* trx, /*!< in: transaction */ table_handle_t table, /*!< in/out: Client table object, for reporting erroneous records */ const dict_table_t* old_table,/*!< in: table where rows are read from */ const dict_table_t* new_table,/*!< in: table where indexes are created; identical to old_table unless creating a PRIMARY KEY */ dict_index_t** index, /*!< in: indexes to be created */ merge_file_t* files, /*!< in: temporary files */ ulint n_index,/*!< in: number of indexes to create */ row_merge_block_t* block) /*!< in/out: file buffer */ { dict_index_t* clust_index; /* Clustered index */ mem_heap_t* row_heap; /* Heap memory to create clustered index records */ row_merge_buf_t** merge_buf; /* Temporary list for records*/ btr_pcur_t pcur; /* Persistent cursor on the clustered index */ mtr_t mtr; /* Mini transaction */ ulint err = DB_SUCCESS;/* Return code */ ulint i; ulint n_nonnull = 0; /* number of columns changed to NOT NULL */ ulint* nonnull = NULL; /* NOT NULL columns */ trx->op_info = "reading clustered index"; ut_ad(trx); ut_ad(old_table); ut_ad(new_table); ut_ad(index); ut_ad(files); /* Create and initialize memory for record buffers */ merge_buf = mem_alloc(n_index * sizeof *merge_buf); for (i = 0; i < n_index; i++) { merge_buf[i] = row_merge_buf_create(index[i]); } mtr_start(&mtr); /* Find the clustered index and create a persistent cursor based on that. */ clust_index = dict_table_get_first_index(old_table); btr_pcur_open_at_index_side( TRUE, clust_index, BTR_SEARCH_LEAF, &pcur, TRUE, &mtr); if (UNIV_UNLIKELY(old_table != new_table)) { ulint n_cols = dict_table_get_n_cols(old_table); /* A primary key will be created. Identify the columns that were flagged NOT NULL in the new table, so that we can quickly check that the records in the (old) clustered index do not violate the added NOT NULL constraints. */ ut_a(n_cols == dict_table_get_n_cols(new_table)); nonnull = mem_alloc(n_cols * sizeof *nonnull); for (i = 0; i < n_cols; i++) { if (dict_table_get_nth_col(old_table, i)->prtype & DATA_NOT_NULL) { continue; } if (dict_table_get_nth_col(new_table, i)->prtype & DATA_NOT_NULL) { nonnull[n_nonnull++] = i; } } if (!n_nonnull) { mem_free(nonnull); nonnull = NULL; } } row_heap = mem_heap_create(sizeof(mrec_buf_t)); /* Scan the clustered index. */ for (;;) { const rec_t* rec; ulint* offsets; dtuple_t* row = NULL; row_ext_t* ext; ibool has_next = TRUE; btr_pcur_move_to_next_on_page(&pcur); /* When switching pages, commit the mini-transaction in order to release the latch on the old page. */ if (btr_pcur_is_after_last_on_page(&pcur)) { if (UNIV_UNLIKELY(trx_is_interrupted(trx))) { i = 0; err = DB_INTERRUPTED; goto err_exit; } btr_pcur_store_position(&pcur, &mtr); mtr_commit(&mtr); mtr_start(&mtr); btr_pcur_restore_position(BTR_SEARCH_LEAF, &pcur, &mtr); has_next = btr_pcur_move_to_next_user_rec(&pcur, &mtr); } if (UNIV_LIKELY(has_next)) { rec = btr_pcur_get_rec(&pcur); offsets = rec_get_offsets(rec, clust_index, NULL, ULINT_UNDEFINED, &row_heap); /* Skip delete marked records. */ if (rec_get_deleted_flag( rec, dict_table_is_comp(old_table))) { continue; } srv_n_rows_inserted++; /* Build a row based on the clustered index. */ row = row_build(ROW_COPY_POINTERS, clust_index, rec, offsets, new_table, &ext, row_heap); if (UNIV_LIKELY_NULL(nonnull)) { for (i = 0; i < n_nonnull; i++) { dfield_t* field = &row->fields[nonnull[i]]; dtype_t* field_type = dfield_get_type(field); ut_a(!(field_type->prtype & DATA_NOT_NULL)); if (dfield_is_null(field)) { err = DB_PRIMARY_KEY_IS_NULL; i = 0; goto err_exit; } field_type->prtype |= DATA_NOT_NULL; } } } /* Build all entries for all the indexes to be created in a single scan of the clustered index. */ for (i = 0; i < n_index; i++) { row_merge_buf_t* buf = merge_buf[i]; merge_file_t* file = &files[i]; const dict_index_t* index = buf->index; if (UNIV_LIKELY (row && row_merge_buf_add(buf, row, ext))) { file->n_rec++; continue; } /* The buffer must be sufficiently large to hold at least one record. */ ut_ad(buf->n_tuples || !has_next); /* We have enough data tuples to form a block. Sort them and write to disk. */ if (buf->n_tuples) { if (dict_index_is_unique(index)) { row_merge_dup_t dup; dup.index = buf->index; dup.table = table; dup.n_dup = 0; row_merge_buf_sort(buf, &dup); if (dup.n_dup) { err = DB_DUPLICATE_KEY; err_exit: trx->error_key_num = i; goto func_exit; } } else { row_merge_buf_sort(buf, NULL); } } row_merge_buf_write(buf, file, block); if (!row_merge_write(file->fd, file->offset++, block)) { err = DB_OUT_OF_FILE_SPACE; goto err_exit; } UNIV_MEM_INVALID(block[0], sizeof block[0]); merge_buf[i] = row_merge_buf_empty(buf); if (UNIV_LIKELY(row != NULL)) { /* Try writing the record again, now that the buffer has been written out and emptied. */ if (UNIV_UNLIKELY (!row_merge_buf_add(buf, row, ext))) { /* An empty buffer should have enough room for at least one record. */ ut_error; } file->n_rec++; } } mem_heap_empty(row_heap); if (UNIV_UNLIKELY(!has_next)) { goto func_exit; } } func_exit: btr_pcur_close(&pcur); mtr_commit(&mtr); mem_heap_free(row_heap); if (UNIV_LIKELY_NULL(nonnull)) { mem_free(nonnull); } for (i = 0; i < n_index; i++) { row_merge_buf_free(merge_buf[i]); } mem_free(merge_buf); trx->op_info = ""; return(err); } /** Write a record via buffer 2 and read the next record to buffer N. @param N number of the buffer (0 or 1) @param AT_END statement to execute at end of input */ #define ROW_MERGE_WRITE_GET_NEXT(N, AT_END) \ do { \ b2 = row_merge_write_rec(&block[2], &buf[2], b2, \ of->fd, &of->offset, \ mrec##N, offsets##N); \ if (UNIV_UNLIKELY(!b2 || ++of->n_rec > file->n_rec)) { \ goto corrupt; \ } \ b##N = row_merge_read_rec(&block[N], &buf[N], \ b##N, index, \ file->fd, foffs##N, \ &mrec##N, offsets##N); \ if (UNIV_UNLIKELY(!b##N)) { \ if (mrec##N) { \ goto corrupt; \ } \ AT_END; \ } \ } while (0) /*************************************************************//** Merge two blocks of records on disk and write a bigger block. @return DB_SUCCESS or error code */ static ulint row_merge_blocks( /*=============*/ const dict_index_t* index, /*!< in: index being created */ const merge_file_t* file, /*!< in: file containing index entries */ row_merge_block_t* block, /*!< in/out: 3 buffers */ ulint* foffs0, /*!< in/out: offset of first source list in the file */ ulint* foffs1, /*!< in/out: offset of second source list in the file */ merge_file_t* of, /*!< in/out: output file */ table_handle_t table) /*!< in/out: Client table, for reporting erroneous key value if applicable */ { mem_heap_t* heap; /*!< memory heap for offsets0, offsets1 */ mrec_buf_t buf[3]; /*!< buffer for handling split mrec in block[] */ const byte* b0; /*!< pointer to block[0] */ const byte* b1; /*!< pointer to block[1] */ byte* b2; /*!< pointer to block[2] */ const mrec_t* mrec0; /*!< merge rec, points to block[0] or buf[0] */ const mrec_t* mrec1; /*!< merge rec, points to block[1] or buf[1] */ ulint* offsets0;/* offsets of mrec0 */ ulint* offsets1;/* offsets of mrec1 */ #ifdef UNIV_DEBUG if (row_merge_print_block) { ib_logger(ib_stream, "row_merge_blocks fd=%d ofs=%lu + fd=%d ofs=%lu" " = fd=%d ofs=%lu\n", file->fd, (ulong) *foffs0, file->fd, (ulong) *foffs1, of->fd, (ulong) of->offset); } #endif /* UNIV_DEBUG */ heap = row_merge_heap_create(index, &offsets0, &offsets1); /* Write a record and read the next record. Split the output file in two halves, which can be merged on the following pass. */ if (!row_merge_read(file->fd, *foffs0, &block[0]) || !row_merge_read(file->fd, *foffs1, &block[1])) { corrupt: mem_heap_free(heap); return(DB_CORRUPTION); } b0 = block[0]; b1 = block[1]; b2 = block[2]; b0 = row_merge_read_rec(&block[0], &buf[0], b0, index, file->fd, foffs0, &mrec0, offsets0); b1 = row_merge_read_rec(&block[1], &buf[1], b1, index, file->fd, foffs1, &mrec1, offsets1); if (UNIV_UNLIKELY(!b0 && mrec0) || UNIV_UNLIKELY(!b1 && mrec1)) { goto corrupt; } while (mrec0 && mrec1) { switch (row_merge_cmp(mrec0, mrec1, offsets0, offsets1, index)) { case 0: if (UNIV_UNLIKELY (dict_index_is_unique(index))) { mem_heap_free(heap); return(DB_DUPLICATE_KEY); } /* fall through */ case -1: ROW_MERGE_WRITE_GET_NEXT(0, goto merged); break; case 1: ROW_MERGE_WRITE_GET_NEXT(1, goto merged); break; default: ut_error; } } merged: if (mrec0) { /* append all mrec0 to output */ for (;;) { ROW_MERGE_WRITE_GET_NEXT(0, goto done0); } } done0: if (mrec1) { /* append all mrec1 to output */ for (;;) { ROW_MERGE_WRITE_GET_NEXT(1, goto done1); } } done1: mem_heap_free(heap); b2 = row_merge_write_eof(&block[2], b2, of->fd, &of->offset); return(b2 ? DB_SUCCESS : DB_CORRUPTION); } /*************************************************************//** Copy a block of index entries. @return TRUE on success, FALSE on failure */ static __attribute__((nonnull)) ibool row_merge_blocks_copy( /*==================*/ const dict_index_t* index, /*!< in: index being created */ const merge_file_t* file, /*!< in: input file */ row_merge_block_t* block, /*!< in/out: 3 buffers */ ulint* foffs0, /*!< in/out: input file offset */ merge_file_t* of) /*!< in/out: output file */ { mem_heap_t* heap; /*!< memory heap for offsets0, offsets1 */ mrec_buf_t buf[3]; /*!< buffer for handling split mrec in block[] */ const byte* b0; /*!< pointer to block[0] */ byte* b2; /*!< pointer to block[2] */ const mrec_t* mrec0; /*!< merge rec, points to block[0] */ ulint* offsets0;/* offsets of mrec0 */ ulint* offsets1;/* dummy offsets */ #ifdef UNIV_DEBUG if (row_merge_print_block) { ib_logger(ib_stream, "row_merge_blocks_copy fd=%d ofs=%lu" " = fd=%d ofs=%lu\n", file->fd, (ulong) foffs0, of->fd, (ulong) of->offset); } #endif /* UNIV_DEBUG */ heap = row_merge_heap_create(index, &offsets0, &offsets1); /* Write a record and read the next record. Split the output file in two halves, which can be merged on the following pass. */ if (!row_merge_read(file->fd, *foffs0, &block[0])) { corrupt: mem_heap_free(heap); return(FALSE); } b0 = block[0]; b2 = block[2]; b0 = row_merge_read_rec(&block[0], &buf[0], b0, index, file->fd, foffs0, &mrec0, offsets0); if (UNIV_UNLIKELY(!b0 && mrec0)) { goto corrupt; } if (mrec0) { /* append all mrec0 to output */ for (;;) { ROW_MERGE_WRITE_GET_NEXT(0, goto done0); } } done0: /* The file offset points to the beginning of the last page that has been read. Update it to point to the next block. */ (*foffs0)++; mem_heap_free(heap); return(row_merge_write_eof(&block[2], b2, of->fd, &of->offset) != NULL); } /*************************************************************//** Merge disk files. @return DB_SUCCESS or error code */ static __attribute__((nonnull)) ulint row_merge( /*======*/ trx_t* trx, /*!< in: transaction */ const dict_index_t* index, /*!< in: index being created */ merge_file_t* file, /*!< in/out: file containing index entries */ ulint* half, /*!< in/out: half the file */ row_merge_block_t* block, /*!< in/out: 3 buffers */ int* tmpfd, /*!< in/out: temporary file handle */ table_handle_t table) /*!< in/out: Client table, for reporting erroneous key value if applicable */ { ulint foffs0; /*!< first input offset */ ulint foffs1; /*!< second input offset */ ulint error; /*!< error code */ merge_file_t of; /*!< output file */ const ulint ihalf = *half; /*!< half the input file */ ulint ohalf; /*!< half the output file */ UNIV_MEM_ASSERT_W(block[0], 3 * sizeof block[0]); ut_ad(ihalf < file->offset); of.fd = *tmpfd; of.offset = 0; of.n_rec = 0; /* Merge blocks to the output file. */ ohalf = 0; foffs0 = 0; foffs1 = ihalf; for (; foffs0 < ihalf && foffs1 < file->offset; foffs0++, foffs1++) { ulint ahalf; /*!< arithmetic half the input file */ if (UNIV_UNLIKELY(trx_is_interrupted(trx))) { return(DB_INTERRUPTED); } error = row_merge_blocks(index, file, block, &foffs0, &foffs1, &of, table); if (error != DB_SUCCESS) { return(error); } /* Record the offset of the output file when approximately half the output has been generated. In this way, the next invocation of row_merge() will spend most of the time in this loop. The initial estimate is ohalf==0. */ ahalf = file->offset / 2; ut_ad(ohalf <= of.offset); /* Improve the estimate until reaching half the input file size, or we can not get any closer to it. All comparands should be non-negative when !(ohalf < ahalf) because ohalf <= of.offset. */ if (ohalf < ahalf || of.offset - ahalf < ohalf - ahalf) { ohalf = of.offset; } } /* Copy the last blocks, if there are any. */ while (foffs0 < ihalf) { if (UNIV_UNLIKELY(trx_is_interrupted(trx))) { return(DB_INTERRUPTED); } if (!row_merge_blocks_copy(index, file, block, &foffs0, &of)) { return(DB_CORRUPTION); } } ut_ad(foffs0 == ihalf); while (foffs1 < file->offset) { if (UNIV_UNLIKELY(trx_is_interrupted(trx))) { return(DB_INTERRUPTED); } if (!row_merge_blocks_copy(index, file, block, &foffs1, &of)) { return(DB_CORRUPTION); } } ut_ad(foffs1 == file->offset); if (UNIV_UNLIKELY(of.n_rec != file->n_rec)) { return(DB_CORRUPTION); } /* Swap file descriptors for the next pass. */ *tmpfd = file->fd; *file = of; *half = ohalf; UNIV_MEM_INVALID(block[0], 3 * sizeof block[0]); return(DB_SUCCESS); } /*************************************************************//** Merge disk files. @return DB_SUCCESS or error code */ static ulint row_merge_sort( /*===========*/ trx_t* trx, /*!< in: transaction */ const dict_index_t* index, /*!< in: index being created */ merge_file_t* file, /*!< in/out: file containing index entries */ row_merge_block_t* block, /*!< in/out: 3 buffers */ int* tmpfd, /*!< in/out: temporary file handle */ table_handle_t table) /*!< in/out: User table, for reporting erroneous key value if applicable */ { ulint half = file->offset / 2; /* The file should always contain at least one byte (the end of file marker). Thus, it must be at least one block. */ ut_ad(file->offset > 0); do { ulint error; error = row_merge(trx, index, file, &half, block, tmpfd, table); if (error != DB_SUCCESS) { return(error); } /* half > 0 should hold except when the file consists of one block. No need to merge further then. */ ut_ad(half > 0 || file->offset == 1); } while (half < file->offset && half > 0); return(DB_SUCCESS); } /*************************************************************//** Copy externally stored columns to the data tuple. */ static void row_merge_copy_blobs( /*=================*/ const mrec_t* mrec, /*!< in: merge record */ const ulint* offsets,/*!< in: offsets of mrec */ ulint zip_size,/*!< in: compressed page size in bytes, or 0 */ dtuple_t* tuple, /*!< in/out: data tuple */ mem_heap_t* heap) /*!< in/out: memory heap */ { ulint i; ulint n_fields = dtuple_get_n_fields(tuple); for (i = 0; i < n_fields; i++) { ulint len; const void* data; dfield_t* field = dtuple_get_nth_field(tuple, i); if (!dfield_is_ext(field)) { continue; } ut_ad(!dfield_is_null(field)); /* The table is locked during index creation. Therefore, externally stored columns cannot possibly be freed between the time the BLOB pointers are read (row_merge_read_clustered_index()) and dereferenced (below). */ data = btr_rec_copy_externally_stored_field( mrec, offsets, zip_size, i, &len, heap); dfield_set_data(field, data, len); } } /********************************************************************//** Read sorted file containing index data tuples and insert these data tuples to the index @return DB_SUCCESS or error number */ static ulint row_merge_insert_index_tuples( /*==========================*/ trx_t* trx, /*!< in: transaction */ dict_index_t* index, /*!< in: index */ dict_table_t* table, /*!< in: new table */ ulint zip_size,/*!< in: compressed page size of the old table, or 0 if uncompressed */ int fd, /*!< in: file descriptor */ row_merge_block_t* block) /*!< in/out: file buffer */ { mrec_buf_t buf; const byte* b; que_thr_t* thr; ins_node_t* node; mem_heap_t* tuple_heap; mem_heap_t* graph_heap; enum db_err err = DB_SUCCESS; ulint foffs = 0; ulint* offsets; ut_ad(trx); ut_ad(index); ut_ad(table); /* We use the insert query graph as the dummy graph needed in the row module call */ trx->op_info = "inserting index entries"; graph_heap = mem_heap_create(500); node = row_ins_node_create(INS_DIRECT, table, graph_heap); thr = pars_complete_graph_for_exec(node, trx, graph_heap); que_thr_move_to_run_state(thr); tuple_heap = mem_heap_create(1000); { ulint i = 1 + REC_OFFS_HEADER_SIZE + dict_index_get_n_fields(index); offsets = mem_heap_alloc(graph_heap, i * sizeof *offsets); offsets[0] = i; offsets[1] = dict_index_get_n_fields(index); } b = *block; if (!row_merge_read(fd, foffs, block)) { err = DB_CORRUPTION; } else { for (;;) { const mrec_t* mrec; dtuple_t* dtuple; ulint n_ext; b = row_merge_read_rec(block, &buf, b, index, fd, &foffs, &mrec, offsets); if (UNIV_UNLIKELY(!b)) { /* End of list, or I/O error */ if (mrec) { err = DB_CORRUPTION; } break; } dtuple = row_rec_to_index_entry_low( mrec, index, offsets, &n_ext, tuple_heap); if (UNIV_UNLIKELY(n_ext)) { row_merge_copy_blobs(mrec, offsets, zip_size, dtuple, tuple_heap); } node->row = dtuple; node->table = table; node->trx_id = trx->id; ut_ad(dtuple_validate(dtuple)); do { thr->run_node = thr; thr->prev_node = thr->common.parent; err = row_ins_index_entry(index, dtuple, 0, FALSE, thr); if (UNIV_LIKELY(err == DB_SUCCESS)) { goto next_rec; } thr->lock_state = QUE_THR_LOCK_ROW; trx->error_state = err; que_thr_stop_client(thr); thr->lock_state = QUE_THR_LOCK_NOLOCK; } while (ib_handle_errors(&err, trx, thr, NULL)); goto err_exit; next_rec: mem_heap_empty(tuple_heap); } } que_thr_stop_for_client_no_error(thr, trx); err_exit: que_graph_free(thr->graph); trx->op_info = ""; mem_heap_free(tuple_heap); return(err); } /*********************************************************************//** Drop an index from the InnoDB system tables. The data dictionary must have been locked exclusively by the caller, because the transaction will not be committed. */ UNIV_INTERN void row_merge_drop_index( /*=================*/ dict_index_t* index, /*!< in: index to be removed */ dict_table_t* table, /*!< in: table */ trx_t* trx) /*!< in: transaction handle */ { if (index != NULL) { ddl_drop_index(table, index, trx); } } /*********************************************************************//** Drop those indexes which were created before an error occurred when building an index. The data dictionary must have been locked exclusively by the caller, because the transaction will not be committed. */ UNIV_INTERN void row_merge_drop_indexes( /*===================*/ trx_t* trx, /*!< in: transaction */ dict_table_t* table, /*!< in: table containing the indexes */ dict_index_t** index, /*!< in: indexes to drop */ ulint num_created) /*!< in: number of elements in index[] */ { ulint key_num; for (key_num = 0; key_num < num_created; key_num++) { row_merge_drop_index(index[key_num], table, trx); } } /*********************************************************************//** Create a merge file. */ static void row_merge_file_create( /*==================*/ merge_file_t* merge_file) /*!< out: merge file structure */ { merge_file->fd = ib_create_tempfile("ibmrg"); merge_file->offset = 0; merge_file->n_rec = 0; } /*********************************************************************//** Destroy a merge file. */ static void row_merge_file_destroy( /*===================*/ merge_file_t* merge_file) /*!< out: merge file structure */ { if (merge_file->fd != -1) { #ifdef __WIN__ _close(merge_file->fd); #else close(merge_file->fd); #endif merge_file->fd = -1; } } /*********************************************************************//** Determine the precise type of a column that is added to a tem if a column must be constrained NOT NULL. @return col->prtype, possibly ORed with DATA_NOT_NULL */ UNIV_INLINE ulint row_merge_col_prtype( /*=================*/ const dict_col_t* col, /*!< in: column */ const char* col_name, /*!< in: name of the column */ const merge_index_def_t*index_def) /*!< in: the index definition of the primary key */ { ulint prtype = col->prtype; ulint i; ut_ad(index_def->ind_type & DICT_CLUSTERED); if (prtype & DATA_NOT_NULL) { return(prtype); } /* All columns that are included in the PRIMARY KEY must be NOT NULL. */ for (i = 0; i < index_def->n_fields; i++) { if (!strcmp(col_name, index_def->fields[i].field_name)) { return(prtype | DATA_NOT_NULL); } } return(prtype); } /*********************************************************************//** Create a temporary table for creating a primary key, using the definition of an existing table. @return table, or NULL on error */ UNIV_INTERN dict_table_t* row_merge_create_temporary_table( /*=============================*/ const char* table_name, /*!< in: new table name */ const merge_index_def_t*index_def, /*!< in: the index definition of the primary key */ const dict_table_t* table, /*!< in: old table definition */ trx_t* trx) /*!< in/out: transaction (sets error_state) */ { ulint i; dict_table_t* new_table = NULL; ulint n_cols = dict_table_get_n_user_cols(table); ulint error; mem_heap_t* heap = mem_heap_create(1000); ut_ad(table_name); ut_ad(index_def); ut_ad(table); ut_ad(mutex_own(&dict_sys->mutex)); new_table = dict_mem_table_create(table_name, 0, n_cols, table->flags); for (i = 0; i < n_cols; i++) { const dict_col_t* col; const char* col_name; col = dict_table_get_nth_col(table, i); col_name = dict_table_get_col_name(table, i); dict_mem_table_add_col(new_table, heap, col_name, col->mtype, row_merge_col_prtype(col, col_name, index_def), col->len); } error = ddl_create_table(new_table, trx); mem_heap_free(heap); if (error != DB_SUCCESS) { trx->error_state = error; new_table = NULL; } return(new_table); } /*********************************************************************//** Rename the temporary indexes in the dictionary to permanent ones. The data dictionary must have been locked exclusively by the caller, because the transaction will not be committed. @return DB_SUCCESS if all OK */ UNIV_INTERN ulint row_merge_rename_indexes( /*=====================*/ trx_t* trx, /*!< in/out: transaction */ dict_table_t* table) /*!< in/out: table with new indexes */ { ulint err = DB_SUCCESS; pars_info_t* info = pars_info_create(); /* We use the private SQL parser of Innobase to generate the query graphs needed in renaming indexes. */ static const char rename_indexes[] = "PROCEDURE RENAME_INDEXES_PROC () IS\n" "BEGIN\n" "UPDATE SYS_INDEXES SET NAME=SUBSTR(NAME,1,LENGTH(NAME)-1)\n" "WHERE TABLE_ID = :tableid AND SUBSTR(NAME,0,1)='" TEMP_INDEX_PREFIX_STR "';\n" "END;\n"; ut_ad(table); ut_ad(trx); ut_a(trx->dict_operation_lock_mode == RW_X_LATCH); trx->op_info = "renaming indexes"; pars_info_add_dulint_literal(info, "tableid", table->id); err = que_eval_sql(info, rename_indexes, FALSE, trx); if (err == DB_SUCCESS) { dict_index_t* index = dict_table_get_first_index(table); do { if (*index->name == TEMP_INDEX_PREFIX) { index->name++; } index = dict_table_get_next_index(index); } while (index); } trx->op_info = ""; return(err); } /*********************************************************************//** Rename the tables in the data dictionary. The data dictionary must have been locked exclusively by the caller, because the transaction will not be committed. @return error code or DB_SUCCESS */ UNIV_INTERN ulint row_merge_rename_tables( /*====================*/ dict_table_t* old_table, /*!< in/out: old table, renamed to tmp_name */ dict_table_t* new_table, /*!< in/out: new table, renamed to old_table->name */ const char* tmp_name, /*!< in: new name for old_table */ trx_t* trx) /*!< in: transaction handle */ { ulint err = DB_ERROR; pars_info_t* info; const char* old_name= old_table->name; ut_ad(trx->client_thread_id == os_thread_get_curr_id()); ut_ad(old_table != new_table); ut_ad(mutex_own(&dict_sys->mutex)); ut_a(trx->dict_operation_lock_mode == RW_X_LATCH); trx->op_info = "renaming tables"; /* We use the private SQL parser of Innobase to generate the query graphs needed in updating the dictionary data in system tables. */ info = pars_info_create(); pars_info_add_str_literal(info, "new_name", new_table->name); pars_info_add_str_literal(info, "old_name", old_name); pars_info_add_str_literal(info, "tmp_name", tmp_name); err = que_eval_sql(info, "PROCEDURE RENAME_TABLES () IS\n" "BEGIN\n" "UPDATE SYS_TABLES SET NAME = :tmp_name\n" " WHERE NAME = :old_name;\n" "UPDATE SYS_TABLES SET NAME = :old_name\n" " WHERE NAME = :new_name;\n" "END;\n", FALSE, trx); if (err != DB_SUCCESS) { goto err_exit; } /* The following calls will also rename the .ibd data files if the tables are stored in a single-table tablespace */ if (!dict_table_rename_in_cache(old_table, tmp_name, FALSE) || !dict_table_rename_in_cache(new_table, old_name, FALSE)) { err = DB_ERROR; goto err_exit; } err = dict_load_foreigns(old_name, TRUE); if (err != DB_SUCCESS) { err_exit: trx->error_state = DB_SUCCESS; trx_general_rollback(trx, FALSE, NULL); trx->error_state = DB_SUCCESS; } trx->op_info = ""; return(err); } /*********************************************************************//** Create the index and load in to the dictionary. @return index, or NULL on error */ UNIV_INTERN dict_index_t* row_merge_create_index( /*===================*/ trx_t* trx, /*!< in/out: trx (sets error_state) */ dict_table_t* table, /*!< in: the index is on this table */ const merge_index_def_t*index_def) /*!< in: the index definition */ { dict_index_t* index; ulint err; ulint n_fields = index_def->n_fields; ulint i; /* Create the index prototype, using the passed in def, this is not a persistent operation. We pass 0 as the space id, and determine at a lower level the space id where to store the table. */ index = dict_mem_index_create(table->name, index_def->name, 0, index_def->ind_type, n_fields); ut_a(index); for (i = 0; i < n_fields; i++) { merge_index_field_t* ifield = &index_def->fields[i]; dict_mem_index_add_field(index, ifield->field_name, ifield->prefix_len); } /* Add the index to SYS_INDEXES, using the index prototype. */ index->table = table; err = ddl_create_index(index, trx); if (err == DB_SUCCESS) { index = row_merge_dict_table_get_index(table, index_def); ut_a(index); /* Note the id of the transaction that created this index, we use it to restrict readers from accessing this index, to ensure read consistency. */ index->trx_id = (ib_uint64_t) ut_conv_dulint_to_longlong(trx->id); } else { index = NULL; } return(index); } /*********************************************************************//** Check if a transaction can use an index. */ UNIV_INTERN ibool row_merge_is_index_usable( /*======================*/ const trx_t* trx, /*!< in: transaction */ const dict_index_t* index) /*!< in: index to check */ { return(!trx->read_view || read_view_sees_trx_id( trx->read_view, ut_dulint_create((ulint) (index->trx_id >> 32), (ulint) index->trx_id & 0xFFFFFFFF))); } /*********************************************************************//** Drop the old table. @return DB_SUCCESS or error code */ UNIV_INTERN ulint row_merge_drop_table( /*=================*/ trx_t* trx, /*!< in: transaction */ dict_table_t* table) /*!< in: table to drop */ { ulint err; /* There must be no open transactions on the table. */ ut_a(table->n_handles_opened == 0); err = ddl_drop_table(table->name, trx, FALSE); trx_commit(trx); return(err); } /*********************************************************************//** Build indexes on a table by reading a clustered index, creating a temporary file containing index entries, merge sorting these index entries and inserting sorted index entries to indexes. @return DB_SUCCESS or error code */ UNIV_INTERN ulint row_merge_build_indexes( /*====================*/ trx_t* trx, /*!< in: transaction */ dict_table_t* old_table, /*!< in: table where rows are read from */ dict_table_t* new_table, /*!< in: table where indexes are created; identical to old_table unless creating a PRIMARY KEY */ dict_index_t** indexes, /*!< in: indexes to be created */ ulint n_indexes, /*!< in: size of indexes[] */ table_handle_t table) /*!< in/out: Client table, for reporting erroneous key value if applicable */ { merge_file_t* merge_files; row_merge_block_t* block; ulint block_size; ulint i; ulint error; int tmpfd; ut_ad(trx); ut_ad(old_table); ut_ad(new_table); ut_ad(indexes); ut_ad(n_indexes); ut_a(trx->conc_state != TRX_NOT_STARTED); /* Allocate memory for merge file data structure and initialize fields */ merge_files = mem_alloc(n_indexes * sizeof *merge_files); block_size = 3 * sizeof *block; block = os_mem_alloc_large(&block_size); for (i = 0; i < n_indexes; i++) { row_merge_file_create(&merge_files[i]); } tmpfd = ib_create_tempfile("mrg"); /* Read clustered index of the table and create files for secondary index entries for merge sort */ error = row_merge_read_clustered_index( trx, table, old_table, new_table, indexes, merge_files, n_indexes, block); if (error != DB_SUCCESS) { goto func_exit; } /* Now we have files containing index entries ready for sorting and inserting. */ for (i = 0; i < n_indexes; i++) { error = row_merge_sort(trx, indexes[i], &merge_files[i], block, &tmpfd, table); if (error == DB_SUCCESS) { error = row_merge_insert_index_tuples( trx, indexes[i], new_table, dict_table_zip_size(old_table), merge_files[i].fd, block); } /* Close the temporary file to free up space. */ row_merge_file_destroy(&merge_files[i]); if (error != DB_SUCCESS) { trx->error_key_num = i; goto func_exit; } } func_exit: #ifdef __WIN__ _close(tmpfd); #else close(tmpfd); #endif for (i = 0; i < n_indexes; i++) { row_merge_file_destroy(&merge_files[i]); } mem_free(merge_files); os_mem_free_large(block, block_size); return(error); } haildb-2.3.2/row/row0row.c0000644000175000017500000005034011513177357016237 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2010, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file row/row0row.c General row routines Created 4/20/1996 Heikki Tuuri *******************************************************/ #include "row0row.h" #ifdef UNIV_NONINL #include "row0row.ic" #endif #include "data0type.h" #include "dict0dict.h" #include "btr0btr.h" #include "api0ucode.h" #include "mach0data.h" #include "trx0rseg.h" #include "trx0trx.h" #include "trx0roll.h" #include "trx0undo.h" #include "trx0purge.h" #include "trx0rec.h" #include "que0que.h" #include "row0ext.h" #include "row0upd.h" #include "rem0cmp.h" #include "read0read.h" #include "ut0mem.h" /*********************************************************************//** Gets the offset of trx id field, in bytes relative to the origin of a clustered index record. @return offset of DATA_TRX_ID */ UNIV_INTERN ulint row_get_trx_id_offset( /*==================*/ const rec_t* rec __attribute__((unused)), /*!< in: record */ dict_index_t* index, /*!< in: clustered index */ const ulint* offsets)/*!< in: rec_get_offsets(rec, index) */ { ulint pos; ulint offset; ulint len; ut_ad(dict_index_is_clust(index)); ut_ad(rec_offs_validate(rec, index, offsets)); pos = dict_index_get_sys_col_pos(index, DATA_TRX_ID); offset = rec_get_nth_field_offs(offsets, pos, &len); ut_ad(len == DATA_TRX_ID_LEN); return(offset); } /*****************************************************************//** When an insert or purge to a table is performed, this function builds the entry to be inserted into or purged from an index on the table. @return index entry which should be inserted or purged, or NULL if the externally stored columns in the clustered index record are unavailable and ext != NULL */ UNIV_INTERN dtuple_t* row_build_index_entry( /*==================*/ const dtuple_t* row, /*!< in: row which should be inserted or purged */ row_ext_t* ext, /*!< in: externally stored column prefixes, or NULL */ dict_index_t* index, /*!< in: index on the table */ mem_heap_t* heap) /*!< in: memory heap from which the memory for the index entry is allocated */ { dtuple_t* entry; ulint entry_len; ulint i; ut_ad(row && index && heap); ut_ad(dtuple_check_typed(row)); entry_len = dict_index_get_n_fields(index); entry = dtuple_create(heap, entry_len); if (UNIV_UNLIKELY(index->type & DICT_UNIVERSAL)) { dtuple_set_n_fields_cmp(entry, entry_len); /* There may only be externally stored columns in a clustered index B-tree of a user table. */ ut_a(!ext); } else { dtuple_set_n_fields_cmp( entry, dict_index_get_n_unique_in_tree(index)); if (dict_index_is_clust(index)) { /* Do not fetch externally stored columns to the clustered index. Such columns are handled at a higher level. */ ext = NULL; } } for (i = 0; i < entry_len; i++) { const dict_field_t* ind_field = dict_index_get_nth_field(index, i); const dict_col_t* col = ind_field->col; ulint col_no = dict_col_get_no(col); dfield_t* dfield = dtuple_get_nth_field(entry, i); const dfield_t* dfield2 = dtuple_get_nth_field(row, col_no); ulint len = dfield_get_len(dfield2); dfield_copy(dfield, dfield2); if (dfield_is_null(dfield)) { } else if (UNIV_LIKELY_NULL(ext)) { /* See if the column is stored externally. */ const byte* buf = row_ext_lookup(ext, col_no, &len); if (UNIV_LIKELY_NULL(buf)) { if (UNIV_UNLIKELY(buf == field_ref_zero)) { return(NULL); } dfield_set_data(dfield, buf, len); } } else if (dfield_is_ext(dfield)) { ut_a(len >= BTR_EXTERN_FIELD_REF_SIZE); len -= BTR_EXTERN_FIELD_REF_SIZE; ut_a(ind_field->prefix_len <= len || dict_index_is_clust(index)); } /* If a column prefix index, take only the prefix */ if (ind_field->prefix_len > 0 && !dfield_is_null(dfield)) { ut_ad(col->ord_part); len = dtype_get_at_most_n_mbchars( col->prtype, col->mbminlen, col->mbmaxlen, ind_field->prefix_len, len, dfield_get_data(dfield)); dfield_set_len(dfield, len); } } ut_ad(dtuple_check_typed(entry)); return(entry); } /*******************************************************************//** An inverse function to row_build_index_entry. Builds a row from a record in a clustered index. @return own: row built; see the NOTE below! */ UNIV_INTERN dtuple_t* row_build( /*======*/ ulint type, /*!< in: ROW_COPY_POINTERS or ROW_COPY_DATA; the latter copies also the data fields to heap while the first only places pointers to data fields on the index page, and thus is more efficient */ const dict_index_t* index, /*!< in: clustered index */ const rec_t* rec, /*!< in: record in the clustered index; NOTE: in the case ROW_COPY_POINTERS the data fields in the row will point directly into this record, therefore, the buffer page of this record must be at least s-latched and the latch held as long as the row dtuple is used! */ const ulint* offsets,/*!< in: rec_get_offsets(rec,index) or NULL, in which case this function will invoke rec_get_offsets() */ const dict_table_t* col_table, /*!< in: table, to check which externally stored columns occur in the ordering columns of an index, or NULL if index->table should be consulted instead */ row_ext_t** ext, /*!< out, own: cache of externally stored column prefixes, or NULL */ mem_heap_t* heap) /*!< in: memory heap from which the memory needed is allocated */ { dtuple_t* row; const dict_table_t* table; ulint n_fields; ulint n_ext_cols; ulint* ext_cols = NULL; /* remove warning */ ulint len; ulint row_len; byte* buf; ulint i; ulint j; mem_heap_t* tmp_heap = NULL; ulint offsets_[REC_OFFS_NORMAL_SIZE]; rec_offs_init(offsets_); ut_ad(index && rec && heap); ut_ad(dict_index_is_clust(index)); if (!offsets) { offsets = rec_get_offsets(rec, index, offsets_, ULINT_UNDEFINED, &tmp_heap); } else { ut_ad(rec_offs_validate(rec, index, offsets)); } if (type != ROW_COPY_POINTERS) { /* Take a copy of rec to heap */ buf = mem_heap_alloc(heap, rec_offs_size(offsets)); rec = rec_copy(buf, rec, offsets); /* Avoid a debug assertion in rec_offs_validate(). */ rec_offs_make_valid(rec, index, (ulint*) offsets); } table = index->table; row_len = dict_table_get_n_cols(table); row = dtuple_create(heap, row_len); dict_table_copy_types(row, table); dtuple_set_info_bits(row, rec_get_info_bits( rec, dict_table_is_comp(table))); n_fields = rec_offs_n_fields(offsets); n_ext_cols = rec_offs_n_extern(offsets); if (n_ext_cols) { ext_cols = mem_heap_alloc(heap, n_ext_cols * sizeof *ext_cols); } for (i = j = 0; i < n_fields; i++) { dict_field_t* ind_field = dict_index_get_nth_field(index, i); const dict_col_t* col = dict_field_get_col(ind_field); ulint col_no = dict_col_get_no(col); dfield_t* dfield = dtuple_get_nth_field(row, col_no); if (ind_field->prefix_len == 0) { const byte* field = rec_get_nth_field( rec, offsets, i, &len); dfield_set_data(dfield, field, len); } if (rec_offs_nth_extern(offsets, i)) { dfield_set_ext(dfield); if (UNIV_LIKELY_NULL(col_table)) { ut_a(col_no < dict_table_get_n_cols(col_table)); col = dict_table_get_nth_col( col_table, col_no); } if (col->ord_part) { /* We will have to fetch prefixes of externally stored columns that are referenced by column prefixes. */ ext_cols[j++] = col_no; } } } ut_ad(dtuple_check_typed(row)); if (j) { *ext = row_ext_create(j, ext_cols, row, dict_table_zip_size(index->table), heap); } else { *ext = NULL; } if (tmp_heap) { mem_heap_free(tmp_heap); } return(row); } /*******************************************************************//** Converts an index record to a typed data tuple. @return index entry built; does not set info_bits, and the data fields in the entry will point directly to rec */ UNIV_INTERN dtuple_t* row_rec_to_index_entry_low( /*=======================*/ const rec_t* rec, /*!< in: record in the index */ const dict_index_t* index, /*!< in: index */ const ulint* offsets,/*!< in: rec_get_offsets(rec, index) */ ulint* n_ext, /*!< out: number of externally stored columns */ mem_heap_t* heap) /*!< in: memory heap from which the memory needed is allocated */ { dtuple_t* entry; dfield_t* dfield; ulint i; const byte* field; ulint len; ulint rec_len; ut_ad(rec && heap && index); /* Because this function may be invoked by row0merge.c on a record whose header is in different format, the check rec_offs_validate(rec, index, offsets) must be avoided here. */ ut_ad(n_ext); *n_ext = 0; rec_len = rec_offs_n_fields(offsets); entry = dtuple_create(heap, rec_len); dtuple_set_n_fields_cmp(entry, dict_index_get_n_unique_in_tree(index)); ut_ad(rec_len == dict_index_get_n_fields(index)); dict_index_copy_types(entry, index, rec_len); for (i = 0; i < rec_len; i++) { dfield = dtuple_get_nth_field(entry, i); field = rec_get_nth_field(rec, offsets, i, &len); dfield_set_data(dfield, field, len); if (rec_offs_nth_extern(offsets, i)) { dfield_set_ext(dfield); (*n_ext)++; } } ut_ad(dtuple_check_typed(entry)); return(entry); } /*******************************************************************//** Converts an index record to a typed data tuple. NOTE that externally stored (often big) fields are NOT copied to heap. @return own: index entry built; see the NOTE below! */ UNIV_INTERN dtuple_t* row_rec_to_index_entry( /*===================*/ ulint type, /*!< in: ROW_COPY_DATA, or ROW_COPY_POINTERS: the former copies also the data fields to heap as the latter only places pointers to data fields on the index page */ const rec_t* rec, /*!< in: record in the index; NOTE: in the case ROW_COPY_POINTERS the data fields in the row will point directly into this record, therefore, the buffer page of this record must be at least s-latched and the latch held as long as the dtuple is used! */ const dict_index_t* index, /*!< in: index */ ulint* offsets,/*!< in/out: rec_get_offsets(rec) */ ulint* n_ext, /*!< out: number of externally stored columns */ mem_heap_t* heap) /*!< in: memory heap from which the memory needed is allocated */ { dtuple_t* entry; byte* buf; ut_ad(rec && heap && index); ut_ad(rec_offs_validate(rec, index, offsets)); if (type == ROW_COPY_DATA) { /* Take a copy of rec to heap */ buf = mem_heap_alloc(heap, rec_offs_size(offsets)); rec = rec_copy(buf, rec, offsets); /* Avoid a debug assertion in rec_offs_validate(). */ rec_offs_make_valid(rec, index, offsets); } entry = row_rec_to_index_entry_low(rec, index, offsets, n_ext, heap); dtuple_set_info_bits(entry, rec_get_info_bits(rec, rec_offs_comp(offsets))); return(entry); } /*******************************************************************//** Builds from a secondary index record a row reference with which we can search the clustered index record. @return own: row reference built; see the NOTE below! */ UNIV_INTERN dtuple_t* row_build_row_ref( /*==============*/ ulint type, /*!< in: ROW_COPY_DATA, or ROW_COPY_POINTERS: the former copies also the data fields to heap, whereas the latter only places pointers to data fields on the index page */ dict_index_t* index, /*!< in: secondary index */ const rec_t* rec, /*!< in: record in the index; NOTE: in the case ROW_COPY_POINTERS the data fields in the row will point directly into this record, therefore, the buffer page of this record must be at least s-latched and the latch held as long as the row reference is used! */ mem_heap_t* heap) /*!< in: memory heap from which the memory needed is allocated */ { dict_table_t* table; dict_index_t* clust_index; dfield_t* dfield; dtuple_t* ref; const byte* field; ulint len; ulint ref_len; ulint pos; byte* buf; ulint clust_col_prefix_len; ulint i; mem_heap_t* tmp_heap = NULL; ulint offsets_[REC_OFFS_NORMAL_SIZE]; ulint* offsets = offsets_; rec_offs_init(offsets_); ut_ad(index && rec && heap); ut_ad(!dict_index_is_clust(index)); offsets = rec_get_offsets(rec, index, offsets, ULINT_UNDEFINED, &tmp_heap); /* Secondary indexes must not contain externally stored columns. */ ut_ad(!rec_offs_any_extern(offsets)); if (type == ROW_COPY_DATA) { /* Take a copy of rec to heap */ buf = mem_heap_alloc(heap, rec_offs_size(offsets)); rec = rec_copy(buf, rec, offsets); /* Avoid a debug assertion in rec_offs_validate(). */ rec_offs_make_valid(rec, index, offsets); } table = index->table; clust_index = dict_table_get_first_index(table); ref_len = dict_index_get_n_unique(clust_index); ref = dtuple_create(heap, ref_len); dict_index_copy_types(ref, clust_index, ref_len); for (i = 0; i < ref_len; i++) { dfield = dtuple_get_nth_field(ref, i); pos = dict_index_get_nth_field_pos(index, clust_index, i); ut_a(pos != ULINT_UNDEFINED); field = rec_get_nth_field(rec, offsets, pos, &len); dfield_set_data(dfield, field, len); /* If the primary key contains a column prefix, then the secondary index may contain a longer prefix of the same column, or the full column, and we must adjust the length accordingly. */ clust_col_prefix_len = dict_index_get_nth_field( clust_index, i)->prefix_len; if (clust_col_prefix_len > 0) { if (len != UNIV_SQL_NULL) { const dtype_t* dtype = dfield_get_type(dfield); dfield_set_len(dfield, dtype_get_at_most_n_mbchars( dtype->prtype, dtype->mbminlen, dtype->mbmaxlen, clust_col_prefix_len, len, (char*) field)); } } } ut_ad(dtuple_check_typed(ref)); if (tmp_heap) { mem_heap_free(tmp_heap); } return(ref); } /*******************************************************************//** Builds from a secondary index record a row reference with which we can search the clustered index record. */ UNIV_INTERN void row_build_row_ref_in_tuple( /*=======================*/ dtuple_t* ref, /*!< in/out: row reference built; see the NOTE below! */ const rec_t* rec, /*!< in: record in the index; NOTE: the data fields in ref will point directly into this record, therefore, the buffer page of this record must be at least s-latched and the latch held as long as the row reference is used! */ const dict_index_t* index, /*!< in: secondary index */ ulint* offsets,/*!< in: rec_get_offsets(rec, index) or NULL */ trx_t* trx) /*!< in: transaction */ { const dict_index_t* clust_index; dfield_t* dfield; const byte* field; ulint len; ulint ref_len; ulint pos; ulint clust_col_prefix_len; ulint i; mem_heap_t* heap = NULL; ulint offsets_[REC_OFFS_NORMAL_SIZE]; rec_offs_init(offsets_); ut_a(ref); ut_a(index); ut_a(rec); ut_ad(!dict_index_is_clust(index)); if (UNIV_UNLIKELY(!index->table)) { ib_logger(ib_stream, "InnoDB: table "); notfound: ut_print_name(ib_stream, trx, TRUE, index->table_name); ib_logger(ib_stream, " for index "); ut_print_name(ib_stream, trx, FALSE, index->name); ib_logger(ib_stream, " not found\n"); ut_error; } clust_index = dict_table_get_first_index(index->table); if (UNIV_UNLIKELY(!clust_index)) { ib_logger(ib_stream, "InnoDB: clust index for table "); goto notfound; } if (!offsets) { offsets = rec_get_offsets(rec, index, offsets_, ULINT_UNDEFINED, &heap); } else { ut_ad(rec_offs_validate(rec, index, offsets)); } /* Secondary indexes must not contain externally stored columns. */ ut_ad(!rec_offs_any_extern(offsets)); ref_len = dict_index_get_n_unique(clust_index); ut_ad(ref_len == dtuple_get_n_fields(ref)); dict_index_copy_types(ref, clust_index, ref_len); for (i = 0; i < ref_len; i++) { dfield = dtuple_get_nth_field(ref, i); pos = dict_index_get_nth_field_pos(index, clust_index, i); ut_a(pos != ULINT_UNDEFINED); field = rec_get_nth_field(rec, offsets, pos, &len); dfield_set_data(dfield, field, len); /* If the primary key contains a column prefix, then the secondary index may contain a longer prefix of the same column, or the full column, and we must adjust the length accordingly. */ clust_col_prefix_len = dict_index_get_nth_field( clust_index, i)->prefix_len; if (clust_col_prefix_len > 0) { if (len != UNIV_SQL_NULL) { const dtype_t* dtype = dfield_get_type(dfield); dfield_set_len(dfield, dtype_get_at_most_n_mbchars( dtype->prtype, dtype->mbminlen, dtype->mbmaxlen, clust_col_prefix_len, len, (char*) field)); } } } ut_ad(dtuple_check_typed(ref)); if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } } /***************************************************************//** Searches the clustered index record for a row, if we have the row reference. @return TRUE if found */ UNIV_INTERN ibool row_search_on_row_ref( /*==================*/ btr_pcur_t* pcur, /*!< out: persistent cursor, which must be closed by the caller */ ulint mode, /*!< in: BTR_MODIFY_LEAF, ... */ const dict_table_t* table, /*!< in: table */ const dtuple_t* ref, /*!< in: row reference */ mtr_t* mtr) /*!< in/out: mtr */ { ulint low_match; rec_t* rec; dict_index_t* index; ut_ad(dtuple_check_typed(ref)); index = dict_table_get_first_index(table); ut_a(dtuple_get_n_fields(ref) == dict_index_get_n_unique(index)); btr_pcur_open(index, ref, PAGE_CUR_LE, mode, pcur, mtr); low_match = btr_pcur_get_low_match(pcur); rec = btr_pcur_get_rec(pcur); if (page_rec_is_infimum(rec)) { return(FALSE); } if (low_match != dtuple_get_n_fields(ref)) { return(FALSE); } return(TRUE); } /*********************************************************************//** Fetches the clustered index record for a secondary index record. The latches on the secondary index record are preserved. @return record or NULL, if no record found */ UNIV_INTERN rec_t* row_get_clust_rec( /*==============*/ ulint mode, /*!< in: BTR_MODIFY_LEAF, ... */ const rec_t* rec, /*!< in: record in a secondary index */ dict_index_t* index, /*!< in: secondary index */ dict_index_t** clust_index,/*!< out: clustered index */ mtr_t* mtr) /*!< in: mtr */ { mem_heap_t* heap; dtuple_t* ref; dict_table_t* table; btr_pcur_t pcur; ibool found; rec_t* clust_rec; ut_ad(!dict_index_is_clust(index)); table = index->table; heap = mem_heap_create(256); ref = row_build_row_ref(ROW_COPY_POINTERS, index, rec, heap); found = row_search_on_row_ref(&pcur, mode, table, ref, mtr); clust_rec = found ? btr_pcur_get_rec(&pcur) : NULL; mem_heap_free(heap); btr_pcur_close(&pcur); *clust_index = dict_table_get_first_index(table); return(clust_rec); } /***************************************************************//** Searches an index record. @return TRUE if found */ UNIV_INTERN ibool row_search_index_entry( /*===================*/ dict_index_t* index, /*!< in: index */ const dtuple_t* entry, /*!< in: index entry */ ulint mode, /*!< in: BTR_MODIFY_LEAF, ... */ btr_pcur_t* pcur, /*!< in/out: persistent cursor, which must be closed by the caller */ mtr_t* mtr) /*!< in: mtr */ { ulint n_fields; ulint low_match; rec_t* rec; ut_ad(dtuple_check_typed(entry)); btr_pcur_open(index, entry, PAGE_CUR_LE, mode, pcur, mtr); low_match = btr_pcur_get_low_match(pcur); rec = btr_pcur_get_rec(pcur); n_fields = dtuple_get_n_fields(entry); return(!page_rec_is_infimum(rec) && low_match == n_fields); } haildb-2.3.2/row/row0prebuilt.c0000644000175000017500000001427511513177357017265 0ustar00pcrewspcrews00000000000000 /***************************************************************************** Copyright (c) 1997, 2010, Innobase Oy. All Rights Reserved. Copyright (c) 2008, Google Inc. Portions of this file contain modifications contributed and copyrighted by Google, Inc. Those modifications are gratefully acknowledged and are described briefly in the InnoDB documentation. The contributions by Google are incorporated with their permission, and subject to the conditions contained in the file COPYING.Google. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /******************************************************* Row select prebuilt structure function. Created 02/03/2009 Sunny Bains *******************************************************/ #include "row0ins.h" #include "row0prebuilt.h" #include "pars0pars.h" #include "que0que.h" #include "row0merge.h" /************************************************************************ Create a prebuilt struct for a user table handle. @return own: a prebuilt struct */ UNIV_INTERN row_prebuilt_t* row_prebuilt_create( /*================*/ dict_table_t* table) /*!< in: Innobase table handle */ { ulint sz; mem_heap_t* heap; dtuple_t* ref; ulint ref_len; row_prebuilt_t* prebuilt; ib_row_cache_t* row_cache; dict_index_t* clust_index; heap = mem_heap_create(128); prebuilt = mem_heap_zalloc(heap, sizeof(row_prebuilt_t)); prebuilt->magic_n = ROW_PREBUILT_ALLOCATED; prebuilt->magic_n2 = ROW_PREBUILT_ALLOCATED; prebuilt->heap = heap; prebuilt->table = table; prebuilt->sql_stat_start = TRUE; prebuilt->pcur = btr_pcur_create(); prebuilt->clust_pcur = btr_pcur_create(); prebuilt->select_lock_type = LOCK_NONE; prebuilt->search_tuple = dtuple_create( heap, 2 * dict_table_get_n_cols(table)); clust_index = dict_table_get_first_index(table); /* Make sure that search_tuple is long enough for clustered index */ ut_a(2 * dict_table_get_n_cols(table) >= clust_index->n_fields); ref_len = dict_index_get_n_unique(clust_index); ref = dtuple_create(heap, ref_len); dict_index_copy_types(ref, clust_index, ref_len); prebuilt->clust_ref = ref; row_cache = &prebuilt->row_cache; row_cache->n_max = FETCH_CACHE_SIZE; row_cache->n_size = row_cache->n_max; sz = sizeof(*row_cache->ptr) * row_cache->n_max; row_cache->heap = mem_heap_create(sz); row_cache->ptr= mem_heap_zalloc(row_cache->heap, sz); return(prebuilt); } /************************************************************************ Free a prebuilt struct for a user table handle. */ UNIV_INTERN void row_prebuilt_free( /*==============*/ row_prebuilt_t* prebuilt, /*!< in, own: prebuilt struct */ ibool dict_locked) /*!< in: TRUE if dict was locked */ { ulint i; ib_row_cache_t* row_cache; if (prebuilt->magic_n != ROW_PREBUILT_ALLOCATED || prebuilt->magic_n2 != ROW_PREBUILT_ALLOCATED) { ib_logger(ib_stream, "InnoDB: Error: trying to free a corrupt\n" "InnoDB: table handle. Magic n %lu," " magic n2 %lu, table name", (ulong) prebuilt->magic_n, (ulong) prebuilt->magic_n2); ut_print_name(ib_stream, NULL, TRUE, prebuilt->table->name); ib_logger(ib_stream, "\n"); ut_error; } prebuilt->magic_n = ROW_PREBUILT_FREED; prebuilt->magic_n2 = ROW_PREBUILT_FREED; btr_pcur_free(prebuilt->pcur); btr_pcur_free(prebuilt->clust_pcur); if (prebuilt->sel_graph) { que_graph_free_recursive(prebuilt->sel_graph); } if (prebuilt->old_vers_heap) { mem_heap_free(prebuilt->old_vers_heap); } row_cache = &prebuilt->row_cache; for (i = 0; i < row_cache->n_max; i++) { ib_cached_row_t* row = &row_cache->ptr[i]; if (row->ptr != NULL) { mem_free(row->ptr); } } mem_heap_free(row_cache->heap); if (prebuilt->table != NULL) { dict_table_decrement_handle_count(prebuilt->table, dict_locked); } mem_heap_free(prebuilt->heap); } /************************************************************************ Reset a prebuilt struct for a user table handle. */ UNIV_INTERN void row_prebuilt_reset( /*===============*/ row_prebuilt_t* prebuilt) /*!< in/out: prebuilt struct */ { ut_a(prebuilt->magic_n == ROW_PREBUILT_ALLOCATED); ut_a(prebuilt->magic_n2 == ROW_PREBUILT_ALLOCATED); prebuilt->sql_stat_start = TRUE; prebuilt->client_has_locked = FALSE; prebuilt->need_to_access_clustered = FALSE; prebuilt->index_usable = FALSE; prebuilt->simple_select = FALSE; prebuilt->select_lock_type = LOCK_NONE; if (prebuilt->old_vers_heap) { mem_heap_free(prebuilt->old_vers_heap); prebuilt->old_vers_heap = NULL; } prebuilt->trx = NULL; if (prebuilt->sel_graph) { prebuilt->sel_graph->trx = NULL; } } /************************************************************************* Updates the transaction pointers in query graphs stored in the prebuilt struct. */ UNIV_INTERN void row_prebuilt_update_trx( /*====================*/ row_prebuilt_t* prebuilt, /*!< in/out: prebuilt struct handle */ trx_t* trx) /*!< in: transaction handle */ { ut_a(trx != NULL); if (trx->magic_n != TRX_MAGIC_N) { ib_logger(ib_stream, "InnoDB: Error: trying to use a corrupt\n" "InnoDB: trx handle. Magic n %lu\n", (ulong) trx->magic_n); ut_error; } else if (prebuilt->magic_n != ROW_PREBUILT_ALLOCATED) { ib_logger(ib_stream, "InnoDB: Error: trying to use a corrupt\n" "InnoDB: table handle. Magic n %lu, table name", (ulong) prebuilt->magic_n); ut_print_name(ib_stream, NULL, TRUE, prebuilt->table->name); ib_logger(ib_stream, "\n"); ut_error; } else { prebuilt->trx = trx; if (prebuilt->sel_graph) { prebuilt->sel_graph->trx = trx; } prebuilt->index_usable = row_merge_is_index_usable( prebuilt->trx, prebuilt->index); } } haildb-2.3.2/row/row0undo.c0000644000175000017500000002655411513177357016407 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1997, 2010, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file row/row0undo.c Row undo Created 1/8/1997 Heikki Tuuri *******************************************************/ #include "row0undo.h" #ifdef UNIV_NONINL #include "row0undo.ic" #endif #include "fsp0fsp.h" #include "mach0data.h" #include "trx0rseg.h" #include "trx0trx.h" #include "trx0roll.h" #include "trx0undo.h" #include "trx0purge.h" #include "trx0rec.h" #include "que0que.h" #include "row0row.h" #include "row0uins.h" #include "row0umod.h" #include "row0upd.h" #include "srv0srv.h" /* How to undo row operations? (1) For an insert, we have stored a prefix of the clustered index record in the undo log. Using it, we look for the clustered record, and using that we look for the records in the secondary indexes. The insert operation may have been left incomplete, if the database crashed, for example. We may have look at the trx id and roll ptr to make sure the record in the clustered index is really the one for which the undo log record was written. We can use the framework we get from the original insert op. (2) Delete marking: We can use the framework we get from the original delete mark op. We only have to check the trx id. (3) Update: This may be the most complicated. We have to use the framework we get from the original update op. What if the same trx repeatedly deletes and inserts an identical row. Then the row id changes and also roll ptr. What if the row id was not part of the ordering fields in the clustered index? Maybe we have to write it to undo log. Well, maybe not, because if we order the row id and trx id in descending order, then the only undeleted copy is the first in the index. Our searches in row operations always position the cursor before the first record in the result set. But, if there is no key defined for a table, then it would be desirable that row id is in ascending order. So, lets store row id in descending order only if it is not an ordering field in the clustered index. NOTE: Deletes and inserts may lead to situation where there are identical records in a secondary index. Is that a problem in the B-tree? Yes. Also updates can lead to this, unless trx id and roll ptr are included in ord fields. (1) Fix in clustered indexes: include row id, trx id, and roll ptr in node pointers of B-tree. (2) Fix in secondary indexes: include all fields in node pointers, and if an entry is inserted, check if it is equal to the right neighbor, in which case update the right neighbor: the neighbor must be delete marked, set it unmarked and write the trx id of the current transaction. What if the same trx repeatedly updates the same row, updating a secondary index field or not? Updating a clustered index ordering field? (1) If it does not update the secondary index and not the clustered index ord field. Then the secondary index record stays unchanged, but the trx id in the secondary index record may be smaller than in the clustered index record. This is no problem? (2) If it updates secondary index ord field but not clustered: then in secondary index there are delete marked records, which differ in an ord field. No problem. (3) Updates clustered ord field but not secondary, and secondary index is unique. Then the record in secondary index is just updated at the clustered ord field. (4) Problem with duplicate records: Fix 1: Add a trx op no field to all indexes. A problem: if a trx with a bigger trx id has inserted and delete marked a similar row, our trx inserts again a similar row, and a trx with an even bigger id delete marks it. Then the position of the row should change in the index if the trx id affects the alphabetical ordering. Fix 2: If an insert encounters a similar row marked deleted, we turn the insert into an 'update' of the row marked deleted. Then we must write undo info on the update. A problem: what if a purge operation tries to remove the delete marked row? We can think of the database row versions as a linked list which starts from the record in the clustered index, and is linked by roll ptrs through undo logs. The secondary index records are references which tell what kinds of records can be found in this linked list for a record in the clustered index. How to do the purge? A record can be removed from the clustered index if its linked list becomes empty, i.e., the row has been marked deleted and its roll ptr points to the record in the undo log we are going through, doing the purge. Similarly, during a rollback, a record can be removed if the stored roll ptr in the undo log points to a trx already (being) purged, or if the roll ptr is NULL, i.e., it was a fresh insert. */ /********************************************************************//** Creates a row undo node to a query graph. @return own: undo node */ UNIV_INTERN undo_node_t* row_undo_node_create( /*=================*/ trx_t* trx, /*!< in: transaction */ que_thr_t* parent, /*!< in: parent node, i.e., a thr node */ mem_heap_t* heap) /*!< in: memory heap where created */ { undo_node_t* undo; ut_ad(trx && parent && heap); undo = mem_heap_alloc(heap, sizeof(undo_node_t)); undo->common.type = QUE_NODE_UNDO; undo->common.parent = parent; undo->state = UNDO_NODE_FETCH_NEXT; undo->trx = trx; btr_pcur_init(&(undo->pcur)); undo->heap = mem_heap_create(256); return(undo); } /***********************************************************//** Looks for the clustered index record when node has the row reference. The pcur in node is used in the search. If found, stores the row to node, and stores the position of pcur, and detaches it. The pcur must be closed by the caller in any case. @return TRUE if found; NOTE the node->pcur must be closed by the caller, regardless of the return value */ UNIV_INTERN ibool row_undo_search_clust_to_pcur( /*==========================*/ undo_node_t* node) /*!< in: row undo node */ { dict_index_t* clust_index; ibool found; mtr_t mtr; ibool ret; rec_t* rec; mem_heap_t* heap = NULL; ulint offsets_[REC_OFFS_NORMAL_SIZE]; ulint* offsets = offsets_; rec_offs_init(offsets_); mtr_start(&mtr); clust_index = dict_table_get_first_index(node->table); found = row_search_on_row_ref(&(node->pcur), BTR_MODIFY_LEAF, node->table, node->ref, &mtr); rec = btr_pcur_get_rec(&(node->pcur)); offsets = rec_get_offsets(rec, clust_index, offsets, ULINT_UNDEFINED, &heap); if (!found || 0 != ut_dulint_cmp(node->roll_ptr, row_get_rec_roll_ptr(rec, clust_index, offsets))) { /* We must remove the reservation on the undo log record BEFORE releasing the latch on the clustered index page: this is to make sure that some thread will eventually undo the modification corresponding to node->roll_ptr. */ /* ib_logger(ib_stream, "--------------------undoing a previous version\n"); */ ret = FALSE; } else { node->row = row_build(ROW_COPY_DATA, clust_index, rec, offsets, NULL, &node->ext, node->heap); if (node->update) { node->undo_row = dtuple_copy(node->row, node->heap); row_upd_replace(node->undo_row, &node->undo_ext, clust_index, node->update, node->heap); } else { node->undo_row = NULL; node->undo_ext = NULL; } btr_pcur_store_position(&(node->pcur), &mtr); ret = TRUE; } btr_pcur_commit_specify_mtr(&(node->pcur), &mtr); if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } return(ret); } /***********************************************************//** Fetches an undo log record and does the undo for the recorded operation. If none left, or a partial rollback completed, returns control to the parent node, which is always a query thread node. @return DB_SUCCESS if operation successfully completed, else error code */ static ulint row_undo( /*=====*/ undo_node_t* node, /*!< in: row undo node */ que_thr_t* thr) /*!< in: query thread */ { ulint err; trx_t* trx; roll_ptr_t roll_ptr; ibool locked_data_dict; ut_ad(node && thr); trx = node->trx; if (node->state == UNDO_NODE_FETCH_NEXT) { node->undo_rec = trx_roll_pop_top_rec_of_trx(trx, trx->roll_limit, &roll_ptr, node->heap); if (!node->undo_rec) { /* Rollback completed for this query thread */ thr->run_node = que_node_get_parent(node); return(DB_SUCCESS); } node->roll_ptr = roll_ptr; node->undo_no = trx_undo_rec_get_undo_no(node->undo_rec); if (trx_undo_roll_ptr_is_insert(roll_ptr)) { node->state = UNDO_NODE_INSERT; } else { node->state = UNDO_NODE_MODIFY; } } else if (node->state == UNDO_NODE_PREV_VERS) { /* Undo should be done to the same clustered index record again in this same rollback, restoring the previous version */ roll_ptr = node->new_roll_ptr; node->undo_rec = trx_undo_get_undo_rec_low(roll_ptr, node->heap); node->roll_ptr = roll_ptr; node->undo_no = trx_undo_rec_get_undo_no(node->undo_rec); if (trx_undo_roll_ptr_is_insert(roll_ptr)) { node->state = UNDO_NODE_INSERT; } else { node->state = UNDO_NODE_MODIFY; } } /* Prevent DROP TABLE etc. while we are rolling back this row. If we are doing a TABLE CREATE or some other dictionary operation, then we already have dict_operation_lock locked in x-mode. Do not try to lock again, because that would cause a hang. */ locked_data_dict = (trx->dict_operation_lock_mode == 0); if (locked_data_dict) { dict_lock_data_dictionary(trx); ut_a(trx->dict_operation_lock_mode != 0); } if (node->state == UNDO_NODE_INSERT) { err = row_undo_ins(node); node->state = UNDO_NODE_FETCH_NEXT; } else { ut_ad(node->state == UNDO_NODE_MODIFY); err = row_undo_mod(node, thr); } if (locked_data_dict) { dict_unlock_data_dictionary(trx); } /* Do some cleanup */ btr_pcur_close(&(node->pcur)); mem_heap_empty(node->heap); thr->run_node = node; return(err); } /***********************************************************//** Undoes a row operation in a table. This is a high-level function used in SQL execution graphs. @return query thread to run next or NULL */ UNIV_INTERN que_thr_t* row_undo_step( /*==========*/ que_thr_t* thr) /*!< in: query thread */ { ulint err; undo_node_t* node; trx_t* trx; ut_ad(thr); srv_activity_count++; trx = thr_get_trx(thr); node = thr->run_node; ut_ad(que_node_get_type(node) == QUE_NODE_UNDO); err = row_undo(node, thr); trx->error_state = err; if (err != DB_SUCCESS) { /* SQL error detected */ ib_logger(ib_stream, "InnoDB: Fatal error %lu in rollback.\n", (ulong) err); if (err == DB_OUT_OF_FILE_SPACE) { srv_panic(DB_OUT_OF_FILE_SPACE, "InnoDB: Error 13 means out of tablespace.\n" "InnoDB: Consider increasing" " your tablespace.\n"); } ut_error; return(NULL); } return(thr); } haildb-2.3.2/row/row0ext.c0000644000175000017500000000747511513177357016243 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 2006, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file row/row0ext.c Caching of externally stored column prefixes Created September 2006 Marko Makela *******************************************************/ #include "row0ext.h" #ifdef UNIV_NONINL #include "row0ext.ic" #endif #include "btr0cur.h" /********************************************************************//** Fills the column prefix cache of an externally stored column. */ static void row_ext_cache_fill( /*===============*/ row_ext_t* ext, /*!< in/out: column prefix cache */ ulint i, /*!< in: index of ext->ext[] */ ulint zip_size,/*!< compressed page size in bytes, or 0 */ const dfield_t* dfield) /*!< in: data field */ { const byte* field = dfield_get_data(dfield); ulint f_len = dfield_get_len(dfield); byte* buf = ext->buf + i * REC_MAX_INDEX_COL_LEN; ut_ad(i < ext->n_ext); ut_ad(dfield_is_ext(dfield)); ut_a(f_len >= BTR_EXTERN_FIELD_REF_SIZE); if (UNIV_UNLIKELY(!memcmp(field_ref_zero, field + f_len - BTR_EXTERN_FIELD_REF_SIZE, BTR_EXTERN_FIELD_REF_SIZE))) { /* The BLOB pointer is not set: we cannot fetch it */ ext->len[i] = 0; } else { /* Fetch at most REC_MAX_INDEX_COL_LEN of the column. The column should be non-empty. However, trx_rollback_or_clean_all_recovered() may try to access a half-deleted BLOB if the server previously crashed during the execution of btr_free_externally_stored_field(). */ ext->len[i] = btr_copy_externally_stored_field_prefix( buf, REC_MAX_INDEX_COL_LEN, zip_size, field, f_len); } } /********************************************************************//** Creates a cache of column prefixes of externally stored columns. @return own: column prefix cache */ UNIV_INTERN row_ext_t* row_ext_create( /*===========*/ ulint n_ext, /*!< in: number of externally stored columns */ const ulint* ext, /*!< in: col_no's of externally stored columns in the InnoDB table object, as reported by dict_col_get_no(); NOT relative to the records in the clustered index */ const dtuple_t* tuple, /*!< in: data tuple containing the field references of the externally stored columns; must be indexed by col_no; the clustered index record must be covered by a lock or a page latch to prevent deletion (rollback or purge). */ ulint zip_size,/*!< compressed page size in bytes, or 0 */ mem_heap_t* heap) /*!< in: heap where created */ { ulint i; row_ext_t* ret = mem_heap_alloc(heap, (sizeof *ret) + (n_ext - 1) * sizeof ret->len); ut_ad(ut_is_2pow(zip_size)); ut_ad(zip_size <= UNIV_PAGE_SIZE); ret->n_ext = n_ext; ret->ext = ext; ret->buf = mem_heap_alloc(heap, n_ext * REC_MAX_INDEX_COL_LEN); #ifdef UNIV_DEBUG memset(ret->buf, 0xaa, n_ext * REC_MAX_INDEX_COL_LEN); UNIV_MEM_ALLOC(ret->buf, n_ext * REC_MAX_INDEX_COL_LEN); #endif /* Fetch the BLOB prefixes */ for (i = 0; i < n_ext; i++) { const dfield_t* dfield; dfield = dtuple_get_nth_field(tuple, ext[i]); row_ext_cache_fill(ret, i, zip_size, dfield); } return(ret); } haildb-2.3.2/row/row0upd.c0000644000175000017500000016125211513177357016225 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2010, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file row/row0upd.c Update of a row Created 12/27/1996 Heikki Tuuri *******************************************************/ #include "row0upd.h" #ifdef UNIV_NONINL #include "row0upd.ic" #endif #include "dict0dict.h" #include "trx0undo.h" #include "rem0rec.h" #ifndef UNIV_HOTBACKUP #include "dict0boot.h" #include "dict0crea.h" #include "mach0data.h" #include "btr0btr.h" #include "btr0cur.h" #include "que0que.h" #include "row0ext.h" #include "row0ins.h" #include "row0sel.h" #include "row0row.h" #include "rem0cmp.h" #include "lock0lock.h" #include "log0log.h" #include "pars0sym.h" #include "eval0eval.h" #include "buf0lru.h" /* What kind of latch and lock can we assume when the control comes to ------------------------------------------------------------------- an update node? -------------- Efficiency of massive updates would require keeping an x-latch on a clustered index page through many updates, and not setting an explicit x-lock on clustered index records, as they anyway will get an implicit x-lock when they are updated. A problem is that the read nodes in the graph should know that they must keep the latch when passing the control up to the update node, and not set any record lock on the record which will be updated. Another problem occurs if the execution is stopped, as the kernel switches to another query thread, or the transaction must wait for a lock. Then we should be able to release the latch and, maybe, acquire an explicit x-lock on the record. Because this seems too complicated, we conclude that the less efficient solution of releasing all the latches when the control is transferred to another node, and acquiring explicit x-locks, is better. */ /* How is a delete performed? If there is a delete without an explicit cursor, i.e., a searched delete, there are at least two different situations: the implicit select cursor may run on (1) the clustered index or on (2) a secondary index. The delete is performed by setting the delete bit in the record and substituting the id of the deleting transaction for the original trx id, and substituting a new roll ptr for previous roll ptr. The old trx id and roll ptr are saved in the undo log record. Thus, no physical changes occur in the index tree structure at the time of the delete. Only when the undo log is purged, the index records will be physically deleted from the index trees. The query graph executing a searched delete would consist of a delete node which has as a subtree a select subgraph. The select subgraph should return a (persistent) cursor in the clustered index, placed on page which is x-latched. The delete node should look for all secondary index records for this clustered index entry and mark them as deleted. When is the x-latch freed? The most efficient way for performing a searched delete is obviously to keep the x-latch for several steps of query graph execution. */ /***********************************************************//** Checks if an update vector changes some of the first ordering fields of an index record. This is only used in foreign key checks and we can assume that index does not contain column prefixes. @return TRUE if changes */ static ibool row_upd_changes_first_fields_binary( /*================================*/ dtuple_t* entry, /*!< in: old value of index entry */ dict_index_t* index, /*!< in: index of entry */ const upd_t* update, /*!< in: update vector for the row */ ulint n); /*!< in: how many first fields to check */ /*********************************************************************//** Checks if index currently is mentioned as a referenced index in a foreign key constraint. NOTE that since we do not hold dict_operation_lock when leaving the function, it may be that the referencing table has been dropped when we leave this function: this function is only for heuristic use! @return TRUE if referenced */ static ibool row_upd_index_is_referenced( /*========================*/ dict_index_t* index, /*!< in: index */ trx_t* trx) /*!< in: transaction */ { dict_table_t* table = index->table; dict_foreign_t* foreign; ibool froze_data_dict = FALSE; ibool is_referenced = FALSE; if (!UT_LIST_GET_FIRST(table->referenced_list)) { return(FALSE); } if (trx->dict_operation_lock_mode == 0) { dict_freeze_data_dictionary(trx); froze_data_dict = TRUE; } foreign = UT_LIST_GET_FIRST(table->referenced_list); while (foreign) { if (foreign->referenced_index == index) { is_referenced = TRUE; goto func_exit; } foreign = UT_LIST_GET_NEXT(referenced_list, foreign); } func_exit: if (froze_data_dict) { dict_unfreeze_data_dictionary(trx); } return(is_referenced); } /*********************************************************************//** Checks if possible foreign key constraints hold after a delete of the record under pcur. NOTE that this function will temporarily commit mtr and lose the pcur position! @return DB_SUCCESS or an error code */ static ulint row_upd_check_references_constraints( /*=================================*/ upd_node_t* node, /*!< in: row update node */ btr_pcur_t* pcur, /*!< in: cursor positioned on a record; NOTE: the cursor position is lost in this function! */ dict_table_t* table, /*!< in: table in question */ dict_index_t* index, /*!< in: index of the cursor */ ulint* offsets,/*!< in/out: rec_get_offsets(pcur.rec, index) */ que_thr_t* thr, /*!< in: query thread */ mtr_t* mtr) /*!< in: mtr */ { dict_foreign_t* foreign; mem_heap_t* heap; dtuple_t* entry; trx_t* trx; const rec_t* rec; ulint n_ext; ulint err; ibool got_s_lock = FALSE; if (UT_LIST_GET_FIRST(table->referenced_list) == NULL) { return(DB_SUCCESS); } trx = thr_get_trx(thr); rec = btr_pcur_get_rec(pcur); ut_ad(rec_offs_validate(rec, index, offsets)); heap = mem_heap_create(500); entry = row_rec_to_index_entry(ROW_COPY_DATA, rec, index, offsets, &n_ext, heap); mtr_commit(mtr); mtr_start(mtr); if (trx->dict_operation_lock_mode == 0) { got_s_lock = TRUE; dict_freeze_data_dictionary(trx); } foreign = UT_LIST_GET_FIRST(table->referenced_list); while (foreign) { /* Note that we may have an update which updates the index record, but does NOT update the first fields which are referenced in a foreign key constraint. Then the update does NOT break the constraint. */ if (foreign->referenced_index == index && (node->is_delete || row_upd_changes_first_fields_binary( entry, index, node->update, foreign->n_fields))) { if (foreign->foreign_table == NULL) { dict_table_get(foreign->foreign_table_name, FALSE); } if (foreign->foreign_table) { mutex_enter(&(dict_sys->mutex)); (foreign->foreign_table ->n_foreign_key_checks_running)++; mutex_exit(&(dict_sys->mutex)); } /* NOTE that if the thread ends up waiting for a lock we will release dict_operation_lock temporarily! But the counter on the table protects 'foreign' from being dropped while the check is running. */ err = row_ins_check_foreign_constraint( FALSE, foreign, table, entry, thr); if (foreign->foreign_table) { mutex_enter(&(dict_sys->mutex)); ut_a(foreign->foreign_table ->n_foreign_key_checks_running > 0); (foreign->foreign_table ->n_foreign_key_checks_running)--; mutex_exit(&(dict_sys->mutex)); } if (err != DB_SUCCESS) { goto func_exit; } } foreign = UT_LIST_GET_NEXT(referenced_list, foreign); } err = DB_SUCCESS; func_exit: if (got_s_lock) { dict_unfreeze_data_dictionary(trx); } mem_heap_free(heap); return(err); } /*********************************************************************//** Creates an update node for a query graph. @return own: update node */ UNIV_INTERN upd_node_t* upd_node_create( /*============*/ mem_heap_t* heap) /*!< in: mem heap where created */ { upd_node_t* node; node = mem_heap_alloc(heap, sizeof(upd_node_t)); #ifdef UNIV_DEBUG memset(node, '\0', sizeof(*node)); #endif node->common.type = QUE_NODE_UPDATE; node->state = UPD_NODE_UPDATE_CLUSTERED; node->in_client_interface = FALSE; node->row = NULL; node->ext = NULL; node->upd_row = NULL; node->upd_ext = NULL; node->index = NULL; node->update = NULL; node->foreign = NULL; node->cascade_heap = NULL; node->cascade_node = NULL; node->select = NULL; node->heap = mem_heap_create(128); node->magic_n = UPD_NODE_MAGIC_N; node->cmpl_info = 0; return(node); } #endif /* !UNIV_HOTBACKUP */ /*********************************************************************//** Updates the trx id and roll ptr field in a clustered index record in database recovery. */ UNIV_INTERN void row_upd_rec_sys_fields_in_recovery( /*===============================*/ rec_t* rec, /*!< in/out: record */ page_zip_des_t* page_zip,/*!< in/out: compressed page, or NULL */ const ulint* offsets,/*!< in: array returned by rec_get_offsets() */ ulint pos, /*!< in: TRX_ID position in rec */ trx_id_t trx_id, /*!< in: transaction id */ roll_ptr_t roll_ptr)/*!< in: roll ptr of the undo log record */ { ut_ad(rec_offs_validate(rec, NULL, offsets)); if (UNIV_LIKELY_NULL(page_zip)) { page_zip_write_trx_id_and_roll_ptr( page_zip, rec, offsets, pos, trx_id, roll_ptr); } else { byte* field; ulint len; field = rec_get_nth_field(rec, offsets, pos, &len); ut_ad(len == DATA_TRX_ID_LEN); #if DATA_TRX_ID + 1 != DATA_ROLL_PTR # error "DATA_TRX_ID + 1 != DATA_ROLL_PTR" #endif trx_write_trx_id(field, trx_id); trx_write_roll_ptr(field + DATA_TRX_ID_LEN, roll_ptr); } } #ifndef UNIV_HOTBACKUP /*********************************************************************//** Sets the trx id or roll ptr field of a clustered index entry. */ UNIV_INTERN void row_upd_index_entry_sys_field( /*==========================*/ const dtuple_t* entry, /*!< in: index entry, where the memory buffers for sys fields are already allocated: the function just copies the new values to them */ dict_index_t* index, /*!< in: clustered index */ ulint type, /*!< in: DATA_TRX_ID or DATA_ROLL_PTR */ dulint val) /*!< in: value to write */ { dfield_t* dfield; byte* field; ulint pos; ut_ad(dict_index_is_clust(index)); pos = dict_index_get_sys_col_pos(index, type); dfield = dtuple_get_nth_field(entry, pos); field = dfield_get_data(dfield); if (type == DATA_TRX_ID) { trx_write_trx_id(field, val); } else { ut_ad(type == DATA_ROLL_PTR); trx_write_roll_ptr(field, val); } } /***********************************************************//** Returns TRUE if row update changes size of some field in index or if some field to be updated is stored externally in rec or update. @return TRUE if the update changes the size of some field in index or the field is external in rec or update */ UNIV_INTERN ibool row_upd_changes_field_size_or_external( /*===================================*/ dict_index_t* index, /*!< in: index */ const ulint* offsets,/*!< in: rec_get_offsets(rec, index) */ const upd_t* update) /*!< in: update vector */ { const upd_field_t* upd_field; const dfield_t* new_val; ulint old_len; ulint new_len; ulint n_fields; ulint i; ut_ad(rec_offs_validate(NULL, index, offsets)); n_fields = upd_get_n_fields(update); for (i = 0; i < n_fields; i++) { upd_field = upd_get_nth_field(update, i); new_val = &(upd_field->new_val); new_len = dfield_get_len(new_val); if (dfield_is_null(new_val) && !rec_offs_comp(offsets)) { /* A bug fixed on Dec 31st, 2004: we looked at the SQL NULL size from the wrong field! We may backport this fix also to 4.0. The merge to 5.0 will be made manually immediately after we commit this to 4.1. */ new_len = dict_col_get_sql_null_size( dict_index_get_nth_col(index, upd_field->field_no), 0); } old_len = rec_offs_nth_size(offsets, upd_field->field_no); if (rec_offs_comp(offsets) && rec_offs_nth_sql_null(offsets, upd_field->field_no)) { /* Note that in the compact table format, for a variable length field, an SQL NULL will use zero bytes in the offset array at the start of the physical record, but a zero-length value (empty string) will use one byte! Thus, we cannot use update-in-place if we update an SQL NULL varchar to an empty string! */ old_len = UNIV_SQL_NULL; } if (dfield_is_ext(new_val) || old_len != new_len || rec_offs_nth_extern(offsets, upd_field->field_no)) { return(TRUE); } } return(FALSE); } #endif /* !UNIV_HOTBACKUP */ /***********************************************************//** Replaces the new column values stored in the update vector to the record given. No field size changes are allowed. */ UNIV_INTERN void row_upd_rec_in_place( /*=================*/ rec_t* rec, /*!< in/out: record where replaced */ dict_index_t* index, /*!< in: the index the record belongs to */ const ulint* offsets,/*!< in: array returned by rec_get_offsets() */ const upd_t* update, /*!< in: update vector */ page_zip_des_t* page_zip)/*!< in: compressed page with enough space available, or NULL */ { const upd_field_t* upd_field; const dfield_t* new_val; ulint n_fields; ulint i; ut_ad(rec_offs_validate(rec, index, offsets)); if (rec_offs_comp(offsets)) { rec_set_info_bits_new(rec, update->info_bits); } else { rec_set_info_bits_old(rec, update->info_bits); } n_fields = upd_get_n_fields(update); for (i = 0; i < n_fields; i++) { upd_field = upd_get_nth_field(update, i); new_val = &(upd_field->new_val); ut_ad(!dfield_is_ext(new_val) == !rec_offs_nth_extern(offsets, upd_field->field_no)); rec_set_nth_field(rec, offsets, upd_field->field_no, dfield_get_data(new_val), dfield_get_len(new_val)); } if (UNIV_LIKELY_NULL(page_zip)) { page_zip_write_rec(page_zip, rec, index, offsets, 0); } } #ifndef UNIV_HOTBACKUP /*********************************************************************//** Writes into the redo log the values of trx id and roll ptr and enough info to determine their positions within a clustered index record. @return new pointer to mlog */ UNIV_INTERN byte* row_upd_write_sys_vals_to_log( /*==========================*/ dict_index_t* index, /*!< in: clustered index */ trx_t* trx, /*!< in: transaction */ roll_ptr_t roll_ptr,/*!< in: roll ptr of the undo log record */ byte* log_ptr,/*!< pointer to a buffer of size > 20 opened in mlog */ mtr_t* mtr __attribute__((unused))) /*!< in: mtr */ { ut_ad(dict_index_is_clust(index)); ut_ad(mtr); log_ptr += mach_write_compressed(log_ptr, dict_index_get_sys_col_pos( index, DATA_TRX_ID)); trx_write_roll_ptr(log_ptr, roll_ptr); log_ptr += DATA_ROLL_PTR_LEN; log_ptr += mach_dulint_write_compressed(log_ptr, trx->id); return(log_ptr); } #endif /* !UNIV_HOTBACKUP */ /*********************************************************************//** Parses the log data of system field values. @return log data end or NULL */ UNIV_INTERN byte* row_upd_parse_sys_vals( /*===================*/ byte* ptr, /*!< in: buffer */ byte* end_ptr,/*!< in: buffer end */ ulint* pos, /*!< out: TRX_ID position in record */ trx_id_t* trx_id, /*!< out: trx id */ roll_ptr_t* roll_ptr)/*!< out: roll ptr */ { ptr = mach_parse_compressed(ptr, end_ptr, pos); if (ptr == NULL) { return(NULL); } if (end_ptr < ptr + DATA_ROLL_PTR_LEN) { return(NULL); } *roll_ptr = trx_read_roll_ptr(ptr); ptr += DATA_ROLL_PTR_LEN; ptr = mach_dulint_parse_compressed(ptr, end_ptr, trx_id); return(ptr); } #ifndef UNIV_HOTBACKUP /***********************************************************//** Writes to the redo log the new values of the fields occurring in the index. */ UNIV_INTERN void row_upd_index_write_log( /*====================*/ const upd_t* update, /*!< in: update vector */ byte* log_ptr,/*!< in: pointer to mlog buffer: must contain at least MLOG_BUF_MARGIN bytes of free space; the buffer is closed within this function */ mtr_t* mtr) /*!< in: mtr into whose log to write */ { const upd_field_t* upd_field; const dfield_t* new_val; ulint len; ulint n_fields; byte* buf_end; ulint i; n_fields = upd_get_n_fields(update); buf_end = log_ptr + MLOG_BUF_MARGIN; mach_write_to_1(log_ptr, update->info_bits); log_ptr++; log_ptr += mach_write_compressed(log_ptr, n_fields); for (i = 0; i < n_fields; i++) { #if MLOG_BUF_MARGIN <= 30 # error "MLOG_BUF_MARGIN <= 30" #endif if (log_ptr + 30 > buf_end) { mlog_close(mtr, log_ptr); log_ptr = mlog_open(mtr, MLOG_BUF_MARGIN); buf_end = log_ptr + MLOG_BUF_MARGIN; } upd_field = upd_get_nth_field(update, i); new_val = &(upd_field->new_val); len = dfield_get_len(new_val); log_ptr += mach_write_compressed(log_ptr, upd_field->field_no); log_ptr += mach_write_compressed(log_ptr, len); if (len != UNIV_SQL_NULL) { if (log_ptr + len < buf_end) { memcpy(log_ptr, dfield_get_data(new_val), len); log_ptr += len; } else { mlog_close(mtr, log_ptr); mlog_catenate_string(mtr, dfield_get_data(new_val), len); log_ptr = mlog_open(mtr, MLOG_BUF_MARGIN); buf_end = log_ptr + MLOG_BUF_MARGIN; } } } mlog_close(mtr, log_ptr); } #endif /* !UNIV_HOTBACKUP */ /*********************************************************************//** Parses the log data written by row_upd_index_write_log. @return log data end or NULL */ UNIV_INTERN byte* row_upd_index_parse( /*================*/ byte* ptr, /*!< in: buffer */ byte* end_ptr,/*!< in: buffer end */ mem_heap_t* heap, /*!< in: memory heap where update vector is built */ upd_t** update_out)/*!< out: update vector */ { upd_t* update; upd_field_t* upd_field; dfield_t* new_val; ulint len; ulint n_fields; ulint info_bits; ulint i; if (end_ptr < ptr + 1) { return(NULL); } info_bits = mach_read_from_1(ptr); ptr++; ptr = mach_parse_compressed(ptr, end_ptr, &n_fields); if (ptr == NULL) { return(NULL); } update = upd_create(n_fields, heap); update->info_bits = info_bits; for (i = 0; i < n_fields; i++) { ulint field_no; upd_field = upd_get_nth_field(update, i); new_val = &(upd_field->new_val); ptr = mach_parse_compressed(ptr, end_ptr, &field_no); if (ptr == NULL) { return(NULL); } upd_field->field_no = field_no; ptr = mach_parse_compressed(ptr, end_ptr, &len); if (ptr == NULL) { return(NULL); } if (len != UNIV_SQL_NULL) { if (end_ptr < ptr + len) { return(NULL); } dfield_set_data(new_val, mem_heap_dup(heap, ptr, len), len); ptr += len; } else { dfield_set_null(new_val); } } *update_out = update; return(ptr); } #ifndef UNIV_HOTBACKUP /***************************************************************//** Builds an update vector from those fields which in a secondary index entry differ from a record that has the equal ordering fields. NOTE: we compare the fields as binary strings! @return own: update vector of differing fields */ UNIV_INTERN upd_t* row_upd_build_sec_rec_difference_binary( /*====================================*/ dict_index_t* index, /*!< in: index */ const dtuple_t* entry, /*!< in: entry to insert */ const rec_t* rec, /*!< in: secondary index record */ trx_t* trx, /*!< in: transaction */ mem_heap_t* heap) /*!< in: memory heap from which allocated */ { upd_field_t* upd_field; const dfield_t* dfield; const byte* data; ulint len; upd_t* update; ulint n_diff; ulint i; ulint offsets_[REC_OFFS_SMALL_SIZE]; const ulint* offsets; rec_offs_init(offsets_); /* This function is used only for a secondary index */ ut_a(!dict_index_is_clust(index)); update = upd_create(dtuple_get_n_fields(entry), heap); n_diff = 0; offsets = rec_get_offsets(rec, index, offsets_, ULINT_UNDEFINED, &heap); for (i = 0; i < dtuple_get_n_fields(entry); i++) { data = rec_get_nth_field(rec, offsets, i, &len); dfield = dtuple_get_nth_field(entry, i); /* NOTE that it may be that len != dfield_get_len(dfield) if we are updating in a character set and collation where strings of different length can be equal in an alphabetical comparison, and also in the case where we have a column prefix index and the last characters in the index field are spaces; the latter case probably caused the assertion failures reported at row0upd.c line 713 in versions 4.0.14 - 4.0.16. */ /* NOTE: we compare the fields as binary strings! (No collation) */ if (!dfield_data_is_binary_equal(dfield, len, data)) { upd_field = upd_get_nth_field(update, n_diff); dfield_copy(&(upd_field->new_val), dfield); upd_field_set_field_no(upd_field, i, index, trx); n_diff++; } } update->n_fields = n_diff; return(update); } /***************************************************************//** Builds an update vector from those fields, excluding the roll ptr and trx id fields, which in an index entry differ from a record that has the equal ordering fields. NOTE: we compare the fields as binary strings! @return own: update vector of differing fields, excluding roll ptr and trx id */ UNIV_INTERN upd_t* row_upd_build_difference_binary( /*============================*/ dict_index_t* index, /*!< in: clustered index */ const dtuple_t* entry, /*!< in: entry to insert */ const rec_t* rec, /*!< in: clustered index record */ trx_t* trx, /*!< in: transaction */ mem_heap_t* heap) /*!< in: memory heap from which allocated */ { upd_field_t* upd_field; const dfield_t* dfield; const byte* data; ulint len; upd_t* update; ulint n_diff; ulint roll_ptr_pos; ulint trx_id_pos; ulint i; ulint offsets_[REC_OFFS_NORMAL_SIZE]; const ulint* offsets; rec_offs_init(offsets_); /* This function is used only for a clustered index */ ut_a(dict_index_is_clust(index)); update = upd_create(dtuple_get_n_fields(entry), heap); n_diff = 0; roll_ptr_pos = dict_index_get_sys_col_pos(index, DATA_ROLL_PTR); trx_id_pos = dict_index_get_sys_col_pos(index, DATA_TRX_ID); offsets = rec_get_offsets(rec, index, offsets_, ULINT_UNDEFINED, &heap); for (i = 0; i < dtuple_get_n_fields(entry); i++) { data = rec_get_nth_field(rec, offsets, i, &len); dfield = dtuple_get_nth_field(entry, i); /* NOTE: we compare the fields as binary strings! (No collation) */ if (i == trx_id_pos || i == roll_ptr_pos) { goto skip_compare; } if (UNIV_UNLIKELY(!dfield_is_ext(dfield) != !rec_offs_nth_extern(offsets, i)) || !dfield_data_is_binary_equal(dfield, len, data)) { upd_field = upd_get_nth_field(update, n_diff); dfield_copy(&(upd_field->new_val), dfield); upd_field_set_field_no(upd_field, i, index, trx); n_diff++; } skip_compare: ; } update->n_fields = n_diff; return(update); } /***********************************************************//** Fetch a prefix of an externally stored column. This is similar to row_ext_lookup(), but the row_ext_t holds the old values of the column and must not be poisoned with the new values. @return BLOB prefix */ static byte* row_upd_ext_fetch( /*==============*/ const byte* data, /*!< in: 'internally' stored part of the field containing also the reference to the external part */ ulint local_len, /*!< in: length of data, in bytes */ ulint zip_size, /*!< in: nonzero=compressed BLOB page size, zero for uncompressed BLOBs */ ulint* len, /*!< in: length of prefix to fetch; out: fetched length of the prefix */ mem_heap_t* heap) /*!< in: heap where to allocate */ { byte* buf = mem_heap_alloc(heap, *len); *len = btr_copy_externally_stored_field_prefix(buf, *len, zip_size, data, local_len); /* We should never update records containing a half-deleted BLOB. */ ut_a(*len); return(buf); } /***********************************************************//** Replaces the new column value stored in the update vector in the given index entry field. */ static void row_upd_index_replace_new_col_val( /*==============================*/ dfield_t* dfield, /*!< in/out: data field of the index entry */ const dict_field_t* field, /*!< in: index field */ const dict_col_t* col, /*!< in: field->col */ const upd_field_t* uf, /*!< in: update field */ mem_heap_t* heap, /*!< in: memory heap for allocating and copying the new value */ ulint zip_size)/*!< in: compressed page size of the table, or 0 */ { ulint len; const byte* data; dfield_copy_data(dfield, &uf->new_val); if (dfield_is_null(dfield)) { return; } len = dfield_get_len(dfield); data = dfield_get_data(dfield); if (field->prefix_len > 0) { ibool fetch_ext = dfield_is_ext(dfield) && len < (ulint) field->prefix_len + BTR_EXTERN_FIELD_REF_SIZE; if (fetch_ext) { ulint l = len; len = field->prefix_len; data = row_upd_ext_fetch(data, l, zip_size, &len, heap); } len = dtype_get_at_most_n_mbchars(col->prtype, col->mbminlen, col->mbmaxlen, field->prefix_len, len, (const char*) data); dfield_set_data(dfield, data, len); if (!fetch_ext) { dfield_dup(dfield, heap); } return; } switch (uf->orig_len) { byte* buf; case BTR_EXTERN_FIELD_REF_SIZE: /* Restore the original locally stored part of the column. In the undo log, InnoDB writes a longer prefix of externally stored columns, so that column prefixes in secondary indexes can be reconstructed. */ dfield_set_data(dfield, data + len - BTR_EXTERN_FIELD_REF_SIZE, BTR_EXTERN_FIELD_REF_SIZE); dfield_set_ext(dfield); /* fall through */ case 0: dfield_dup(dfield, heap); break; default: /* Reconstruct the original locally stored part of the column. The data will have to be copied. */ ut_a(uf->orig_len > BTR_EXTERN_FIELD_REF_SIZE); buf = mem_heap_alloc(heap, uf->orig_len); /* Copy the locally stored prefix. */ memcpy(buf, data, uf->orig_len - BTR_EXTERN_FIELD_REF_SIZE); /* Copy the BLOB pointer. */ memcpy(buf + uf->orig_len - BTR_EXTERN_FIELD_REF_SIZE, data + len - BTR_EXTERN_FIELD_REF_SIZE, BTR_EXTERN_FIELD_REF_SIZE); dfield_set_data(dfield, buf, uf->orig_len); dfield_set_ext(dfield); break; } } /***********************************************************//** Replaces the new column values stored in the update vector to the index entry given. */ UNIV_INTERN void row_upd_index_replace_new_col_vals_index_pos( /*=========================================*/ dtuple_t* entry, /*!< in/out: index entry where replaced; the clustered index record must be covered by a lock or a page latch to prevent deletion (rollback or purge) */ dict_index_t* index, /*!< in: index; NOTE that this may also be a non-clustered index */ const upd_t* update, /*!< in: an update vector built for the index so that the field number in an upd_field is the index position */ ibool order_only, /*!< in: if TRUE, limit the replacement to ordering fields of index; note that this does not work for non-clustered indexes. */ mem_heap_t* heap) /*!< in: memory heap for allocating and copying the new values */ { ulint i; ulint n_fields; const ulint zip_size = dict_table_zip_size(index->table); ut_ad(index); dtuple_set_info_bits(entry, update->info_bits); if (order_only) { n_fields = dict_index_get_n_unique(index); } else { n_fields = dict_index_get_n_fields(index); } for (i = 0; i < n_fields; i++) { const dict_field_t* field; const dict_col_t* col; const upd_field_t* uf; field = dict_index_get_nth_field(index, i); col = dict_field_get_col(field); uf = upd_get_field_by_field_no(update, i); if (uf) { row_upd_index_replace_new_col_val( dtuple_get_nth_field(entry, i), field, col, uf, heap, zip_size); } } } /***********************************************************//** Replaces the new column values stored in the update vector to the index entry given. */ UNIV_INTERN void row_upd_index_replace_new_col_vals( /*===============================*/ dtuple_t* entry, /*!< in/out: index entry where replaced; the clustered index record must be covered by a lock or a page latch to prevent deletion (rollback or purge) */ dict_index_t* index, /*!< in: index; NOTE that this may also be a non-clustered index */ const upd_t* update, /*!< in: an update vector built for the CLUSTERED index so that the field number in an upd_field is the clustered index position */ mem_heap_t* heap) /*!< in: memory heap for allocating and copying the new values */ { ulint i; const dict_index_t* clust_index = dict_table_get_first_index(index->table); const ulint zip_size = dict_table_zip_size(index->table); dtuple_set_info_bits(entry, update->info_bits); for (i = 0; i < dict_index_get_n_fields(index); i++) { const dict_field_t* field; const dict_col_t* col; const upd_field_t* uf; field = dict_index_get_nth_field(index, i); col = dict_field_get_col(field); uf = upd_get_field_by_field_no( update, dict_col_get_clust_pos(col, clust_index)); if (uf) { row_upd_index_replace_new_col_val( dtuple_get_nth_field(entry, i), field, col, uf, heap, zip_size); } } } /***********************************************************//** Replaces the new column values stored in the update vector. */ UNIV_INTERN void row_upd_replace( /*============*/ dtuple_t* row, /*!< in/out: row where replaced, indexed by col_no; the clustered index record must be covered by a lock or a page latch to prevent deletion (rollback or purge) */ row_ext_t** ext, /*!< out, own: NULL, or externally stored column prefixes */ const dict_index_t* index, /*!< in: clustered index */ const upd_t* update, /*!< in: an update vector built for the clustered index */ mem_heap_t* heap) /*!< in: memory heap */ { ulint col_no; ulint i; ulint n_cols; ulint n_ext_cols; ulint* ext_cols; const dict_table_t* table; ut_ad(row); ut_ad(ext); ut_ad(index); ut_ad(dict_index_is_clust(index)); ut_ad(update); ut_ad(heap); n_cols = dtuple_get_n_fields(row); table = index->table; ut_ad(n_cols == dict_table_get_n_cols(table)); ext_cols = mem_heap_alloc(heap, n_cols * sizeof *ext_cols); n_ext_cols = 0; dtuple_set_info_bits(row, update->info_bits); for (col_no = 0; col_no < n_cols; col_no++) { const dict_col_t* col = dict_table_get_nth_col(table, col_no); const ulint clust_pos = dict_col_get_clust_pos(col, index); dfield_t* dfield; if (UNIV_UNLIKELY(clust_pos == ULINT_UNDEFINED)) { continue; } dfield = dtuple_get_nth_field(row, col_no); for (i = 0; i < upd_get_n_fields(update); i++) { const upd_field_t* upd_field = upd_get_nth_field(update, i); if (upd_field->field_no != clust_pos) { continue; } dfield_copy_data(dfield, &upd_field->new_val); break; } if (dfield_is_ext(dfield) && col->ord_part) { ext_cols[n_ext_cols++] = col_no; } } if (n_ext_cols) { *ext = row_ext_create(n_ext_cols, ext_cols, row, dict_table_zip_size(table), heap); } else { *ext = NULL; } } /***********************************************************//** Checks if an update vector changes an ordering field of an index record. This function is fast if the update vector is short or the number of ordering fields in the index is small. Otherwise, this can be quadratic. NOTE: we compare the fields as binary strings! @return TRUE if update vector changes an ordering field in the index record */ UNIV_INTERN ibool row_upd_changes_ord_field_binary( /*=============================*/ const dtuple_t* row, /*!< in: old value of row, or NULL if the row and the data values in update are not known when this function is called, e.g., at compile time */ dict_index_t* index, /*!< in: index of the record */ const upd_t* update) /*!< in: update vector for the row; NOTE: the field numbers in this MUST be clustered index positions! */ { ulint n_unique; ulint n_upd_fields; ulint i, j; dict_index_t* clust_index; ut_ad(update && index); n_unique = dict_index_get_n_unique(index); n_upd_fields = upd_get_n_fields(update); clust_index = dict_table_get_first_index(index->table); for (i = 0; i < n_unique; i++) { const dict_field_t* ind_field; const dict_col_t* col; ulint col_pos; ulint col_no; ind_field = dict_index_get_nth_field(index, i); col = dict_field_get_col(ind_field); col_pos = dict_col_get_clust_pos(col, clust_index); col_no = dict_col_get_no(col); for (j = 0; j < n_upd_fields; j++) { const upd_field_t* upd_field = upd_get_nth_field(update, j); /* Note that if the index field is a column prefix then it may be that row does not contain an externally stored part of the column value, and we cannot compare the datas */ if (col_pos == upd_field->field_no && (row == NULL || ind_field->prefix_len > 0 || !dfield_datas_are_binary_equal( dtuple_get_nth_field(row, col_no), &(upd_field->new_val)))) { return(TRUE); } } } return(FALSE); } /***********************************************************//** Checks if an update vector changes an ordering field of an index record. NOTE: we compare the fields as binary strings! @return TRUE if update vector may change an ordering field in an index record */ UNIV_INTERN ibool row_upd_changes_some_index_ord_field_binary( /*========================================*/ const dict_table_t* table, /*!< in: table */ const upd_t* update) /*!< in: update vector for the row */ { upd_field_t* upd_field; dict_index_t* index; ulint i; index = dict_table_get_first_index(table); for (i = 0; i < upd_get_n_fields(update); i++) { upd_field = upd_get_nth_field(update, i); if (dict_field_get_col(dict_index_get_nth_field( index, upd_field->field_no)) ->ord_part) { return(TRUE); } } return(FALSE); } /***********************************************************//** Checks if an update vector changes some of the first ordering fields of an index record. This is only used in foreign key checks and we can assume that index does not contain column prefixes. @return TRUE if changes */ static ibool row_upd_changes_first_fields_binary( /*================================*/ dtuple_t* entry, /*!< in: index entry */ dict_index_t* index, /*!< in: index of entry */ const upd_t* update, /*!< in: update vector for the row */ ulint n) /*!< in: how many first fields to check */ { ulint n_upd_fields; ulint i, j; dict_index_t* clust_index; ut_ad(update && index); ut_ad(n <= dict_index_get_n_fields(index)); n_upd_fields = upd_get_n_fields(update); clust_index = dict_table_get_first_index(index->table); for (i = 0; i < n; i++) { const dict_field_t* ind_field; const dict_col_t* col; ulint col_pos; ind_field = dict_index_get_nth_field(index, i); col = dict_field_get_col(ind_field); col_pos = dict_col_get_clust_pos(col, clust_index); ut_a(ind_field->prefix_len == 0); for (j = 0; j < n_upd_fields; j++) { upd_field_t* upd_field = upd_get_nth_field(update, j); if (col_pos == upd_field->field_no && !dfield_datas_are_binary_equal( dtuple_get_nth_field(entry, i), &(upd_field->new_val))) { return(TRUE); } } } return(FALSE); } /*********************************************************************//** Copies the column values from a record. */ UNIV_INLINE void row_upd_copy_columns( /*=================*/ rec_t* rec, /*!< in: record in a clustered index */ const ulint* offsets,/*!< in: array returned by rec_get_offsets() */ sym_node_t* column) /*!< in: first column in a column list, or NULL */ { byte* data; ulint len; while (column) { data = rec_get_nth_field(rec, offsets, column->field_nos[SYM_CLUST_FIELD_NO], &len); eval_node_copy_and_alloc_val(column, data, len); column = UT_LIST_GET_NEXT(col_var_list, column); } } /*********************************************************************//** Calculates the new values for fields to update. Note that row_upd_copy_columns must have been called first. */ UNIV_INLINE void row_upd_eval_new_vals( /*==================*/ upd_t* update) /*!< in/out: update vector */ { que_node_t* exp; upd_field_t* upd_field; ulint n_fields; ulint i; n_fields = upd_get_n_fields(update); for (i = 0; i < n_fields; i++) { upd_field = upd_get_nth_field(update, i); exp = upd_field->exp; eval_exp(exp); dfield_copy_data(&(upd_field->new_val), que_node_get_val(exp)); } } /***********************************************************//** Stores to the heap the row on which the node->pcur is positioned. */ static void row_upd_store_row( /*==============*/ upd_node_t* node) /*!< in: row update node */ { dict_index_t* clust_index; rec_t* rec; mem_heap_t* heap = NULL; ulint offsets_[REC_OFFS_NORMAL_SIZE]; const ulint* offsets; rec_offs_init(offsets_); ut_ad(node->pcur->latch_mode != BTR_NO_LATCHES); if (node->row != NULL) { mem_heap_empty(node->heap); } clust_index = dict_table_get_first_index(node->table); rec = btr_pcur_get_rec(node->pcur); offsets = rec_get_offsets(rec, clust_index, offsets_, ULINT_UNDEFINED, &heap); node->row = row_build(ROW_COPY_DATA, clust_index, rec, offsets, NULL, &node->ext, node->heap); if (node->is_delete) { node->upd_row = NULL; node->upd_ext = NULL; } else { node->upd_row = dtuple_copy(node->row, node->heap); row_upd_replace(node->upd_row, &node->upd_ext, clust_index, node->update, node->heap); } if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } } /***********************************************************//** Updates a secondary index entry of a row. @return DB_SUCCESS if operation successfully completed, else error code or DB_LOCK_WAIT */ static ulint row_upd_sec_index_entry( /*====================*/ upd_node_t* node, /*!< in: row update node */ que_thr_t* thr) /*!< in: query thread */ { ibool check_ref; ibool found; dict_index_t* index; dtuple_t* entry; btr_pcur_t pcur; btr_cur_t* btr_cur; mem_heap_t* heap; rec_t* rec; ulint err = DB_SUCCESS; mtr_t mtr; trx_t* trx = thr_get_trx(thr); index = node->index; check_ref = row_upd_index_is_referenced(index, trx); heap = mem_heap_create(1024); /* Build old index entry */ entry = row_build_index_entry(node->row, node->ext, index, heap); ut_a(entry); log_free_check(); mtr_start(&mtr); found = row_search_index_entry(index, entry, BTR_MODIFY_LEAF, &pcur, &mtr); btr_cur = btr_pcur_get_btr_cur(&pcur); rec = btr_cur_get_rec(btr_cur); if (UNIV_UNLIKELY(!found)) { ib_logger(ib_stream, "InnoDB: error in sec index entry update in\n" "InnoDB: "); dict_index_name_print(ib_stream, trx, index); ib_logger(ib_stream, "\nInnoDB: tuple "); dtuple_print(ib_stream, entry); ib_logger(ib_stream, "\nInnoDB: record "); rec_print(ib_stream, rec, index); ib_logger(ib_stream, "\n"); trx_print(ib_stream, trx, 0); ib_logger(ib_stream, "\n" "InnoDB: Submit a detailed bug report, check the" "InnoDB website for details"); } else { /* Delete mark the old index record; it can already be delete marked if we return after a lock wait in row_ins_index_entry below */ if (!rec_get_deleted_flag(rec, dict_table_is_comp(index->table))) { err = btr_cur_del_mark_set_sec_rec(0, btr_cur, TRUE, thr, &mtr); if (err == DB_SUCCESS && check_ref) { ulint* offsets = rec_get_offsets( rec, index, NULL, ULINT_UNDEFINED, &heap); /* NOTE that the following call loses the position of pcur ! */ err = row_upd_check_references_constraints( node, &pcur, index->table, index, offsets, thr, &mtr); } } } btr_pcur_close(&pcur); mtr_commit(&mtr); if (node->is_delete || err != DB_SUCCESS) { goto func_exit; } /* Build a new index entry */ entry = row_build_index_entry(node->upd_row, node->upd_ext, index, heap); ut_a(entry); /* Insert new index entry */ err = row_ins_index_entry(index, entry, 0, TRUE, thr); func_exit: mem_heap_free(heap); return(err); } /***********************************************************//** Updates the secondary index record if it is changed in the row update or deletes it if this is a delete. @return DB_SUCCESS if operation successfully completed, else error code or DB_LOCK_WAIT */ UNIV_INLINE ulint row_upd_sec_step( /*=============*/ upd_node_t* node, /*!< in: row update node */ que_thr_t* thr) /*!< in: query thread */ { ut_ad((node->state == UPD_NODE_UPDATE_ALL_SEC) || (node->state == UPD_NODE_UPDATE_SOME_SEC)); ut_ad(!dict_index_is_clust(node->index)); if (node->state == UPD_NODE_UPDATE_ALL_SEC || row_upd_changes_ord_field_binary(node->row, node->index, node->update)) { return(row_upd_sec_index_entry(node, thr)); } return(DB_SUCCESS); } /***********************************************************//** Marks the clustered index record deleted and inserts the updated version of the record to the index. This function should be used when the ordering fields of the clustered index record change. This should be quite rare in database applications. @return DB_SUCCESS if operation successfully completed, else error code or DB_LOCK_WAIT */ static ulint row_upd_clust_rec_by_insert( /*========================*/ upd_node_t* node, /*!< in: row update node */ dict_index_t* index, /*!< in: clustered index of the record */ que_thr_t* thr, /*!< in: query thread */ ibool check_ref,/*!< in: TRUE if index may be referenced in a foreign key constraint */ mtr_t* mtr) /*!< in: mtr; gets committed here */ { mem_heap_t* heap = NULL; btr_pcur_t* pcur; btr_cur_t* btr_cur; trx_t* trx; dict_table_t* table; dtuple_t* entry; ulint err; ut_ad(node); ut_ad(dict_index_is_clust(index)); trx = thr_get_trx(thr); table = node->table; pcur = node->pcur; btr_cur = btr_pcur_get_btr_cur(pcur); if (node->state != UPD_NODE_INSERT_CLUSTERED) { rec_t* rec; dict_index_t* index; ulint offsets_[REC_OFFS_NORMAL_SIZE]; ulint* offsets; rec_offs_init(offsets_); err = btr_cur_del_mark_set_clust_rec(BTR_NO_LOCKING_FLAG, btr_cur, TRUE, thr, mtr); if (err != DB_SUCCESS) { mtr_commit(mtr); return(err); } /* Mark as not-owned the externally stored fields which the new row inherits from the delete marked record: purge should not free those externally stored fields even if the delete marked record is removed from the index tree, or updated. */ rec = btr_cur_get_rec(btr_cur); index = dict_table_get_first_index(table); offsets = rec_get_offsets(rec, index, offsets_, ULINT_UNDEFINED, &heap); btr_cur_mark_extern_inherited_fields( btr_cur_get_page_zip(btr_cur), rec, index, offsets, node->update, mtr); if (check_ref) { /* NOTE that the following call loses the position of pcur ! */ err = row_upd_check_references_constraints( node, pcur, table, index, offsets, thr, mtr); if (err != DB_SUCCESS) { mtr_commit(mtr); if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } return(err); } } } mtr_commit(mtr); if (!heap) { heap = mem_heap_create(500); } node->state = UPD_NODE_INSERT_CLUSTERED; entry = row_build_index_entry(node->upd_row, node->upd_ext, index, heap); ut_a(entry); row_upd_index_entry_sys_field(entry, index, DATA_TRX_ID, trx->id); if (node->upd_ext) { /* If we return from a lock wait, for example, we may have extern fields marked as not-owned in entry (marked in the if-branch above). We must unmark them. */ btr_cur_unmark_dtuple_extern_fields(entry); /* We must mark non-updated extern fields in entry as inherited, so that a possible rollback will not free them. */ btr_cur_mark_dtuple_inherited_extern(entry, node->update); } err = row_ins_index_entry(index, entry, node->upd_ext ? node->upd_ext->n_ext : 0, TRUE, thr); mem_heap_free(heap); return(err); } /***********************************************************//** Updates a clustered index record of a row when the ordering fields do not change. @return DB_SUCCESS if operation successfully completed, else error code or DB_LOCK_WAIT */ static ulint row_upd_clust_rec( /*==============*/ upd_node_t* node, /*!< in: row update node */ dict_index_t* index, /*!< in: clustered index */ que_thr_t* thr, /*!< in: query thread */ mtr_t* mtr) /*!< in: mtr; gets committed here */ { mem_heap_t* heap = NULL; big_rec_t* big_rec = NULL; btr_pcur_t* pcur; btr_cur_t* btr_cur; ulint err; ut_ad(node); ut_ad(dict_index_is_clust(index)); pcur = node->pcur; btr_cur = btr_pcur_get_btr_cur(pcur); ut_ad(!rec_get_deleted_flag(btr_pcur_get_rec(pcur), dict_table_is_comp(index->table))); /* Try optimistic updating of the record, keeping changes within the page; we do not check locks because we assume the x-lock on the record to update */ if (node->cmpl_info & UPD_NODE_NO_SIZE_CHANGE) { err = btr_cur_update_in_place(BTR_NO_LOCKING_FLAG, btr_cur, node->update, node->cmpl_info, thr, mtr); } else { err = btr_cur_optimistic_update(BTR_NO_LOCKING_FLAG, btr_cur, node->update, node->cmpl_info, thr, mtr); } mtr_commit(mtr); if (UNIV_LIKELY(err == DB_SUCCESS)) { return(DB_SUCCESS); } if (buf_LRU_buf_pool_running_out()) { return(DB_LOCK_TABLE_FULL); } /* We may have to modify the tree structure: do a pessimistic descent down the index tree */ mtr_start(mtr); /* NOTE: this transaction has an s-lock or x-lock on the record and therefore other transactions cannot modify the record when we have no latch on the page. In addition, we assume that other query threads of the same transaction do not modify the record in the meantime. Therefore we can assert that the restoration of the cursor succeeds. */ ut_a(btr_pcur_restore_position(BTR_MODIFY_TREE, pcur, mtr)); ut_ad(!rec_get_deleted_flag(btr_pcur_get_rec(pcur), dict_table_is_comp(index->table))); err = btr_cur_pessimistic_update(BTR_NO_LOCKING_FLAG, btr_cur, &heap, &big_rec, node->update, node->cmpl_info, thr, mtr); mtr_commit(mtr); if (err == DB_SUCCESS && big_rec) { ulint offsets_[REC_OFFS_NORMAL_SIZE]; rec_t* rec; rec_offs_init(offsets_); mtr_start(mtr); ut_a(btr_pcur_restore_position(BTR_MODIFY_TREE, pcur, mtr)); rec = btr_cur_get_rec(btr_cur); err = btr_store_big_rec_extern_fields( index, btr_cur_get_block(btr_cur), rec, rec_get_offsets(rec, index, offsets_, ULINT_UNDEFINED, &heap), big_rec, mtr); mtr_commit(mtr); } if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } if (big_rec) { dtuple_big_rec_free(big_rec); } return(err); } /***********************************************************//** Delete marks a clustered index record. @return DB_SUCCESS if operation successfully completed, else error code */ static ulint row_upd_del_mark_clust_rec( /*=======================*/ upd_node_t* node, /*!< in: row update node */ dict_index_t* index, /*!< in: clustered index */ ulint* offsets,/*!< in/out: rec_get_offsets() for the record under the cursor */ que_thr_t* thr, /*!< in: query thread */ ibool check_ref,/*!< in: TRUE if index may be referenced in a foreign key constraint */ mtr_t* mtr) /*!< in: mtr; gets committed here */ { btr_pcur_t* pcur; btr_cur_t* btr_cur; ulint err; ut_ad(node); ut_ad(dict_index_is_clust(index)); ut_ad(node->is_delete); pcur = node->pcur; btr_cur = btr_pcur_get_btr_cur(pcur); /* Store row because we have to build also the secondary index entries */ row_upd_store_row(node); /* Mark the clustered index record deleted; we do not have to check locks, because we assume that we have an x-lock on the record */ err = btr_cur_del_mark_set_clust_rec(BTR_NO_LOCKING_FLAG, btr_cur, TRUE, thr, mtr); if (err == DB_SUCCESS && check_ref) { /* NOTE that the following call loses the position of pcur ! */ err = row_upd_check_references_constraints(node, pcur, index->table, index, offsets, thr, mtr); } mtr_commit(mtr); return(err); } /***********************************************************//** Updates the clustered index record. @return DB_SUCCESS if operation successfully completed, DB_LOCK_WAIT in case of a lock wait, else error code */ static ulint row_upd_clust_step( /*===============*/ upd_node_t* node, /*!< in: row update node */ que_thr_t* thr) /*!< in: query thread */ { dict_index_t* index; btr_pcur_t* pcur; ibool success; ibool check_ref; ulint err; mtr_t* mtr; mtr_t mtr_buf; rec_t* rec; mem_heap_t* heap = NULL; ulint offsets_[REC_OFFS_NORMAL_SIZE]; ulint* offsets; rec_offs_init(offsets_); index = dict_table_get_first_index(node->table); check_ref = row_upd_index_is_referenced(index, thr_get_trx(thr)); pcur = node->pcur; /* We have to restore the cursor to its position */ mtr = &mtr_buf; mtr_start(mtr); /* If the restoration does not succeed, then the same transaction has deleted the record on which the cursor was, and that is an SQL error. If the restoration succeeds, it may still be that the same transaction has successively deleted and inserted a record with the same ordering fields, but in that case we know that the transaction has at least an implicit x-lock on the record. */ ut_a(pcur->rel_pos == BTR_PCUR_ON); success = btr_pcur_restore_position(BTR_MODIFY_LEAF, pcur, mtr); if (!success) { err = DB_RECORD_NOT_FOUND; mtr_commit(mtr); return(err); } /* If this is a row in SYS_INDEXES table of the data dictionary, then we have to free the file segments of the index tree associated with the index */ if (node->is_delete && ut_dulint_cmp(node->table->id, DICT_INDEXES_ID) == 0) { dict_drop_index_tree(btr_pcur_get_rec(pcur), mtr); mtr_commit(mtr); mtr_start(mtr); success = btr_pcur_restore_position(BTR_MODIFY_LEAF, pcur, mtr); if (!success) { err = DB_ERROR; mtr_commit(mtr); return(err); } } rec = btr_pcur_get_rec(pcur); offsets = rec_get_offsets(rec, index, offsets_, ULINT_UNDEFINED, &heap); if (!node->has_clust_rec_x_lock) { err = lock_clust_rec_modify_check_and_lock( 0, btr_pcur_get_block(pcur), rec, index, offsets, thr); if (err != DB_SUCCESS) { mtr_commit(mtr); goto exit_func; } } /* NOTE: the following function calls will also commit mtr */ if (node->is_delete) { err = row_upd_del_mark_clust_rec(node, index, offsets, thr, check_ref, mtr); if (err == DB_SUCCESS) { node->state = UPD_NODE_UPDATE_ALL_SEC; node->index = dict_table_get_next_index(index); } exit_func: if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } return(err); } /* If the update is made for a user, we already have the update vector ready, else we have to do some evaluation: */ if (UNIV_UNLIKELY(!node->in_client_interface)) { /* Copy the necessary columns from clust_rec and calculate the new values to set */ row_upd_copy_columns(rec, offsets, UT_LIST_GET_FIRST(node->columns)); row_upd_eval_new_vals(node->update); } if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } if (node->cmpl_info & UPD_NODE_NO_ORD_CHANGE) { err = row_upd_clust_rec(node, index, thr, mtr); return(err); } row_upd_store_row(node); if (row_upd_changes_ord_field_binary(node->row, index, node->update)) { /* Update causes an ordering field (ordering fields within the B-tree) of the clustered index record to change: perform the update by delete marking and inserting. TODO! What to do to the 'Halloween problem', where an update moves the record forward in index so that it is again updated when the cursor arrives there? Solution: the read operation must check the undo record undo number when choosing records to update. FIXME: MySQL used to solve the problem externally! */ err = row_upd_clust_rec_by_insert(node, index, thr, check_ref, mtr); if (err != DB_SUCCESS) { return(err); } node->state = UPD_NODE_UPDATE_ALL_SEC; } else { err = row_upd_clust_rec(node, index, thr, mtr); if (err != DB_SUCCESS) { return(err); } node->state = UPD_NODE_UPDATE_SOME_SEC; } node->index = dict_table_get_next_index(index); return(err); } /***********************************************************//** Updates the affected index records of a row. When the control is transferred to this node, we assume that we have a persistent cursor which was on a record, and the position of the cursor is stored in the cursor. @return DB_SUCCESS if operation successfully completed, else error code or DB_LOCK_WAIT */ static ulint row_upd( /*====*/ upd_node_t* node, /*!< in: row update node */ que_thr_t* thr) /*!< in: query thread */ { ulint err = DB_SUCCESS; ut_ad(node && thr); if (UNIV_LIKELY(node->in_client_interface)) { /* We do not get the cmpl_info value from the user interpreter: we must calculate it on the fly: */ if (node->is_delete || row_upd_changes_some_index_ord_field_binary( node->table, node->update)) { node->cmpl_info = 0; } else { node->cmpl_info = UPD_NODE_NO_ORD_CHANGE; } } if (node->state == UPD_NODE_UPDATE_CLUSTERED || node->state == UPD_NODE_INSERT_CLUSTERED) { err = row_upd_clust_step(node, thr); if (err != DB_SUCCESS) { goto function_exit; } } if (!node->is_delete && (node->cmpl_info & UPD_NODE_NO_ORD_CHANGE)) { goto function_exit; } while (node->index != NULL) { err = row_upd_sec_step(node, thr); if (err != DB_SUCCESS) { goto function_exit; } node->index = dict_table_get_next_index(node->index); } function_exit: if (err == DB_SUCCESS) { /* Do some cleanup */ if (node->row != NULL) { node->row = NULL; node->ext = NULL; node->upd_row = NULL; node->upd_ext = NULL; mem_heap_empty(node->heap); } node->state = UPD_NODE_UPDATE_CLUSTERED; } return(err); } /***********************************************************//** Updates a row in a table. This is a high-level function used in SQL execution graphs. @return query thread to run next or NULL */ UNIV_INTERN que_thr_t* row_upd_step( /*=========*/ que_thr_t* thr) /*!< in: query thread */ { upd_node_t* node; sel_node_t* sel_node; que_node_t* parent; ulint err = DB_SUCCESS; trx_t* trx; ut_ad(thr); trx = thr_get_trx(thr); node = thr->run_node; sel_node = node->select; parent = que_node_get_parent(node); ut_ad(que_node_get_type(node) == QUE_NODE_UPDATE); if (thr->prev_node == parent) { node->state = UPD_NODE_SET_IX_LOCK; } if (node->state == UPD_NODE_SET_IX_LOCK) { if (!node->has_clust_rec_x_lock) { /* It may be that the current session has not yet started its transaction, or it has been committed: */ err = lock_table(0, node->table, LOCK_IX, thr); if (err != DB_SUCCESS) { goto error_handling; } } node->state = UPD_NODE_UPDATE_CLUSTERED; if (node->searched_update) { /* Reset the cursor */ sel_node->state = SEL_NODE_OPEN; /* Fetch a row to update */ thr->run_node = sel_node; return(thr); } } /* sel_node is NULL if we are in the client interface */ if (sel_node && (sel_node->state != SEL_NODE_FETCH)) { if (!node->searched_update) { /* An explicit cursor should be positioned on a row to update */ ut_error; err = DB_ERROR; goto error_handling; } ut_ad(sel_node->state == SEL_NODE_NO_MORE_ROWS); /* No more rows to update, or the select node performed the updates directly in-place */ thr->run_node = parent; return(thr); } /* DO THE CHECKS OF THE CONSISTENCY CONSTRAINTS HERE */ err = row_upd(node, thr); error_handling: trx->error_state = err; if (err != DB_SUCCESS) { return(NULL); } /* DO THE TRIGGER ACTIONS HERE */ if (node->searched_update) { /* Fetch next row to update */ thr->run_node = sel_node; } else { /* It was an explicit cursor update */ thr->run_node = parent; } node->state = UPD_NODE_UPDATE_CLUSTERED; return(thr); } /************************************************************************* Creates an query graph node of 'update' type. @return own: update node */ UNIV_INTERN upd_node_t* row_create_update_node( /*===================*/ dict_table_t* table, /*!< in: table to update */ mem_heap_t* heap) /*!< in: mem heap from which allocated */ { upd_node_t* node; node = upd_node_create(heap); node->in_client_interface = TRUE; node->is_delete = FALSE; node->searched_update = FALSE; node->select = NULL; node->pcur = btr_pcur_create(); node->table = table; node->update = upd_create(dict_table_get_n_cols(table), heap); node->update_n_fields = dict_table_get_n_cols(table); UT_LIST_INIT(node->columns); node->has_clust_rec_x_lock = TRUE; node->cmpl_info = 0; node->table_sym = NULL; node->col_assign_list = NULL; return(node); } #endif /* !UNIV_HOTBACKUP */ haildb-2.3.2/row/row0umod.c0000644000175000017500000005376311513177357016410 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1997, 2010, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file row/row0umod.c Undo modify of a row Created 2/27/1997 Heikki Tuuri *******************************************************/ #include "row0umod.h" #ifdef UNIV_NONINL #include "row0umod.ic" #endif #include "dict0dict.h" #include "dict0boot.h" #include "trx0undo.h" #include "trx0roll.h" #include "btr0btr.h" #include "mach0data.h" #include "row0undo.h" #include "row0vers.h" #include "trx0trx.h" #include "trx0rec.h" #include "row0row.h" #include "row0upd.h" #include "que0que.h" #include "log0log.h" /* Considerations on undoing a modify operation. (1) Undoing a delete marking: all index records should be found. Some of them may have delete mark already FALSE, if the delete mark operation was stopped underway, or if the undo operation ended prematurely because of a system crash. (2) Undoing an update of a delete unmarked record: the newer version of an updated secondary index entry should be removed if no prior version of the clustered index record requires its existence. Otherwise, it should be delete marked. (3) Undoing an update of a delete marked record. In this kind of update a delete marked clustered index record was delete unmarked and possibly also some of its fields were changed. Now, it is possible that the delete marked version has become obsolete at the time the undo is started. */ /***********************************************************//** Checks if also the previous version of the clustered index record was modified or inserted by the same transaction, and its undo number is such that it should be undone in the same rollback. @return TRUE if also previous modify or insert of this row should be undone */ UNIV_INLINE ibool row_undo_mod_undo_also_prev_vers( /*=============================*/ undo_node_t* node, /*!< in: row undo node */ undo_no_t* undo_no)/*!< out: the undo number */ { trx_undo_rec_t* undo_rec; trx_t* trx; trx = node->trx; if (0 != ut_dulint_cmp(node->new_trx_id, trx->id)) { *undo_no = ut_dulint_zero; return(FALSE); } undo_rec = trx_undo_get_undo_rec_low(node->new_roll_ptr, node->heap); *undo_no = trx_undo_rec_get_undo_no(undo_rec); return(ut_dulint_cmp(trx->roll_limit, *undo_no) <= 0); } /***********************************************************//** Undoes a modify in a clustered index record. @return DB_SUCCESS, DB_FAIL, or error code: we may run out of file space */ static ulint row_undo_mod_clust_low( /*===================*/ undo_node_t* node, /*!< in: row undo node */ que_thr_t* thr, /*!< in: query thread */ mtr_t* mtr, /*!< in: mtr; must be committed before latching any further pages */ ulint mode) /*!< in: BTR_MODIFY_LEAF or BTR_MODIFY_TREE */ { btr_pcur_t* pcur; btr_cur_t* btr_cur; ulint err; ibool success; pcur = &(node->pcur); btr_cur = btr_pcur_get_btr_cur(pcur); success = btr_pcur_restore_position(mode, pcur, mtr); ut_ad(success); if (mode == BTR_MODIFY_LEAF) { err = btr_cur_optimistic_update(BTR_NO_LOCKING_FLAG | BTR_NO_UNDO_LOG_FLAG | BTR_KEEP_SYS_FLAG, btr_cur, node->update, node->cmpl_info, thr, mtr); } else { mem_heap_t* heap = NULL; big_rec_t* dummy_big_rec; ut_ad(mode == BTR_MODIFY_TREE); err = btr_cur_pessimistic_update( BTR_NO_LOCKING_FLAG | BTR_NO_UNDO_LOG_FLAG | BTR_KEEP_SYS_FLAG, btr_cur, &heap, &dummy_big_rec, node->update, node->cmpl_info, thr, mtr); ut_a(!dummy_big_rec); if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } } return(err); } /***********************************************************//** Removes a clustered index record after undo if possible. This is attempted when the record was inserted by updating a delete-marked record and there no longer exist transactions that would see the delete-marked record. In other words, we roll back the insert by purging the record. @return DB_SUCCESS, DB_FAIL, or error code: we may run out of file space */ static ulint row_undo_mod_remove_clust_low( /*==========================*/ undo_node_t* node, /*!< in: row undo node */ que_thr_t* thr, /*!< in: query thread */ mtr_t* mtr, /*!< in: mtr */ ulint mode) /*!< in: BTR_MODIFY_LEAF or BTR_MODIFY_TREE */ { btr_pcur_t* pcur; btr_cur_t* btr_cur; ulint err; ibool success; ut_ad(node->rec_type == TRX_UNDO_UPD_DEL_REC); pcur = &(node->pcur); btr_cur = btr_pcur_get_btr_cur(pcur); success = btr_pcur_restore_position(mode, pcur, mtr); if (!success) { return(DB_SUCCESS); } /* Find out if we can remove the whole clustered index record */ if (node->rec_type == TRX_UNDO_UPD_DEL_REC && !row_vers_must_preserve_del_marked(node->new_trx_id, mtr)) { /* Ok, we can remove */ } else { return(DB_SUCCESS); } if (mode == BTR_MODIFY_LEAF) { success = btr_cur_optimistic_delete(btr_cur, mtr); if (success) { err = DB_SUCCESS; } else { err = DB_FAIL; } } else { ut_ad(mode == BTR_MODIFY_TREE); /* This operation is analogous to purge, we can free also inherited externally stored fields */ btr_cur_pessimistic_delete(&err, FALSE, btr_cur, thr_is_recv(thr) ? RB_RECOVERY_PURGE_REC : RB_NONE, mtr); /* The delete operation may fail if we have little file space left: TODO: easiest to crash the database and restart with more file space */ } return(err); } /***********************************************************//** Undoes a modify in a clustered index record. Sets also the node state for the next round of undo. @return DB_SUCCESS or error code: we may run out of file space */ static ulint row_undo_mod_clust( /*===============*/ undo_node_t* node, /*!< in: row undo node */ que_thr_t* thr) /*!< in: query thread */ { btr_pcur_t* pcur; mtr_t mtr; ulint err; ibool success; ibool more_vers; undo_no_t new_undo_no; ut_ad(node && thr); /* Check if also the previous version of the clustered index record should be undone in this same rollback operation */ more_vers = row_undo_mod_undo_also_prev_vers(node, &new_undo_no); pcur = &(node->pcur); mtr_start(&mtr); /* Try optimistic processing of the record, keeping changes within the index page */ err = row_undo_mod_clust_low(node, thr, &mtr, BTR_MODIFY_LEAF); if (err != DB_SUCCESS) { btr_pcur_commit_specify_mtr(pcur, &mtr); /* We may have to modify tree structure: do a pessimistic descent down the index tree */ mtr_start(&mtr); err = row_undo_mod_clust_low(node, thr, &mtr, BTR_MODIFY_TREE); } btr_pcur_commit_specify_mtr(pcur, &mtr); if (err == DB_SUCCESS && node->rec_type == TRX_UNDO_UPD_DEL_REC) { mtr_start(&mtr); err = row_undo_mod_remove_clust_low(node, thr, &mtr, BTR_MODIFY_LEAF); if (err != DB_SUCCESS) { btr_pcur_commit_specify_mtr(pcur, &mtr); /* We may have to modify tree structure: do a pessimistic descent down the index tree */ mtr_start(&mtr); err = row_undo_mod_remove_clust_low(node, thr, &mtr, BTR_MODIFY_TREE); } btr_pcur_commit_specify_mtr(pcur, &mtr); } node->state = UNDO_NODE_FETCH_NEXT; trx_undo_rec_release(node->trx, node->undo_no); if (more_vers && err == DB_SUCCESS) { /* Reserve the undo log record to the prior version after committing &mtr: this is necessary to comply with the latching order, as &mtr may contain the fsp latch which is lower in the latch hierarchy than trx->undo_mutex. */ success = trx_undo_rec_reserve(node->trx, new_undo_no); if (success) { node->state = UNDO_NODE_PREV_VERS; } } return(err); } /***********************************************************//** Delete marks or removes a secondary index entry if found. @return DB_SUCCESS, DB_FAIL, or DB_OUT_OF_FILE_SPACE */ static ulint row_undo_mod_del_mark_or_remove_sec_low( /*====================================*/ undo_node_t* node, /*!< in: row undo node */ que_thr_t* thr, /*!< in: query thread */ dict_index_t* index, /*!< in: index */ dtuple_t* entry, /*!< in: index entry */ ulint mode) /*!< in: latch mode BTR_MODIFY_LEAF or BTR_MODIFY_TREE */ { ibool found; btr_pcur_t pcur; btr_cur_t* btr_cur; ibool success; ibool old_has; ulint err; mtr_t mtr; mtr_t mtr_vers; log_free_check(); mtr_start(&mtr); found = row_search_index_entry(index, entry, mode, &pcur, &mtr); btr_cur = btr_pcur_get_btr_cur(&pcur); if (!found) { /* In crash recovery, the secondary index record may be missing if the UPDATE did not have time to insert the secondary index records before the crash. When we are undoing that UPDATE in crash recovery, the record may be missing. In normal processing, if an update ends in a deadlock before it has inserted all updated secondary index records, then the undo will not find those records. */ btr_pcur_close(&pcur); mtr_commit(&mtr); return(DB_SUCCESS); } /* We should remove the index record if no prior version of the row, which cannot be purged yet, requires its existence. If some requires, we should delete mark the record. */ mtr_start(&mtr_vers); success = btr_pcur_restore_position(BTR_SEARCH_LEAF, &(node->pcur), &mtr_vers); ut_a(success); old_has = row_vers_old_has_index_entry(FALSE, btr_pcur_get_rec(&(node->pcur)), &mtr_vers, index, entry); if (old_has) { err = btr_cur_del_mark_set_sec_rec(BTR_NO_LOCKING_FLAG, btr_cur, TRUE, thr, &mtr); ut_ad(err == DB_SUCCESS); } else { /* Remove the index record */ if (mode == BTR_MODIFY_LEAF) { success = btr_cur_optimistic_delete(btr_cur, &mtr); if (success) { err = DB_SUCCESS; } else { err = DB_FAIL; } } else { ut_ad(mode == BTR_MODIFY_TREE); /* No need to distinguish RB_RECOVERY_PURGE here, because we are deleting a secondary index record: the distinction between RB_NORMAL and RB_RECOVERY_PURGE only matters when deleting a record that contains externally stored columns. */ ut_ad(!dict_index_is_clust(index)); btr_cur_pessimistic_delete(&err, FALSE, btr_cur, RB_NORMAL, &mtr); /* The delete operation may fail if we have little file space left: TODO: easiest to crash the database and restart with more file space */ } } btr_pcur_commit_specify_mtr(&(node->pcur), &mtr_vers); btr_pcur_close(&pcur); mtr_commit(&mtr); return(err); } /***********************************************************//** Delete marks or removes a secondary index entry if found. NOTE that if we updated the fields of a delete-marked secondary index record so that alphabetically they stayed the same, e.g., 'abc' -> 'aBc', we cannot return to the original values because we do not know them. But this should not cause problems because in row0sel.c, in queries we always retrieve the clustered index record or an earlier version of it, if the secondary index record through which we do the search is delete-marked. @return DB_SUCCESS or DB_OUT_OF_FILE_SPACE */ static ulint row_undo_mod_del_mark_or_remove_sec( /*================================*/ undo_node_t* node, /*!< in: row undo node */ que_thr_t* thr, /*!< in: query thread */ dict_index_t* index, /*!< in: index */ dtuple_t* entry) /*!< in: index entry */ { ulint err; err = row_undo_mod_del_mark_or_remove_sec_low(node, thr, index, entry, BTR_MODIFY_LEAF); if (err == DB_SUCCESS) { return(err); } err = row_undo_mod_del_mark_or_remove_sec_low(node, thr, index, entry, BTR_MODIFY_TREE); return(err); } /***********************************************************//** Delete unmarks a secondary index entry which must be found. It might not be delete-marked at the moment, but it does not harm to unmark it anyway. We also need to update the fields of the secondary index record if we updated its fields but alphabetically they stayed the same, e.g., 'abc' -> 'aBc'. @return DB_FAIL or DB_SUCCESS or DB_OUT_OF_FILE_SPACE */ static ulint row_undo_mod_del_unmark_sec_and_undo_update( /*========================================*/ ulint mode, /*!< in: search mode: BTR_MODIFY_LEAF or BTR_MODIFY_TREE */ que_thr_t* thr, /*!< in: query thread */ dict_index_t* index, /*!< in: index */ const dtuple_t* entry) /*!< in: index entry */ { mem_heap_t* heap; btr_pcur_t pcur; upd_t* update; ulint err = DB_SUCCESS; big_rec_t* dummy_big_rec; mtr_t mtr; trx_t* trx = thr_get_trx(thr); /* Ignore indexes that are being created. */ if (UNIV_UNLIKELY(*index->name == TEMP_INDEX_PREFIX)) { return(DB_SUCCESS); } log_free_check(); mtr_start(&mtr); if (UNIV_UNLIKELY(!row_search_index_entry(index, entry, mode, &pcur, &mtr))) { ib_logger(ib_stream, "InnoDB: error in sec index entry del undo in\n" "InnoDB: "); dict_index_name_print(ib_stream, trx, index); ib_logger(ib_stream, "\nInnoDB: tuple "); dtuple_print(ib_stream, entry); ib_logger(ib_stream, "\nInnoDB: record "); rec_print(ib_stream, btr_pcur_get_rec(&pcur), index); ib_logger(ib_stream,"\n", ib_stream); trx_print(ib_stream, trx, 0); ib_logger(ib_stream, "\n" "InnoDB: Submit a detailed bug report, check the " "InnoDB website for details"); } else { btr_cur_t* btr_cur = btr_pcur_get_btr_cur(&pcur); err = btr_cur_del_mark_set_sec_rec(BTR_NO_LOCKING_FLAG, btr_cur, FALSE, thr, &mtr); ut_a(err == DB_SUCCESS); heap = mem_heap_create(100); update = row_upd_build_sec_rec_difference_binary( index, entry, btr_cur_get_rec(btr_cur), trx, heap); if (upd_get_n_fields(update) == 0) { /* Do nothing */ } else if (mode == BTR_MODIFY_LEAF) { /* Try an optimistic updating of the record, keeping changes within the page */ err = btr_cur_optimistic_update( BTR_KEEP_SYS_FLAG | BTR_NO_LOCKING_FLAG, btr_cur, update, 0, thr, &mtr); switch (err) { case DB_OVERFLOW: case DB_UNDERFLOW: case DB_ZIP_OVERFLOW: err = DB_FAIL; } } else { ut_a(mode == BTR_MODIFY_TREE); err = btr_cur_pessimistic_update( BTR_KEEP_SYS_FLAG | BTR_NO_LOCKING_FLAG, btr_cur, &heap, &dummy_big_rec, update, 0, thr, &mtr); ut_a(!dummy_big_rec); } mem_heap_free(heap); } btr_pcur_close(&pcur); mtr_commit(&mtr); return(err); } /***********************************************************//** Undoes a modify in secondary indexes when undo record type is UPD_DEL. @return DB_SUCCESS or DB_OUT_OF_FILE_SPACE */ static ulint row_undo_mod_upd_del_sec( /*=====================*/ undo_node_t* node, /*!< in: row undo node */ que_thr_t* thr) /*!< in: query thread */ { mem_heap_t* heap; dtuple_t* entry; dict_index_t* index; ulint err = DB_SUCCESS; ut_ad(node->rec_type == TRX_UNDO_UPD_DEL_REC); heap = mem_heap_create(1024); while (node->index != NULL) { index = node->index; entry = row_build_index_entry(node->row, node->ext, index, heap); if (UNIV_UNLIKELY(!entry)) { /* The database must have crashed after inserting a clustered index record but before writing all the externally stored columns of that record. Because secondary index entries are inserted after the clustered index record, we may assume that the secondary index record does not exist. However, this situation may only occur during the rollback of incomplete transactions. */ ut_a(thr_is_recv(thr)); } else { err = row_undo_mod_del_mark_or_remove_sec( node, thr, index, entry); if (err != DB_SUCCESS) { break; } } mem_heap_empty(heap); node->index = dict_table_get_next_index(node->index); } mem_heap_free(heap); return(err); } /***********************************************************//** Undoes a modify in secondary indexes when undo record type is DEL_MARK. @return DB_SUCCESS or DB_OUT_OF_FILE_SPACE */ static ulint row_undo_mod_del_mark_sec( /*======================*/ undo_node_t* node, /*!< in: row undo node */ que_thr_t* thr) /*!< in: query thread */ { mem_heap_t* heap; dtuple_t* entry; dict_index_t* index; ulint err; heap = mem_heap_create(1024); while (node->index != NULL) { index = node->index; entry = row_build_index_entry(node->row, node->ext, index, heap); ut_a(entry); err = row_undo_mod_del_unmark_sec_and_undo_update( BTR_MODIFY_LEAF, thr, index, entry); if (err == DB_FAIL) { err = row_undo_mod_del_unmark_sec_and_undo_update( BTR_MODIFY_TREE, thr, index, entry); } if (err != DB_SUCCESS) { mem_heap_free(heap); return(err); } node->index = dict_table_get_next_index(node->index); } mem_heap_free(heap); return(DB_SUCCESS); } /***********************************************************//** Undoes a modify in secondary indexes when undo record type is UPD_EXIST. @return DB_SUCCESS or DB_OUT_OF_FILE_SPACE */ static ulint row_undo_mod_upd_exist_sec( /*=======================*/ undo_node_t* node, /*!< in: row undo node */ que_thr_t* thr) /*!< in: query thread */ { mem_heap_t* heap; dtuple_t* entry; dict_index_t* index; ulint err; if (node->cmpl_info & UPD_NODE_NO_ORD_CHANGE) { /* No change in secondary indexes */ return(DB_SUCCESS); } heap = mem_heap_create(1024); while (node->index != NULL) { index = node->index; if (row_upd_changes_ord_field_binary(node->row, node->index, node->update)) { /* Build the newest version of the index entry */ entry = row_build_index_entry(node->row, node->ext, index, heap); ut_a(entry); /* NOTE that if we updated the fields of a delete-marked secondary index record so that alphabetically they stayed the same, e.g., 'abc' -> 'aBc', we cannot return to the original values because we do not know them. But this should not cause problems because in row0sel.c, in queries we always retrieve the clustered index record or an earlier version of it, if the secondary index record through which we do the search is delete-marked. */ err = row_undo_mod_del_mark_or_remove_sec(node, thr, index, entry); if (err != DB_SUCCESS) { mem_heap_free(heap); return(err); } /* We may have to update the delete mark in the secondary index record of the previous version of the row. We also need to update the fields of the secondary index record if we updated its fields but alphabetically they stayed the same, e.g., 'abc' -> 'aBc'. */ mem_heap_empty(heap); entry = row_build_index_entry(node->undo_row, node->undo_ext, index, heap); ut_a(entry); err = row_undo_mod_del_unmark_sec_and_undo_update( BTR_MODIFY_LEAF, thr, index, entry); if (err == DB_FAIL) { err = row_undo_mod_del_unmark_sec_and_undo_update( BTR_MODIFY_TREE, thr, index, entry); } if (err != DB_SUCCESS) { mem_heap_free(heap); return(err); } } node->index = dict_table_get_next_index(node->index); } mem_heap_free(heap); return(DB_SUCCESS); } /***********************************************************//** Parses the row reference and other info in a modify undo log record. */ static void row_undo_mod_parse_undo_rec( /*========================*/ ib_recovery_t recovery, /*!< in: recovery flag */ undo_node_t* node, /*!< in: row undo node */ que_thr_t* thr) /*!< in: query thread */ { dict_index_t* clust_index; byte* ptr; undo_no_t undo_no; dulint table_id; trx_id_t trx_id; roll_ptr_t roll_ptr; ulint info_bits; ulint type; ulint cmpl_info; ibool dummy_extern; trx_t* trx; ut_ad(node && thr); trx = thr_get_trx(thr); ptr = trx_undo_rec_get_pars(node->undo_rec, &type, &cmpl_info, &dummy_extern, &undo_no, &table_id); node->rec_type = type; node->table = dict_table_get_on_id(recovery, table_id, trx); /* TODO: other fixes associated with DROP TABLE + rollback in the same table by another user */ if (node->table == NULL) { /* Table was dropped */ return; } if (node->table->ibd_file_missing) { /* We skip undo operations to missing .ibd files */ node->table = NULL; return; } clust_index = dict_table_get_first_index(node->table); ptr = trx_undo_update_rec_get_sys_cols(ptr, &trx_id, &roll_ptr, &info_bits); ptr = trx_undo_rec_get_row_ref(ptr, clust_index, &(node->ref), node->heap); trx_undo_update_rec_get_update(ptr, clust_index, type, trx_id, roll_ptr, info_bits, trx, node->heap, &(node->update)); node->new_roll_ptr = roll_ptr; node->new_trx_id = trx_id; node->cmpl_info = cmpl_info; } /***********************************************************//** Undoes a modify operation on a row of a table. @return DB_SUCCESS or error code */ UNIV_INTERN ulint row_undo_mod( /*=========*/ undo_node_t* node, /*!< in: row undo node */ que_thr_t* thr) /*!< in: query thread */ { ulint err; ut_ad(node && thr); ut_ad(node->state == UNDO_NODE_MODIFY); // FIXME: Get rid of this global variable access row_undo_mod_parse_undo_rec(srv_force_recovery, node, thr); if (!node->table || !row_undo_search_clust_to_pcur(node)) { /* It is already undone, or will be undone by another query thread, or table was dropped */ trx_undo_rec_release(node->trx, node->undo_no); node->state = UNDO_NODE_FETCH_NEXT; return(DB_SUCCESS); } node->index = dict_table_get_next_index( dict_table_get_first_index(node->table)); if (node->rec_type == TRX_UNDO_UPD_EXIST_REC) { err = row_undo_mod_upd_exist_sec(node, thr); } else if (node->rec_type == TRX_UNDO_DEL_MARK_REC) { err = row_undo_mod_del_mark_sec(node, thr); } else { ut_ad(node->rec_type == TRX_UNDO_UPD_DEL_REC); err = row_undo_mod_upd_del_sec(node, thr); } if (err != DB_SUCCESS) { return(err); } err = row_undo_mod_clust(node, thr); return(err); } haildb-2.3.2/row/row0vers.c0000644000175000017500000005357311513177357016422 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1997, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file row/row0vers.c Row versions Created 2/6/1997 Heikki Tuuri *******************************************************/ #include "row0vers.h" #ifdef UNIV_NONINL #include "row0vers.ic" #endif #include "dict0dict.h" #include "dict0boot.h" #include "btr0btr.h" #include "mach0data.h" #include "trx0rseg.h" #include "trx0trx.h" #include "trx0roll.h" #include "trx0undo.h" #include "trx0purge.h" #include "trx0rec.h" #include "que0que.h" #include "row0row.h" #include "row0upd.h" #include "rem0cmp.h" #include "read0read.h" #include "lock0lock.h" /*****************************************************************//** Finds out if an active transaction has inserted or modified a secondary index record. NOTE: the kernel mutex is temporarily released in this function! @return NULL if committed, else the active transaction */ UNIV_INTERN trx_t* row_vers_impl_x_locked_off_kernel( /*==============================*/ const rec_t* rec, /*!< in: record in a secondary index */ dict_index_t* index, /*!< in: the secondary index */ const ulint* offsets)/*!< in: rec_get_offsets(rec, index) */ { dict_index_t* clust_index; rec_t* clust_rec; ulint* clust_offsets; rec_t* version; trx_id_t trx_id; mem_heap_t* heap; mem_heap_t* heap2; dtuple_t* row; dtuple_t* entry = NULL; /* assignment to eliminate compiler warning */ trx_t* trx; ulint rec_del; ulint err; mtr_t mtr; ulint comp; ut_ad(mutex_own(&kernel_mutex)); #ifdef UNIV_SYNC_DEBUG ut_ad(!rw_lock_own(&(purge_sys->latch), RW_LOCK_SHARED)); #endif /* UNIV_SYNC_DEBUG */ mutex_exit(&kernel_mutex); mtr_start(&mtr); /* Search for the clustered index record: this is a time-consuming operation: therefore we release the kernel mutex; also, the release is required by the latching order convention. The latch on the clustered index locks the top of the stack of versions. We also reserve purge_latch to lock the bottom of the version stack. */ clust_rec = row_get_clust_rec(BTR_SEARCH_LEAF, rec, index, &clust_index, &mtr); if (!clust_rec) { /* In a rare case it is possible that no clust rec is found for a secondary index record: if in row0umod.c row_undo_mod_remove_clust_low() we have already removed the clust rec, while purge is still cleaning and removing secondary index records associated with earlier versions of the clustered index record. In that case there cannot be any implicit lock on the secondary index record, because an active transaction which has modified the secondary index record has also modified the clustered index record. And in a rollback we always undo the modifications to secondary index records before the clustered index record. */ mutex_enter(&kernel_mutex); mtr_commit(&mtr); return(NULL); } heap = mem_heap_create(1024); clust_offsets = rec_get_offsets(clust_rec, clust_index, NULL, ULINT_UNDEFINED, &heap); trx_id = row_get_rec_trx_id(clust_rec, clust_index, clust_offsets); mtr_s_lock(&(purge_sys->latch), &mtr); mutex_enter(&kernel_mutex); trx = NULL; if (!trx_is_active(trx_id)) { /* The transaction that modified or inserted clust_rec is no longer active: no implicit lock on rec */ goto exit_func; } if (!lock_check_trx_id_sanity(trx_id, clust_rec, clust_index, clust_offsets, TRUE)) { /* Corruption noticed: try to avoid a crash by returning */ goto exit_func; } comp = page_rec_is_comp(rec); ut_ad(index->table == clust_index->table); ut_ad(!!comp == dict_table_is_comp(index->table)); ut_ad(!comp == !page_rec_is_comp(clust_rec)); /* We look up if some earlier version, which was modified by the trx_id transaction, of the clustered index record would require rec to be in a different state (delete marked or unmarked, or have different field values, or not existing). If there is such a version, then rec was modified by the trx_id transaction, and it has an implicit x-lock on rec. Note that if clust_rec itself would require rec to be in a different state, then the trx_id transaction has not yet had time to modify rec, and does not necessarily have an implicit x-lock on rec. */ rec_del = rec_get_deleted_flag(rec, comp); trx = NULL; version = clust_rec; for (;;) { rec_t* prev_version; ulint vers_del; row_ext_t* ext; trx_id_t prev_trx_id; mutex_exit(&kernel_mutex); /* While we retrieve an earlier version of clust_rec, we release the kernel mutex, because it may take time to access the disk. After the release, we have to check if the trx_id transaction is still active. We keep the semaphore in mtr on the clust_rec page, so that no other transaction can update it and get an implicit x-lock on rec. */ heap2 = heap; heap = mem_heap_create(1024); err = trx_undo_prev_version_build(clust_rec, &mtr, version, clust_index, clust_offsets, heap, &prev_version); mem_heap_free(heap2); /* free version and clust_offsets */ if (prev_version == NULL) { mutex_enter(&kernel_mutex); if (!trx_is_active(trx_id)) { /* Transaction no longer active: no implicit x-lock */ break; } /* If the transaction is still active, clust_rec must be a fresh insert, because no previous version was found. */ ut_ad(err == DB_SUCCESS); /* It was a freshly inserted version: there is an implicit x-lock on rec */ trx = trx_get_on_id(trx_id); break; } clust_offsets = rec_get_offsets(prev_version, clust_index, NULL, ULINT_UNDEFINED, &heap); vers_del = rec_get_deleted_flag(prev_version, comp); prev_trx_id = row_get_rec_trx_id(prev_version, clust_index, clust_offsets); /* If the trx_id and prev_trx_id are different and if the prev_version is marked deleted then the prev_trx_id must have already committed for the trx_id to be able to modify the row. Therefore, prev_trx_id cannot hold any implicit lock. */ if (vers_del && 0 != ut_dulint_cmp(trx_id, prev_trx_id)) { mutex_enter(&kernel_mutex); break; } /* The stack of versions is locked by mtr. Thus, it is safe to fetch the prefixes for externally stored columns. */ row = row_build(ROW_COPY_POINTERS, clust_index, prev_version, clust_offsets, NULL, &ext, heap); entry = row_build_index_entry(row, ext, index, heap); /* entry may be NULL if a record was inserted in place of a deleted record, and the BLOB pointers of the new record were not initialized yet. But in that case, prev_version should be NULL. */ ut_a(entry); mutex_enter(&kernel_mutex); if (!trx_is_active(trx_id)) { /* Transaction no longer active: no implicit x-lock */ break; } /* If we get here, we know that the trx_id transaction is still active and it has modified prev_version. Let us check if prev_version would require rec to be in a different state. */ /* The previous version of clust_rec must be accessible, because the transaction is still active and clust_rec was not a fresh insert. */ ut_ad(err == DB_SUCCESS); /* We check if entry and rec are identified in the alphabetical ordering */ if (0 == cmp_dtuple_rec(index->cmp_ctx, entry, rec, offsets)) { /* The delete marks of rec and prev_version should be equal for rec to be in the state required by prev_version */ if (rec_del != vers_del) { trx = trx_get_on_id(trx_id); break; } /* It is possible that the row was updated so that the secondary index record remained the same in alphabetical ordering, but the field values changed still. For example, 'abc' -> 'ABC'. Check also that. */ dtuple_set_types_binary( entry, dtuple_get_n_fields(entry)); if (0 != cmp_dtuple_rec( index->cmp_ctx, entry, rec, offsets)) { trx = trx_get_on_id(trx_id); break; } } else if (!rec_del) { /* The delete mark should be set in rec for it to be in the state required by prev_version */ trx = trx_get_on_id(trx_id); break; } if (0 != ut_dulint_cmp(trx_id, prev_trx_id)) { /* The versions modified by the trx_id transaction end to prev_version: no implicit x-lock */ break; } version = prev_version; }/* for (;;) */ exit_func: mtr_commit(&mtr); mem_heap_free(heap); return(trx); } /*****************************************************************//** Finds out if we must preserve a delete marked earlier version of a clustered index record, because it is >= the purge view. @return TRUE if earlier version should be preserved */ UNIV_INTERN ibool row_vers_must_preserve_del_marked( /*==============================*/ trx_id_t trx_id, /*!< in: transaction id in the version */ mtr_t* mtr) /*!< in: mtr holding the latch on the clustered index record; it will also hold the latch on purge_view */ { #ifdef UNIV_SYNC_DEBUG ut_ad(!rw_lock_own(&(purge_sys->latch), RW_LOCK_SHARED)); #endif /* UNIV_SYNC_DEBUG */ mtr_s_lock(&(purge_sys->latch), mtr); if (trx_purge_update_undo_must_exist(trx_id)) { /* A purge operation is not yet allowed to remove this delete marked record */ return(TRUE); } return(FALSE); } /*****************************************************************//** Finds out if a version of the record, where the version >= the current purge view, should have ientry as its secondary index entry. We check if there is any not delete marked version of the record where the trx id >= purge view, and the secondary index entry and ientry are identified in the alphabetical ordering; exactly in this case we return TRUE. @return TRUE if earlier version should have */ UNIV_INTERN ibool row_vers_old_has_index_entry( /*=========================*/ ibool also_curr,/*!< in: TRUE if also rec is included in the versions to search; otherwise only versions prior to it are searched */ const rec_t* rec, /*!< in: record in the clustered index; the caller must have a latch on the page */ mtr_t* mtr, /*!< in: mtr holding the latch on rec; it will also hold the latch on purge_view */ dict_index_t* index, /*!< in: the secondary index */ const dtuple_t* ientry) /*!< in: the secondary index entry */ { const rec_t* version; rec_t* prev_version; dict_index_t* clust_index; ulint* clust_offsets; mem_heap_t* heap; mem_heap_t* heap2; const dtuple_t* row; const dtuple_t* entry; ulint err; ulint comp; ut_ad(mtr_memo_contains_page(mtr, rec, MTR_MEMO_PAGE_X_FIX) || mtr_memo_contains_page(mtr, rec, MTR_MEMO_PAGE_S_FIX)); #ifdef UNIV_SYNC_DEBUG ut_ad(!rw_lock_own(&(purge_sys->latch), RW_LOCK_SHARED)); #endif /* UNIV_SYNC_DEBUG */ mtr_s_lock(&(purge_sys->latch), mtr); clust_index = dict_table_get_first_index(index->table); comp = page_rec_is_comp(rec); ut_ad(!dict_table_is_comp(index->table) == !comp); heap = mem_heap_create(1024); clust_offsets = rec_get_offsets(rec, clust_index, NULL, ULINT_UNDEFINED, &heap); if (also_curr && !rec_get_deleted_flag(rec, comp)) { row_ext_t* ext; /* The stack of versions is locked by mtr. Thus, it is safe to fetch the prefixes for externally stored columns. */ row = row_build(ROW_COPY_POINTERS, clust_index, rec, clust_offsets, NULL, &ext, heap); entry = row_build_index_entry(row, ext, index, heap); /* If entry == NULL, the record contains unset BLOB pointers. This must be a freshly inserted record. If this is called from row_purge_remove_sec_if_poss_low(), the thread will hold latches on the clustered index and the secondary index. Because the insert works in three steps: (1) insert the record to clustered index (2) store the BLOBs and update BLOB pointers (3) insert records to secondary indexes the purge thread can safely ignore freshly inserted records and delete the secondary index record. The thread that inserted the new record will be inserting the secondary index records. */ /* NOTE that we cannot do the comparison as binary fields because the row is maybe being modified so that the clustered index record has already been updated to a different binary value in a char field, but the collation identifies the old and new value anyway! */ if (entry && !dtuple_coll_cmp(index->cmp_ctx, ientry, entry)) { mem_heap_free(heap); return(TRUE); } } version = rec; for (;;) { heap2 = heap; heap = mem_heap_create(1024); err = trx_undo_prev_version_build(rec, mtr, version, clust_index, clust_offsets, heap, &prev_version); mem_heap_free(heap2); /* free version and clust_offsets */ if (err != DB_SUCCESS || !prev_version) { /* Versions end here */ mem_heap_free(heap); return(FALSE); } clust_offsets = rec_get_offsets(prev_version, clust_index, NULL, ULINT_UNDEFINED, &heap); if (!rec_get_deleted_flag(prev_version, comp)) { row_ext_t* ext; /* The stack of versions is locked by mtr. Thus, it is safe to fetch the prefixes for externally stored columns. */ row = row_build(ROW_COPY_POINTERS, clust_index, prev_version, clust_offsets, NULL, &ext, heap); entry = row_build_index_entry(row, ext, index, heap); /* If entry == NULL, the record contains unset BLOB pointers. This must be a freshly inserted record that we can safely ignore. For the justification, see the comments after the previous row_build_index_entry() call. */ /* NOTE that we cannot do the comparison as binary fields because maybe the secondary index record has already been updated to a different binary value in a char field, but the collation identifies the old and new value anyway! */ if (entry != NULL && !dtuple_coll_cmp( index->cmp_ctx, ientry, entry)) { mem_heap_free(heap); return(TRUE); } } version = prev_version; } } /*****************************************************************//** Constructs the version of a clustered index record which a consistent read should see. We assume that the trx id stored in rec is such that the consistent read should not see rec in its present version. @return DB_SUCCESS or DB_MISSING_HISTORY */ UNIV_INTERN ulint row_vers_build_for_consistent_read( /*===============================*/ const rec_t* rec, /*!< in: record in a clustered index; the caller must have a latch on the page; this latch locks the top of the stack of versions of this records */ mtr_t* mtr, /*!< in: mtr holding the latch on rec */ dict_index_t* index, /*!< in: the clustered index */ ulint** offsets,/*!< in/out: offsets returned by rec_get_offsets(rec, index) */ read_view_t* view, /*!< in: the consistent read view */ mem_heap_t** offset_heap,/*!< in/out: memory heap from which the offsets are allocated */ mem_heap_t* in_heap,/*!< in: memory heap from which the memory for *old_vers is allocated; memory for possible intermediate versions is allocated and freed locally within the function */ rec_t** old_vers)/*!< out, own: old version, or NULL if the record does not exist in the view, that is, it was freshly inserted afterwards */ { const rec_t* version; rec_t* prev_version; trx_id_t trx_id; mem_heap_t* heap = NULL; byte* buf; ulint err; ut_ad(dict_index_is_clust(index)); ut_ad(mtr_memo_contains_page(mtr, rec, MTR_MEMO_PAGE_X_FIX) || mtr_memo_contains_page(mtr, rec, MTR_MEMO_PAGE_S_FIX)); #ifdef UNIV_SYNC_DEBUG ut_ad(!rw_lock_own(&(purge_sys->latch), RW_LOCK_SHARED)); #endif /* UNIV_SYNC_DEBUG */ ut_ad(rec_offs_validate(rec, index, *offsets)); trx_id = row_get_rec_trx_id(rec, index, *offsets); ut_ad(!read_view_sees_trx_id(view, trx_id)); rw_lock_s_lock(&(purge_sys->latch)); version = rec; for (;;) { mem_heap_t* heap2 = heap; trx_undo_rec_t* undo_rec; roll_ptr_t roll_ptr; undo_no_t undo_no; heap = mem_heap_create(1024); /* If we have high-granularity consistent read view and creating transaction of the view is the same as trx_id in the record we see this record only in the case when undo_no of the record is < undo_no in the view. */ if (view->type == VIEW_HIGH_GRANULARITY && ut_dulint_cmp(view->creator_trx_id, trx_id) == 0) { roll_ptr = row_get_rec_roll_ptr(version, index, *offsets); undo_rec = trx_undo_get_undo_rec_low(roll_ptr, heap); undo_no = trx_undo_rec_get_undo_no(undo_rec); mem_heap_empty(heap); if (ut_dulint_cmp(view->undo_no, undo_no) > 0) { /* The view already sees this version: we can copy it to in_heap and return */ buf = mem_heap_alloc(in_heap, rec_offs_size(*offsets)); *old_vers = rec_copy(buf, version, *offsets); rec_offs_make_valid(*old_vers, index, *offsets); err = DB_SUCCESS; break; } } err = trx_undo_prev_version_build(rec, mtr, version, index, *offsets, heap, &prev_version); if (heap2) { mem_heap_free(heap2); /* free version */ } if (err != DB_SUCCESS) { break; } if (prev_version == NULL) { /* It was a freshly inserted version */ *old_vers = NULL; err = DB_SUCCESS; break; } *offsets = rec_get_offsets(prev_version, index, *offsets, ULINT_UNDEFINED, offset_heap); trx_id = row_get_rec_trx_id(prev_version, index, *offsets); if (read_view_sees_trx_id(view, trx_id)) { /* The view already sees this version: we can copy it to in_heap and return */ buf = mem_heap_alloc(in_heap, rec_offs_size(*offsets)); *old_vers = rec_copy(buf, prev_version, *offsets); rec_offs_make_valid(*old_vers, index, *offsets); err = DB_SUCCESS; break; } version = prev_version; }/* for (;;) */ mem_heap_free(heap); rw_lock_s_unlock(&(purge_sys->latch)); return(err); } /*****************************************************************//** Constructs the last committed version of a clustered index record, which should be seen by a semi-consistent read. @return DB_SUCCESS or DB_MISSING_HISTORY */ UNIV_INTERN ulint row_vers_build_for_semi_consistent_read( /*====================================*/ const rec_t* rec, /*!< in: record in a clustered index; the caller must have a latch on the page; this latch locks the top of the stack of versions of this records */ mtr_t* mtr, /*!< in: mtr holding the latch on rec */ dict_index_t* index, /*!< in: the clustered index */ ulint** offsets,/*!< in/out: offsets returned by rec_get_offsets(rec, index) */ mem_heap_t** offset_heap,/*!< in/out: memory heap from which the offsets are allocated */ mem_heap_t* in_heap,/*!< in: memory heap from which the memory for *old_vers is allocated; memory for possible intermediate versions is allocated and freed locally within the function */ const rec_t** old_vers)/*!< out: rec, old version, or NULL if the record does not exist in the view, that is, it was freshly inserted afterwards */ { const rec_t* version; mem_heap_t* heap = NULL; byte* buf; ulint err; trx_id_t rec_trx_id = ut_dulint_zero; ut_ad(dict_index_is_clust(index)); ut_ad(mtr_memo_contains_page(mtr, rec, MTR_MEMO_PAGE_X_FIX) || mtr_memo_contains_page(mtr, rec, MTR_MEMO_PAGE_S_FIX)); #ifdef UNIV_SYNC_DEBUG ut_ad(!rw_lock_own(&(purge_sys->latch), RW_LOCK_SHARED)); #endif /* UNIV_SYNC_DEBUG */ ut_ad(rec_offs_validate(rec, index, *offsets)); rw_lock_s_lock(&(purge_sys->latch)); /* The S-latch on purge_sys prevents the purge view from changing. Thus, if we have an uncommitted transaction at this point, then purge cannot remove its undo log even if the transaction could commit now. */ version = rec; for (;;) { trx_t* version_trx; mem_heap_t* heap2; rec_t* prev_version; trx_id_t version_trx_id; version_trx_id = row_get_rec_trx_id(version, index, *offsets); if (rec == version) { rec_trx_id = version_trx_id; } mutex_enter(&kernel_mutex); version_trx = trx_get_on_id(version_trx_id); mutex_exit(&kernel_mutex); if (!version_trx || version_trx->conc_state == TRX_NOT_STARTED || version_trx->conc_state == TRX_COMMITTED_IN_MEMORY) { /* We found a version that belongs to a committed transaction: return it. */ if (rec == version) { *old_vers = rec; err = DB_SUCCESS; break; } /* We assume that a rolled-back transaction stays in TRX_ACTIVE state until all the changes have been rolled back and the transaction is removed from the global list of transactions. */ if (!ut_dulint_cmp(rec_trx_id, version_trx_id)) { /* The transaction was committed while we searched for earlier versions. Return the current version as a semi-consistent read. */ version = rec; *offsets = rec_get_offsets(version, index, *offsets, ULINT_UNDEFINED, offset_heap); } buf = mem_heap_alloc(in_heap, rec_offs_size(*offsets)); *old_vers = rec_copy(buf, version, *offsets); rec_offs_make_valid(*old_vers, index, *offsets); err = DB_SUCCESS; break; } heap2 = heap; heap = mem_heap_create(1024); err = trx_undo_prev_version_build(rec, mtr, version, index, *offsets, heap, &prev_version); if (heap2) { mem_heap_free(heap2); /* free version */ } if (UNIV_UNLIKELY(err != DB_SUCCESS)) { break; } if (prev_version == NULL) { /* It was a freshly inserted version */ *old_vers = NULL; err = DB_SUCCESS; break; } version = prev_version; *offsets = rec_get_offsets(version, index, *offsets, ULINT_UNDEFINED, offset_heap); }/* for (;;) */ if (heap) { mem_heap_free(heap); } rw_lock_s_unlock(&(purge_sys->latch)); return(err); } haildb-2.3.2/row/row0uins.c0000644000175000017500000002164711513177357016416 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1997, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file row/row0uins.c Fresh insert undo Created 2/25/1997 Heikki Tuuri *******************************************************/ #include "row0uins.h" #ifdef UNIV_NONINL #include "row0uins.ic" #endif #include "dict0dict.h" #include "dict0boot.h" #include "dict0crea.h" #include "trx0undo.h" #include "trx0roll.h" #include "btr0btr.h" #include "mach0data.h" #include "row0undo.h" #include "row0vers.h" #include "trx0trx.h" #include "trx0rec.h" #include "row0row.h" #include "row0upd.h" #include "que0que.h" #include "ibuf0ibuf.h" #include "log0log.h" /***************************************************************//** Removes a clustered index record. The pcur in node was positioned on the record, now it is detached. @return DB_SUCCESS or DB_OUT_OF_FILE_SPACE */ static ulint row_undo_ins_remove_clust_rec( /*==========================*/ undo_node_t* node) /*!< in: undo node */ { btr_cur_t* btr_cur; ibool success; ulint err; ulint n_tries = 0; mtr_t mtr; mtr_start(&mtr); success = btr_pcur_restore_position(BTR_MODIFY_LEAF, &(node->pcur), &mtr); ut_a(success); if (ut_dulint_cmp(node->table->id, DICT_INDEXES_ID) == 0) { ut_ad(node->trx->dict_operation_lock_mode == RW_X_LATCH); /* Drop the index tree associated with the row in SYS_INDEXES table: */ dict_drop_index_tree(btr_pcur_get_rec(&(node->pcur)), &mtr); mtr_commit(&mtr); mtr_start(&mtr); success = btr_pcur_restore_position(BTR_MODIFY_LEAF, &(node->pcur), &mtr); ut_a(success); } btr_cur = btr_pcur_get_btr_cur(&(node->pcur)); success = btr_cur_optimistic_delete(btr_cur, &mtr); btr_pcur_commit_specify_mtr(&(node->pcur), &mtr); if (success) { trx_undo_rec_release(node->trx, node->undo_no); return(DB_SUCCESS); } retry: /* If did not succeed, try pessimistic descent to tree */ mtr_start(&mtr); success = btr_pcur_restore_position(BTR_MODIFY_TREE, &(node->pcur), &mtr); ut_a(success); btr_cur_pessimistic_delete(&err, FALSE, btr_cur, trx_is_recv(node->trx) ? RB_RECOVERY : RB_NORMAL, &mtr); /* The delete operation may fail if we have little file space left: TODO: easiest to crash the database and restart with more file space */ if (err == DB_OUT_OF_FILE_SPACE && n_tries < BTR_CUR_RETRY_DELETE_N_TIMES) { btr_pcur_commit_specify_mtr(&(node->pcur), &mtr); n_tries++; os_thread_sleep(BTR_CUR_RETRY_SLEEP_TIME); goto retry; } btr_pcur_commit_specify_mtr(&(node->pcur), &mtr); trx_undo_rec_release(node->trx, node->undo_no); return(err); } /***************************************************************//** Removes a secondary index entry if found. @return DB_SUCCESS, DB_FAIL, or DB_OUT_OF_FILE_SPACE */ static ulint row_undo_ins_remove_sec_low( /*========================*/ ulint mode, /*!< in: BTR_MODIFY_LEAF or BTR_MODIFY_TREE, depending on whether we wish optimistic or pessimistic descent down the index tree */ dict_index_t* index, /*!< in: index */ dtuple_t* entry) /*!< in: index entry to remove */ { btr_pcur_t pcur; btr_cur_t* btr_cur; ibool found; ibool success; ulint err; mtr_t mtr; log_free_check(); mtr_start(&mtr); found = row_search_index_entry(index, entry, mode, &pcur, &mtr); btr_cur = btr_pcur_get_btr_cur(&pcur); if (!found) { /* Not found */ btr_pcur_close(&pcur); mtr_commit(&mtr); return(DB_SUCCESS); } if (mode == BTR_MODIFY_LEAF) { success = btr_cur_optimistic_delete(btr_cur, &mtr); if (success) { err = DB_SUCCESS; } else { err = DB_FAIL; } } else { ut_ad(mode == BTR_MODIFY_TREE); /* No need to distinguish RB_RECOVERY here, because we are deleting a secondary index record: the distinction between RB_NORMAL and RB_RECOVERY only matters when deleting a record that contains externally stored columns. */ ut_ad(!dict_index_is_clust(index)); btr_cur_pessimistic_delete(&err, FALSE, btr_cur, RB_NORMAL, &mtr); } btr_pcur_close(&pcur); mtr_commit(&mtr); return(err); } /***************************************************************//** Removes a secondary index entry from the index if found. Tries first optimistic, then pessimistic descent down the tree. @return DB_SUCCESS or DB_OUT_OF_FILE_SPACE */ static ulint row_undo_ins_remove_sec( /*====================*/ dict_index_t* index, /*!< in: index */ dtuple_t* entry) /*!< in: index entry to insert */ { ulint err; ulint n_tries = 0; /* Try first optimistic descent to the B-tree */ err = row_undo_ins_remove_sec_low(BTR_MODIFY_LEAF, index, entry); if (err == DB_SUCCESS) { return(err); } /* Try then pessimistic descent to the B-tree */ retry: err = row_undo_ins_remove_sec_low(BTR_MODIFY_TREE, index, entry); /* The delete operation may fail if we have little file space left: TODO: easiest to crash the database and restart with more file space */ if (err != DB_SUCCESS && n_tries < BTR_CUR_RETRY_DELETE_N_TIMES) { n_tries++; os_thread_sleep(BTR_CUR_RETRY_SLEEP_TIME); goto retry; } return(err); } /***********************************************************//** Parses the row reference and other info in a fresh insert undo record. */ static void row_undo_ins_parse_undo_rec( /*========================*/ ib_recovery_t recovery, /*!< in: recovery flag */ undo_node_t* node) /*!< in/out: row undo node */ { dict_index_t* clust_index; byte* ptr; undo_no_t undo_no; dulint table_id; ulint type; ulint dummy; ibool dummy_extern; ut_ad(node); ptr = trx_undo_rec_get_pars(node->undo_rec, &type, &dummy, &dummy_extern, &undo_no, &table_id); ut_ad(type == TRX_UNDO_INSERT_REC); node->rec_type = type; node->update = NULL; node->table = dict_table_get_on_id( srv_force_recovery, table_id, node->trx); /* Skip the UNDO if we can't find the table or the .ibd file. */ if (UNIV_UNLIKELY(node->table == NULL)) { } else if (UNIV_UNLIKELY(node->table->ibd_file_missing)) { node->table = NULL; } else { clust_index = dict_table_get_first_index(node->table); if (clust_index != NULL) { ptr = trx_undo_rec_get_row_ref( ptr, clust_index, &node->ref, node->heap); } else { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: table "); ut_print_name(ib_stream, node->trx, TRUE, node->table->name); ib_logger(ib_stream, " has no indexes, " "ignoring the table\n"); node->table = NULL; } } } /***********************************************************//** Undoes a fresh insert of a row to a table. A fresh insert means that the same clustered index unique key did not have any record, even delete marked, at the time of the insert. InnoDB is eager in a rollback: if it figures out that an index record will be removed in the purge anyway, it will remove it in the rollback. @return DB_SUCCESS or DB_OUT_OF_FILE_SPACE */ UNIV_INTERN ulint row_undo_ins( /*=========*/ undo_node_t* node) /*!< in: row undo node */ { ut_ad(node); ut_ad(node->state == UNDO_NODE_INSERT); row_undo_ins_parse_undo_rec(srv_force_recovery, node); if (!node->table || !row_undo_search_clust_to_pcur(node)) { trx_undo_rec_release(node->trx, node->undo_no); return(DB_SUCCESS); } /* Iterate over all the indexes and undo the insert.*/ /* Skip the clustered index (the first index) */ node->index = dict_table_get_next_index( dict_table_get_first_index(node->table)); while (node->index != NULL) { dtuple_t* entry; ulint err; entry = row_build_index_entry(node->row, node->ext, node->index, node->heap); if (UNIV_UNLIKELY(!entry)) { /* The database must have crashed after inserting a clustered index record but before writing all the externally stored columns of that record. Because secondary index entries are inserted after the clustered index record, we may assume that the secondary index record does not exist. However, this situation may only occur during the rollback of incomplete transactions. */ ut_a(trx_is_recv(node->trx)); } else { err = row_undo_ins_remove_sec(node->index, entry); if (err != DB_SUCCESS) { return(err); } } node->index = dict_table_get_next_index(node->index); } return(row_undo_ins_remove_clust_rec(node)); } haildb-2.3.2/row/row0purge.c0000644000175000017500000004122011513177357016547 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1997, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file row/row0purge.c Purge obsolete records Created 3/14/1997 Heikki Tuuri *******************************************************/ #include "row0purge.h" #ifdef UNIV_NONINL #include "row0purge.ic" #endif #include "fsp0fsp.h" #include "mach0data.h" #include "trx0rseg.h" #include "trx0trx.h" #include "trx0roll.h" #include "trx0undo.h" #include "trx0purge.h" #include "trx0rec.h" #include "que0que.h" #include "row0row.h" #include "row0upd.h" #include "row0vers.h" #include "log0log.h" /********************************************************************//** Creates a purge node to a query graph. @return own: purge node */ UNIV_INTERN purge_node_t* row_purge_node_create( /*==================*/ que_thr_t* parent, /*!< in: parent node, i.e., a thr node */ mem_heap_t* heap) /*!< in: memory heap where created */ { purge_node_t* node; ut_ad(parent && heap); node = mem_heap_alloc(heap, sizeof(purge_node_t)); node->common.type = QUE_NODE_PURGE; node->common.parent = parent; node->heap = mem_heap_create(256); return(node); } /***********************************************************//** Repositions the pcur in the purge node on the clustered index record, if found. @return TRUE if the record was found */ static ibool row_purge_reposition_pcur( /*======================*/ ulint mode, /*!< in: latching mode */ purge_node_t* node, /*!< in: row purge node */ mtr_t* mtr) /*!< in: mtr */ { ibool found; if (node->found_clust) { found = btr_pcur_restore_position(mode, &(node->pcur), mtr); return(found); } found = row_search_on_row_ref(&(node->pcur), mode, node->table, node->ref, mtr); node->found_clust = found; if (found) { btr_pcur_store_position(&(node->pcur), mtr); } return(found); } /***********************************************************//** Removes a delete marked clustered index record if possible. @return TRUE if success, or if not found, or if modified after the delete marking */ static ibool row_purge_remove_clust_if_poss_low( /*===============================*/ purge_node_t* node, /*!< in: row purge node */ ulint mode) /*!< in: BTR_MODIFY_LEAF or BTR_MODIFY_TREE */ { dict_index_t* index; btr_pcur_t* pcur; btr_cur_t* btr_cur; ibool success; ulint err; mtr_t mtr; rec_t* rec; mem_heap_t* heap = NULL; ulint offsets_[REC_OFFS_NORMAL_SIZE]; rec_offs_init(offsets_); index = dict_table_get_first_index(node->table); pcur = &(node->pcur); btr_cur = btr_pcur_get_btr_cur(pcur); mtr_start(&mtr); success = row_purge_reposition_pcur(mode, node, &mtr); if (!success) { /* The record is already removed */ btr_pcur_commit_specify_mtr(pcur, &mtr); return(TRUE); } rec = btr_pcur_get_rec(pcur); if (0 != ut_dulint_cmp(node->roll_ptr, row_get_rec_roll_ptr( rec, index, rec_get_offsets( rec, index, offsets_, ULINT_UNDEFINED, &heap)))) { if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } /* Someone else has modified the record later: do not remove */ btr_pcur_commit_specify_mtr(pcur, &mtr); return(TRUE); } if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } if (mode == BTR_MODIFY_LEAF) { success = btr_cur_optimistic_delete(btr_cur, &mtr); } else { ut_ad(mode == BTR_MODIFY_TREE); btr_cur_pessimistic_delete(&err, FALSE, btr_cur, RB_NONE, &mtr); if (err == DB_SUCCESS) { success = TRUE; } else if (err == DB_OUT_OF_FILE_SPACE) { success = FALSE; } else { ut_error; } } btr_pcur_commit_specify_mtr(pcur, &mtr); return(success); } /***********************************************************//** Removes a clustered index record if it has not been modified after the delete marking. */ static void row_purge_remove_clust_if_poss( /*===========================*/ purge_node_t* node) /*!< in: row purge node */ { ibool success; ulint n_tries = 0; /* ib_logger(ib_stream, "Purge: Removing clustered record\n"); */ success = row_purge_remove_clust_if_poss_low(node, BTR_MODIFY_LEAF); if (success) { return; } retry: success = row_purge_remove_clust_if_poss_low(node, BTR_MODIFY_TREE); /* The delete operation may fail if we have little file space left: TODO: easiest to crash the database and restart with more file space */ if (!success && n_tries < BTR_CUR_RETRY_DELETE_N_TIMES) { n_tries++; os_thread_sleep(BTR_CUR_RETRY_SLEEP_TIME); goto retry; } ut_a(success); } /***********************************************************//** Removes a secondary index entry if possible. @return TRUE if success or if not found */ static ibool row_purge_remove_sec_if_poss_low( /*=============================*/ purge_node_t* node, /*!< in: row purge node */ dict_index_t* index, /*!< in: index */ const dtuple_t* entry, /*!< in: index entry */ ulint mode) /*!< in: latch mode BTR_MODIFY_LEAF or BTR_MODIFY_TREE */ { btr_pcur_t pcur; btr_cur_t* btr_cur; ibool success; ibool old_has = 0; /* remove warning */ ibool found; ulint err; mtr_t mtr; mtr_t mtr_vers; log_free_check(); mtr_start(&mtr); found = row_search_index_entry(index, entry, mode, &pcur, &mtr); if (!found) { /* Not found. This is a legitimate condition. In a rollback, InnoDB will remove secondary recs that would be purged anyway. Then the actual purge will not find the secondary index record. Also, the purge itself is eager: if it comes to consider a secondary index record, and notices it does not need to exist in the index, it will remove it. Then if/when the purge comes to consider the secondary index record a second time, it will not exist any more in the index. */ /* ib_logger(ib_stream, "PURGE:........sec entry not found\n"); */ /* dtuple_print(ib_stream, entry); */ btr_pcur_close(&pcur); mtr_commit(&mtr); return(TRUE); } btr_cur = btr_pcur_get_btr_cur(&pcur); /* We should remove the index record if no later version of the row, which cannot be purged yet, requires its existence. If some requires, we should do nothing. */ mtr_start(&mtr_vers); success = row_purge_reposition_pcur(BTR_SEARCH_LEAF, node, &mtr_vers); if (success) { old_has = row_vers_old_has_index_entry( TRUE, btr_pcur_get_rec(&(node->pcur)), &mtr_vers, index, entry); } btr_pcur_commit_specify_mtr(&(node->pcur), &mtr_vers); if (!success || !old_has) { /* Remove the index record */ if (mode == BTR_MODIFY_LEAF) { success = btr_cur_optimistic_delete(btr_cur, &mtr); } else { ut_ad(mode == BTR_MODIFY_TREE); btr_cur_pessimistic_delete(&err, FALSE, btr_cur, RB_NONE, &mtr); success = err == DB_SUCCESS; ut_a(success || err == DB_OUT_OF_FILE_SPACE); } } btr_pcur_close(&pcur); mtr_commit(&mtr); return(success); } /***********************************************************//** Removes a secondary index entry if possible. */ static void row_purge_remove_sec_if_poss( /*=========================*/ purge_node_t* node, /*!< in: row purge node */ dict_index_t* index, /*!< in: index */ dtuple_t* entry) /*!< in: index entry */ { ibool success; ulint n_tries = 0; /* ib_logger(ib_stream, "Purge: Removing secondary record\n"); */ success = row_purge_remove_sec_if_poss_low(node, index, entry, BTR_MODIFY_LEAF); if (success) { return; } retry: success = row_purge_remove_sec_if_poss_low(node, index, entry, BTR_MODIFY_TREE); /* The delete operation may fail if we have little file space left: TODO: easiest to crash the database and restart with more file space */ if (!success && n_tries < BTR_CUR_RETRY_DELETE_N_TIMES) { n_tries++; os_thread_sleep(BTR_CUR_RETRY_SLEEP_TIME); goto retry; } ut_a(success); } /***********************************************************//** Purges a delete marking of a record. */ static void row_purge_del_mark( /*===============*/ purge_node_t* node) /*!< in: row purge node */ { mem_heap_t* heap; dtuple_t* entry; dict_index_t* index; ut_ad(node); heap = mem_heap_create(1024); while (node->index != NULL) { index = node->index; /* Build the index entry */ entry = row_build_index_entry(node->row, NULL, index, heap); ut_a(entry); row_purge_remove_sec_if_poss(node, index, entry); node->index = dict_table_get_next_index(node->index); } mem_heap_free(heap); row_purge_remove_clust_if_poss(node); } /***********************************************************//** Purges an update of an existing record. Also purges an update of a delete marked record if that record contained an externally stored field. */ static void row_purge_upd_exist_or_extern( /*==========================*/ purge_node_t* node) /*!< in: row purge node */ { mem_heap_t* heap; dtuple_t* entry; dict_index_t* index; ibool is_insert; ulint rseg_id; ulint page_no; ulint offset; ulint i; mtr_t mtr; ut_ad(node); if (node->rec_type == TRX_UNDO_UPD_DEL_REC) { goto skip_secondaries; } heap = mem_heap_create(1024); while (node->index != NULL) { index = node->index; if (row_upd_changes_ord_field_binary(NULL, node->index, node->update)) { /* Build the older version of the index entry */ entry = row_build_index_entry(node->row, NULL, index, heap); ut_a(entry); row_purge_remove_sec_if_poss(node, index, entry); } node->index = dict_table_get_next_index(node->index); } mem_heap_free(heap); skip_secondaries: /* Free possible externally stored fields */ for (i = 0; i < upd_get_n_fields(node->update); i++) { const upd_field_t* ufield = upd_get_nth_field(node->update, i); if (dfield_is_ext(&ufield->new_val)) { buf_block_t* block; ulint internal_offset; byte* data_field; /* We use the fact that new_val points to node->undo_rec and get thus the offset of dfield data inside the undo record. Then we can calculate from node->roll_ptr the file address of the new_val data */ internal_offset = ((const byte*) dfield_get_data(&ufield->new_val)) - node->undo_rec; ut_a(internal_offset < UNIV_PAGE_SIZE); trx_undo_decode_roll_ptr(node->roll_ptr, &is_insert, &rseg_id, &page_no, &offset); mtr_start(&mtr); /* We have to acquire an X-latch to the clustered index tree */ index = dict_table_get_first_index(node->table); mtr_x_lock(dict_index_get_lock(index), &mtr); /* NOTE: we must also acquire an X-latch to the root page of the tree. We will need it when we free pages from the tree. If the tree is of height 1, the tree X-latch does NOT protect the root page, because it is also a leaf page. Since we will have a latch on an undo log page, we would break the latching order if we would only later latch the root page of such a tree! */ btr_root_get(index, &mtr); /* We assume in purge of externally stored fields that the space id of the undo log record is 0! */ block = buf_page_get(0, 0, page_no, RW_X_LATCH, &mtr); buf_block_dbg_add_level(block, SYNC_TRX_UNDO_PAGE); data_field = buf_block_get_frame(block) + offset + internal_offset; ut_a(dfield_get_len(&ufield->new_val) >= BTR_EXTERN_FIELD_REF_SIZE); btr_free_externally_stored_field( index, data_field + dfield_get_len(&ufield->new_val) - BTR_EXTERN_FIELD_REF_SIZE, NULL, NULL, NULL, 0, RB_NONE, &mtr); mtr_commit(&mtr); } } } /***********************************************************//** Parses the row reference and other info in a modify undo log record. @return TRUE if purge operation required: NOTE that then the CALLER must unfreeze data dictionary! */ static ibool row_purge_parse_undo_rec( /*=====================*/ purge_node_t* node, /*!< in: row undo node */ ibool* updated_extern, /*!< out: TRUE if an externally stored field was updated */ que_thr_t* thr) /*!< in: query thread */ { dict_index_t* clust_index; byte* ptr; trx_t* trx; undo_no_t undo_no; dulint table_id; trx_id_t trx_id; roll_ptr_t roll_ptr; ulint info_bits; ulint type; ulint cmpl_info; ut_ad(node && thr); trx = thr_get_trx(thr); ptr = trx_undo_rec_get_pars(node->undo_rec, &type, &cmpl_info, updated_extern, &undo_no, &table_id); node->rec_type = type; if (type == TRX_UNDO_UPD_DEL_REC && !(*updated_extern)) { return(FALSE); } ptr = trx_undo_update_rec_get_sys_cols(ptr, &trx_id, &roll_ptr, &info_bits); node->table = NULL; if (type == TRX_UNDO_UPD_EXIST_REC && cmpl_info & UPD_NODE_NO_ORD_CHANGE && !(*updated_extern)) { /* Purge requires no changes to indexes: we may return */ return(FALSE); } /* Prevent DROP TABLE etc. from running when we are doing the purge for this row */ dict_freeze_data_dictionary(trx); mutex_enter(&(dict_sys->mutex)); // FIXME: srv_force_recovery should be passed in as an arg node->table = dict_table_get_on_id_low(srv_force_recovery, table_id); mutex_exit(&(dict_sys->mutex)); if (node->table == NULL) { /* The table has been dropped: no need to do purge */ err_exit: dict_unfreeze_data_dictionary(trx); return(FALSE); } if (node->table->ibd_file_missing) { /* We skip purge of missing .ibd files */ node->table = NULL; goto err_exit; } clust_index = dict_table_get_first_index(node->table); if (clust_index == NULL) { /* The table was corrupt in the data dictionary */ goto err_exit; } ptr = trx_undo_rec_get_row_ref(ptr, clust_index, &(node->ref), node->heap); ptr = trx_undo_update_rec_get_update(ptr, clust_index, type, trx_id, roll_ptr, info_bits, trx, node->heap, &(node->update)); /* Read to the partial row the fields that occur in indexes */ if (!(cmpl_info & UPD_NODE_NO_ORD_CHANGE)) { ptr = trx_undo_rec_get_partial_row( ptr, clust_index, &node->row, type == TRX_UNDO_UPD_DEL_REC, node->heap); } return(TRUE); } /***********************************************************//** Fetches an undo log record and does the purge for the recorded operation. If none left, or the current purge completed, returns the control to the parent node, which is always a query thread node. @return DB_SUCCESS if operation successfully completed, else error code */ static ulint row_purge( /*======*/ purge_node_t* node, /*!< in: row purge node */ que_thr_t* thr) /*!< in: query thread */ { roll_ptr_t roll_ptr; ibool purge_needed; ibool updated_extern; trx_t* trx; ut_ad(node && thr); trx = thr_get_trx(thr); node->undo_rec = trx_purge_fetch_next_rec(&roll_ptr, &(node->reservation), node->heap); if (!node->undo_rec) { /* Purge completed for this query thread */ thr->run_node = que_node_get_parent(node); return(DB_SUCCESS); } node->roll_ptr = roll_ptr; if (node->undo_rec == &trx_purge_dummy_rec) { purge_needed = FALSE; } else { purge_needed = row_purge_parse_undo_rec(node, &updated_extern, thr); /* If purge_needed == TRUE, we must also remember to unfreeze data dictionary! */ } if (purge_needed) { dict_index_t* clust_index; clust_index = dict_table_get_first_index(node->table); node->found_clust = FALSE; node->index = dict_table_get_next_index(clust_index); if (node->rec_type == TRX_UNDO_DEL_MARK_REC) { row_purge_del_mark(node); } else if (updated_extern || node->rec_type == TRX_UNDO_UPD_EXIST_REC) { row_purge_upd_exist_or_extern(node); } if (node->found_clust) { btr_pcur_close(&(node->pcur)); } dict_unfreeze_data_dictionary(trx); } /* Do some cleanup */ trx_purge_rec_release(node->reservation); mem_heap_empty(node->heap); thr->run_node = node; return(DB_SUCCESS); } /***********************************************************//** Does the purge operation for a single undo log record. This is a high-level function used in an SQL execution graph. @return query thread to run next or NULL */ UNIV_INTERN que_thr_t* row_purge_step( /*===========*/ que_thr_t* thr) /*!< in: query thread */ { purge_node_t* node; ulint err; ut_ad(thr); node = thr->run_node; ut_ad(que_node_get_type(node) == QUE_NODE_PURGE); err = row_purge(node, thr); ut_ad(err == DB_SUCCESS); return(thr); } haildb-2.3.2/row/row0sel.c0000644000175000017500000031553611513177357016226 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1997, 2010, Innobase Oy. All Rights Reserved. Copyright (c) 2008, Google Inc. Portions of this file contain modifications contributed and copyrighted by Google, Inc. Those modifications are gratefully acknowledged and are described briefly in the InnoDB documentation. The contributions by Google are incorporated with their permission, and subject to the conditions contained in the file COPYING.Google. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /***************************************************//** @file row/row0sel.c Select Created 12/19/1997 Heikki Tuuri *******************************************************/ #include "row0sel.h" #include "row0prebuilt.h" #ifdef UNIV_NONINL #include "row0sel.ic" #endif #include "dict0dict.h" #include "dict0boot.h" #include "trx0undo.h" #include "trx0trx.h" #include "btr0btr.h" #include "btr0cur.h" #include "btr0sea.h" #include "mach0data.h" #include "que0que.h" #include "row0upd.h" #include "row0row.h" #include "row0vers.h" #include "rem0cmp.h" #include "lock0lock.h" #include "eval0eval.h" #include "pars0sym.h" #include "pars0pars.h" #include "read0read.h" #include "buf0lru.h" #include "api0ucode.h" #include "api0misc.h" /* Maximum number of rows to prefetch. */ #define SEL_MAX_N_PREFETCH FETCH_CACHE_SIZE /* Number of rows fetched, after which to start prefetching. */ #define SEL_PREFETCH_LIMIT 1 /* When a select has accessed about this many pages, it returns control back to que_run_threads: this is to allow canceling runaway queries */ #define SEL_COST_LIMIT 100 /* Flags for search shortcut */ #define SEL_FOUND 0 #define SEL_EXHAUSTED 1 #define SEL_RETRY 2 /********************************************************************//** Returns TRUE if the user-defined column in a secondary index record is alphabetically the same as the corresponding BLOB column in the clustered index record. NOTE: the comparison is NOT done as a binary comparison, but character fields are compared with collation! @return TRUE if the columns are equal */ static ibool row_sel_sec_rec_is_for_blob( /*========================*/ ulint mtype, /*!< in: main type */ ulint prtype, /*!< in: precise type */ ulint mbminlen, /*!< in: minimum length of a multi-byte character */ ulint mbmaxlen, /*!< in: maximum length of a multi-byte character */ const byte* clust_field, /*!< in: the locally stored part of the clustered index column, including the BLOB pointer; the clustered index record must be covered by a lock or a page latch to protect it against deletion (rollback or purge) */ ulint clust_len, /*!< in: length of clust_field */ const byte* sec_field, /*!< in: column in secondary index */ ulint sec_len, /*!< in: length of sec_field */ ulint zip_size) /*!< in: compressed page size, or 0 */ { ulint len; byte buf[DICT_MAX_INDEX_COL_LEN]; len = btr_copy_externally_stored_field_prefix(buf, sizeof buf, zip_size, clust_field, clust_len); if (UNIV_UNLIKELY(len == 0)) { /* The BLOB was being deleted as the server crashed. There should not be any secondary index records referring to this clustered index record, because btr_free_externally_stored_field() is called after all secondary index entries of the row have been purged. */ return(FALSE); } len = dtype_get_at_most_n_mbchars(prtype, mbminlen, mbmaxlen, sec_len, len, (const char*) buf); /* FIXME: Pass a NULL compare context, the compare context will be required once we support comparison operations outside of rem0cmp.c. */ return(!cmp_data_data( NULL, mtype, prtype, buf, len, sec_field, sec_len)); } /********************************************************************//** Returns TRUE if the user-defined column values in a secondary index record are alphabetically the same as the corresponding columns in the clustered index record. NOTE: the comparison is NOT done as a binary comparison, but character fields are compared with collation! @return TRUE if the secondary record is equal to the corresponding fields in the clustered record, when compared with collation; FALSE if not equal or if the clustered record has been marked for deletion */ static ibool row_sel_sec_rec_is_for_clust_rec( /*=============================*/ const rec_t* sec_rec, /*!< in: secondary index record */ dict_index_t* sec_index, /*!< in: secondary index */ const rec_t* clust_rec, /*!< in: clustered index record; must be protected by a lock or a page latch against deletion in rollback or purge */ dict_index_t* clust_index) /*!< in: clustered index */ { const byte* sec_field; ulint sec_len; const byte* clust_field; ulint n; ulint i; mem_heap_t* heap = NULL; ulint clust_offsets_[REC_OFFS_NORMAL_SIZE]; ulint sec_offsets_[REC_OFFS_SMALL_SIZE]; ulint* clust_offs = clust_offsets_; ulint* sec_offs = sec_offsets_; ibool is_equal = TRUE; rec_offs_init(clust_offsets_); rec_offs_init(sec_offsets_); if (rec_get_deleted_flag(clust_rec, dict_table_is_comp(clust_index->table))) { /* The clustered index record is delete-marked; it is not visible in the read view. Besides, if there are any externally stored columns, some of them may have already been purged. */ return(FALSE); } clust_offs = rec_get_offsets(clust_rec, clust_index, clust_offs, ULINT_UNDEFINED, &heap); sec_offs = rec_get_offsets(sec_rec, sec_index, sec_offs, ULINT_UNDEFINED, &heap); n = dict_index_get_n_ordering_defined_by_user(sec_index); for (i = 0; i < n; i++) { const dict_field_t* ifield; const dict_col_t* col; ulint clust_pos; ulint clust_len; ulint len; ifield = dict_index_get_nth_field(sec_index, i); col = dict_field_get_col(ifield); clust_pos = dict_col_get_clust_pos(col, clust_index); clust_field = rec_get_nth_field( clust_rec, clust_offs, clust_pos, &clust_len); sec_field = rec_get_nth_field(sec_rec, sec_offs, i, &sec_len); len = clust_len; if (ifield->prefix_len > 0 && len != UNIV_SQL_NULL) { if (rec_offs_nth_extern(clust_offs, clust_pos)) { len -= BTR_EXTERN_FIELD_REF_SIZE; } len = dtype_get_at_most_n_mbchars( col->prtype, col->mbminlen, col->mbmaxlen, ifield->prefix_len, len, (char*) clust_field); if (rec_offs_nth_extern(clust_offs, clust_pos) && len < sec_len) { if (!row_sel_sec_rec_is_for_blob( col->mtype, col->prtype, col->mbminlen, col->mbmaxlen, clust_field, clust_len, sec_field, sec_len, dict_table_zip_size( clust_index->table))) { goto inequal; } continue; } } if (0 != cmp_data_data( clust_index->cmp_ctx, col->mtype, col->prtype, clust_field, len, sec_field, sec_len)) { inequal: is_equal = FALSE; goto func_exit; } } func_exit: if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } return(is_equal); } /*********************************************************************//** Creates a select node struct. @return own: select node struct */ UNIV_INTERN sel_node_t* sel_node_create( /*============*/ mem_heap_t* heap) /*!< in: memory heap where created */ { sel_node_t* node; node = mem_heap_alloc(heap, sizeof(sel_node_t)); node->common.type = QUE_NODE_SELECT; node->state = SEL_NODE_OPEN; node->plans = NULL; return(node); } /*********************************************************************//** Frees the memory private to a select node when a query graph is freed, does not free the heap where the node was originally created. */ UNIV_INTERN void sel_node_free_private( /*==================*/ sel_node_t* node) /*!< in: select node struct */ { ulint i; plan_t* plan; if (node->plans != NULL) { for (i = 0; i < node->n_tables; i++) { plan = sel_node_get_nth_plan(node, i); btr_pcur_close(&(plan->pcur)); btr_pcur_close(&(plan->clust_pcur)); if (plan->old_vers_heap) { mem_heap_free(plan->old_vers_heap); } } } } /*********************************************************************//** Evaluates the values in a select list. If there are aggregate functions, their argument value is added to the aggregate total. */ UNIV_INLINE void sel_eval_select_list( /*=================*/ sel_node_t* node) /*!< in: select node */ { que_node_t* exp; exp = node->select_list; while (exp) { eval_exp(exp); exp = que_node_get_next(exp); } } /*********************************************************************//** Assigns the values in the select list to the possible into-variables in SELECT ... INTO ... */ UNIV_INLINE void sel_assign_into_var_values( /*=======================*/ sym_node_t* var, /*!< in: first variable in a list of variables */ sel_node_t* node) /*!< in: select node */ { que_node_t* exp; if (var == NULL) { return; } exp = node->select_list; while (var) { ut_ad(exp); eval_node_copy_val(var->alias, exp); exp = que_node_get_next(exp); var = que_node_get_next(var); } } /*********************************************************************//** Resets the aggregate value totals in the select list of an aggregate type query. */ UNIV_INLINE void sel_reset_aggregate_vals( /*=====================*/ sel_node_t* node) /*!< in: select node */ { func_node_t* func_node; ut_ad(node->is_aggregate); func_node = node->select_list; while (func_node) { eval_node_set_int_val(func_node, 0); func_node = que_node_get_next(func_node); } node->aggregate_already_fetched = FALSE; } /*********************************************************************//** Copies the input variable values when an explicit cursor is opened. */ UNIV_INLINE void row_sel_copy_input_variable_vals( /*=============================*/ sel_node_t* node) /*!< in: select node */ { sym_node_t* var; var = UT_LIST_GET_FIRST(node->copy_variables); while (var) { eval_node_copy_val(var, var->alias); var->indirection = NULL; var = UT_LIST_GET_NEXT(col_var_list, var); } } /*********************************************************************//** Fetches the column values from a record. */ static void row_sel_fetch_columns( /*==================*/ dict_index_t* index, /*!< in: record index */ const rec_t* rec, /*!< in: record in a clustered or non-clustered index; must be protected by a page latch */ const ulint* offsets,/*!< in: rec_get_offsets(rec, index) */ sym_node_t* column) /*!< in: first column in a column list, or NULL */ { dfield_t* val; ulint index_type; ulint field_no; const byte* data; ulint len; ut_ad(rec_offs_validate(rec, index, offsets)); if (dict_index_is_clust(index)) { index_type = SYM_CLUST_FIELD_NO; } else { index_type = SYM_SEC_FIELD_NO; } while (column) { mem_heap_t* heap = NULL; ibool needs_copy; field_no = column->field_nos[index_type]; if (field_no != ULINT_UNDEFINED) { if (UNIV_UNLIKELY(rec_offs_nth_extern(offsets, field_no))) { /* Copy an externally stored field to the temporary heap */ heap = mem_heap_create(1); data = btr_rec_copy_externally_stored_field( rec, offsets, dict_table_zip_size(index->table), field_no, &len, heap); ut_a(len != UNIV_SQL_NULL); needs_copy = TRUE; } else { data = rec_get_nth_field(rec, offsets, field_no, &len); needs_copy = column->copy_val; } if (needs_copy) { eval_node_copy_and_alloc_val(column, data, len); } else { val = que_node_get_val(column); dfield_set_data(val, data, len); } if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } } column = UT_LIST_GET_NEXT(col_var_list, column); } } /*********************************************************************//** Allocates a prefetch buffer for a column when prefetch is first time done. */ static void sel_col_prefetch_buf_alloc( /*=======================*/ sym_node_t* column) /*!< in: symbol table node for a column */ { sel_buf_t* sel_buf; ulint i; ut_ad(que_node_get_type(column) == QUE_NODE_SYMBOL); column->prefetch_buf = mem_alloc(SEL_MAX_N_PREFETCH * sizeof(sel_buf_t)); for (i = 0; i < SEL_MAX_N_PREFETCH; i++) { sel_buf = column->prefetch_buf + i; sel_buf->data = NULL; sel_buf->val_buf_size = 0; } } /*********************************************************************//** Frees a prefetch buffer for a column, including the dynamically allocated memory for data stored there. */ UNIV_INTERN void sel_col_prefetch_buf_free( /*======================*/ sel_buf_t* prefetch_buf) /*!< in, own: prefetch buffer */ { sel_buf_t* sel_buf; ulint i; for (i = 0; i < SEL_MAX_N_PREFETCH; i++) { sel_buf = prefetch_buf + i; if (sel_buf->val_buf_size > 0) { mem_free(sel_buf->data); } } } /*********************************************************************//** Pops the column values for a prefetched, cached row from the column prefetch buffers and places them to the val fields in the column nodes. */ static void sel_pop_prefetched_row( /*===================*/ plan_t* plan) /*!< in: plan node for a table */ { sym_node_t* column; sel_buf_t* sel_buf; dfield_t* val; byte* data; ulint len; ulint val_buf_size; ut_ad(plan->n_rows_prefetched > 0); column = UT_LIST_GET_FIRST(plan->columns); while (column) { val = que_node_get_val(column); if (!column->copy_val) { /* We did not really push any value for the column */ ut_ad(!column->prefetch_buf); ut_ad(que_node_get_val_buf_size(column) == 0); ut_d(dfield_set_null(val)); goto next_col; } ut_ad(column->prefetch_buf); ut_ad(!dfield_is_ext(val)); sel_buf = column->prefetch_buf + plan->first_prefetched; data = sel_buf->data; len = sel_buf->len; val_buf_size = sel_buf->val_buf_size; /* We must keep track of the allocated memory for column values to be able to free it later: therefore we swap the values for sel_buf and val */ sel_buf->data = dfield_get_data(val); sel_buf->len = dfield_get_len(val); sel_buf->val_buf_size = que_node_get_val_buf_size(column); dfield_set_data(val, data, len); que_node_set_val_buf_size(column, val_buf_size); next_col: column = UT_LIST_GET_NEXT(col_var_list, column); } plan->n_rows_prefetched--; plan->first_prefetched++; } /*********************************************************************//** Pushes the column values for a prefetched, cached row to the column prefetch buffers from the val fields in the column nodes. */ UNIV_INLINE void sel_push_prefetched_row( /*====================*/ plan_t* plan) /*!< in: plan node for a table */ { sym_node_t* column; sel_buf_t* sel_buf; dfield_t* val; byte* data; ulint len; ulint pos; ulint val_buf_size; if (plan->n_rows_prefetched == 0) { pos = 0; plan->first_prefetched = 0; } else { pos = plan->n_rows_prefetched; /* We have the convention that pushing new rows starts only after the prefetch stack has been emptied: */ ut_ad(plan->first_prefetched == 0); } plan->n_rows_prefetched++; ut_ad(pos < SEL_MAX_N_PREFETCH); column = UT_LIST_GET_FIRST(plan->columns); while (column) { if (!column->copy_val) { /* There is no sense to push pointers to database page fields when we do not keep latch on the page! */ goto next_col; } if (!column->prefetch_buf) { /* Allocate a new prefetch buffer */ sel_col_prefetch_buf_alloc(column); } sel_buf = column->prefetch_buf + pos; val = que_node_get_val(column); data = dfield_get_data(val); len = dfield_get_len(val); val_buf_size = que_node_get_val_buf_size(column); /* We must keep track of the allocated memory for column values to be able to free it later: therefore we swap the values for sel_buf and val */ dfield_set_data(val, sel_buf->data, sel_buf->len); que_node_set_val_buf_size(column, sel_buf->val_buf_size); sel_buf->data = data; sel_buf->len = len; sel_buf->val_buf_size = val_buf_size; next_col: column = UT_LIST_GET_NEXT(col_var_list, column); } } /*********************************************************************//** Tests the conditions which determine when the index segment we are searching through has been exhausted. @return TRUE if row passed the tests */ UNIV_INLINE ibool row_sel_test_end_conds( /*===================*/ plan_t* plan) /*!< in: plan for the table; the column values must already have been retrieved and the right sides of comparisons evaluated */ { func_node_t* cond; /* All conditions in end_conds are comparisons of a column to an expression */ cond = UT_LIST_GET_FIRST(plan->end_conds); while (cond) { /* Evaluate the left side of the comparison, i.e., get the column value if there is an indirection */ eval_sym(cond->args); /* Do the comparison */ if (!eval_cmp(cond)) { return(FALSE); } cond = UT_LIST_GET_NEXT(cond_list, cond); } return(TRUE); } /*********************************************************************//** Tests the other conditions. @return TRUE if row passed the tests */ UNIV_INLINE ibool row_sel_test_other_conds( /*=====================*/ plan_t* plan) /*!< in: plan for the table; the column values must already have been retrieved */ { func_node_t* cond; cond = UT_LIST_GET_FIRST(plan->other_conds); while (cond) { eval_exp(cond); if (!eval_node_get_ibool_val(cond)) { return(FALSE); } cond = UT_LIST_GET_NEXT(cond_list, cond); } return(TRUE); } /*********************************************************************//** Builds a previous version of a clustered index record for a consistent read @return DB_SUCCESS or error code */ static ulint row_sel_build_prev_vers( /*====================*/ read_view_t* read_view, /*!< in: read view */ dict_index_t* index, /*!< in: plan node for table */ const rec_t* rec, /*!< in: record in a clustered index */ ulint** offsets, /*!< in/out: offsets returned by rec_get_offsets(rec, plan->index) */ mem_heap_t** offset_heap, /*!< in/out: memory heap from which the offsets are allocated */ mem_heap_t** old_vers_heap, /*!< out: old version heap to use */ rec_t** old_vers, /*!< out: old version, or NULL if the record does not exist in the view: i.e., it was freshly inserted afterwards */ mtr_t* mtr) /*!< in: mtr */ { ulint err; if (*old_vers_heap) { mem_heap_empty(*old_vers_heap); } else { *old_vers_heap = mem_heap_create(512); } err = row_vers_build_for_consistent_read( rec, mtr, index, offsets, read_view, offset_heap, *old_vers_heap, old_vers); return(err); } /************************************************************************* Retrieves the clustered index record corresponding to a record in a non-clustered index. Does the necessary locking. @return DB_SUCCESS or error code */ static ulint row_sel_get_clust_rec( /*==================*/ sel_node_t* node, /*!< in: select_node */ plan_t* plan, /*!< in: plan node for table */ rec_t* rec, /*!< in: record in a non-clustered index */ que_thr_t* thr, /*!< in: query thread */ rec_t** out_rec,/*!< out: clustered record or an old version of it, NULL if the old version did not exist in the read view, i.e., it was a fresh inserted version */ mtr_t* mtr) /*!< in: mtr used to get access to the non-clustered record; the same mtr is used to access the clustered index */ { dict_index_t* index; rec_t* clust_rec; rec_t* old_vers; ulint err; mem_heap_t* heap = NULL; ulint offsets_[REC_OFFS_NORMAL_SIZE]; ulint* offsets = offsets_; rec_offs_init(offsets_); *out_rec = NULL; offsets = rec_get_offsets(rec, btr_pcur_get_btr_cur(&plan->pcur)->index, offsets, ULINT_UNDEFINED, &heap); row_build_row_ref_fast(plan->clust_ref, plan->clust_map, rec, offsets); index = dict_table_get_first_index(plan->table); btr_pcur_open_with_no_init(index, plan->clust_ref, PAGE_CUR_LE, BTR_SEARCH_LEAF, &plan->clust_pcur, 0, mtr); clust_rec = btr_pcur_get_rec(&(plan->clust_pcur)); /* Note: only if the search ends up on a non-infimum record is the low_match value the real match to the search tuple */ if (!page_rec_is_user_rec(clust_rec) || btr_pcur_get_low_match(&(plan->clust_pcur)) < dict_index_get_n_unique(index)) { ut_a(rec_get_deleted_flag(rec, dict_table_is_comp(plan->table))); ut_a(node->read_view); /* In a rare case it is possible that no clust rec is found for a delete-marked secondary index record: if in row0umod.c in row_undo_mod_remove_clust_low() we have already removed the clust rec, while purge is still cleaning and removing secondary index records associated with earlier versions of the clustered index record. In that case we know that the clustered index record did not exist in the read view of trx. */ goto func_exit; } offsets = rec_get_offsets(clust_rec, index, offsets, ULINT_UNDEFINED, &heap); if (!node->read_view) { /* Try to place a lock on the index record */ /* If this session is using READ COMMITTED isolation level we lock only the record, i.e., next-key locking is not used. */ ulint lock_type; trx_t* trx; trx = thr_get_trx(thr); if (trx->isolation_level == TRX_ISO_READ_COMMITTED) { lock_type = LOCK_REC_NOT_GAP; } else { lock_type = LOCK_ORDINARY; } err = lock_clust_rec_read_check_and_lock( 0, btr_pcur_get_block(&plan->clust_pcur), clust_rec, index, offsets, node->row_lock_mode, lock_type, thr); if (err != DB_SUCCESS) { goto err_exit; } } else { /* This is a non-locking consistent read: if necessary, fetch a previous version of the record */ old_vers = NULL; if (!lock_clust_rec_cons_read_sees(clust_rec, index, offsets, node->read_view)) { err = row_sel_build_prev_vers( node->read_view, index, clust_rec, &offsets, &heap, &plan->old_vers_heap, &old_vers, mtr); if (err != DB_SUCCESS) { goto err_exit; } clust_rec = old_vers; if (clust_rec == NULL) { goto func_exit; } } /* If we had to go to an earlier version of row or the secondary index record is delete marked, then it may be that the secondary index record corresponding to clust_rec (or old_vers) is not rec; in that case we must ignore such row because in our snapshot rec would not have existed. Remember that from rec we cannot see directly which transaction id corresponds to it: we have to go to the clustered index record. A query where we want to fetch all rows where the secondary index value is in some interval would return a wrong result if we would not drop rows which we come to visit through secondary index records that would not really exist in our snapshot. */ if ((old_vers || rec_get_deleted_flag(rec, dict_table_is_comp( plan->table))) && !row_sel_sec_rec_is_for_clust_rec(rec, plan->index, clust_rec, index)) { goto func_exit; } } /* Fetch the columns needed in test conditions. The clustered index record is protected by a page latch that was acquired when plan->clust_pcur was positioned. The latch will not be released until mtr_commit(mtr). */ row_sel_fetch_columns(index, clust_rec, offsets, UT_LIST_GET_FIRST(plan->columns)); *out_rec = clust_rec; func_exit: err = DB_SUCCESS; err_exit: if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } return(err); } /*********************************************************************//** Sets a lock on a record. @return DB_SUCCESS or error code */ UNIV_INLINE ulint sel_set_rec_lock( /*=============*/ const buf_block_t* block, /*!< in: buffer block of rec */ const rec_t* rec, /*!< in: record */ dict_index_t* index, /*!< in: index */ const ulint* offsets,/*!< in: rec_get_offsets(rec, index) */ ulint mode, /*!< in: lock mode */ ulint type, /*!< in: LOCK_ORDINARY, LOCK_GAP, or LOC_REC_NOT_GAP */ que_thr_t* thr) /*!< in: query thread */ { trx_t* trx; ulint err; trx = thr_get_trx(thr); if (UT_LIST_GET_LEN(trx->trx_locks) > 10000) { if (buf_LRU_buf_pool_running_out()) { return(DB_LOCK_TABLE_FULL); } } if (dict_index_is_clust(index)) { err = lock_clust_rec_read_check_and_lock( 0, block, rec, index, offsets, mode, type, thr); } else { err = lock_sec_rec_read_check_and_lock( 0, block, rec, index, offsets, mode, type, thr); } return(err); } /*********************************************************************//** Opens a pcur to a table index. */ static void row_sel_open_pcur( /*==============*/ plan_t* plan, /*!< in: table plan */ ibool search_latch_locked, /*!< in: TRUE if the thread currently has the search latch locked in s-mode */ mtr_t* mtr) /*!< in: mtr */ { dict_index_t* index; func_node_t* cond; que_node_t* exp; ulint n_fields; ulint has_search_latch = 0; /* RW_S_LATCH or 0 */ ulint i; if (search_latch_locked) { has_search_latch = RW_S_LATCH; } index = plan->index; /* Calculate the value of the search tuple: the exact match columns get their expressions evaluated when we evaluate the right sides of end_conds */ cond = UT_LIST_GET_FIRST(plan->end_conds); while (cond) { eval_exp(que_node_get_next(cond->args)); cond = UT_LIST_GET_NEXT(cond_list, cond); } if (plan->tuple) { n_fields = dtuple_get_n_fields(plan->tuple); if (plan->n_exact_match < n_fields) { /* There is a non-exact match field which must be evaluated separately */ eval_exp(plan->tuple_exps[n_fields - 1]); } for (i = 0; i < n_fields; i++) { exp = plan->tuple_exps[i]; dfield_copy_data(dtuple_get_nth_field(plan->tuple, i), que_node_get_val(exp)); } /* Open pcur to the index */ btr_pcur_open_with_no_init(index, plan->tuple, plan->mode, BTR_SEARCH_LEAF, &plan->pcur, has_search_latch, mtr); } else { /* Open the cursor to the start or the end of the index (FALSE: no init) */ btr_pcur_open_at_index_side(plan->asc, index, BTR_SEARCH_LEAF, &(plan->pcur), FALSE, mtr); } ut_ad(plan->n_rows_prefetched == 0); ut_ad(plan->n_rows_fetched == 0); ut_ad(plan->cursor_at_end == FALSE); plan->pcur_is_open = TRUE; } /*********************************************************************//** Restores a stored pcur position to a table index. @return TRUE if the cursor should be moved to the next record after we return from this function (moved to the previous, in the case of a descending cursor) without processing again the current cursor record */ static ibool row_sel_restore_pcur_pos( /*=====================*/ plan_t* plan, /*!< in: table plan */ mtr_t* mtr) /*!< in: mtr */ { ibool equal_position; ulint relative_position; ut_ad(!plan->cursor_at_end); relative_position = btr_pcur_get_rel_pos(&(plan->pcur)); equal_position = btr_pcur_restore_position(BTR_SEARCH_LEAF, &(plan->pcur), mtr); /* If the cursor is traveling upwards, and relative_position is (1) BTR_PCUR_BEFORE: this is not allowed, as we did not have a lock yet on the successor of the page infimum; (2) BTR_PCUR_AFTER: btr_pcur_restore_position placed the cursor on the first record GREATER than the predecessor of a page supremum; we have not yet processed the cursor record: no need to move the cursor to the next record; (3) BTR_PCUR_ON: btr_pcur_restore_position placed the cursor on the last record LESS or EQUAL to the old stored user record; (a) if equal_position is FALSE, this means that the cursor is now on a record less than the old user record, and we must move to the next record; (b) if equal_position is TRUE, then if plan->stored_cursor_rec_processed is TRUE, we must move to the next record, else there is no need to move the cursor. */ if (plan->asc) { if (relative_position == BTR_PCUR_ON) { if (equal_position) { return(plan->stored_cursor_rec_processed); } return(TRUE); } ut_ad(relative_position == BTR_PCUR_AFTER || relative_position == BTR_PCUR_AFTER_LAST_IN_TREE); return(FALSE); } /* If the cursor is traveling downwards, and relative_position is (1) BTR_PCUR_BEFORE: btr_pcur_restore_position placed the cursor on the last record LESS than the successor of a page infimum; we have not processed the cursor record: no need to move the cursor; (2) BTR_PCUR_AFTER: btr_pcur_restore_position placed the cursor on the first record GREATER than the predecessor of a page supremum; we have processed the cursor record: we should move the cursor to the previous record; (3) BTR_PCUR_ON: btr_pcur_restore_position placed the cursor on the last record LESS or EQUAL to the old stored user record; (a) if equal_position is FALSE, this means that the cursor is now on a record less than the old user record, and we need not move to the previous record; (b) if equal_position is TRUE, then if plan->stored_cursor_rec_processed is TRUE, we must move to the previous record, else there is no need to move the cursor. */ if (relative_position == BTR_PCUR_BEFORE || relative_position == BTR_PCUR_BEFORE_FIRST_IN_TREE) { return(FALSE); } if (relative_position == BTR_PCUR_ON) { if (equal_position) { return(plan->stored_cursor_rec_processed); } return(FALSE); } ut_ad(relative_position == BTR_PCUR_AFTER || relative_position == BTR_PCUR_AFTER_LAST_IN_TREE); return(TRUE); } /*********************************************************************//** Resets a plan cursor to a closed state. */ UNIV_INLINE void plan_reset_cursor( /*==============*/ plan_t* plan) /*!< in: plan */ { plan->pcur_is_open = FALSE; plan->cursor_at_end = FALSE; plan->n_rows_fetched = 0; plan->n_rows_prefetched = 0; } /*********************************************************************//** Tries to do a shortcut to fetch a clustered index record with a unique key, using the hash index if possible (not always). @return SEL_FOUND, SEL_EXHAUSTED, SEL_RETRY */ static ulint row_sel_try_search_shortcut( /*========================*/ sel_node_t* node, /*!< in: select node for a consistent read */ plan_t* plan, /*!< in: plan for a unique search in clustered index */ mtr_t* mtr) /*!< in: mtr */ { dict_index_t* index; rec_t* rec; mem_heap_t* heap = NULL; ulint offsets_[REC_OFFS_NORMAL_SIZE]; ulint* offsets = offsets_; ulint ret; rec_offs_init(offsets_); index = plan->index; ut_ad(node->read_view); ut_ad(plan->unique_search); ut_ad(!plan->must_get_clust); #ifdef UNIV_SYNC_DEBUG ut_ad(rw_lock_own(&btr_search_latch, RW_LOCK_SHARED)); #endif /* UNIV_SYNC_DEBUG */ row_sel_open_pcur(plan, TRUE, mtr); rec = btr_pcur_get_rec(&(plan->pcur)); if (!page_rec_is_user_rec(rec)) { return(SEL_RETRY); } ut_ad(plan->mode == PAGE_CUR_GE); /* As the cursor is now placed on a user record after a search with the mode PAGE_CUR_GE, the up_match field in the cursor tells how many fields in the user record matched to the search tuple */ if (btr_pcur_get_up_match(&(plan->pcur)) < plan->n_exact_match) { return(SEL_EXHAUSTED); } /* This is a non-locking consistent read: if necessary, fetch a previous version of the record */ offsets = rec_get_offsets(rec, index, offsets, ULINT_UNDEFINED, &heap); if (dict_index_is_clust(index)) { if (!lock_clust_rec_cons_read_sees(rec, index, offsets, node->read_view)) { ret = SEL_RETRY; goto func_exit; } } else if (!lock_sec_rec_cons_read_sees(rec, node->read_view)) { ret = SEL_RETRY; goto func_exit; } /* Test the deleted flag. */ if (rec_get_deleted_flag(rec, dict_table_is_comp(plan->table))) { ret = SEL_EXHAUSTED; goto func_exit; } /* Fetch the columns needed in test conditions. The index record is protected by a page latch that was acquired when plan->pcur was positioned. The latch will not be released until mtr_commit(mtr). */ row_sel_fetch_columns(index, rec, offsets, UT_LIST_GET_FIRST(plan->columns)); /* Test the rest of search conditions */ if (!row_sel_test_other_conds(plan)) { ret = SEL_EXHAUSTED; goto func_exit; } ut_ad(plan->pcur.latch_mode == BTR_SEARCH_LEAF); plan->n_rows_fetched++; ret = SEL_FOUND; func_exit: if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } return(ret); } /*********************************************************************//** Performs a select step. @return DB_SUCCESS or error code */ static ulint row_sel( /*====*/ sel_node_t* node, /*!< in: select node */ que_thr_t* thr) /*!< in: query thread */ { dict_index_t* index; plan_t* plan; mtr_t mtr; ibool moved; rec_t* rec; rec_t* old_vers; rec_t* clust_rec; ibool search_latch_locked; ibool consistent_read; /* The following flag becomes TRUE when we are doing a consistent read from a non-clustered index and we must look at the clustered index to find out the previous delete mark state of the non-clustered record: */ ibool cons_read_requires_clust_rec = FALSE; ulint cost_counter = 0; ibool cursor_just_opened; ibool must_go_to_next; ibool mtr_has_extra_clust_latch = FALSE; /* TRUE if the search was made using a non-clustered index, and we had to access the clustered record: now &mtr contains a clustered index latch, and &mtr must be committed before we move to the next non-clustered record */ ulint found_flag; ulint err; mem_heap_t* heap = NULL; ulint offsets_[REC_OFFS_NORMAL_SIZE]; ulint* offsets = offsets_; rec_offs_init(offsets_); ut_ad(thr->run_node == node); search_latch_locked = FALSE; if (node->read_view) { /* In consistent reads, we try to do with the hash index and not to use the buffer page get. This is to reduce memory bus load resulting from semaphore operations. The search latch will be s-locked when we access an index with a unique search condition, but not locked when we access an index with a less selective search condition. */ consistent_read = TRUE; } else { consistent_read = FALSE; } table_loop: /* TABLE LOOP ---------- This is the outer major loop in calculating a join. We come here when node->fetch_table changes, and after adding a row to aggregate totals and, of course, when this function is called. */ ut_ad(mtr_has_extra_clust_latch == FALSE); plan = sel_node_get_nth_plan(node, node->fetch_table); index = plan->index; if (plan->n_rows_prefetched > 0) { sel_pop_prefetched_row(plan); goto next_table_no_mtr; } if (plan->cursor_at_end) { /* The cursor has already reached the result set end: no more rows to process for this table cursor, as also the prefetch stack was empty */ ut_ad(plan->pcur_is_open); goto table_exhausted_no_mtr; } /* Open a cursor to index, or restore an open cursor position */ mtr_start(&mtr); if (consistent_read && plan->unique_search && !plan->pcur_is_open && !plan->must_get_clust && !plan->table->big_rows) { if (!search_latch_locked) { rw_lock_s_lock(&btr_search_latch); search_latch_locked = TRUE; } else if (rw_lock_get_writer(&btr_search_latch) == RW_LOCK_WAIT_EX) { /* There is an x-latch request waiting: release the s-latch for a moment; as an s-latch here is often kept for some 10 searches before being released, a waiting x-latch request would block other threads from acquiring an s-latch for a long time, lowering performance significantly in multiprocessors. */ rw_lock_s_unlock(&btr_search_latch); rw_lock_s_lock(&btr_search_latch); } found_flag = row_sel_try_search_shortcut(node, plan, &mtr); if (found_flag == SEL_FOUND) { goto next_table; } else if (found_flag == SEL_EXHAUSTED) { goto table_exhausted; } ut_ad(found_flag == SEL_RETRY); plan_reset_cursor(plan); mtr_commit(&mtr); mtr_start(&mtr); } if (search_latch_locked) { rw_lock_s_unlock(&btr_search_latch); search_latch_locked = FALSE; } if (!plan->pcur_is_open) { /* Evaluate the expressions to build the search tuple and open the cursor */ row_sel_open_pcur(plan, search_latch_locked, &mtr); cursor_just_opened = TRUE; /* A new search was made: increment the cost counter */ cost_counter++; } else { /* Restore pcur position to the index */ must_go_to_next = row_sel_restore_pcur_pos(plan, &mtr); cursor_just_opened = FALSE; if (must_go_to_next) { /* We have already processed the cursor record: move to the next */ goto next_rec; } } rec_loop: /* RECORD LOOP ----------- In this loop we use pcur and try to fetch a qualifying row, and also fill the prefetch buffer for this table if n_rows_fetched has exceeded a threshold. While we are inside this loop, the following holds: (1) &mtr is started, (2) pcur is positioned and open. NOTE that if cursor_just_opened is TRUE here, it means that we came to this point right after row_sel_open_pcur. */ ut_ad(mtr_has_extra_clust_latch == FALSE); rec = btr_pcur_get_rec(&(plan->pcur)); /* PHASE 1: Set a lock if specified */ if (!node->asc && cursor_just_opened && !page_rec_is_supremum(rec)) { /* When we open a cursor for a descending search, we must set a next-key lock on the successor record: otherwise it would be possible to insert new records next to the cursor position, and it might be that these new records should appear in the search result set, resulting in the phantom problem. */ if (!consistent_read) { /* If this session is using READ COMMITTED isolation level, we lock only the record, i.e., next-key locking is not used. */ rec_t* next_rec = page_rec_get_next(rec); ulint lock_type; trx_t* trx; trx = thr_get_trx(thr); offsets = rec_get_offsets(next_rec, index, offsets, ULINT_UNDEFINED, &heap); if (trx->isolation_level == TRX_ISO_READ_COMMITTED) { if (page_rec_is_supremum(next_rec)) { goto skip_lock; } lock_type = LOCK_REC_NOT_GAP; } else { lock_type = LOCK_ORDINARY; } err = sel_set_rec_lock(btr_pcur_get_block(&plan->pcur), next_rec, index, offsets, node->row_lock_mode, lock_type, thr); if (err != DB_SUCCESS) { /* Note that in this case we will store in pcur the PREDECESSOR of the record we are waiting the lock for */ goto lock_wait_or_error; } } } skip_lock: if (page_rec_is_infimum(rec)) { /* The infimum record on a page cannot be in the result set, and neither can a record lock be placed on it: we skip such a record. We also increment the cost counter as we may have processed yet another page of index. */ cost_counter++; goto next_rec; } if (!consistent_read) { /* Try to place a lock on the index record */ /* If this session is using READ COMMITTED isolation level, we lock only the record, i.e., next-key locking is not used. */ ulint lock_type; trx_t* trx; offsets = rec_get_offsets(rec, index, offsets, ULINT_UNDEFINED, &heap); trx = thr_get_trx(thr); if (trx->isolation_level == TRX_ISO_READ_COMMITTED) { if (page_rec_is_supremum(rec)) { goto next_rec; } lock_type = LOCK_REC_NOT_GAP; } else { lock_type = LOCK_ORDINARY; } err = sel_set_rec_lock(btr_pcur_get_block(&plan->pcur), rec, index, offsets, node->row_lock_mode, lock_type, thr); if (err != DB_SUCCESS) { goto lock_wait_or_error; } } if (page_rec_is_supremum(rec)) { /* A page supremum record cannot be in the result set: skip it now when we have placed a possible lock on it */ goto next_rec; } ut_ad(page_rec_is_user_rec(rec)); if (cost_counter > SEL_COST_LIMIT) { /* Now that we have placed the necessary locks, we can stop for a while and store the cursor position; NOTE that if we would store the cursor position BEFORE placing a record lock, it might happen that the cursor would jump over some records that another transaction could meanwhile insert adjacent to the cursor: this would result in the phantom problem. */ goto stop_for_a_while; } /* PHASE 2: Check a mixed index mix id if needed */ if (plan->unique_search && cursor_just_opened) { ut_ad(plan->mode == PAGE_CUR_GE); /* As the cursor is now placed on a user record after a search with the mode PAGE_CUR_GE, the up_match field in the cursor tells how many fields in the user record matched to the search tuple */ if (btr_pcur_get_up_match(&(plan->pcur)) < plan->n_exact_match) { goto table_exhausted; } /* Ok, no need to test end_conds or mix id */ } /* We are ready to look at a possible new index entry in the result set: the cursor is now placed on a user record */ /* PHASE 3: Get previous version in a consistent read */ cons_read_requires_clust_rec = FALSE; offsets = rec_get_offsets(rec, index, offsets, ULINT_UNDEFINED, &heap); if (consistent_read) { /* This is a non-locking consistent read: if necessary, fetch a previous version of the record */ if (dict_index_is_clust(index)) { if (!lock_clust_rec_cons_read_sees(rec, index, offsets, node->read_view)) { err = row_sel_build_prev_vers( node->read_view, index, rec, &offsets, &heap, &plan->old_vers_heap, &old_vers, &mtr); if (err != DB_SUCCESS) { goto lock_wait_or_error; } if (old_vers == NULL) { offsets = rec_get_offsets( rec, index, offsets, ULINT_UNDEFINED, &heap); /* Fetch the columns needed in test conditions. The clustered index record is protected by a page latch that was acquired by row_sel_open_pcur() or row_sel_restore_pcur_pos(). The latch will not be released until mtr_commit(mtr). */ row_sel_fetch_columns( index, rec, offsets, UT_LIST_GET_FIRST( plan->columns)); if (!row_sel_test_end_conds(plan)) { goto table_exhausted; } goto next_rec; } rec = old_vers; } } else if (!lock_sec_rec_cons_read_sees(rec, node->read_view)) { cons_read_requires_clust_rec = TRUE; } } /* PHASE 4: Test search end conditions and deleted flag */ /* Fetch the columns needed in test conditions. The record is protected by a page latch that was acquired by row_sel_open_pcur() or row_sel_restore_pcur_pos(). The latch will not be released until mtr_commit(mtr). */ row_sel_fetch_columns(index, rec, offsets, UT_LIST_GET_FIRST(plan->columns)); /* Test the selection end conditions: these can only contain columns which already are found in the index, even though the index might be non-clustered */ if (plan->unique_search && cursor_just_opened) { /* No test necessary: the test was already made above */ } else if (!row_sel_test_end_conds(plan)) { goto table_exhausted; } if (rec_get_deleted_flag(rec, dict_table_is_comp(plan->table)) && !cons_read_requires_clust_rec) { /* The record is delete marked: we can skip it if this is not a consistent read which might see an earlier version of a non-clustered index record */ if (plan->unique_search) { goto table_exhausted; } goto next_rec; } /* PHASE 5: Get the clustered index record, if needed and if we did not do the search using the clustered index */ if (plan->must_get_clust || cons_read_requires_clust_rec) { /* It was a non-clustered index and we must fetch also the clustered index record */ err = row_sel_get_clust_rec(node, plan, rec, thr, &clust_rec, &mtr); mtr_has_extra_clust_latch = TRUE; if (err != DB_SUCCESS) { goto lock_wait_or_error; } /* Retrieving the clustered record required a search: increment the cost counter */ cost_counter++; if (clust_rec == NULL) { /* The record did not exist in the read view */ ut_ad(consistent_read); goto next_rec; } if (rec_get_deleted_flag(clust_rec, dict_table_is_comp(plan->table))) { /* The record is delete marked: we can skip it */ goto next_rec; } if (node->can_get_updated) { btr_pcur_store_position(&(plan->clust_pcur), &mtr); } } /* PHASE 6: Test the rest of search conditions */ if (!row_sel_test_other_conds(plan)) { if (plan->unique_search) { goto table_exhausted; } goto next_rec; } /* PHASE 7: We found a new qualifying row for the current table; push the row if prefetch is on, or move to the next table in the join */ plan->n_rows_fetched++; ut_ad(plan->pcur.latch_mode == BTR_SEARCH_LEAF); if ((plan->n_rows_fetched <= SEL_PREFETCH_LIMIT) || plan->unique_search || plan->no_prefetch || plan->table->big_rows) { /* No prefetch in operation: go to the next table */ goto next_table; } sel_push_prefetched_row(plan); if (plan->n_rows_prefetched == SEL_MAX_N_PREFETCH) { /* The prefetch buffer is now full */ sel_pop_prefetched_row(plan); goto next_table; } next_rec: ut_ad(!search_latch_locked); if (mtr_has_extra_clust_latch) { /* We must commit &mtr if we are moving to the next non-clustered index record, because we could break the latching order if we would access a different clustered index page right away without releasing the previous. */ goto commit_mtr_for_a_while; } if (node->asc) { moved = btr_pcur_move_to_next(&(plan->pcur), &mtr); } else { moved = btr_pcur_move_to_prev(&(plan->pcur), &mtr); } if (!moved) { goto table_exhausted; } cursor_just_opened = FALSE; /* END OF RECORD LOOP ------------------ */ goto rec_loop; next_table: /* We found a record which satisfies the conditions: we can move to the next table or return a row in the result set */ ut_ad(btr_pcur_is_on_user_rec(&plan->pcur)); if (plan->unique_search && !node->can_get_updated) { plan->cursor_at_end = TRUE; } else { ut_ad(!search_latch_locked); plan->stored_cursor_rec_processed = TRUE; btr_pcur_store_position(&(plan->pcur), &mtr); } mtr_commit(&mtr); mtr_has_extra_clust_latch = FALSE; next_table_no_mtr: /* If we use 'goto' to this label, it means that the row was popped from the prefetched rows stack, and &mtr is already committed */ if (node->fetch_table + 1 == node->n_tables) { sel_eval_select_list(node); if (node->is_aggregate) { goto table_loop; } sel_assign_into_var_values(node->into_list, node); thr->run_node = que_node_get_parent(node); err = DB_SUCCESS; goto func_exit; } node->fetch_table++; /* When we move to the next table, we first reset the plan cursor: we do not care about resetting it when we backtrack from a table */ plan_reset_cursor(sel_node_get_nth_plan(node, node->fetch_table)); goto table_loop; table_exhausted: /* The table cursor pcur reached the result set end: backtrack to the previous table in the join if we do not have cached prefetched rows */ plan->cursor_at_end = TRUE; mtr_commit(&mtr); mtr_has_extra_clust_latch = FALSE; if (plan->n_rows_prefetched > 0) { /* The table became exhausted during a prefetch */ sel_pop_prefetched_row(plan); goto next_table_no_mtr; } table_exhausted_no_mtr: if (node->fetch_table == 0) { err = DB_SUCCESS; if (node->is_aggregate && !node->aggregate_already_fetched) { node->aggregate_already_fetched = TRUE; sel_assign_into_var_values(node->into_list, node); thr->run_node = que_node_get_parent(node); } else { node->state = SEL_NODE_NO_MORE_ROWS; thr->run_node = que_node_get_parent(node); } goto func_exit; } node->fetch_table--; goto table_loop; stop_for_a_while: /* Return control for a while to que_run_threads, so that runaway queries can be canceled. NOTE that when we come here, we must, in a locking read, have placed the necessary (possibly waiting request) record lock on the cursor record or its successor: when we reposition the cursor, this record lock guarantees that nobody can meanwhile have inserted new records which should have appeared in the result set, which would result in the phantom problem. */ ut_ad(!search_latch_locked); plan->stored_cursor_rec_processed = FALSE; btr_pcur_store_position(&(plan->pcur), &mtr); mtr_commit(&mtr); #ifdef UNIV_SYNC_DEBUG ut_ad(sync_thread_levels_empty_gen(TRUE)); #endif /* UNIV_SYNC_DEBUG */ err = DB_SUCCESS; goto func_exit; commit_mtr_for_a_while: /* Stores the cursor position and commits &mtr; this is used if &mtr may contain latches which would break the latching order if &mtr would not be committed and the latches released. */ plan->stored_cursor_rec_processed = TRUE; ut_ad(!search_latch_locked); btr_pcur_store_position(&(plan->pcur), &mtr); mtr_commit(&mtr); mtr_has_extra_clust_latch = FALSE; #ifdef UNIV_SYNC_DEBUG ut_ad(sync_thread_levels_empty_gen(TRUE)); #endif /* UNIV_SYNC_DEBUG */ goto table_loop; lock_wait_or_error: /* See the note at stop_for_a_while: the same holds for this case */ ut_ad(!btr_pcur_is_before_first_on_page(&plan->pcur) || !node->asc); ut_ad(!search_latch_locked); plan->stored_cursor_rec_processed = FALSE; btr_pcur_store_position(&(plan->pcur), &mtr); mtr_commit(&mtr); #ifdef UNIV_SYNC_DEBUG ut_ad(sync_thread_levels_empty_gen(TRUE)); #endif /* UNIV_SYNC_DEBUG */ func_exit: if (search_latch_locked) { rw_lock_s_unlock(&btr_search_latch); } if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } return(err); } /**********************************************************************//** Performs a select step. This is a high-level function used in SQL execution graphs. @return query thread to run next or NULL */ UNIV_INTERN que_thr_t* row_sel_step( /*=========*/ que_thr_t* thr) /*!< in: query thread */ { ulint i_lock_mode; sym_node_t* table_node; sel_node_t* node; ulint err; ut_ad(thr); node = thr->run_node; ut_ad(que_node_get_type(node) == QUE_NODE_SELECT); /* If this is a new time this node is executed (or when execution resumes after wait for a table intention lock), set intention locks on the tables, or assign a read view */ if (node->into_list && (thr->prev_node == que_node_get_parent(node))) { node->state = SEL_NODE_OPEN; } if (node->state == SEL_NODE_OPEN) { /* It may be that the current session has not yet started its transaction, or it has been committed: */ ut_a(thr_get_trx(thr)->conc_state != TRX_NOT_STARTED); plan_reset_cursor(sel_node_get_nth_plan(node, 0)); if (node->consistent_read) { /* Assign a read view for the query */ node->read_view = trx_assign_read_view( thr_get_trx(thr)); } else { if (node->set_x_locks) { i_lock_mode = LOCK_IX; } else { i_lock_mode = LOCK_IS; } table_node = node->table_list; while (table_node) { err = lock_table(0, table_node->table, i_lock_mode, thr); if (err != DB_SUCCESS) { thr_get_trx(thr)->error_state = err; return(NULL); } table_node = que_node_get_next(table_node); } } /* If this is an explicit cursor, copy stored procedure variable values, so that the values cannot change between fetches (currently, we copy them also for non-explicit cursors) */ if (node->explicit_cursor && UT_LIST_GET_FIRST(node->copy_variables)) { row_sel_copy_input_variable_vals(node); } node->state = SEL_NODE_FETCH; node->fetch_table = 0; if (node->is_aggregate) { /* Reset the aggregate total values */ sel_reset_aggregate_vals(node); } } err = row_sel(node, thr); /* NOTE! if queries are parallelized, the following assignment may have problems; the assignment should be made only if thr is the only top-level thr in the graph: */ thr->graph->last_sel_node = node; if (err != DB_SUCCESS) { thr_get_trx(thr)->error_state = err; return(NULL); } return(thr); } /**********************************************************************//** Performs a fetch for a cursor. @return query thread to run next or NULL */ UNIV_INTERN que_thr_t* fetch_step( /*=======*/ que_thr_t* thr) /*!< in: query thread */ { sel_node_t* sel_node; fetch_node_t* node; ut_ad(thr); node = thr->run_node; sel_node = node->cursor_def; ut_ad(que_node_get_type(node) == QUE_NODE_FETCH); if (thr->prev_node != que_node_get_parent(node)) { if (sel_node->state != SEL_NODE_NO_MORE_ROWS) { if (node->into_list) { sel_assign_into_var_values(node->into_list, sel_node); } else { void* ret = (*node->func->func)( sel_node, node->func->arg); if (!ret) { sel_node->state = SEL_NODE_NO_MORE_ROWS; } } } thr->run_node = que_node_get_parent(node); return(thr); } /* Make the fetch node the parent of the cursor definition for the time of the fetch, so that execution knows to return to this fetch node after a row has been selected or we know that there is no row left */ sel_node->common.parent = node; if (sel_node->state == SEL_NODE_CLOSED) { ib_logger(ib_stream, "InnoDB: Error: fetch called on a closed cursor\n"); thr_get_trx(thr)->error_state = DB_ERROR; return(NULL); } thr->run_node = sel_node; return(thr); } /****************************************************************//** Sample callback function for fetch that prints each row. @return always returns non-NULL */ UNIV_INTERN void* row_fetch_print( /*============*/ void* row, /*!< in: sel_node_t* */ void* user_arg) /*!< in: not used */ { sel_node_t* node = row; que_node_t* exp; ulint i = 0; UT_NOT_USED(user_arg); ib_logger(ib_stream, "row_fetch_print: row %p\n", row); exp = node->select_list; while (exp) { dfield_t* dfield = que_node_get_val(exp); const dtype_t* type = dfield_get_type(dfield); ib_logger(ib_stream, " column %lu:\n", (ulong) i); dtype_print(type); ib_logger(ib_stream, "\n"); if (dfield_get_len(dfield) != UNIV_SQL_NULL) { ut_print_buf(ib_stream, dfield_get_data(dfield), dfield_get_len(dfield)); ib_logger(ib_stream, "\n"); } else { ib_logger(ib_stream, " ;\n"); } exp = que_node_get_next(exp); i++; } return((void*)42); } /****************************************************************//** Callback function for fetch that stores an unsigned 4 byte integer to the location pointed. The column's type must be DATA_INT, DATA_UNSIGNED, length = 4. @return always returns NULL */ UNIV_INTERN void* row_fetch_store_uint4( /*==================*/ void* row, /*!< in: sel_node_t* */ void* user_arg) /*!< in: data pointer */ { sel_node_t* node = row; ib_uint32_t* val = user_arg; ulint tmp; dfield_t* dfield = que_node_get_val(node->select_list); const dtype_t* type = dfield_get_type(dfield); ulint len = dfield_get_len(dfield); ut_a(dtype_get_mtype(type) == DATA_INT); ut_a(dtype_get_prtype(type) & DATA_UNSIGNED); ut_a(len == 4); tmp = mach_read_from_4(dfield_get_data(dfield)); *val = (ib_uint32_t) tmp; return(NULL); } /***********************************************************//** Prints a row in a select result. @return query thread to run next or NULL */ UNIV_INTERN que_thr_t* row_printf_step( /*============*/ que_thr_t* thr) /*!< in: query thread */ { row_printf_node_t* node; sel_node_t* sel_node; que_node_t* arg; ut_ad(thr); node = thr->run_node; sel_node = node->sel_node; ut_ad(que_node_get_type(node) == QUE_NODE_ROW_PRINTF); if (thr->prev_node == que_node_get_parent(node)) { /* Reset the cursor */ sel_node->state = SEL_NODE_OPEN; /* Fetch next row to print */ thr->run_node = sel_node; return(thr); } if (sel_node->state != SEL_NODE_FETCH) { ut_ad(sel_node->state == SEL_NODE_NO_MORE_ROWS); /* No more rows to print */ thr->run_node = que_node_get_parent(node); return(thr); } arg = sel_node->select_list; while (arg) { dfield_print_also_hex(que_node_get_val(arg)); ib_logger(ib_stream, " ::: "); arg = que_node_get_next(arg); } ib_logger(ib_stream, "\n"); /* Fetch next row to print */ thr->run_node = sel_node; return(thr); } UNIV_INTERN void row_sel_prebuild_graph( /*===================*/ row_prebuilt_t* prebuilt) /*!< in: prebuilt handle */ { sel_node_t* node; ut_ad(prebuilt && prebuilt->trx); if (prebuilt->sel_graph == NULL) { node = sel_node_create(prebuilt->heap); prebuilt->sel_graph = que_node_get_parent( pars_complete_graph_for_exec(node, prebuilt->trx, prebuilt->heap)); prebuilt->sel_graph->state = QUE_FORK_ACTIVE; } } /*********************************************************************//** Retrieves the clustered index record corresponding to a record in a non-clustered index. Does the necessary locking. @return DB_SUCCESS or error code */ static ulint row_sel_get_clust_rec_with_prebuilt( /*================================*/ row_prebuilt_t* prebuilt,/*!< in: prebuilt struct in the handle */ dict_index_t* sec_index,/*!< in: secondary index where rec resides */ const rec_t* rec, /*!< in: record in a non-clustered index; if this is a locking read, then rec is not allowed to be delete-marked, and that would not make sense either */ que_thr_t* thr, /*!< in: query thread */ const rec_t** out_rec,/*!< out: clustered record or an old version of it, NULL if the old version did not exist in the read view, i.e., it was a fresh inserted version */ ulint** offsets,/*!< in: offsets returned by rec_get_offsets(rec, sec_index); out: offsets returned by rec_get_offsets(out_rec, clust_index) */ mem_heap_t** offset_heap,/*!< in/out: memory heap from which the offsets are allocated */ mtr_t* mtr) /*!< in: mtr used to get access to the non-clustered record; the same mtr is used to access the clustered index */ { dict_index_t* clust_index; const rec_t* clust_rec; rec_t* old_vers; ulint err; trx_t* trx; *out_rec = NULL; trx = thr_get_trx(thr); row_build_row_ref_in_tuple(prebuilt->clust_ref, rec, sec_index, *offsets, trx); clust_index = dict_table_get_first_index(sec_index->table); btr_pcur_open_with_no_init(clust_index, prebuilt->clust_ref, PAGE_CUR_LE, BTR_SEARCH_LEAF, prebuilt->clust_pcur, 0, mtr); clust_rec = btr_pcur_get_rec(prebuilt->clust_pcur); prebuilt->clust_pcur->trx_if_known = trx; /* Note: only if the search ends up on a non-infimum record is the low_match value the real match to the search tuple */ if (!page_rec_is_user_rec(clust_rec) || btr_pcur_get_low_match(prebuilt->clust_pcur) < dict_index_get_n_unique(clust_index)) { /* In a rare case it is possible that no clust rec is found for a delete-marked secondary index record: if in row0umod.c in row_undo_mod_remove_clust_low() we have already removed the clust rec, while purge is still cleaning and removing secondary index records associated with earlier versions of the clustered index record. In that case we know that the clustered index record did not exist in the read view of trx. */ if (!rec_get_deleted_flag(rec, dict_table_is_comp(sec_index->table)) || prebuilt->select_lock_type != LOCK_NONE) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: error clustered record" " for sec rec not found\n" "InnoDB: "); dict_index_name_print(ib_stream, trx, sec_index); ib_logger(ib_stream, "\n" "InnoDB: sec index record "); rec_print(ib_stream, rec, sec_index); ib_logger(ib_stream, "\n" "InnoDB: clust index record "); rec_print(ib_stream, clust_rec, clust_index); ib_logger(ib_stream, "\n"); trx_print(ib_stream, trx, 600); ib_logger(ib_stream, "\n" "InnoDB: Submit a detailed bug report, check the" "InnoDB website for details"); } clust_rec = NULL; goto func_exit; } *offsets = rec_get_offsets(clust_rec, clust_index, *offsets, ULINT_UNDEFINED, offset_heap); if (prebuilt->select_lock_type != LOCK_NONE) { /* Try to place a lock on the index record; we are searching the clust rec with a unique condition, hence we set a LOCK_REC_NOT_GAP type lock */ err = lock_clust_rec_read_check_and_lock( 0, btr_pcur_get_block(prebuilt->clust_pcur), clust_rec, clust_index, *offsets, prebuilt->select_lock_type, LOCK_REC_NOT_GAP, thr); if (err != DB_SUCCESS) { goto err_exit; } } else { /* This is a non-locking consistent read: if necessary, fetch a previous version of the record */ old_vers = NULL; /* If the isolation level allows reading of uncommitted data, then we never look for an earlier version */ if (trx->isolation_level > TRX_ISO_READ_UNCOMMITTED && !lock_clust_rec_cons_read_sees( clust_rec, clust_index, *offsets, trx->read_view)) { /* The following call returns 'offsets' associated with 'old_vers' */ err = row_sel_build_prev_vers( trx->read_view, clust_index, clust_rec, offsets, offset_heap, &prebuilt->old_vers_heap, &old_vers, mtr); if (err != DB_SUCCESS || old_vers == NULL) { goto err_exit; } clust_rec = old_vers; } /* If we had to go to an earlier version of row or the secondary index record is delete marked, then it may be that the secondary index record corresponding to clust_rec (or old_vers) is not rec; in that case we must ignore such row because in our snapshot rec would not have existed. Remember that from rec we cannot see directly which transaction id corresponds to it: we have to go to the clustered index record. A query where we want to fetch all rows where the secondary index value is in some interval would return a wrong result if we would not drop rows which we come to visit through secondary index records that would not really exist in our snapshot. */ if (clust_rec && (old_vers || trx->isolation_level <= TRX_ISO_READ_UNCOMMITTED || rec_get_deleted_flag(rec, dict_table_is_comp( sec_index->table))) && !row_sel_sec_rec_is_for_clust_rec( rec, sec_index, clust_rec, clust_index)) { clust_rec = NULL; #ifdef UNIV_SEARCH_DEBUG } else { ut_a(clust_rec == NULL || row_sel_sec_rec_is_for_clust_rec( rec, sec_index, clust_rec, clust_index)); #endif } } func_exit: *out_rec = clust_rec; if (prebuilt->select_lock_type != LOCK_NONE) { /* We may use the cursor in update or in unlock_row(): store its position */ btr_pcur_store_position(prebuilt->clust_pcur, mtr); } err = DB_SUCCESS; err_exit: return(err); } /********************************************************************//** Restores cursor position after it has been stored. We have to take into account that the record cursor was positioned on may have been deleted. Then we may have to move the cursor one step up or down. @return TRUE if we may need to process the record the cursor is now positioned on (i.e. we should not go to the next record yet) */ static ibool row_sel_restore_position( /*=====================*/ ibool* same_user_rec, /*!< out: TRUE if we were able to restore the cursor on a user record with the same ordering prefix in in the B-tree index */ ulint latch_mode, /*!< in: latch mode wished in restoration */ btr_pcur_t* pcur, /*!< in: cursor whose position has been stored */ ibool moves_up, /*!< in: TRUE if the cursor moves up in the index */ mtr_t* mtr) /*!< in: mtr; CAUTION: may commit mtr temporarily! */ { ibool success; ulint relative_position; relative_position = pcur->rel_pos; success = btr_pcur_restore_position(latch_mode, pcur, mtr); *same_user_rec = success; if (relative_position == BTR_PCUR_ON) { if (success) { return(FALSE); } if (moves_up) { btr_pcur_move_to_next(pcur, mtr); } return(TRUE); } if (relative_position == BTR_PCUR_AFTER || relative_position == BTR_PCUR_AFTER_LAST_IN_TREE) { if (moves_up) { return(TRUE); } if (btr_pcur_is_on_user_rec(pcur)) { btr_pcur_move_to_prev(pcur, mtr); } return(TRUE); } ut_ad(relative_position == BTR_PCUR_BEFORE || relative_position == BTR_PCUR_BEFORE_FIRST_IN_TREE); if (moves_up && btr_pcur_is_on_user_rec(pcur)) { btr_pcur_move_to_next(pcur, mtr); } return(TRUE); } /********************************************************************//** Reset the row cache. The memory is not freed only the stack pointers are reset. */ UNIV_INLINE void row_sel_row_cache_reset( /*====================*/ row_prebuilt_t* prebuilt) /*!< in: prebuilt struct */ { prebuilt->row_cache.first = 0; prebuilt->row_cache.n_cached = 0; } /************************************************************************ Check if there are any rows in the cache that can be popped. */ UNIV_INTERN ibool row_sel_row_cache_is_empty( /*=======================*/ row_prebuilt_t* prebuilt) /*!< in: prebuilt struct */ { return(prebuilt->row_cache.n_cached == 0); } /************************************************************************ Check if there is a fetch in progress. ie. at lease one row was cached and read from the cache. */ UNIV_INTERN ibool row_sel_row_cache_fetch_in_progress( /*================================*/ row_prebuilt_t* prebuilt) /*!< in: prebuilt struct */ { return(prebuilt->row_cache.first > 0 && prebuilt->row_cache.first < prebuilt->row_cache.n_size); } /************************************************************************ Check if row cache is full. */ UNIV_INLINE ibool row_sel_row_cache_is_full( /*======================*/ row_prebuilt_t* prebuilt) /*!< in: prebuilt struct */ { ut_a(prebuilt->row_cache.n_cached <= prebuilt->row_cache.n_size); return(prebuilt->row_cache.n_cached == prebuilt->row_cache.n_size- 1); } /************************************************************************ Reads the current row from the fetch cache. */ UNIV_INTERN const rec_t* row_sel_row_cache_get( /*==================*/ row_prebuilt_t* prebuilt) /*!< in: prebuilt struct */ { ib_cached_row_t* row; ut_ad(!row_sel_row_cache_is_empty(prebuilt)); row = &prebuilt->row_cache.ptr[prebuilt->row_cache.first]; return(row->rec); } /************************************************************************ Pops a cached row from the fetch cache. */ UNIV_INTERN void row_sel_row_cache_next( /*===================*/ row_prebuilt_t* prebuilt) /*!< in: prebuilt struct */ { if (!row_sel_row_cache_is_empty(prebuilt)) { --prebuilt->row_cache.n_cached; ++prebuilt->row_cache.first;; if (row_sel_row_cache_is_empty(prebuilt)) { prebuilt->row_cache.first = 0; } } } /********************************************************************//** Add a record to the fetch cache. */ UNIV_INLINE void row_sel_row_cache_add( /*==================*/ row_prebuilt_t* prebuilt, /*!< in: prebuilt struct */ const rec_t* rec, /*!< in: record to push; must be protected by a page latch */ const ulint* offsets) /*!< in: rec_get_offsets() */ { ib_cached_row_t*row; ulint rec_len; ib_row_cache_t* row_cache; row_cache = &prebuilt->row_cache; ut_a(row_cache->first == 0); ut_ad(!row_sel_row_cache_is_full(prebuilt)); ut_ad(rec_offs_validate(rec, NULL, offsets)); row = &prebuilt->row_cache.ptr[row_cache->n_cached]; /* Get the size of the physical record in the page */ rec_len = rec_offs_size(offsets); /* Check if there is enough space for the record being added to the cache. Free existing memory if it won't fit. */ if (row->max_len < rec_len) { if (row->ptr != NULL) { ut_a(row->max_len > 0); ut_a(row->rec_len > 0); mem_free(row->ptr); row->ptr = NULL; row->rec = NULL; row->max_len = 0; row->rec_len = 0; } else { ut_a(row->ptr == NULL); ut_a(row->rec == NULL); ut_a(row->max_len == 0); ut_a(row->rec_len == 0); } } row->rec_len = rec_len; if (row->ptr == NULL) { row->max_len = row->rec_len * 2; row->ptr = mem_alloc(row->max_len); } ut_a(row->max_len >= row->rec_len); /* Note that the pointer returned by rec_copy() is actually an offset into cache->ptr, to the start of the record data. */ row->rec = rec_copy(row->ptr, rec, offsets); ++row_cache->n_cached; ut_a(row_cache->n_cached < row_cache->n_size); } /*********************************************************************//** Tries to do a shortcut to fetch a clustered index record with a unique key, using the hash index if possible (not always). We assume that the search mode is PAGE_CUR_GE, it is a consistent read, there is a read view in trx, btr search latch has been locked in S-mode. @return SEL_FOUND, SEL_EXHAUSTED, SEL_RETRY */ static ulint row_sel_try_search_shortcut_for_prebuilt( /*=====================================*/ const rec_t** out_rec,/*!< out: record if found */ row_prebuilt_t* prebuilt,/*!< in: prebuilt struct */ ulint** offsets,/*!< in/out: for rec_get_offsets(*out_rec) */ mem_heap_t** heap, /*!< in/out: heap for rec_get_offsets() */ mtr_t* mtr) /*!< in: started mtr */ { dict_index_t* index = prebuilt->index; const dtuple_t* search_tuple = prebuilt->search_tuple; btr_pcur_t* pcur = prebuilt->pcur; trx_t* trx = prebuilt->trx; const rec_t* rec; ut_ad(dict_index_is_clust(index)); #ifndef UNIV_SEARCH_DEBUG btr_pcur_open_with_no_init(index, search_tuple, PAGE_CUR_GE, BTR_SEARCH_LEAF, pcur, RW_S_LATCH, mtr); #else /* UNIV_SEARCH_DEBUG */ btr_pcur_open_with_no_init(index, search_tuple, PAGE_CUR_GE, BTR_SEARCH_LEAF, pcur, 0, mtr); #endif /* UNIV_SEARCH_DEBUG */ rec = btr_pcur_get_rec(pcur); if (!page_rec_is_user_rec(rec)) { return(SEL_RETRY); } /* As the cursor is now placed on a user record after a search with the mode PAGE_CUR_GE, the up_match field in the cursor tells how many fields in the user record matched to the search tuple */ if (btr_pcur_get_up_match(pcur) < dtuple_get_n_fields(search_tuple)) { return(SEL_EXHAUSTED); } /* This is a non-locking consistent read: if necessary, fetch a previous version of the record */ *offsets = rec_get_offsets(rec, index, *offsets, ULINT_UNDEFINED, heap); if (!lock_clust_rec_cons_read_sees(rec, index, *offsets, trx->read_view)) { return(SEL_RETRY); } if (rec_get_deleted_flag(rec, dict_table_is_comp(index->table))) { return(SEL_EXHAUSTED); } *out_rec = rec; return(SEL_FOUND); } /***********************************************************************//** This can only be used when this session is using a READ COMMITTED isolation level. Before calling this function we must use trx_reset_new_rec_lock_info() and trx_register_new_rec_lock() to store the information which new record locks really were set. This function removes a newly set lock under prebuilt->pcur, and also under prebuilt->clust_pcur. Currently, this is only used and tested in the case of an UPDATE or a DELETE statement, where the row lock is of the LOCK_X type. Thus, this implements a 'mini-rollback' that releases the latest record locks we set. @return error code or DB_SUCCESS */ UNIV_INTERN int row_unlock_for_client( /*==================*/ row_prebuilt_t* prebuilt, /*!< in: prebuilt struct handle */ ibool has_latches_on_recs)/*!< TRUE if called so that we have the latches on the records under pcur and clust_pcur, and we do not need to reposition the cursors. */ { btr_pcur_t* pcur = prebuilt->pcur; btr_pcur_t* clust_pcur = prebuilt->clust_pcur; trx_t* trx = prebuilt->trx; rec_t* rec; mtr_t mtr; ut_ad(prebuilt && trx); ut_ad(trx->client_thread_id == os_thread_get_curr_id()); if (UNIV_UNLIKELY(trx->isolation_level != TRX_ISO_READ_COMMITTED)) { ib_logger(ib_stream, "InnoDB: Error: row_unlock_for_client called though\n" "InnoDB: this session is not using" " READ COMMITTED isolation level.\n"); return(DB_SUCCESS); } trx->op_info = "unlock_row"; if (prebuilt->new_rec_locks >= 1) { mtr_start(&mtr); /* Restore the cursor position and find the record */ if (!has_latches_on_recs) { btr_pcur_restore_position(BTR_SEARCH_LEAF, pcur, &mtr); } rec = btr_pcur_get_rec(pcur); lock_rec_unlock(trx, btr_pcur_get_block(pcur), rec, prebuilt->select_lock_type); mtr_commit(&mtr); /* If the search was done through the clustered index, then we have not used clust_pcur at all, and we must NOT try to reset locks on clust_pcur. The values in clust_pcur may be garbage! */ if (dict_index_is_clust(prebuilt->index)) { goto func_exit; } } if (prebuilt->new_rec_locks >= 1) { mtr_start(&mtr); /* Restore the cursor position and find the record */ if (!has_latches_on_recs) { btr_pcur_restore_position(BTR_SEARCH_LEAF, clust_pcur, &mtr); } rec = btr_pcur_get_rec(clust_pcur); lock_rec_unlock(trx, btr_pcur_get_block(clust_pcur), rec, prebuilt->select_lock_type); mtr_commit(&mtr); } func_exit: trx->op_info = ""; return(DB_SUCCESS); } /********************************************************************//** This function does several things, in fact too many things: 1. Moveto/Search for a record 2. Next record 3. Prev record 4. Set appropriate locks (gap and table locks). 5. Handle rollback This function opens a cursor, and also implements fetch next and fetch prev. NOTE that if we do a search with a full key value from a unique index (ROW_SEL_EXACT), then we will not store the cursor position and fetch next or fetch prev must not be tried to the cursor! @return DB_SUCCESS, DB_RECORD_NOT_FOUND, DB_END_OF_INDEX, DB_DEADLOCK, DB_LOCK_TABLE_FULL, DB_CORRUPTION, or DB_TOO_BIG_RECORD */ UNIV_INTERN enum db_err row_search_for_client( /*==================*/ ib_recovery_t recovery, /*!< in: recovery flag */ ib_srch_mode_t mode, /*!< in: search mode */ row_prebuilt_t* prebuilt, /*!< in: prebuilt struct for the table handle; this contains the info of search_tuple, index; if search tuple contains 0 fields then we position the cursor at the start or the end of the index, depending on 'mode' */ ib_match_t match_mode, /*!< in: mode for matching the key */ ib_cur_op_t direction) /*!< in: cursor operation, NOTE: if this is != ROW_SEL_MOVETO, then prebuilt must have a pcur with stored position! In opening of a cursor 'direction' should be ROW_SEL_MOVETO */ { dict_index_t* index = prebuilt->index; ibool comp = dict_table_is_comp(index->table); const dtuple_t* search_tuple = prebuilt->search_tuple; btr_pcur_t* pcur = prebuilt->pcur; trx_t* trx = prebuilt->trx; dict_index_t* clust_index; que_thr_t* thr; const rec_t* rec; const rec_t* result_rec; const rec_t* clust_rec; enum db_err err = DB_SUCCESS; ibool unique_search = FALSE; ibool unique_search_from_clust_index = FALSE; ibool mtr_has_extra_clust_latch = FALSE; ibool moves_up = FALSE; ibool set_also_gap_locks = TRUE; /* if the returned record was locked and we did a semi-consistent read (fetch the newest committed version), then this is set to TRUE */ #ifdef UNIV_SEARCH_DEBUG ulint cnt = 0; #endif /* UNIV_SEARCH_DEBUG */ ulint next_offs; ibool same_user_rec; mtr_t mtr; mem_heap_t* heap = NULL; ulint offsets_[REC_OFFS_NORMAL_SIZE]; ulint* offsets = offsets_; void* cmp_ctx = index->cmp_ctx; rec_offs_init(offsets_); prebuilt->result = -1; ut_ad(index && pcur && search_tuple); ut_ad(trx->client_thread_id == os_thread_get_curr_id()); if (UNIV_UNLIKELY(prebuilt->table->ibd_file_missing)) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Error:\n" "InnoDB: The client is trying to use a table handle" " but the .ibd file for\n" "InnoDB: table %s does not exist.\n" "InnoDB: Have you deleted the .ibd file" " from the database directory under\n" "InnoDB: the datadir, or have you discarded the " "tablespace?\n" "InnoDB: Check the InnoDB website for details on " "InnoDB: how you can resolve the problem.\n", prebuilt->table->name); return(DB_ERROR); } if (UNIV_UNLIKELY(!prebuilt->index_usable)) { return(DB_MISSING_HISTORY); } if (UNIV_UNLIKELY(prebuilt->magic_n != ROW_PREBUILT_ALLOCATED)) { ib_logger(ib_stream, "InnoDB: Error: trying to free a corrupt\n" "InnoDB: table handle. Magic n %lu, table name ", (ulong) prebuilt->magic_n); ut_print_name(ib_stream, trx, TRUE, prebuilt->table->name); ib_logger(ib_stream, "\n"); ut_error; } #if 0 /* August 19, 2005 by Heikki: temporarily disable this error print until the cursor lock count is done correctly. See bugs #12263 and #12456!*/ if (trx->n_tables_in_use == 0 && UNIV_UNLIKELY(prebuilt->select_lock_type == LOCK_NONE)) { /* Note that if the client uses an InnoDB temp table that it created inside LOCK TABLES, then n_client_tables_in_use can be zero; in that case select_lock_type is set to LOCK_X in ::start_stmt. */ ib_logger(ib_stram, "InnoDB: Error: Client is trying to perform a read\n" "InnoDB: but it has not locked" " any tables.\n"); trx_print(ib_stream, trx, 600); ib_logger(ib_stream, "\n"); } #endif #if 0 ib_logger(ib_stream, "Match mode %lu\n search tuple ", (ulong) match_mode); dtuple_print(search_tuple); ib_logger(ib_stream, "N tables locked %lu\n", (ulong) trx->client_n_tables_locked); #endif /*-------------------------------------------------------------*/ /* PHASE 0: Release a possible s-latch we are holding on the adaptive hash index latch if there is someone waiting behind */ if (rw_lock_get_writer(&btr_search_latch) != RW_LOCK_NOT_LOCKED && trx->has_search_latch) { /* There is an x-latch request on the adaptive hash index: release the s-latch to reduce starvation and wait for BTR_SEA_TIMEOUT rounds before trying to keep it again over calls from the client */ rw_lock_s_unlock(&btr_search_latch); trx->has_search_latch = FALSE; trx->search_latch_timeout = BTR_SEA_TIMEOUT; } /* Reset the new record lock info if session is using a READ COMMITED isolation level. Then we are able to remove the record locks set here on an individual row. */ prebuilt->new_rec_locks = 0; /*-------------------------------------------------------------*/ /* PHASE 1: Try to pop the row from the prefetch cache */ if (UNIV_UNLIKELY(direction == ROW_SEL_MOVETO)) { trx->op_info = "starting index read"; row_sel_row_cache_reset(prebuilt); if (prebuilt->sel_graph == NULL) { /* Build a dummy select query graph */ row_sel_prebuild_graph(prebuilt); } } else { trx->op_info = "fetching rows"; /* Is this the first row being fetched by the cursor ? */ if (row_sel_row_cache_is_empty(prebuilt)) { prebuilt->row_cache.direction = direction; } if (UNIV_UNLIKELY(direction != prebuilt->row_cache.direction)) { if (!row_sel_row_cache_is_empty(prebuilt)) { ut_error; /* TODO: scrollable cursor: restore cursor to the place of the latest returned row, or better: prevent caching for a scroll cursor! */ } row_sel_row_cache_reset(prebuilt); } else if (UNIV_LIKELY(!row_sel_row_cache_is_empty(prebuilt))) { err = DB_SUCCESS; srv_n_rows_read++; goto func_exit; } else if (row_sel_row_cache_fetch_in_progress(prebuilt)) { /* The previous returned row was popped from the fetch cache, but the cache was not full at the time of the popping: no more rows can exist in the result set */ err = DB_RECORD_NOT_FOUND; goto func_exit; } mode = pcur->search_mode; } /* In a search where at most one record in the index may match, we can use a LOCK_REC_NOT_GAP type record lock when locking a non-delete-marked matching record. Note that in a unique secondary index there may be different delete-marked versions of a record where only the primary key values differ: thus in a secondary index we must use next-key locks when locking delete-marked records. */ if (match_mode == ROW_SEL_EXACT && dict_index_is_unique(index) && dtuple_get_n_fields(search_tuple) == dict_index_get_n_unique(index) && (dict_index_is_clust(index) || !dtuple_contains_null(search_tuple))) { /* Note above that a UNIQUE secondary index can contain many rows with the same key value if one of the columns is the SQL null. A clustered index should never contain null PK columns because we demand that all the columns in primary key are non-null. */ unique_search = TRUE; } mtr_start(&mtr); /*-------------------------------------------------------------*/ /* PHASE 2: Try fast adaptive hash index search if possible */ /* Next test if this is the special case where we can use the fast adaptive hash index to try the search. Since we must release the search system latch when we retrieve an externally stored field, we cannot use the adaptive hash index in a search in the case the row may be long and there may be externally stored fields */ if (UNIV_UNLIKELY(direction == ROW_SEL_MOVETO) && unique_search && dict_index_is_clust(index)) { mode = IB_CUR_GE; unique_search_from_clust_index = TRUE; if (trx->client_n_tables_locked == 0 && prebuilt->select_lock_type == LOCK_NONE && trx->isolation_level > TRX_ISO_READ_UNCOMMITTED && trx->read_view) { /* This is a SELECT query done as a consistent read, and the read view has already been allocated: let us try a search shortcut through the hash index. NOTE that we must also test that client_n_tables_locked == 0, because this might also be INSERT INTO ... SELECT ... or CREATE TABLE ... SELECT ... . Our algorithm is NOT prepared to inserts interleaved with the SELECT, and if we try that, we can deadlock on the adaptive hash index semaphore! */ #ifndef UNIV_SEARCH_DEBUG if (!trx->has_search_latch) { rw_lock_s_lock(&btr_search_latch); trx->has_search_latch = TRUE; } #endif switch (row_sel_try_search_shortcut_for_prebuilt( &rec, prebuilt, &offsets, &heap, &mtr)) { case SEL_FOUND: #ifdef UNIV_SEARCH_DEBUG ut_a(0 == cmp_dtuple_rec( cmp_ctx, search_tuple, rec, offsets)); #endif /* When searching for an exact match we don't position the persistent cursor therefore we must read in the record found into the pre-fetch cache for the user to access. */ if (match_mode == ROW_SEL_EXACT) { /* There can be no previous entries when doing a search with this match mode set. */ ut_a(row_sel_row_cache_is_empty( prebuilt)); row_sel_row_cache_add( prebuilt, rec, offsets); } mtr_commit(&mtr); /* ut_print_name(ib_stream, index->name); ib_logger(ib_stream, " shortcut\n"); */ srv_n_rows_read++; prebuilt->result = 0; err = DB_SUCCESS; goto release_search_latch_if_needed; case SEL_EXHAUSTED: mtr_commit(&mtr); /* ut_print_name(ib_stream, index->name); ib_logger(ib_stream, " record not found 2\n"); */ err = DB_RECORD_NOT_FOUND; release_search_latch_if_needed: if (trx->search_latch_timeout > 0 && trx->has_search_latch) { trx->search_latch_timeout--; rw_lock_s_unlock(&btr_search_latch); trx->has_search_latch = FALSE; } /* NOTE that we do NOT store the cursor position */ goto func_exit; case SEL_RETRY: break; default: ut_ad(0); } mtr_commit(&mtr); mtr_start(&mtr); } } /*-------------------------------------------------------------*/ /* PHASE 3: Open or restore index cursor position */ if (trx->has_search_latch) { rw_lock_s_unlock(&btr_search_latch); trx->has_search_latch = FALSE; } ut_a(trx->conc_state != TRX_NOT_STARTED); if (trx->isolation_level <= TRX_ISO_READ_COMMITTED && prebuilt->select_lock_type != LOCK_NONE && prebuilt->simple_select) { /* It is a plain locking SELECT and the isolation level is low: do not lock gaps */ set_also_gap_locks = FALSE; } /* Note that if the search mode was GE or G, then the cursor naturally moves upward (in fetch next) in alphabetical order, otherwise downward */ if (UNIV_UNLIKELY(direction == ROW_SEL_MOVETO)) { if (mode == IB_CUR_GE || mode == IB_CUR_G) { moves_up = TRUE; } } else if (direction == ROW_SEL_NEXT) { moves_up = TRUE; } thr = que_fork_get_first_thr(prebuilt->sel_graph); que_thr_move_to_run_state_for_client(thr, trx); clust_index = dict_table_get_first_index(index->table); if (UNIV_LIKELY(direction != ROW_SEL_MOVETO)) { if (!row_sel_restore_position( &same_user_rec, BTR_SEARCH_LEAF, pcur, moves_up, &mtr)) { goto next_rec; } } else if (dtuple_get_n_fields(search_tuple) > 0) { btr_pcur_open_with_no_init(index, search_tuple, mode, BTR_SEARCH_LEAF, pcur, 0, &mtr); pcur->trx_if_known = trx; rec = btr_pcur_get_rec(pcur); if (!moves_up && !page_rec_is_supremum(rec) && set_also_gap_locks && trx->isolation_level != TRX_ISO_READ_COMMITTED && prebuilt->select_lock_type != LOCK_NONE) { /* Try to place a gap lock on the next index record to prevent phantoms in ORDER BY ... DESC queries */ const rec_t* next = page_rec_get_next_const(rec); offsets = rec_get_offsets(next, index, offsets, ULINT_UNDEFINED, &heap); err = sel_set_rec_lock(btr_pcur_get_block(pcur), next, index, offsets, prebuilt->select_lock_type, LOCK_GAP, thr); if (err != DB_SUCCESS) { goto lock_wait_or_error; } } } else if (mode == IB_CUR_G) { btr_pcur_open_at_index_side( TRUE, index, BTR_SEARCH_LEAF, pcur, FALSE, &mtr); } else if (mode == IB_CUR_L) { btr_pcur_open_at_index_side( FALSE, index, BTR_SEARCH_LEAF, pcur, FALSE, &mtr); } if (!prebuilt->sql_stat_start) { /* No need to set an intention lock or assign a read view */ if (trx->read_view == NULL && prebuilt->select_lock_type == LOCK_NONE) { ib_logger(ib_stream, "InnoDB: Error: The client is trying to" " perform a consistent read\n" "InnoDB: but the read view is not assigned!\n"); trx_print(ib_stream, trx, 600); ib_logger(ib_stream, "\n"); ut_a(0); } } else if (prebuilt->select_lock_type == LOCK_NONE) { /* This is a consistent read */ /* Assign a read view for the query */ trx_assign_read_view(trx); prebuilt->sql_stat_start = FALSE; } else { ulint lock_mode; if (prebuilt->select_lock_type == LOCK_S) { lock_mode = LOCK_IS; } else { lock_mode = LOCK_IX; } err = lock_table(0, index->table, lock_mode, thr); if (err != DB_SUCCESS) { goto lock_wait_or_error; } prebuilt->sql_stat_start = FALSE; } rec_loop: /*-------------------------------------------------------------*/ /* PHASE 4: Look for matching records in a loop */ rec = btr_pcur_get_rec(pcur); ut_ad(!!page_rec_is_comp(rec) == comp); #ifdef UNIV_SEARCH_DEBUG /* ib_logger(ib_stream, "Using "); dict_index_name_print(ib_stream, index); ib_logger(ib_stream, " cnt %lu ; Page no %lu\n", cnt, page_get_page_no(page_align(rec))); rec_print(rec); */ #endif /* UNIV_SEARCH_DEBUG */ if (page_rec_is_infimum(rec)) { /* The infimum record on a page cannot be in the result set, and neither can a record lock be placed on it: we skip such a record. */ goto next_rec; } if (page_rec_is_supremum(rec)) { if (set_also_gap_locks && trx->isolation_level == TRX_ISO_READ_COMMITTED && prebuilt->select_lock_type != LOCK_NONE) { /* Try to place a lock on the index record */ /* If this session is using a READ COMMITTED isolation level we do not lock gaps. Supremum record is really a gap and therefore we do not set locks there. */ offsets = rec_get_offsets(rec, index, offsets, ULINT_UNDEFINED, &heap); err = sel_set_rec_lock(btr_pcur_get_block(pcur), rec, index, offsets, prebuilt->select_lock_type, LOCK_ORDINARY, thr); if (err != DB_SUCCESS) { goto lock_wait_or_error; } } /* A page supremum record cannot be in the result set: skip it now that we have placed a possible lock on it */ goto next_rec; } /*-------------------------------------------------------------*/ /* Do sanity checks in case our cursor has bumped into page corruption */ if (comp) { next_offs = rec_get_next_offs(rec, TRUE); if (UNIV_UNLIKELY(next_offs < PAGE_NEW_SUPREMUM)) { goto wrong_offs; } } else { next_offs = rec_get_next_offs(rec, FALSE); if (UNIV_UNLIKELY(next_offs < PAGE_OLD_SUPREMUM)) { goto wrong_offs; } } if (UNIV_UNLIKELY(next_offs >= UNIV_PAGE_SIZE - PAGE_DIR)) { wrong_offs: if (recovery == IB_RECOVERY_DEFAULT || moves_up == FALSE) { ut_print_timestamp(ib_stream); buf_page_print(page_align(rec), 0); ib_logger(ib_stream, "\nInnoDB: rec address %p," " buf block fix count %lu\n", (void*) rec, (ulong) btr_cur_get_block(btr_pcur_get_btr_cur(pcur)) ->page.buf_fix_count); ib_logger(ib_stream, "InnoDB: Index corruption: rec offs %lu" " next offs %lu, page no %lu,\n" "InnoDB: ", (ulong) page_offset(rec), (ulong) next_offs, (ulong) page_get_page_no(page_align(rec))); dict_index_name_print(ib_stream, trx, index); ib_logger(ib_stream, ". Run CHECK TABLE. You may need to\n" "InnoDB: restore from a backup, or" " dump + drop + reimport the table.\n"); err = DB_CORRUPTION; goto lock_wait_or_error; } else { /* The user may be dumping a corrupt table. Jump over the corruption to recover as much as possible. */ ib_logger(ib_stream, "InnoDB: Index corruption: rec offs %lu" " next offs %lu, page no %lu,\n" "InnoDB: ", (ulong) page_offset(rec), (ulong) next_offs, (ulong) page_get_page_no(page_align(rec))); dict_index_name_print(ib_stream, trx, index); ib_logger(ib_stream, ". We try to skip the rest of the page.\n"); btr_pcur_move_to_last_on_page(pcur, &mtr); goto next_rec; } } /*-------------------------------------------------------------*/ /* Calculate the 'offsets' associated with 'rec' */ offsets = rec_get_offsets(rec, index, offsets, ULINT_UNDEFINED, &heap); if (UNIV_UNLIKELY(recovery != IB_RECOVERY_DEFAULT)) { if (!rec_validate(rec, offsets) || !btr_index_rec_validate(rec, index, FALSE)) { ib_logger(ib_stream, "InnoDB: Index corruption: rec offs %lu" " next offs %lu, page no %lu,\n" "InnoDB: ", (ulong) page_offset(rec), (ulong) next_offs, (ulong) page_get_page_no(page_align(rec))); dict_index_name_print(ib_stream, trx, index); ib_logger(ib_stream, ". We try to skip the record.\n"); goto next_rec; } } /* Note that we cannot trust the up_match value in the cursor at this place because we can arrive here after moving the cursor! Thus we have to recompare rec and search_tuple to determine if they match enough. */ if (match_mode == ROW_SEL_EXACT) { int result; /* Test if the index record matches completely to search_tuple in prebuilt: if not, then we return with DB_RECORD_NOT_FOUND */ /* ib_logger(ib_stream, "Comparing rec and search tuple\n"); */ result = cmp_dtuple_rec(cmp_ctx, search_tuple, rec, offsets); if (row_sel_row_cache_is_empty(prebuilt)) { prebuilt->result = result; } if (result != 0) { goto not_found; } } else if (match_mode == ROW_SEL_EXACT_PREFIX) { if (!cmp_dtuple_is_prefix_of_rec( cmp_ctx, search_tuple, rec, offsets)) { prebuilt->result = -1; not_found: if (set_also_gap_locks && trx->isolation_level != TRX_ISO_READ_COMMITTED && prebuilt->select_lock_type != LOCK_NONE) { /* Try to place a gap lock on the index record only if this session is not using a READ COMMITTED isolation level. */ err = sel_set_rec_lock( btr_pcur_get_block(pcur), rec, index, offsets, prebuilt->select_lock_type, LOCK_GAP, thr); if (err != DB_SUCCESS) { goto lock_wait_or_error; } } btr_pcur_store_position(pcur, &mtr); err = DB_RECORD_NOT_FOUND; /* ut_print_name(ib_stream, index->name); ib_logger(ib_stream, " record not found 4\n"); */ goto normal_return; } } /* We are ready to look at a possible new index entry in the result set: the cursor is now placed on a user record */ if (prebuilt->select_lock_type != LOCK_NONE) { int result = -1; /* Try to place a lock on the index record; note that delete marked records are a special case in a unique search. If there is a non-delete marked record, then it is enough to lock its existence with LOCK_REC_NOT_GAP. */ /* If this session is using a READ COMMITED isolation level we lock only the record, i.e., next-key locking is not used. */ ulint lock_type; if (!set_also_gap_locks || trx->isolation_level == TRX_ISO_READ_COMMITTED || (unique_search && !UNIV_UNLIKELY(rec_get_deleted_flag(rec, comp)))) { goto no_gap_lock; } else { lock_type = LOCK_ORDINARY; } /* If we are doing a 'greater or equal than a primary key value' search from a clustered index, and we find a record that has that exact primary key value, then there is no need to lock the gap before the record, because no insert in the gap can be in our search range. That is, no phantom row can appear that way. An example: if col1 is the primary key, the search is WHERE col1 >= 100, and we find a record where col1 = 100, then no need to lock the gap before that record. */ if (index == clust_index && mode == IB_CUR_GE && direction == ROW_SEL_MOVETO && dtuple_get_n_fields_cmp(search_tuple) == dict_index_get_n_unique(index) && !(result = cmp_dtuple_rec( cmp_ctx, search_tuple, rec, offsets))) { no_gap_lock: lock_type = LOCK_REC_NOT_GAP; } if (row_sel_row_cache_is_empty(prebuilt)) { prebuilt->result = result; } err = sel_set_rec_lock(btr_pcur_get_block(pcur), rec, index, offsets, prebuilt->select_lock_type, lock_type, thr); if (err != DB_SUCCESS) { goto lock_wait_or_error; } } else { /* This is a non-locking consistent read: if necessary, fetch a previous version of the record */ if (trx->isolation_level == TRX_ISO_READ_UNCOMMITTED) { /* Do nothing: we let a non-locking SELECT read the latest version of the record */ } else if (index == clust_index) { /* Fetch a previous version of the row if the current one is not visible in the snapshot; if we have a very high force recovery level set, we try to avoid crashes by skipping this lookup */ if (UNIV_LIKELY(recovery < IB_RECOVERY_NO_UNDO_LOG_SCAN) && !lock_clust_rec_cons_read_sees( rec, index, offsets, trx->read_view)) { rec_t* old_vers; /* The following call returns 'offsets' associated with 'old_vers' */ err = row_sel_build_prev_vers( trx->read_view, clust_index, rec, &offsets, &heap, &prebuilt->old_vers_heap, &old_vers, &mtr); if (err != DB_SUCCESS) { goto lock_wait_or_error; } if (old_vers == NULL) { /* The row did not exist yet in the read view */ goto next_rec; } rec = old_vers; } } else if (!lock_sec_rec_cons_read_sees(rec, trx->read_view)) { /* We are looking into a non-clustered index, and to get the right version of the record we have to look also into the clustered index: this is necessary, because we can only get the undo information via the clustered index record. */ ut_ad(index != clust_index); if (direction == ROW_SEL_MOVETO && row_sel_row_cache_is_empty(prebuilt)) { prebuilt->result = cmp_dtuple_rec( cmp_ctx, search_tuple, rec, offsets); } goto requires_clust_rec; } } /* NOTE that at this point rec can be an old version of a clustered index record built for a consistent read. We cannot assume after this point that rec is on a buffer pool page. Functions like page_rec_is_comp() cannot be used! */ if (UNIV_UNLIKELY(rec_get_deleted_flag(rec, comp))) { /* The record is delete-marked: we can skip it */ if (trx->isolation_level == TRX_ISO_READ_COMMITTED && prebuilt->select_lock_type != LOCK_NONE) { /* No need to keep a lock on a delete-marked record if we do not want to use next-key locking. */ row_unlock_for_client(prebuilt, TRUE); } /* This is an optimization to skip setting the next key lock on the record that follows this delete-marked record. This optimization works because of the unique search criteria which precludes the presence of a range lock between this delete marked record and the record following it. For now this is applicable only to clustered indexes while doing a unique search. There is scope for further optimization applicable to unique secondary indexes. Current behaviour is to widen the scope of a lock on an already delete marked record if the same record is deleted twice by the same transaction */ if (index == clust_index && unique_search) { prebuilt->result = -1; err = DB_RECORD_NOT_FOUND; goto normal_return; } goto next_rec; } else if (direction == ROW_SEL_MOVETO && row_sel_row_cache_is_empty(prebuilt)) { prebuilt->result = cmp_dtuple_rec( cmp_ctx, search_tuple, rec, offsets); } /* Get the clustered index record if needed, if we did not do the search using the clustered index. */ if (index != clust_index && prebuilt->need_to_access_clustered) { requires_clust_rec: /* We use a 'goto' to the preceding label if a consistent read of a secondary index record requires us to look up old versions of the associated clustered index record. */ ut_ad(rec_offs_validate(rec, index, offsets)); /* It was a non-clustered index and we must fetch also the clustered index record */ mtr_has_extra_clust_latch = TRUE; /* The following call returns 'offsets' associated with 'clust_rec'. Note that 'clust_rec' can be an old version built for a consistent read. */ err = row_sel_get_clust_rec_with_prebuilt( prebuilt, index, rec, thr, &clust_rec, &offsets, &heap, &mtr); if (err != DB_SUCCESS) { goto lock_wait_or_error; } if (clust_rec == NULL) { /* The record did not exist in the read view */ ut_ad(prebuilt->select_lock_type == LOCK_NONE); goto next_rec; } if (trx->isolation_level == TRX_ISO_READ_COMMITTED && prebuilt->select_lock_type != LOCK_NONE) { /* Note that both the secondary index record and the clustered index record were locked. */ ut_ad(prebuilt->new_rec_locks == 1); prebuilt->new_rec_locks = 2; } if (UNIV_UNLIKELY(rec_get_deleted_flag(clust_rec, comp))) { /* The record is delete marked: we can skip it */ if (trx->isolation_level == TRX_ISO_READ_COMMITTED && prebuilt->select_lock_type != LOCK_NONE) { /* No need to keep a lock on a delete-marked record if we do not want to use next-key locking. */ row_unlock_for_client(prebuilt, TRUE); } goto next_rec; } if (row_sel_row_cache_is_empty(prebuilt)) { prebuilt->result = cmp_dtuple_rec( cmp_ctx, search_tuple, clust_rec, offsets); } if (prebuilt->need_to_access_clustered) { result_rec = clust_rec; ut_ad(rec_offs_validate(result_rec, clust_index, offsets)); } else { /* We used 'offsets' for the clust rec, recalculate them for 'rec' */ offsets = rec_get_offsets(rec, index, offsets, ULINT_UNDEFINED, &heap); result_rec = rec; } } else { result_rec = rec; if (row_sel_row_cache_is_empty(prebuilt)) { prebuilt->result = cmp_dtuple_rec( cmp_ctx, search_tuple, rec, offsets); } } /* We found a qualifying record 'result_rec'. At this point, 'offsets' are associated with 'result_rec'. */ ut_ad(rec_offs_validate(result_rec, result_rec != rec ? clust_index : index, offsets)); /* At this point, the clustered index record is protected by a page latch that was acquired when pcur was positioned. The latch will not be released until mtr_commit(&mtr). */ if ((match_mode == ROW_SEL_EXACT || prebuilt->row_cache.n_cached >= prebuilt->row_cache.n_size - 1) && prebuilt->select_lock_type == LOCK_NONE && !prebuilt->clust_index_was_generated) { /* Inside an update, for example, we do not cache rows, since we may use the cursor position to do the actual update, that is why we require ...lock_type == LOCK_NONE. FIXME: How do we handle scrollable cursors ? */ row_sel_row_cache_add(prebuilt, result_rec, offsets); /* An exact match means a unique lookup, no need to fill the cache with more records. */ if (unique_search || row_sel_row_cache_is_full(prebuilt)) { goto got_row; } goto next_rec; } ut_a(!row_sel_row_cache_is_full(prebuilt)); row_sel_row_cache_add(prebuilt, result_rec, offsets); /* From this point on, 'offsets' are invalid. */ got_row: /* We have an optimization to save CPU time: if this is a consistent read on a unique condition on the clustered index, then we do not store the pcur position, because any fetch next or prev will anyway return 'end of file'. Exceptions are locking reads and where the user can move the cursor with PREV or NEXT even after a unique search. */ if (!unique_search_from_clust_index || prebuilt->select_lock_type != LOCK_NONE) { /* Inside an update always store the cursor position */ btr_pcur_store_position(pcur, &mtr); } err = DB_SUCCESS; goto normal_return; next_rec: prebuilt->new_rec_locks = 0; /*-------------------------------------------------------------*/ /* PHASE 5: Move the cursor to the next index record */ if (UNIV_UNLIKELY(mtr_has_extra_clust_latch)) { /* We must commit mtr if we are moving to the next non-clustered index record, because we could break the latching order if we would access a different clustered index page right away without releasing the previous. */ btr_pcur_store_position(pcur, &mtr); mtr_commit(&mtr); mtr_has_extra_clust_latch = FALSE; mtr_start(&mtr); if (row_sel_restore_position( &same_user_rec, BTR_SEARCH_LEAF, pcur, moves_up, &mtr)) { #ifdef UNIV_SEARCH_DEBUG cnt++; #endif /* UNIV_SEARCH_DEBUG */ goto rec_loop; } } if (moves_up) { if (UNIV_UNLIKELY(!btr_pcur_move_to_next(pcur, &mtr))) { not_moved: btr_pcur_store_position(pcur, &mtr); if (match_mode != ROW_SEL_DEFAULT) { err = DB_RECORD_NOT_FOUND; } else { err = DB_END_OF_INDEX; } prebuilt->result = -1; goto normal_return; } } else if (UNIV_UNLIKELY(!btr_pcur_move_to_prev(pcur, &mtr))) { goto not_moved; } #ifdef UNIV_SEARCH_DEBUG cnt++; #endif /* UNIV_SEARCH_DEBUG */ goto rec_loop; lock_wait_or_error: /*-------------------------------------------------------------*/ btr_pcur_store_position(pcur, &mtr); mtr_commit(&mtr); mtr_has_extra_clust_latch = FALSE; trx->error_state = err; /* Stop the dummy thread we created for this query. */ que_thr_stop_client(thr); thr->lock_state = QUE_THR_LOCK_ROW; if (ib_handle_errors(&err, trx, thr, NULL)) { /* It was a lock wait, and it ended */ thr->lock_state = QUE_THR_LOCK_NOLOCK; mtr_start(&mtr); row_sel_restore_position( &same_user_rec, BTR_SEARCH_LEAF, pcur, moves_up, &mtr); if (trx->isolation_level == TRX_ISO_READ_COMMITTED && !same_user_rec) { /* Since we were not able to restore the cursor on the same user record, we cannot use row_unlock_for_client() to unlock any records, and we must thus reset the new rec lock info. Since in lock0lock.c we have blocked the inheriting of gap X-locks, we actually do not have any new record locks set in this case. Note that if we were able to restore on the 'same' user record, it is still possible that we were actually waiting on a delete-marked record, and meanwhile it was removed by purge and inserted again by some other user. But that is no problem, because in rec_loop we will again try to set a lock, and new_rec_lock_info in trx will be right at the end. */ prebuilt->new_rec_locks = 0; } mode = pcur->search_mode; goto rec_loop; } thr->lock_state = QUE_THR_LOCK_NOLOCK; #ifdef UNIV_SEARCH_DEBUG /* ib_logger(ib_stream, "Using "); dict_index_name_print(ib_stream, index); ib_logger(ib_stream, " cnt %lu ret value %lu err\n", cnt, err); */ #endif /* UNIV_SEARCH_DEBUG */ goto func_exit; normal_return: /*-------------------------------------------------------------*/ que_thr_stop_for_client_no_error(thr, trx); mtr_commit(&mtr); if (prebuilt->row_cache.n_cached > 0) { err = DB_SUCCESS; } #ifdef UNIV_SEARCH_DEBUG /* ib_logger(ib_strean, "Using "); dict_index_name_print(ib_stream, index); ib_logger(ib_stream, " cnt %lu ret value %lu err\n", cnt, err); */ #endif /* UNIV_SEARCH_DEBUG */ if (err == DB_SUCCESS) { srv_n_rows_read++; } func_exit: trx->op_info = ""; if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } return(err); } haildb-2.3.2/row/row0ins.c0000644000175000017500000020021611513177357016220 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2010, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file row/row0ins.c Insert into a table Created 4/20/1996 Heikki Tuuri *******************************************************/ #include "row0ins.h" #ifdef UNIV_NONINL #include "row0ins.ic" #endif #include "dict0dict.h" #include "dict0boot.h" #include "trx0undo.h" #include "btr0btr.h" #include "btr0cur.h" #include "mach0data.h" #include "que0que.h" #include "row0upd.h" #include "row0sel.h" #include "row0row.h" #include "rem0cmp.h" #include "lock0lock.h" #include "log0log.h" #include "eval0eval.h" #include "data0data.h" #include "usr0sess.h" #include "buf0lru.h" #include "api0misc.h" #define ROW_INS_PREV 1 #define ROW_INS_NEXT 2 /*********************************************************************//** Creates an insert node struct. @return own: insert node struct */ UNIV_INTERN ins_node_t* row_ins_node_create( /*================*/ ib_ins_mode_t ins_type, /*!< in: INS_VALUES, ... */ dict_table_t* table, /*!< in: table where to insert */ mem_heap_t* heap) /*!< in: mem heap where created */ { ins_node_t* node; node = mem_heap_alloc(heap, sizeof(ins_node_t)); node->common.type = QUE_NODE_INSERT; node->ins_type = ins_type; node->state = INS_NODE_SET_IX_LOCK; node->table = table; node->index = NULL; node->entry = NULL; node->select = NULL; node->trx_id = ut_dulint_zero; node->entry_sys_heap = mem_heap_create(128); node->magic_n = INS_NODE_MAGIC_N; return(node); } /***********************************************************//** Creates an entry template for each index of a table. */ UNIV_INTERN void row_ins_node_create_entry_list( /*=======================*/ ins_node_t* node) /*!< in: row insert node */ { dict_index_t* index; dtuple_t* entry; ut_ad(node->entry_sys_heap); UT_LIST_INIT(node->entry_list); index = dict_table_get_first_index(node->table); while (index != NULL) { entry = row_build_index_entry( node->row, NULL, index, node->entry_sys_heap); UT_LIST_ADD_LAST(tuple_list, node->entry_list, entry); index = dict_table_get_next_index(index); } } /*****************************************************************//** Adds system field buffers to a row. */ static void row_ins_alloc_sys_fields( /*=====================*/ ins_node_t* node) /*!< in: insert node */ { dtuple_t* row; dict_table_t* table; mem_heap_t* heap; const dict_col_t* col; dfield_t* dfield; byte* ptr; row = node->row; table = node->table; heap = node->entry_sys_heap; ut_ad(row && table && heap); ut_ad(dtuple_get_n_fields(row) == dict_table_get_n_cols(table)); /* 1. Allocate buffer for row id */ col = dict_table_get_sys_col(table, DATA_ROW_ID); dfield = dtuple_get_nth_field(row, dict_col_get_no(col)); ptr = mem_heap_zalloc(heap, DATA_ROW_ID_LEN); dfield_set_data(dfield, ptr, DATA_ROW_ID_LEN); node->row_id_buf = ptr; /* 3. Allocate buffer for trx id */ col = dict_table_get_sys_col(table, DATA_TRX_ID); dfield = dtuple_get_nth_field(row, dict_col_get_no(col)); ptr = mem_heap_zalloc(heap, DATA_TRX_ID_LEN); dfield_set_data(dfield, ptr, DATA_TRX_ID_LEN); node->trx_id_buf = ptr; /* 4. Allocate buffer for roll ptr */ col = dict_table_get_sys_col(table, DATA_ROLL_PTR); dfield = dtuple_get_nth_field(row, dict_col_get_no(col)); ptr = mem_heap_zalloc(heap, DATA_ROLL_PTR_LEN); dfield_set_data(dfield, ptr, DATA_ROLL_PTR_LEN); } /*********************************************************************//** Sets a new row to insert for an INS_DIRECT node. This function is only used if we have constructed the row separately, which is a rare case; this function is quite slow. */ UNIV_INTERN void row_ins_node_set_new_row( /*=====================*/ ins_node_t* node, /*!< in: insert node */ dtuple_t* row) /*!< in: new row (or first row) for the node */ { node->state = INS_NODE_SET_IX_LOCK; node->index = NULL; node->entry = NULL; node->row = row; mem_heap_empty(node->entry_sys_heap); /* Create templates for index entries */ row_ins_node_create_entry_list(node); /* Allocate from entry_sys_heap buffers for sys fields */ row_ins_alloc_sys_fields(node); /* As we allocated a new trx id buf, the trx id should be written there again: */ node->trx_id = ut_dulint_zero; } /*******************************************************************//** Does an insert operation by updating a delete-marked existing record in the index. This situation can occur if the delete-marked record is kept in the index for consistent reads. @return DB_SUCCESS or error code */ static ulint row_ins_sec_index_entry_by_modify( /*==============================*/ ulint mode, /*!< in: BTR_MODIFY_LEAF or BTR_MODIFY_TREE, depending on whether mtr holds just a leaf latch or also a tree latch */ btr_cur_t* cursor, /*!< in: B-tree cursor */ const dtuple_t* entry, /*!< in: index entry to insert */ que_thr_t* thr, /*!< in: query thread */ mtr_t* mtr) /*!< in: mtr; must be committed before latching any further pages */ { big_rec_t* dummy_big_rec; mem_heap_t* heap; upd_t* update; rec_t* rec; ulint err; rec = btr_cur_get_rec(cursor); ut_ad(!dict_index_is_clust(cursor->index)); ut_ad(rec_get_deleted_flag(rec, dict_table_is_comp(cursor->index->table))); /* We know that in the alphabetical ordering, entry and rec are identified. But in their binary form there may be differences if there are char fields in them. Therefore we have to calculate the difference. */ heap = mem_heap_create(1024); update = row_upd_build_sec_rec_difference_binary( cursor->index, entry, rec, thr_get_trx(thr), heap); if (mode == BTR_MODIFY_LEAF) { /* Try an optimistic updating of the record, keeping changes within the page */ err = btr_cur_optimistic_update(BTR_KEEP_SYS_FLAG, cursor, update, 0, thr, mtr); switch (err) { case DB_OVERFLOW: case DB_UNDERFLOW: case DB_ZIP_OVERFLOW: err = DB_FAIL; } } else { ut_a(mode == BTR_MODIFY_TREE); if (buf_LRU_buf_pool_running_out()) { err = DB_LOCK_TABLE_FULL; goto func_exit; } err = btr_cur_pessimistic_update(BTR_KEEP_SYS_FLAG, cursor, &heap, &dummy_big_rec, update, 0, thr, mtr); ut_ad(!dummy_big_rec); } func_exit: mem_heap_free(heap); return(err); } /*******************************************************************//** Does an insert operation by delete unmarking and updating a delete marked existing record in the index. This situation can occur if the delete marked record is kept in the index for consistent reads. @return DB_SUCCESS, DB_FAIL, or error code */ static ulint row_ins_clust_index_entry_by_modify( /*================================*/ ulint mode, /*!< in: BTR_MODIFY_LEAF or BTR_MODIFY_TREE, depending on whether mtr holds just a leaf latch or also a tree latch */ btr_cur_t* cursor, /*!< in: B-tree cursor */ mem_heap_t** heap, /*!< in/out: pointer to memory heap, or NULL */ big_rec_t** big_rec,/*!< out: possible big rec vector of fields which have to be stored externally by the caller */ const dtuple_t* entry, /*!< in: index entry to insert */ que_thr_t* thr, /*!< in: query thread */ mtr_t* mtr) /*!< in: mtr; must be committed before latching any further pages */ { rec_t* rec; upd_t* update; ulint err; ut_ad(dict_index_is_clust(cursor->index)); *big_rec = NULL; rec = btr_cur_get_rec(cursor); ut_ad(rec_get_deleted_flag(rec, dict_table_is_comp(cursor->index->table))); if (!*heap) { *heap = mem_heap_create(1024); } /* Build an update vector containing all the fields to be modified; NOTE that this vector may NOT contain system columns trx_id or roll_ptr */ update = row_upd_build_difference_binary(cursor->index, entry, rec, thr_get_trx(thr), *heap); if (mode == BTR_MODIFY_LEAF) { /* Try optimistic updating of the record, keeping changes within the page */ err = btr_cur_optimistic_update(0, cursor, update, 0, thr, mtr); switch (err) { case DB_OVERFLOW: case DB_UNDERFLOW: case DB_ZIP_OVERFLOW: err = DB_FAIL; } } else { ut_a(mode == BTR_MODIFY_TREE); if (buf_LRU_buf_pool_running_out()) { return(DB_LOCK_TABLE_FULL); } err = btr_cur_pessimistic_update(0, cursor, heap, big_rec, update, 0, thr, mtr); } return(err); } /*********************************************************************//** Returns TRUE if in a cascaded update/delete an ancestor node of node updates (not DELETE, but UPDATE) table. @return TRUE if an ancestor updates table */ static ibool row_ins_cascade_ancestor_updates_table( /*===================================*/ que_node_t* node, /*!< in: node in a query graph */ dict_table_t* table) /*!< in: table */ { que_node_t* parent; upd_node_t* upd_node; parent = que_node_get_parent(node); while (que_node_get_type(parent) == QUE_NODE_UPDATE) { upd_node = parent; if (upd_node->table == table && upd_node->is_delete == FALSE) { return(TRUE); } parent = que_node_get_parent(parent); ut_a(parent); } return(FALSE); } /*********************************************************************//** Returns the number of ancestor UPDATE or DELETE nodes of a cascaded update/delete node. @return number of ancestors */ static ulint row_ins_cascade_n_ancestors( /*========================*/ que_node_t* node) /*!< in: node in a query graph */ { que_node_t* parent; ulint n_ancestors = 0; parent = que_node_get_parent(node); while (que_node_get_type(parent) == QUE_NODE_UPDATE) { n_ancestors++; parent = que_node_get_parent(parent); ut_a(parent); } return(n_ancestors); } /******************************************************************//** Calculates the update vector node->cascade->update for a child table in a cascaded update. @return number of fields in the calculated update vector; the value can also be 0 if no foreign key fields changed; the returned value is ULINT_UNDEFINED if the column type in the child table is too short to fit the new value in the parent table: that means the update fails */ static ulint row_ins_cascade_calc_update_vec( /*============================*/ upd_node_t* node, /*!< in: update node of the parent table */ dict_foreign_t* foreign, /*!< in: foreign key constraint whose type is != 0 */ mem_heap_t* heap) /*!< in: memory heap to use as temporary storage */ { upd_node_t* cascade = node->cascade_node; dict_table_t* table = foreign->foreign_table; dict_index_t* index = foreign->foreign_index; upd_t* update; upd_field_t* ufield; dict_table_t* parent_table; dict_index_t* parent_index; upd_t* parent_update; upd_field_t* parent_ufield; ulint n_fields_updated; ulint parent_field_no; ulint i; ulint j; ut_a(node); ut_a(foreign); ut_a(cascade); ut_a(table); ut_a(index); /* Calculate the appropriate update vector which will set the fields in the child index record to the same value (possibly padded with spaces if the column is a fixed length CHAR or FIXBINARY column) as the referenced index record will get in the update. */ parent_table = node->table; ut_a(parent_table == foreign->referenced_table); parent_index = foreign->referenced_index; parent_update = node->update; update = cascade->update; update->info_bits = 0; update->n_fields = foreign->n_fields; n_fields_updated = 0; for (i = 0; i < foreign->n_fields; i++) { parent_field_no = dict_table_get_nth_col_pos( parent_table, dict_index_get_nth_col_no(parent_index, i)); for (j = 0; j < parent_update->n_fields; j++) { parent_ufield = parent_update->fields + j; if (parent_ufield->field_no == parent_field_no) { ulint min_size; const dict_col_t* col; ulint ufield_len; col = dict_index_get_nth_col(index, i); /* A field in the parent index record is updated. Let us make the update vector field for the child table. */ ufield = update->fields + n_fields_updated; ufield->field_no = dict_table_get_nth_col_pos( table, dict_col_get_no(col)); ufield->exp = NULL; ufield->new_val = parent_ufield->new_val; ufield_len = dfield_get_len(&ufield->new_val); /* Clear the "external storage" flag */ dfield_set_len(&ufield->new_val, ufield_len); /* Do not allow a NOT NULL column to be updated as NULL */ if (dfield_is_null(&ufield->new_val) && (col->prtype & DATA_NOT_NULL)) { return(ULINT_UNDEFINED); } /* If the new value would not fit in the column, do not allow the update */ if (!dfield_is_null(&ufield->new_val) && dtype_get_at_most_n_mbchars( col->prtype, col->mbminlen, col->mbmaxlen, col->len, ufield_len, dfield_get_data(&ufield->new_val)) < ufield_len) { return(ULINT_UNDEFINED); } /* If the parent column type has a different length than the child column type, we may need to pad with spaces the new value of the child column */ min_size = dict_col_get_min_size(col); /* Because UNIV_SQL_NULL (the marker of SQL NULL values) exceeds all possible values of min_size, the test below will not hold for SQL NULL columns. */ if (min_size > ufield_len) { char* pad_start; const char* pad_end; char* padded_data = mem_heap_alloc( heap, min_size); pad_start = padded_data + ufield_len; pad_end = padded_data + min_size; memcpy(padded_data, dfield_get_data(&ufield ->new_val), dfield_get_len(&ufield ->new_val)); switch (UNIV_EXPECT(col->mbminlen,1)) { default: ut_error; return(ULINT_UNDEFINED); case 1: if (UNIV_UNLIKELY (dtype_get_charset_coll( col->prtype) == DATA_CLIENT_BINARY_CHARSET_COLL)) { /* Do not pad BINARY columns. */ return(ULINT_UNDEFINED); } /* space=0x20 */ memset(pad_start, 0x20, pad_end - pad_start); break; case 2: /* space=0x0020 */ ut_a(!(ufield_len % 2)); ut_a(!(min_size % 2)); do { *pad_start++ = 0x00; *pad_start++ = 0x20; } while (pad_start < pad_end); break; } dfield_set_data(&ufield->new_val, padded_data, min_size); } n_fields_updated++; } } } update->n_fields = n_fields_updated; return(n_fields_updated); } /*********************************************************************//** Set detailed error message associated with foreign key errors for the given transaction. */ static void row_ins_set_detailed( /*=================*/ trx_t* trx, /*!< in: transaction */ dict_foreign_t* foreign) /*!< in: foreign key constraint */ { ut_print_name(ib_stream, trx, TRUE, foreign->foreign_table_name); dict_print_info_on_foreign_key_in_create_format( ib_stream, trx, foreign, FALSE); trx_set_detailed_error(trx, "foreign key error"); } /*********************************************************************//** Reports a foreign key error associated with an update or a delete of a parent table index entry. */ static void row_ins_foreign_report_err( /*=======================*/ const char* errstr, /*!< in: error string from the viewpoint of the parent table */ que_thr_t* thr, /*!< in: query thread whose run_node is an update node */ dict_foreign_t* foreign, /*!< in: foreign key constraint */ const rec_t* rec, /*!< in: a matching index record in the child table */ const dtuple_t* entry) /*!< in: index entry in the parent table */ { trx_t* trx = thr_get_trx(thr); row_ins_set_detailed(trx, foreign); mutex_enter(&dict_foreign_err_mutex); ut_print_timestamp(ib_stream); ib_logger(ib_stream, " Transaction:\n"); trx_print(ib_stream, trx, 600); ib_logger(ib_stream, "Foreign key constraint fails for table "); ut_print_name(ib_stream, trx, TRUE, foreign->foreign_table_name); ib_logger(ib_stream, ":\n"); dict_print_info_on_foreign_key_in_create_format( ib_stream, trx, foreign, TRUE); ib_logger(ib_stream, "\n%s", errstr); ib_logger(ib_stream, " in parent table, in index "); ut_print_name(ib_stream, trx, FALSE, foreign->referenced_index->name); if (entry) { ib_logger(ib_stream, " tuple:\n"); dtuple_print(ib_stream, entry); } ib_logger(ib_stream, "\nBut in child table "); ut_print_name(ib_stream, trx, TRUE, foreign->foreign_table_name); ib_logger(ib_stream, ", in index "); ut_print_name(ib_stream, trx, FALSE, foreign->foreign_index->name); if (rec) { ib_logger(ib_stream, ", there is a record:\n"); rec_print(ib_stream, rec, foreign->foreign_index); } else { ib_logger(ib_stream, ", the record is not available\n"); } ib_logger(ib_stream, "\n"); mutex_exit(&dict_foreign_err_mutex); } /*********************************************************************//** Reports a foreign key error to ib_stream when we are trying to add an index entry to a child table. Note that the adding may be the result of an update, too. */ static void row_ins_foreign_report_add_err( /*===========================*/ trx_t* trx, /*!< in: transaction */ dict_foreign_t* foreign, /*!< in: foreign key constraint */ const rec_t* rec, /*!< in: a record in the parent table: it does not match entry because we have an error! */ const dtuple_t* entry) /*!< in: index entry to insert in the child table */ { row_ins_set_detailed(trx, foreign); mutex_enter(&dict_foreign_err_mutex); ut_print_timestamp(ib_stream); ib_logger(ib_stream, " Transaction:\n"); trx_print(ib_stream, trx, 600); ib_logger(ib_stream, "Foreign key constraint fails for table "); ut_print_name(ib_stream, trx, TRUE, foreign->foreign_table_name); ib_logger(ib_stream, ":\n"); dict_print_info_on_foreign_key_in_create_format( ib_stream, trx, foreign, TRUE); ib_logger(ib_stream, "\nTrying to add in child table, in index "); ut_print_name(ib_stream, trx, FALSE, foreign->foreign_index->name); if (entry) { ib_logger(ib_stream, " tuple:\n"); /* TODO: DB_TRX_ID and DB_ROLL_PTR may be uninitialized. It would be better to only display the user columns. */ dtuple_print(ib_stream, entry); } ib_logger(ib_stream, "\nBut in parent table "); ut_print_name(ib_stream, trx, TRUE, foreign->referenced_table_name); ib_logger(ib_stream, ", in index "); ut_print_name(ib_stream, trx, FALSE, foreign->referenced_index->name); ib_logger(ib_stream, ",\nthe closest match we can find is record:\n"); if (rec && page_rec_is_supremum(rec)) { /* If the cursor ended on a supremum record, it is better to report the previous record in the error message, so that the user gets a more descriptive error message. */ rec = page_rec_get_prev_const(rec); } if (rec) { rec_print(ib_stream, rec, foreign->referenced_index); } ib_logger(ib_stream, "\n"); mutex_exit(&dict_foreign_err_mutex); } /************************************************************************** Does a cascaded delete or set null in a foreign key operation. @return error code or DB_SUCCESS */ static ulint row_update_cascade_client( /*======================*/ que_thr_t* thr, /*!< in: query thread */ upd_node_t* node, /*!< in: update node used in the cascade or set null operation */ dict_table_t* table) /*!< in: table where we do the operation */ { ulint err; trx_t* trx; trx = thr_get_trx(thr); run_again: thr->run_node = node; thr->prev_node = node; row_upd_step(thr); err = trx->error_state; /* Note that the cascade node is a subnode of another InnoDB query graph node. We do a normal lock wait in this node, but all errors are handled by the parent node. */ if (err == DB_LOCK_WAIT) { /* Handle lock wait here */ que_thr_stop_client(thr); srv_suspend_user_thread(thr); /* Note that a lock wait may also end in a lock wait timeout, or this transaction is picked as a victim in selective deadlock resolution */ if (trx->error_state != DB_SUCCESS) { return(trx->error_state); } /* Retry operation after a normal lock wait */ goto run_again; } if (err != DB_SUCCESS) { return(err); } if (node->is_delete) { if (table->stat_n_rows > 0) { table->stat_n_rows--; } srv_n_rows_deleted++; } else { srv_n_rows_updated++; } ib_update_statistics_if_needed(table); return(err); } /*********************************************************************//** Perform referential actions or checks when a parent row is deleted or updated and the constraint had an ON DELETE or ON UPDATE condition which was not RESTRICT. @return DB_SUCCESS, DB_LOCK_WAIT, or error code */ static ulint row_ins_foreign_check_on_constraint( /*================================*/ que_thr_t* thr, /*!< in: query thread whose run_node is an update node */ dict_foreign_t* foreign, /*!< in: foreign key constraint whose type is != 0 */ btr_pcur_t* pcur, /*!< in: cursor placed on a matching index record in the child table */ dtuple_t* entry, /*!< in: index entry in the parent table */ mtr_t* mtr) /*!< in: mtr holding the latch of pcur page */ { upd_node_t* node; upd_node_t* cascade; dict_table_t* table = foreign->foreign_table; dict_index_t* index; dict_index_t* clust_index; dtuple_t* ref; mem_heap_t* upd_vec_heap = NULL; const rec_t* rec; const rec_t* clust_rec; const buf_block_t* clust_block; upd_t* update; ulint n_to_update; ulint err; ulint i; trx_t* trx; mem_heap_t* tmp_heap = NULL; ut_a(thr); ut_a(foreign); ut_a(pcur); ut_a(mtr); trx = thr_get_trx(thr); node = thr->run_node; if (node->is_delete && 0 == (foreign->type & (DICT_FOREIGN_ON_DELETE_CASCADE | DICT_FOREIGN_ON_DELETE_SET_NULL))) { row_ins_foreign_report_err("Trying to delete", thr, foreign, btr_pcur_get_rec(pcur), entry); return(DB_ROW_IS_REFERENCED); } if (!node->is_delete && 0 == (foreign->type & (DICT_FOREIGN_ON_UPDATE_CASCADE | DICT_FOREIGN_ON_UPDATE_SET_NULL))) { /* This is an UPDATE */ row_ins_foreign_report_err("Trying to update", thr, foreign, btr_pcur_get_rec(pcur), entry); return(DB_ROW_IS_REFERENCED); } if (node->cascade_node == NULL) { /* Extend our query graph by creating a child to current update node. The child is used in the cascade or set null operation. */ node->cascade_heap = mem_heap_create(128); node->cascade_node = row_create_update_node( table, node->cascade_heap); que_node_set_parent(node->cascade_node, node); } /* Initialize cascade_node to do the operation we want. Note that we use the SAME cascade node to do all foreign key operations of the SQL DELETE: the table of the cascade node may change if there are several child tables to the table where the delete is done! */ cascade = node->cascade_node; cascade->table = table; cascade->foreign = foreign; if (node->is_delete && (foreign->type & DICT_FOREIGN_ON_DELETE_CASCADE)) { cascade->is_delete = TRUE; } else { cascade->is_delete = FALSE; if (foreign->n_fields > cascade->update_n_fields) { /* We have to make the update vector longer */ cascade->update = upd_create(foreign->n_fields, node->cascade_heap); cascade->update_n_fields = foreign->n_fields; } } /* We do not allow cyclic cascaded updating (DELETE is allowed, but not UPDATE) of the same table, as this can lead to an infinite cycle. Check that we are not updating the same table which is already being modified in this cascade chain. We have to check this also because the modification of the indexes of a 'parent' table may still be incomplete, and we must avoid seeing the indexes of the parent table in an inconsistent state! */ if (!cascade->is_delete && row_ins_cascade_ancestor_updates_table(cascade, table)) { /* We do not know if this would break foreign key constraints, but play safe and return an error */ err = DB_ROW_IS_REFERENCED; row_ins_foreign_report_err( "Trying an update, possibly causing a cyclic" " cascaded update\n" "in the child table,", thr, foreign, btr_pcur_get_rec(pcur), entry); goto nonstandard_exit_func; } if (row_ins_cascade_n_ancestors(cascade) >= 15) { err = DB_ROW_IS_REFERENCED; row_ins_foreign_report_err( "Trying a too deep cascaded delete or update\n", thr, foreign, btr_pcur_get_rec(pcur), entry); goto nonstandard_exit_func; } index = btr_pcur_get_btr_cur(pcur)->index; ut_a(index == foreign->foreign_index); rec = btr_pcur_get_rec(pcur); if (dict_index_is_clust(index)) { /* pcur is already positioned in the clustered index of the child table */ clust_index = index; clust_rec = rec; clust_block = btr_pcur_get_block(pcur); } else { /* We have to look for the record in the clustered index in the child table */ clust_index = dict_table_get_first_index(table); tmp_heap = mem_heap_create(256); ref = row_build_row_ref(ROW_COPY_POINTERS, index, rec, tmp_heap); btr_pcur_open_with_no_init(clust_index, ref, PAGE_CUR_LE, BTR_SEARCH_LEAF, cascade->pcur, 0, mtr); clust_rec = btr_pcur_get_rec(cascade->pcur); clust_block = btr_pcur_get_block(cascade->pcur); if (!page_rec_is_user_rec(clust_rec) || btr_pcur_get_low_match(cascade->pcur) < dict_index_get_n_unique(clust_index)) { ib_logger(ib_stream, "InnoDB: error in cascade of a foreign key op\n" "InnoDB: "); dict_index_name_print(ib_stream, trx, index); ib_logger(ib_stream, "\nInnoDB: record "); rec_print(ib_stream, rec, index); ib_logger(ib_stream, "\nInnoDB: clustered record "); rec_print(ib_stream, clust_rec, clust_index); ib_logger(ib_stream, "\n" "InnoDB: Submit a detailed bug report, check the" "InnoDB website for details"); err = DB_SUCCESS; goto nonstandard_exit_func; } } /* Set an X-lock on the row to delete or update in the child table */ err = lock_table(0, table, LOCK_IX, thr); if (err == DB_SUCCESS) { /* Here it suffices to use a LOCK_REC_NOT_GAP type lock; we already have a normal shared lock on the appropriate gap if the search criterion was not unique */ err = lock_clust_rec_read_check_and_lock_alt( 0, clust_block, clust_rec, clust_index, LOCK_X, LOCK_REC_NOT_GAP, thr); } if (err != DB_SUCCESS) { goto nonstandard_exit_func; } if (rec_get_deleted_flag(clust_rec, dict_table_is_comp(table))) { /* This can happen if there is a circular reference of rows such that cascading delete comes to delete a row already in the process of being delete marked */ err = DB_SUCCESS; goto nonstandard_exit_func; } if ((node->is_delete && (foreign->type & DICT_FOREIGN_ON_DELETE_SET_NULL)) || (!node->is_delete && (foreign->type & DICT_FOREIGN_ON_UPDATE_SET_NULL))) { /* Build the appropriate update vector which sets foreign->n_fields first fields in rec to SQL NULL */ update = cascade->update; update->info_bits = 0; update->n_fields = foreign->n_fields; for (i = 0; i < foreign->n_fields; i++) { upd_field_t* ufield = &update->fields[i]; ufield->field_no = dict_table_get_nth_col_pos( table, dict_index_get_nth_col_no(index, i)); ufield->orig_len = 0; ufield->exp = NULL; dfield_set_null(&ufield->new_val); } } if (!node->is_delete && (foreign->type & DICT_FOREIGN_ON_UPDATE_CASCADE)) { /* Build the appropriate update vector which sets changing foreign->n_fields first fields in rec to new values */ upd_vec_heap = mem_heap_create(256); n_to_update = row_ins_cascade_calc_update_vec(node, foreign, upd_vec_heap); if (n_to_update == ULINT_UNDEFINED) { err = DB_ROW_IS_REFERENCED; row_ins_foreign_report_err( "Trying a cascaded update where the" " updated value in the child\n" "table would not fit in the length" " of the column, or the value would\n" "be NULL and the column is" " declared as not NULL in the child table,", thr, foreign, btr_pcur_get_rec(pcur), entry); goto nonstandard_exit_func; } if (cascade->update->n_fields == 0) { /* The update does not change any columns referred to in this foreign key constraint: no need to do anything */ err = DB_SUCCESS; goto nonstandard_exit_func; } } /* Store pcur position and initialize or store the cascade node pcur stored position */ btr_pcur_store_position(pcur, mtr); if (index == clust_index) { btr_pcur_copy_stored_position(cascade->pcur, pcur); } else { btr_pcur_store_position(cascade->pcur, mtr); } mtr_commit(mtr); ut_a(cascade->pcur->rel_pos == BTR_PCUR_ON); cascade->state = UPD_NODE_UPDATE_CLUSTERED; err = row_update_cascade_client( thr, cascade, foreign->foreign_table); if (foreign->foreign_table->n_foreign_key_checks_running == 0) { ib_logger(ib_stream, "InnoDB: error: table %s has the counter 0" " though there is\n" "InnoDB: a FOREIGN KEY check running on it.\n", foreign->foreign_table->name); } /* Release the data dictionary latch for a while, so that we do not starve other threads from doing CREATE TABLE etc. if we have a huge cascaded operation running. The counter n_foreign_key_checks_running will prevent other users from dropping or ALTERing the table when we release the latch. */ dict_unfreeze_data_dictionary(thr_get_trx(thr)); dict_freeze_data_dictionary(thr_get_trx(thr)); mtr_start(mtr); /* Restore pcur position */ btr_pcur_restore_position(BTR_SEARCH_LEAF, pcur, mtr); if (tmp_heap) { mem_heap_free(tmp_heap); } if (upd_vec_heap) { mem_heap_free(upd_vec_heap); } return(err); nonstandard_exit_func: if (tmp_heap) { mem_heap_free(tmp_heap); } if (upd_vec_heap) { mem_heap_free(upd_vec_heap); } btr_pcur_store_position(pcur, mtr); mtr_commit(mtr); mtr_start(mtr); btr_pcur_restore_position(BTR_SEARCH_LEAF, pcur, mtr); return(err); } /*********************************************************************//** Sets a shared lock on a record. Used in locking possible duplicate key records and also in checking foreign key constraints. @return DB_SUCCESS or error code */ static ulint row_ins_set_shared_rec_lock( /*========================*/ ulint type, /*!< in: LOCK_ORDINARY, LOCK_GAP, or LOCK_REC_NOT_GAP type lock */ const buf_block_t* block, /*!< in: buffer block of rec */ const rec_t* rec, /*!< in: record */ dict_index_t* index, /*!< in: index */ const ulint* offsets,/*!< in: rec_get_offsets(rec, index) */ que_thr_t* thr) /*!< in: query thread */ { ulint err; ut_ad(rec_offs_validate(rec, index, offsets)); if (dict_index_is_clust(index)) { err = lock_clust_rec_read_check_and_lock( 0, block, rec, index, offsets, LOCK_S, type, thr); } else { err = lock_sec_rec_read_check_and_lock( 0, block, rec, index, offsets, LOCK_S, type, thr); } return(err); } /*********************************************************************//** Sets a exclusive lock on a record. Used in locking possible duplicate key records @return DB_SUCCESS or error code */ static ulint row_ins_set_exclusive_rec_lock( /*===========================*/ ulint type, /*!< in: LOCK_ORDINARY, LOCK_GAP, or LOCK_REC_NOT_GAP type lock */ const buf_block_t* block, /*!< in: buffer block of rec */ const rec_t* rec, /*!< in: record */ dict_index_t* index, /*!< in: index */ const ulint* offsets,/*!< in: rec_get_offsets(rec, index) */ que_thr_t* thr) /*!< in: query thread */ { ulint err; ut_ad(rec_offs_validate(rec, index, offsets)); if (dict_index_is_clust(index)) { err = lock_clust_rec_read_check_and_lock( 0, block, rec, index, offsets, LOCK_X, type, thr); } else { err = lock_sec_rec_read_check_and_lock( 0, block, rec, index, offsets, LOCK_X, type, thr); } return(err); } /***************************************************************//** Checks if foreign key constraint fails for an index entry. Sets shared locks which lock either the success or the failure of the constraint. NOTE that the caller must have a shared latch on dict_operation_lock. @return DB_SUCCESS, DB_NO_REFERENCED_ROW, or DB_ROW_IS_REFERENCED */ UNIV_INTERN ulint row_ins_check_foreign_constraint( /*=============================*/ ibool check_ref,/*!< in: TRUE if we want to check that the referenced table is ok, FALSE if we want to check the foreign key table */ dict_foreign_t* foreign,/*!< in: foreign constraint; NOTE that the tables mentioned in it must be in the dictionary cache if they exist at all */ dict_table_t* table, /*!< in: if check_ref is TRUE, then the foreign table, else the referenced table */ dtuple_t* entry, /*!< in: index entry for index */ que_thr_t* thr) /*!< in: query thread */ { upd_node_t* upd_node; dict_table_t* check_table; dict_index_t* check_index; ulint n_fields_cmp; btr_pcur_t pcur; ibool moved; int cmp; ulint err; ulint i; mtr_t mtr; trx_t* trx = thr_get_trx(thr); mem_heap_t* heap = NULL; ulint offsets_[REC_OFFS_NORMAL_SIZE]; ulint* offsets = offsets_; rec_offs_init(offsets_); run_again: #ifdef UNIV_SYNC_DEBUG ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_SHARED)); #endif /* UNIV_SYNC_DEBUG */ err = DB_SUCCESS; if (trx->check_foreigns == FALSE) { /* The user has suppressed foreign key checks currently for this session */ goto exit_func; } /* If any of the foreign key fields in entry is SQL NULL, we suppress the foreign key check: this is compatible with Oracle, for example */ for (i = 0; i < foreign->n_fields; i++) { if (UNIV_SQL_NULL == dfield_get_len( dtuple_get_nth_field(entry, i))) { goto exit_func; } } if (que_node_get_type(thr->run_node) == QUE_NODE_UPDATE) { upd_node = thr->run_node; if (!upd_node->is_delete && upd_node->foreign == foreign) { /* If a cascaded update is done as defined by a foreign key constraint, do not check that constraint for the child row. In ON UPDATE CASCADE the update of the parent row is only half done when we come here: if we would check the constraint here for the child row it would fail. A QUESTION remains: if in the child table there are several constraints which refer to the same parent table, we should merge all updates to the child as one update? And the updates can be contradictory! Currently we just perform the update associated with each foreign key constraint, one after another, and the user has problems predicting in which order they are performed. */ goto exit_func; } } if (check_ref) { check_table = foreign->referenced_table; check_index = foreign->referenced_index; } else { check_table = foreign->foreign_table; check_index = foreign->foreign_index; } if (check_table == NULL || check_table->ibd_file_missing) { if (check_ref) { row_ins_set_detailed(trx, foreign); mutex_enter(&dict_foreign_err_mutex); ut_print_timestamp(ib_stream); ib_logger(ib_stream, " Transaction:\n"); trx_print(ib_stream, trx, 600); ib_logger(ib_stream, "Foreign key constraint fails for table "); ut_print_name(ib_stream, trx, TRUE, foreign->foreign_table_name); ib_logger(ib_stream, ":\n"); dict_print_info_on_foreign_key_in_create_format( ib_stream, trx, foreign, TRUE); ib_logger(ib_stream, "\nTrying to add to index "); ut_print_name(ib_stream, trx, FALSE, foreign->foreign_index->name); ib_logger(ib_stream, " tuple:\n"); dtuple_print(ib_stream, entry); ib_logger(ib_stream, "\nBut the parent table "); ut_print_name(ib_stream, trx, TRUE, foreign->referenced_table_name); ib_logger(ib_stream, "\nor its .ibd file does" " not currently exist!\n"); mutex_exit(&dict_foreign_err_mutex); err = DB_NO_REFERENCED_ROW; } goto exit_func; } ut_a(check_table); ut_a(check_index); if (check_table != table) { /* We already have a LOCK_IX on table, but not necessarily on check_table */ err = lock_table(0, check_table, LOCK_IS, thr); if (err != DB_SUCCESS) { goto do_possible_lock_wait; } } mtr_start(&mtr); /* Store old value on n_fields_cmp */ n_fields_cmp = dtuple_get_n_fields_cmp(entry); dtuple_set_n_fields_cmp(entry, foreign->n_fields); btr_pcur_open(check_index, entry, PAGE_CUR_GE, BTR_SEARCH_LEAF, &pcur, &mtr); /* Scan index records and check if there is a matching record */ for (;;) { const rec_t* rec = btr_pcur_get_rec(&pcur); const buf_block_t* block = btr_pcur_get_block(&pcur); if (page_rec_is_infimum(rec)) { goto next_rec; } offsets = rec_get_offsets(rec, check_index, offsets, ULINT_UNDEFINED, &heap); if (page_rec_is_supremum(rec)) { err = row_ins_set_shared_rec_lock(LOCK_ORDINARY, block, rec, check_index, offsets, thr); if (err != DB_SUCCESS) { break; } goto next_rec; } cmp = cmp_dtuple_rec(check_index->cmp_ctx, entry, rec, offsets); if (cmp == 0) { if (rec_get_deleted_flag(rec, rec_offs_comp(offsets))) { err = row_ins_set_shared_rec_lock( LOCK_ORDINARY, block, rec, check_index, offsets, thr); if (err != DB_SUCCESS) { break; } } else { /* Found a matching record. Lock only a record because we can allow inserts into gaps */ err = row_ins_set_shared_rec_lock( LOCK_REC_NOT_GAP, block, rec, check_index, offsets, thr); if (err != DB_SUCCESS) { break; } if (check_ref) { err = DB_SUCCESS; break; } else if (foreign->type != 0) { /* There is an ON UPDATE or ON DELETE condition: check them in a separate function */ err = row_ins_foreign_check_on_constraint( thr, foreign, &pcur, entry, &mtr); if (err != DB_SUCCESS) { /* Since reporting a plain "duplicate key" error message to the user in cases where a long CASCADE operation would lead to a duplicate key in some other table is very confusing, map duplicate key errors resulting from FK constraints to a separate error code. */ if (err == DB_DUPLICATE_KEY) { err = DB_FOREIGN_DUPLICATE_KEY; } break; } /* row_ins_foreign_check_on_constraint may have repositioned pcur on a different block */ block = btr_pcur_get_block(&pcur); } else { row_ins_foreign_report_err( "Trying to delete or update", thr, foreign, rec, entry); err = DB_ROW_IS_REFERENCED; break; } } } if (cmp < 0) { err = row_ins_set_shared_rec_lock( LOCK_GAP, block, rec, check_index, offsets, thr); if (err != DB_SUCCESS) { break; } if (check_ref) { err = DB_NO_REFERENCED_ROW; row_ins_foreign_report_add_err( trx, foreign, rec, entry); } else { err = DB_SUCCESS; } break; } ut_a(cmp == 0); next_rec: moved = btr_pcur_move_to_next(&pcur, &mtr); if (!moved) { if (check_ref) { rec = btr_pcur_get_rec(&pcur); row_ins_foreign_report_add_err( trx, foreign, rec, entry); err = DB_NO_REFERENCED_ROW; } else { err = DB_SUCCESS; } break; } } btr_pcur_close(&pcur); mtr_commit(&mtr); /* Restore old value */ dtuple_set_n_fields_cmp(entry, n_fields_cmp); do_possible_lock_wait: if (err == DB_LOCK_WAIT) { trx->error_state = err; que_thr_stop_client(thr); srv_suspend_user_thread(thr); if (trx->error_state == DB_SUCCESS) { goto run_again; } err = trx->error_state; } exit_func: if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } return(err); } /***************************************************************//** Checks if foreign key constraints fail for an index entry. If index is not mentioned in any constraint, this function does nothing, Otherwise does searches to the indexes of referenced tables and sets shared locks which lock either the success or the failure of a constraint. @return DB_SUCCESS or error code */ static ulint row_ins_check_foreign_constraints( /*==============================*/ dict_table_t* table, /*!< in: table */ dict_index_t* index, /*!< in: index */ dtuple_t* entry, /*!< in: index entry for index */ que_thr_t* thr) /*!< in: query thread */ { dict_foreign_t* foreign; ulint err; trx_t* trx; ibool got_s_lock = FALSE; trx = thr_get_trx(thr); foreign = UT_LIST_GET_FIRST(table->foreign_list); while (foreign) { if (foreign->foreign_index == index) { if (foreign->referenced_table == NULL) { dict_table_get(foreign->referenced_table_name, FALSE); } if (0 == trx->dict_operation_lock_mode) { got_s_lock = TRUE; dict_freeze_data_dictionary(trx); } if (foreign->referenced_table) { mutex_enter(&(dict_sys->mutex)); (foreign->referenced_table ->n_foreign_key_checks_running)++; mutex_exit(&(dict_sys->mutex)); } /* NOTE that if the thread ends up waiting for a lock we will release dict_operation_lock temporarily! But the counter on the table protects the referenced table from being dropped while the check is running. */ err = row_ins_check_foreign_constraint( TRUE, foreign, table, entry, thr); if (foreign->referenced_table) { mutex_enter(&(dict_sys->mutex)); ut_a(foreign->referenced_table ->n_foreign_key_checks_running > 0); (foreign->referenced_table ->n_foreign_key_checks_running)--; mutex_exit(&(dict_sys->mutex)); } if (got_s_lock) { dict_unfreeze_data_dictionary(trx); } if (err != DB_SUCCESS) { return(err); } } foreign = UT_LIST_GET_NEXT(foreign_list, foreign); } return(DB_SUCCESS); } /***************************************************************//** Checks if a unique key violation to rec would occur at the index entry insert. @return TRUE if error */ static ibool row_ins_dupl_error_with_rec( /*========================*/ const rec_t* rec, /*!< in: user record; NOTE that we assume that the caller already has a record lock on the record! */ const dtuple_t* entry, /*!< in: entry to insert */ dict_index_t* index, /*!< in: index */ const ulint* offsets)/*!< in: rec_get_offsets(rec, index) */ { ulint matched_fields; ulint matched_bytes; ulint n_unique; ulint i; ut_ad(rec_offs_validate(rec, index, offsets)); n_unique = dict_index_get_n_unique(index); matched_fields = 0; matched_bytes = 0; cmp_dtuple_rec_with_match( index->cmp_ctx, entry, rec, offsets, &matched_fields, &matched_bytes); if (matched_fields < n_unique) { return(FALSE); } /* In a unique secondary index we allow equal key values if they contain SQL NULLs */ if (!dict_index_is_clust(index)) { for (i = 0; i < n_unique; i++) { if (UNIV_SQL_NULL == dfield_get_len( dtuple_get_nth_field(entry, i))) { return(FALSE); } } } return(!rec_get_deleted_flag(rec, rec_offs_comp(offsets))); } /***************************************************************//** Scans a unique non-clustered index at a given index entry to determine whether a uniqueness violation has occurred for the key value of the entry. Set shared locks on possible duplicate records. @return DB_SUCCESS, DB_DUPLICATE_KEY, or DB_LOCK_WAIT */ static ulint row_ins_scan_sec_index_for_duplicate( /*=================================*/ dict_index_t* index, /*!< in: non-clustered unique index */ dtuple_t* entry, /*!< in: index entry */ que_thr_t* thr) /*!< in: query thread */ { ulint n_unique; ulint i; int cmp; ulint n_fields_cmp; btr_pcur_t pcur; ulint err = DB_SUCCESS; unsigned allow_duplicates; mtr_t mtr; mem_heap_t* heap = NULL; ulint offsets_[REC_OFFS_NORMAL_SIZE]; ulint* offsets = offsets_; rec_offs_init(offsets_); n_unique = dict_index_get_n_unique(index); /* If the secondary index is unique, but one of the fields in the n_unique first fields is NULL, a unique key violation cannot occur, since we define NULL != NULL in this case */ for (i = 0; i < n_unique; i++) { if (UNIV_SQL_NULL == dfield_get_len( dtuple_get_nth_field(entry, i))) { return(DB_SUCCESS); } } mtr_start(&mtr); /* Store old value on n_fields_cmp */ n_fields_cmp = dtuple_get_n_fields_cmp(entry); dtuple_set_n_fields_cmp(entry, dict_index_get_n_unique(index)); btr_pcur_open(index, entry, PAGE_CUR_GE, BTR_SEARCH_LEAF, &pcur, &mtr); allow_duplicates = thr_get_trx(thr)->duplicates & TRX_DUP_IGNORE; /* Scan index records and check if there is a duplicate */ do { const rec_t* rec = btr_pcur_get_rec(&pcur); const buf_block_t* block = btr_pcur_get_block(&pcur); if (page_rec_is_infimum(rec)) { continue; } offsets = rec_get_offsets(rec, index, offsets, ULINT_UNDEFINED, &heap); if (allow_duplicates) { /* If the SQL-query will update or replace duplicate key we will take X-lock for duplicates ( REPLACE, LOAD DATAFILE REPLACE, INSERT ON DUPLICATE KEY UPDATE). */ err = row_ins_set_exclusive_rec_lock( LOCK_ORDINARY, block, rec, index, offsets, thr); } else { err = row_ins_set_shared_rec_lock( LOCK_ORDINARY, block, rec, index, offsets, thr); } if (err != DB_SUCCESS) { break; } if (page_rec_is_supremum(rec)) { continue; } cmp = cmp_dtuple_rec(index->cmp_ctx, entry, rec, offsets); if (cmp == 0) { if (row_ins_dupl_error_with_rec(rec, entry, index, offsets)) { err = DB_DUPLICATE_KEY; thr_get_trx(thr)->error_info = index; break; } } if (cmp < 0) { break; } ut_a(cmp == 0); } while (btr_pcur_move_to_next(&pcur, &mtr)); if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } mtr_commit(&mtr); /* Restore old value */ dtuple_set_n_fields_cmp(entry, n_fields_cmp); return(err); } /***************************************************************//** Checks if a unique key violation error would occur at an index entry insert. Sets shared locks on possible duplicate records. Works only for a clustered index! @return DB_SUCCESS if no error, DB_DUPLICATE_KEY if error, DB_LOCK_WAIT if we have to wait for a lock on a possible duplicate record */ static ulint row_ins_duplicate_error_in_clust( /*=============================*/ btr_cur_t* cursor, /*!< in: B-tree cursor */ dtuple_t* entry, /*!< in: entry to insert */ que_thr_t* thr, /*!< in: query thread */ mtr_t* mtr) /*!< in: mtr */ { ulint err; rec_t* rec; ulint n_unique; trx_t* trx = thr_get_trx(thr); mem_heap_t*heap = NULL; ulint offsets_[REC_OFFS_NORMAL_SIZE]; ulint* offsets = offsets_; rec_offs_init(offsets_); UT_NOT_USED(mtr); ut_a(dict_index_is_clust(cursor->index)); ut_ad(dict_index_is_unique(cursor->index)); /* NOTE: For unique non-clustered indexes there may be any number of delete marked records with the same value for the non-clustered index key (remember multiversioning), and which differ only in the row refererence part of the index record, containing the clustered index key fields. For such a secondary index record, to avoid race condition, we must FIRST do the insertion and after that check that the uniqueness condition is not breached! */ /* NOTE: A problem is that in the B-tree node pointers on an upper level may match more to the entry than the actual existing user records on the leaf level. So, even if low_match would suggest that a duplicate key violation may occur, this may not be the case. */ n_unique = dict_index_get_n_unique(cursor->index); if (cursor->low_match >= n_unique) { rec = btr_cur_get_rec(cursor); if (!page_rec_is_infimum(rec)) { offsets = rec_get_offsets(rec, cursor->index, offsets, ULINT_UNDEFINED, &heap); /* We set a lock on the possible duplicate: this is needed in logical logging, we need to make sure that in roll-forward we get the same duplicate errors as in original execution */ if (trx->duplicates & TRX_DUP_IGNORE) { /* If the SQL-query will update or replace duplicate key we will take X-lock for duplicates ( REPLACE, LOAD DATAFILE REPLACE, INSERT ON DUPLICATE KEY UPDATE). */ err = row_ins_set_exclusive_rec_lock( LOCK_REC_NOT_GAP, btr_cur_get_block(cursor), rec, cursor->index, offsets, thr); } else { err = row_ins_set_shared_rec_lock( LOCK_REC_NOT_GAP, btr_cur_get_block(cursor), rec, cursor->index, offsets, thr); } if (err != DB_SUCCESS) { goto func_exit; } if (row_ins_dupl_error_with_rec( rec, entry, cursor->index, offsets)) { trx->error_info = cursor->index; err = DB_DUPLICATE_KEY; goto func_exit; } } } if (cursor->up_match >= n_unique) { rec = page_rec_get_next(btr_cur_get_rec(cursor)); if (!page_rec_is_supremum(rec)) { offsets = rec_get_offsets(rec, cursor->index, offsets, ULINT_UNDEFINED, &heap); if (trx->duplicates & TRX_DUP_IGNORE) { /* If the SQL-query will update or replace duplicate key we will take X-lock for duplicates ( REPLACE, LOAD DATAFILE REPLACE, INSERT ON DUPLICATE KEY UPDATE). */ err = row_ins_set_exclusive_rec_lock( LOCK_REC_NOT_GAP, btr_cur_get_block(cursor), rec, cursor->index, offsets, thr); } else { err = row_ins_set_shared_rec_lock( LOCK_REC_NOT_GAP, btr_cur_get_block(cursor), rec, cursor->index, offsets, thr); } if (err != DB_SUCCESS) { goto func_exit; } if (row_ins_dupl_error_with_rec( rec, entry, cursor->index, offsets)) { trx->error_info = cursor->index; err = DB_DUPLICATE_KEY; goto func_exit; } } ut_a(!dict_index_is_clust(cursor->index)); /* This should never happen */ } err = DB_SUCCESS; func_exit: if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } return(err); } /***************************************************************//** Checks if an index entry has long enough common prefix with an existing record so that the intended insert of the entry must be changed to a modify of the existing record. In the case of a clustered index, the prefix must be n_unique fields long, and in the case of a secondary index, all fields must be equal. @return 0 if no update, ROW_INS_PREV if previous should be updated; currently we do the search so that only the low_match record can match enough to the search tuple, not the next record */ UNIV_INLINE ulint row_ins_must_modify( /*================*/ btr_cur_t* cursor) /*!< in: B-tree cursor */ { ulint enough_match; rec_t* rec; /* NOTE: (compare to the note in row_ins_duplicate_error) Because node pointers on upper levels of the B-tree may match more to entry than to actual user records on the leaf level, we have to check if the candidate record is actually a user record. In a clustered index node pointers contain index->n_unique first fields, and in the case of a secondary index, all fields of the index. */ enough_match = dict_index_get_n_unique_in_tree(cursor->index); if (cursor->low_match >= enough_match) { rec = btr_cur_get_rec(cursor); if (!page_rec_is_infimum(rec)) { return(ROW_INS_PREV); } } return(0); } /***************************************************************//** Tries to insert an index entry to an index. If the index is clustered and a record with the same unique key is found, the other record is necessarily marked deleted by a committed transaction, or a unique key violation error occurs. The delete marked record is then updated to an existing record, and we must write an undo log record on the delete marked record. If the index is secondary, and a record with exactly the same fields is found, the other record is necessarily marked deleted. It is then unmarked. Otherwise, the entry is just inserted to the index. @return DB_SUCCESS, DB_LOCK_WAIT, DB_FAIL if pessimistic retry needed, or error code */ static ulint row_ins_index_entry_low( /*====================*/ ulint mode, /*!< in: BTR_MODIFY_LEAF or BTR_MODIFY_TREE, depending on whether we wish optimistic or pessimistic descent down the index tree */ dict_index_t* index, /*!< in: index */ dtuple_t* entry, /*!< in: index entry to insert */ ulint n_ext, /*!< in: number of externally stored columns */ que_thr_t* thr) /*!< in: query thread */ { btr_cur_t cursor; ulint ignore_sec_unique = 0; ulint modify = 0; /* remove warning */ rec_t* insert_rec; rec_t* rec; ulint err; ulint n_unique; big_rec_t* big_rec = NULL; mtr_t mtr; mem_heap_t* heap = NULL; log_free_check(); mtr_start(&mtr); cursor.thr = thr; /* Note that we use PAGE_CUR_LE as the search mode, because then the function will return in both low_match and up_match of the cursor sensible values */ if (!(thr_get_trx(thr)->check_unique_secondary)) { ignore_sec_unique = BTR_IGNORE_SEC_UNIQUE; } btr_cur_search_to_nth_level(index, 0, entry, PAGE_CUR_LE, mode | BTR_INSERT | ignore_sec_unique, &cursor, 0, __FILE__, __LINE__, &mtr); if (cursor.flag == BTR_CUR_INSERT_TO_IBUF) { /* The insertion was made to the insert buffer already during the search: we are done */ err = DB_SUCCESS; goto function_exit; } #ifdef UNIV_DEBUG { page_t* page = btr_cur_get_page(&cursor); rec_t* first_rec = page_rec_get_next( page_get_infimum_rec(page)); ut_ad(page_rec_is_supremum(first_rec) || rec_get_n_fields(first_rec, index) == dtuple_get_n_fields(entry)); } #endif n_unique = dict_index_get_n_unique(index); if (dict_index_is_unique(index) && (cursor.up_match >= n_unique || cursor.low_match >= n_unique)) { if (dict_index_is_clust(index)) { /* Note that the following may return also DB_LOCK_WAIT */ err = row_ins_duplicate_error_in_clust( &cursor, entry, thr, &mtr); if (err != DB_SUCCESS) { goto function_exit; } } else { mtr_commit(&mtr); err = row_ins_scan_sec_index_for_duplicate( index, entry, thr); mtr_start(&mtr); if (err != DB_SUCCESS) { goto function_exit; } /* We did not find a duplicate and we have now locked with s-locks the necessary records to prevent any insertion of a duplicate by another transaction. Let us now reposition the cursor and continue the insertion. */ btr_cur_search_to_nth_level(index, 0, entry, PAGE_CUR_LE, mode | BTR_INSERT, &cursor, 0, __FILE__, __LINE__, &mtr); } } modify = row_ins_must_modify(&cursor); if (modify != 0) { /* There is already an index entry with a long enough common prefix, we must convert the insert into a modify of an existing record */ if (modify == ROW_INS_NEXT) { rec = page_rec_get_next(btr_cur_get_rec(&cursor)); btr_cur_position(index, rec, btr_cur_get_block(&cursor),&cursor); } if (dict_index_is_clust(index)) { err = row_ins_clust_index_entry_by_modify( mode, &cursor, &heap, &big_rec, entry, thr, &mtr); } else { ut_ad(!n_ext); err = row_ins_sec_index_entry_by_modify( mode, &cursor, entry, thr, &mtr); } } else { if (mode == BTR_MODIFY_LEAF) { err = btr_cur_optimistic_insert( 0, &cursor, entry, &insert_rec, &big_rec, n_ext, thr, &mtr); } else { ut_a(mode == BTR_MODIFY_TREE); if (buf_LRU_buf_pool_running_out()) { err = DB_LOCK_TABLE_FULL; goto function_exit; } err = btr_cur_pessimistic_insert( 0, &cursor, entry, &insert_rec, &big_rec, n_ext, thr, &mtr); } } function_exit: mtr_commit(&mtr); if (UNIV_LIKELY_NULL(big_rec)) { rec_t* rec; ulint* offsets; mtr_start(&mtr); btr_cur_search_to_nth_level(index, 0, entry, PAGE_CUR_LE, BTR_MODIFY_TREE, &cursor, 0, __FILE__, __LINE__, &mtr); rec = btr_cur_get_rec(&cursor); offsets = rec_get_offsets(rec, index, NULL, ULINT_UNDEFINED, &heap); err = btr_store_big_rec_extern_fields( index, btr_cur_get_block(&cursor), rec, offsets, big_rec, &mtr); if (modify) { dtuple_big_rec_free(big_rec); } else { dtuple_convert_back_big_rec(index, entry, big_rec); } mtr_commit(&mtr); } if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } return(err); } /***************************************************************//** Inserts an index entry to index. Tries first optimistic, then pessimistic descent down the tree. If the entry matches enough to a delete marked record, performs the insert by updating or delete unmarking the delete marked record. @return DB_SUCCESS, DB_LOCK_WAIT, DB_DUPLICATE_KEY, or some other error code */ UNIV_INTERN ulint row_ins_index_entry( /*================*/ dict_index_t* index, /*!< in: index */ dtuple_t* entry, /*!< in: index entry to insert */ ulint n_ext, /*!< in: number of externally stored columns */ ibool foreign,/*!< in: TRUE=check foreign key constraints */ que_thr_t* thr) /*!< in: query thread */ { ulint err; if (foreign && UT_LIST_GET_FIRST(index->table->foreign_list)) { err = row_ins_check_foreign_constraints( index->table, index, entry, thr); if (err != DB_SUCCESS) { return(err); } } /* Try first optimistic descent to the B-tree */ err = row_ins_index_entry_low( BTR_MODIFY_LEAF, index, entry, n_ext, thr); if (err != DB_FAIL) { return(err); } /* Try then pessimistic descent to the B-tree */ err = row_ins_index_entry_low( BTR_MODIFY_TREE, index, entry, n_ext, thr); return(err); } /***********************************************************//** Sets the values of the dtuple fields in entry from the values of appropriate columns in row. */ static void row_ins_index_entry_set_vals( /*=========================*/ dict_index_t* index, /*!< in: index */ dtuple_t* entry, /*!< in: index entry to make */ const dtuple_t* row) /*!< in: row */ { ulint n_fields; ulint i; ut_ad(entry && row); n_fields = dtuple_get_n_fields(entry); for (i = 0; i < n_fields; i++) { dict_field_t* ind_field; dfield_t* field; const dfield_t* row_field; ulint len; field = dtuple_get_nth_field(entry, i); ind_field = dict_index_get_nth_field(index, i); row_field = dtuple_get_nth_field(row, ind_field->col->ind); len = dfield_get_len(row_field); /* Check column prefix indexes */ if (ind_field->prefix_len > 0 && dfield_get_len(row_field) != UNIV_SQL_NULL) { const dict_col_t* col; col = dict_field_get_col(ind_field); len = dtype_get_at_most_n_mbchars( col->prtype, col->mbminlen, col->mbmaxlen, ind_field->prefix_len, len, dfield_get_data(row_field)); ut_ad(!dfield_is_ext(row_field)); } dfield_set_data(field, dfield_get_data(row_field), len); if (dfield_is_ext(row_field)) { ut_ad(dict_index_is_clust(index)); dfield_set_ext(field); } } } /***********************************************************//** Inserts a single index entry to the table. @return DB_SUCCESS if operation successfully completed, else error code or DB_LOCK_WAIT */ static ulint row_ins_index_entry_step( /*=====================*/ ins_node_t* node, /*!< in: row insert node */ que_thr_t* thr) /*!< in: query thread */ { ulint err; ut_ad(dtuple_check_typed(node->row)); row_ins_index_entry_set_vals(node->index, node->entry, node->row); ut_ad(dtuple_check_typed(node->entry)); err = row_ins_index_entry(node->index, node->entry, 0, TRUE, thr); return(err); } /***********************************************************//** Allocates a row id for row and inits the node->index field. */ UNIV_INLINE void row_ins_alloc_row_id_step( /*======================*/ ins_node_t* node) /*!< in: row insert node */ { dulint row_id; ut_ad(node->state == INS_NODE_ALLOC_ROW_ID); if (dict_index_is_unique(dict_table_get_first_index(node->table))) { /* No row id is stored if the clustered index is unique */ return; } /* Fill in row id value to row */ row_id = dict_sys_get_new_row_id(); dict_sys_write_row_id(node->row_id_buf, row_id); } /***********************************************************//** Gets a row to insert from the values list. */ UNIV_INLINE void row_ins_get_row_from_values( /*========================*/ ins_node_t* node) /*!< in: row insert node */ { ulint i; dtuple_t* row; que_node_t* list_node; /* The field values are copied in the buffers of the select node and it is safe to use them until we fetch from select again: therefore we can just copy the pointers */ row = node->row; i = 0; list_node = node->values_list; while (list_node) { dfield_t* dfield; eval_exp(list_node); dfield = dtuple_get_nth_field(row, i); dfield_copy_data(dfield, que_node_get_val(list_node)); i++; list_node = que_node_get_next(list_node); } } /***********************************************************//** Gets a row to insert from the select list. */ UNIV_INLINE void row_ins_get_row_from_select( /*========================*/ ins_node_t* node) /*!< in: row insert node */ { ulint i; dtuple_t* row; que_node_t* list_node; /* The field values are copied in the buffers of the select node and it is safe to use them until we fetch from select again: therefore we can just copy the pointers */ row = node->row; i = 0; list_node = node->select->select_list; while (list_node) { dfield_t* dfield; dfield = dtuple_get_nth_field(row, i); dfield_copy_data(dfield, que_node_get_val(list_node)); i++; list_node = que_node_get_next(list_node); } } /***********************************************************//** Inserts a row to a table. @return DB_SUCCESS if operation successfully completed, else error code or DB_LOCK_WAIT */ static ulint row_ins( /*====*/ ins_node_t* node, /*!< in: row insert node */ que_thr_t* thr) /*!< in: query thread */ { ulint err; ut_ad(node && thr); if (node->state == INS_NODE_ALLOC_ROW_ID) { row_ins_alloc_row_id_step(node); node->index = dict_table_get_first_index(node->table); node->entry = UT_LIST_GET_FIRST(node->entry_list); if (node->ins_type == INS_SEARCHED) { row_ins_get_row_from_select(node); } else if (node->ins_type == INS_VALUES) { row_ins_get_row_from_values(node); } node->state = INS_NODE_INSERT_ENTRIES; } ut_ad(node->state == INS_NODE_INSERT_ENTRIES); while (node->index != NULL) { err = row_ins_index_entry_step(node, thr); if (err != DB_SUCCESS) { return(err); } node->index = dict_table_get_next_index(node->index); node->entry = UT_LIST_GET_NEXT(tuple_list, node->entry); } ut_ad(node->entry == NULL); node->state = INS_NODE_ALLOC_ROW_ID; return(DB_SUCCESS); } /***********************************************************//** Inserts a row to a table. This is a high-level function used in SQL execution graphs. @return query thread to run next or NULL */ UNIV_INTERN que_thr_t* row_ins_step( /*=========*/ que_thr_t* thr) /*!< in: query thread */ { ins_node_t* node; que_node_t* parent; sel_node_t* sel_node; trx_t* trx; ulint err; ut_ad(thr); trx = thr_get_trx(thr); ut_a(trx->conc_state != TRX_NOT_STARTED); node = thr->run_node; ut_ad(que_node_get_type(node) == QUE_NODE_INSERT); parent = que_node_get_parent(node); sel_node = node->select; if (thr->prev_node == parent) { node->state = INS_NODE_SET_IX_LOCK; } /* If this is the first time this node is executed (or when execution resumes after wait for the table IX lock), set an IX lock on the table and reset the possible select node. If the client code calls an insert within the same SQL statement AFTER it has used this table handle to do a search. In that case, we have already set the IX lock on the table during the search operation, and there is no need to set it again here. But we must write trx->id to node->trx_id. */ trx_write_trx_id(node->trx_id_buf, trx->id); if (node->state == INS_NODE_SET_IX_LOCK) { /* It may be that the current session has not yet started its transaction, or it has been committed: */ if (UT_DULINT_EQ(trx->id, node->trx_id)) { /* No need to do IX-locking */ goto same_trx; } err = lock_table(0, node->table, LOCK_IX, thr); if (err != DB_SUCCESS) { goto error_handling; } node->trx_id = trx->id; same_trx: node->state = INS_NODE_ALLOC_ROW_ID; if (node->ins_type == INS_SEARCHED) { /* Reset the cursor */ sel_node->state = SEL_NODE_OPEN; /* Fetch a row to insert */ thr->run_node = sel_node; return(thr); } } if ((node->ins_type == INS_SEARCHED) && (sel_node->state != SEL_NODE_FETCH)) { ut_ad(sel_node->state == SEL_NODE_NO_MORE_ROWS); /* No more rows to insert */ thr->run_node = parent; return(thr); } /* DO THE CHECKS OF THE CONSISTENCY CONSTRAINTS HERE */ err = row_ins(node, thr); error_handling: trx->error_state = err; if (err != DB_SUCCESS) { /* err == DB_LOCK_WAIT or SQL error detected */ return(NULL); } /* DO THE TRIGGER ACTIONS HERE */ if (node->ins_type == INS_SEARCHED) { /* Fetch a row to insert */ thr->run_node = sel_node; } else { thr->run_node = que_node_get_parent(node); } return(thr); } haildb-2.3.2/config.h.cmake0000644000175000017500000000770411513177357016350 0ustar00pcrewspcrews00000000000000#cmakedefine HAVE_ALLOCA #cmakedefine HAVE_ALLOCA_H #cmakedefine HAVE_ASSERT_H #cmakedefine HAVE_ATOMIC_ADD_LONG #cmakedefine HAVE_ATOMIC_CAS_32 #cmakedefine HAVE_ATOMIC_CAS_64 #cmakedefine HAVE_ATOMIC_CAS_ULONG #cmakedefine HAVE_ATOMIC_H #cmakedefine HAVE_BCMP #cmakedefine HAVE_CTYPE_H #cmakedefine HAVE_DECL_SHM_HUGETLB #cmakedefine HAVE_DIRENT_H #cmakedefine HAVE_DLFCN_H #cmakedefine HAVE_ERRNO_H #cmakedefine HAVE_FCNTL #cmakedefine HAVE_FCNTL_H #cmakedefine HAVE_FINITE #cmakedefine HAVE_FSYNC #cmakedefine HAVE_FTRUNCATE #cmakedefine HAVE_GCC_ATOMIC_BUILTINS #cmakedefine HAVE_GCC_PTHREAD_T_CAS #cmakedefine HAVE_GETCWD #cmakedefine HAVE_GETPAGESIZE #cmakedefine HAVE_GETRUSAGE #cmakedefine HAVE_INDEX #cmakedefine HAVE_INT16_T #cmakedefine HAVE_INT32_T #cmakedefine HAVE_INT64_T #cmakedefine HAVE_INT8_T #cmakedefine HAVE_INTEGRAL_PTHREAD_T #cmakedefine HAVE_INTTYPES_H #cmakedefine HAVE_LARGE_PAGES #cmakedefine HAVE_LIBINTL_H #cmakedefine HAVE_LIBPOSIX4 #cmakedefine HAVE_LIBZ #cmakedefine HAVE_LOCALTIME_R #cmakedefine HAVE_LOCKING #cmakedefine HAVE_LONG_LONG_INT #cmakedefine HAVE_MALLOC_H #cmakedefine HAVE_MATH_H #cmakedefine HAVE_MEMCPY #cmakedefine HAVE_MEMMOVE #cmakedefine HAVE_MEMORY_H #cmakedefine HAVE_MMAP #cmakedefine HAVE_NDIR_H #cmakedefine HAVE_OFF_T #cmakedefine HAVE_PERROR #cmakedefine HAVE_PREAD #cmakedefine HAVE_PTHREAD_ATTR_SETSTACKSIZE #cmakedefine HAVE_PTHREAD_H #cmakedefine HAVE_PTHREAD_SETPRIO #cmakedefine HAVE_RENAME #cmakedefine HAVE_RINT #cmakedefine HAVE_SCHED_H #cmakedefine HAVE_SHMAT #cmakedefine HAVE_SHMCTL #cmakedefine HAVE_SHMDT #cmakedefine HAVE_SHMGET #cmakedefine HAVE_SIZE_T #cmakedefine HAVE_SLEEP #cmakedefine HAVE_SNPRINTF #cmakedefine HAVE_SOLARIS_ATOMICS #cmakedefine HAVE_STDARG_H #cmakedefine HAVE_STDBOOL_H #cmakedefine HAVE_STDDEF_H #cmakedefine HAVE_STDINT_H #cmakedefine HAVE_STDIO_H #cmakedefine HAVE_STDLIB_H #cmakedefine HAVE_STPCPY #cmakedefine HAVE_STRCASECMP #cmakedefine HAVE_STRDUP #cmakedefine HAVE_STRERROR #cmakedefine HAVE_STRINGS_H #cmakedefine HAVE_STRING_H #cmakedefine HAVE_STRLCPY #cmakedefine HAVE_STRSTR #cmakedefine HAVE_STRTOUL #cmakedefine HAVE_STRUCT_STAT_ST_RDEV #cmakedefine HAVE_ST_RDEV #cmakedefine HAVE_SYS_DIR_H #cmakedefine HAVE_SYS_IPC_H #cmakedefine HAVE_SYS_MMAN_H #cmakedefine HAVE_SYS_NDIR_H #cmakedefine HAVE_SYS_RESOURCE_H #cmakedefine HAVE_SYS_SHM_H #cmakedefine HAVE_SYS_STAT_H #cmakedefine HAVE_SYS_TIME_H #cmakedefine HAVE_SYS_TYPES_H #cmakedefine HAVE_SYS_UTSNAME_H #cmakedefine HAVE_SYS_WAIT_H #cmakedefine HAVE_TELL #cmakedefine HAVE_TIME_H #cmakedefine HAVE_UINT16_T #cmakedefine HAVE_UINT32_T #cmakedefine HAVE_UINT64_T #cmakedefine HAVE_UINT8_T #cmakedefine HAVE_UNISTD_H #cmakedefine HAVE_UNSIGNED_LONG_LONG_INT #cmakedefine HAVE_U_INT32_T #cmakedefine HAVE_VALGRIND_MEMCHECK_H #cmakedefine HAVE_ZLIB_H #cmakedefine HAVE__BOOL #cmakedefine HUGETLB_USE_PROC_MEMINFO #cmakedefine IB_ATOMIC_MODE_INNODB #cmakedefine IB_ATOMIC_MODE_INTRINSICS #cmakedefine IB_ATOMIC_MODE_NATIVE #cmakedefine NO_MINUS_C_MINUS_O #cmakedefine PACKAGE #cmakedefine PACKAGE_BUGREPORT #cmakedefine PACKAGE_NAME #cmakedefine PACKAGE_STRING #cmakedefine PACKAGE_TARNAME #cmakedefine PACKAGE_VERSION #cmakedefine RETSIGTYPE #cmakedefine SIZEOF_CHAR @SIZEOF_CHAR@ #cmakedefine SIZEOF_UCHAR @SIZEOF_UCHAR@ #cmakedefine SIZEOF_SHORT @SIZEOF_SHORT@ #cmakedefine SIZEOF_USHORT @SIZEOF_USHORT@ #cmakedefine SIZEOF_INT @SIZEOF_INT@ #cmakedefine SIZEOF_UINT @SIZEOF_UINT@ #cmakedefine SIZEOF_LONG @SIZEOF_LONG@ #cmakedefine SIZEOF_ULONG @SIZEOF_ULONG@ #cmakedefine SIZEOF_LONG_LONG @SIZEOF_LONG_LONG@ #cmakedefine SIZEOF_ULONG_LONG @SIZEOF_ULONG_LONG@ #cmakedefine SIZEOF_CHARP @SIZEOF_CHARP@ #cmakedefine SIZEOF_VOIDP @SIZEOF_VOIDP@ #cmakedefine SIZEOF_OFF_T @SIZEOF_OFF_T@ #cmakedefine SIZEOF_PTHREAD_T @SIZEOF_PTHREAD_T@ #cmakedefine STACK_DIRECTION #cmakedefine STDC_HEADERS #cmakedefine TIME_WITH_SYS_TIME #cmakedefine TM_IN_SYS_TIME #cmakedefine VERSION #cmakedefine CONST #cmakedefine INLINE #cmakedefine OFF_T #cmakedefine SIZE_T #cmakedefine VOLATILE haildb-2.3.2/ChangeLog0000644000175000017500000004416111513177357015423 0ustar00pcrewspcrews000000000000002010-04-26 The InnoDB Team * row/row0sel.c: Fix Bug#52663 Lost update incrementing column value under READ COMMITTED isolation level 2010-04-22 The InnoDB Team * include/dict0boot.h, dict/dict0boot.c: Fix a bug that prevented the crash recovery of fast CREATE INDEX from dropping partially created indexes. 2010-04-21 The InnoDB Team * btr/btr0btr.c: Fix Bug#52964 Infinite loop in btr_page_split_and_insert() in ROW_FORMAT=COMPRESSED 2010-04-21 The InnoDB Team * data/data0data.c: Fix Bug#52745 Failing assertion: blob_no < page_zip->n_blobs 2010-04-20 The InnoDB Team * btr/btr0btr.c, include/univ.i: Implement UNIV_BTR_AVOID_COPY, for avoiding writes when a B-tree node is split at the first or last record. 2010-04-15 The InnoDB Team * trx/trx0rec.c: Fix Bug#52746 InnoDB purge thread crashed with table containing prefix indexed blobs 2010-03-18 The InnoDB Team * buf0buf.ic: When comparing the time of the first access to a block against innodb_old_blocks_time, use 32-bit arithmetics. The comparison was incorrect on 64-bit systems. 2010-03-11 The InnoDB Team * buf0buf.h, buf0buf.ic: Fix and clarify the latching of some buf_block_t members. Note that check_index_page_at_flush is not protected by any mutex. Note and assert that lock_hash_val is protected by the rw-latch. 2010-03-10 The InnoDB Team * log/log0recv.c: Remove a bogus assertion about page numbers exceeding 0x90000000 in the redo log. Abort when encountering a corrupted redo log record, unless innodb_force_recovery is set. Merged by Stewart Smith from InnoDB tree 2010-03-08 The InnoDB Team * fil/fil0fil.c: Fix ALTER TABLE ... IMPORT TABLESPACE of compressed tables. Merged by Stewart Smith from InnoDB tree 2010-09-21 Stewart Smith * api/api0cfg.c: Change file_io_threads to be a read-only configuration option. It was ignored and replaced with 2 + read_file_io_threads + write_file_io_threads anyway, so by setting it you'd just get a warning (or no action). This represents a slight API change, as instead of DB_SUCCESS you'll now get DB_READONLY if you try to set it. 2010-02-11 The InnoDB Team * api/api0api.c, include/api0api.h, innodb.h: Add a transaction handle argument to ib_table_drop() and ib_index_drop() This forces a change to the API version too. 2010-02-11 The InnoDB Team * api/api0api.c, tests/ib_dict.c: Add an api function to check whether a transaction currently holds a shared lock on the InnoDB data dictionary. 2010-02-11 The InnoDB Team * api/api0api.c, include/api0api.h, innodb.h, tests/ib_cfg.c: Add an api function (ib_cfg_get_all()) to retrieve all configuration variables. 2010-02-11 The InnoDB Team * include/mem0mem.h, include/mem0mem.ic, mem/mem0mem.c: Fix Bug#49535 Available memory check slows down crash recovery tens of times 2010-02-10 The InnoDB Team * api/api0api.c: Normalize the table name arg specified by the client. On Windows we convert the name to lower case (currently using tolower()). 2010-02-09 The InnoDB Team * Makefile.am, configure.in, m4/acx_pthread.m4, tests/Makefile.am: Add a m4 macro to test for POSIX threads library. 2010-02-09 The InnoDB Team * buf/buf0buf.c: Fix Bug#38901 InnoDB logs error repeatedly when trying to load page into buffer pool 2010-02-09 The InnoDB Team * srv/srv0srv.c: Let the master thread sleep if the amount of work to be done is calibrated as taking less than a second. 2010-02-04 The InnoDB Team * api/api0api.c: Check for reserved characters in DOS/Windows when validating tablenames. 2010-02-04 The InnoDB Team * api/api0api.c, api/api0api.h: Add IB_DECIMAL column type. 2010-02-04 The InnoDB Team * btr/btr0btr.c, btr/btr0cur.c, btr/btr0pcur.c, buf/buf0buf.c, include/btr0btr.h, include/btr0cur.h, include/btr0pcur.h, include/btr0pcur.ic, include/buf0buf.h, row/row0ins.c, row/row0sel.c: Pass the file name and line number of the caller of the b-tree cursor functions to the buffer pool requests, in order to make the latch diagnostics more accurate. 2010-02-03 The InnoDB Team * lock/lock0lock.c: Fix Bug#49001 SHOW INNODB STATUS deadlock info incorrect when deadlock detection aborts 2010-02-03 The InnoDB Team * buf/buf0lru.c: Fix Bug#35077 Very slow DROP TABLE (ALTER TABLE, OPTIMIZE TABLE) on compressed tables 2010-01-27 The InnoDB Team * ddl/ddl0ddl.c, include/ddl0ddl.h, log/log0recv.c: Drop temporary tables at startup. This addresses the third aspect of Bug#41609 Crash recovery does not work for InnoDB temporary tables. 2010-01-21 The InnoDB Team * buf/buf0buf.c: Do not merge buffered inserts to compressed pages before the redo log has been applied in crash recovery. 2010-01-21 The InnoDB Team * api/api0api.c, row/row0sel.c: Fix the case where the search match mode is IB_EXACT_MATCH. 2010-01-13 The InnoDB Team * row/row0sel.c: On the READ UNCOMMITTED isolation level, do not attempt to access a clustered index record that has been marked for deletion. 2010-01-13 The InnoDB Team * buf/buf0buf.c: When disabling the adaptive hash index, check the block state before checking block->is_hashed, because the latter may be uninitialized right after server startup. 2009-12-15 The InnoDB Team * api/api0api.c, include/api0api.h, innodb.h: Remove API function that is a no-op (ib_table_schema_set_temp_dir). 2009-12-02 The InnoDB Team * srv/srv0start.c: Display the zlib version number at startup. InnoDB compressed tables use zlib, and the implementation depends on the zlib function compressBound(), whose definition was slightly changed in zlib version 1.2.3.1 in 2006. 2009-11-30 The InnoDB Team * dict/dict0boot.c, dict/dict0crea.c, dict/dict0load.c, dict/dict0mem.c, fil/fil0fil.c, include/dict0mem.h: Fix the bogus warning messages for non-existing temporary tables that were reported in Bug#41609 Crash recovery does not work for InnoDB temporary tables. The actual crash recovery bug was corrected on 2009-04-29. 2009-11-19 The InnoDB Team * btr/btr0btr.c: Fix Bug#48469 when innodb tablespace is configured too small, crash and corruption! 2009-11-19 The InnoDB Team * data/data0type.c: Fix Bug#48526 Data type for float and double is incorrectly reported in InnoDB table monitor 2009-11-19 The InnoDB Team * CMakeLists.txt: Fix Bug#48317 cannot build innodb as static library 2009-11-12 The InnoDB Team * include/db0err.h, row/row0merge.c: Allow CREATE INDEX to be interrupted. Also, when CHECK TABLE is interrupted, report ER_QUERY_INTERRUPTED. 2009-11-11 The InnoDB Team * include/os0file.h, os/os0file.c: Fix Bug#3139 Mysql crashes: "windows error 995" after several selects on a large DB 2009-11-07 The InnoDB Team * row/row0sel.c: Fix a bug in row fetch reported by JonMeredith: http://forums.innodb.com/read.php?8,913,913#msg-913 2009-11-06 The InnoDB Team * row/row0sel.c When doing a unique search there is no need to fetch the next N records. This should speed up search a little too. 2009-10-29 The InnoDB Team * os/os0proc.c: Fix Bug#48237 Error handling in os_mem_alloc_large appears to be incorrect 2009-10-29 The InnoDB Team * buf/buf0buf.c, buf/buf0lru.c, include/buf0buf.h, include/buf0buf.ic: Fix corruption of the buf_pool->LRU_old list and improve debug assertions. 2009-10-28 The InnoDB Team * srv/srv0start.c: Fix Bug#41490 After enlargement of InnoDB page size, the error message become inaccurate 2009-10-26 The InnoDB Team * row/row0ins.c: When allocating a data tuple, zero out the system fields in order to avoid Valgrind warnings about uninitialized fields in dtuple_validate(). 2009-10-15 The InnoDB Team * include/page0page.ic: Fix Bug#47058 Failure to compile on solaris 10u7 + spro cc/CC 5.10 2009-10-15 The InnoDB Team * srv/srv0start.c, srv/srv0srv.c, include/db0err.h: Add a new error code, DB_FATAL. This code now replaces some of the exit(3) calls during the startup phase. 2009-10-13 The InnoDB Team * buf/buf0flu.c: Call fsync() on datafiles after a batch of pages is written to disk even when skip_innodb_doublewrite is set. 2009-10-05 The InnoDB Team * buf/buf0buf.c: Do not invalidate buffer pool while an LRU batch is active. Added code to buf_pool_invalidate() to wait for the running batches to finish. 2009-10-01 The InnoDB Team * fsp/fsp0fsp.c, row/row0merge.c: Clean up after a crash during DROP INDEX. When InnoDB crashes while dropping an index, ensure that the index will be completely dropped during crash recovery. 2009-10-01 The InnoDB Team * api/api0cfg.c, include/srv0srv.h, srv/srv0srv.c: Introduce a new boolean config parameter "adaptive_flushing" which can be set to OFF to disable the adaptive flushing. 2009-09-28 The InnoDB Team * btr/btr0btr.c, buf/buf0buf.c, include/page0page.h, include/page0zip.h, page/page0cur.c, page/page0page.c, page/page0zip.c: Do not write to PAGE_INDEX_ID when restoring an uncompressed page after a compression failure. The field should only be written when creating a B-tree page. This fix addresses a race condition in a debug assertion. 2009-09-28 The InnoDB Team * fil/fil0fil.c: Try to prevent the reuse of tablespace identifiers after InnoDB has crashed during table creation. Also, refuse to start if files with duplicate tablespace identifiers are encountered. 2009-09-25 The InnoDB Team * include/os0file.h, os/os0file.c: Fix Bug#47055 unconditional exit(1) on ERROR_WORKING_SET_QUOTA 1453 (0x5AD) for InnoDB backend 2009-09-19 The InnoDB Team * fsp/fsp0fsp.c: Fix Bug#31183 Tablespace full problems not reported in error log, error message unclear 2009-09-17 The InnoDB Team * api/api0api.c: Fix memory leak reported by JonMeredith: http://forums.innodb.com/read.php?8,821,821#msg-821 2009-08-31 The InnoDB Team * api/api0api.c: Enforce NOT NULL column constraint during insert and update. 2009-08-27 The InnoDB Team * row/row0merge.c: Fix a bug in the merge sort that can corrupt indexes in fast index creation. Add some consistency checks. Check that the number of records remains constant in every merge sort pass. 2009-08-27 The InnoDB Team * buf/buf0buf.c, buf/buf0lru.c, buf/buf0rea.c, include/buf0buf.h, include/buf0buf.ic, include/buf0lru.h, include/ut0ut.h, ut/ut0ut.c: Make it possible to tune the buffer pool LRU eviction policy to be more resistant against index scans. Introduce the settable global variables lru_old_blocks_pct and lru_block_access_recency for controlling the buffer pool eviction policy. The parameter lru_old_blocks_pct (5..95) controls the desired amount of "old" blocks in the LRU list. The default is 37, corresponding to the old fixed ratio of 3/8. Each time a block is accessed, it will be moved to the "new" blocks if its first access was at least innodb_old_blocks_time milliseconds ago (default 0, meaning every block). The idea is that in index scans, blocks will be accessed a few times within lru_block_access_recency, and they will remain in the "old" section of the LRU list. Thus, when lru_block_access_recency is nonzero, blocks retrieved for one-time index scans will be more likely candidates for eviction than blocks that are accessed in random patterns. 2009-08-03 The InnoDB Team * api/api0api.c, include/api0api.h, innodb.h, tests/ib_dict.c: Enable the VARBINARY column type 2009-07-24 The InnoDB Team * api/api0api.c, api/api0status.c, include/api0api.h, innodb.h: Add a new function (ib_status_get_i64()) to the API that allows users to read the InnoDB system variables. 2009-08-11 The InnoDB Team InnoDB Plugin 1.0.4 released 2009-07-20 The InnoDB Team * buf/buf0rea.c, include/srv0srv.h, srv/srv0srv.c: Change the meaning of this parameter to signify the number of pages that must be sequentially accessed for InnoDB to trigger a readahead request. 2009-07-16 The InnoDB Team * include/univ.i: Support inlining of functions and prefetch with Sun Studio. These changes are based on contribution from Sun Microsystems Inc. under a BSD license. 2009-07-10 The InnoDB Team * srv/srv0srv.c: Change the defaults for: sync_spin_loops: 20 -> 30 spin_wait_delay: 5 -> 6 2009-07-08 The InnoDB Team * srv/srv0srv.c, buf/buf0flu.c: include/srv0srv.h, include/log0log.ic, include/buf0flu.h, include/log0log.h: Implement the adaptive flushing of dirty pages, which uses a heuristics based flushing rate of dirty pages to avoid IO bursts at checkpoint. 2009-07-07 The InnoDB Team * srv/srv0srv.c, log/log0log.c, include/srv0srv.h: Implement IO capacity tuning. The ibuf merge is also changed from synchronous to asynchronous. These changes are based on contribution from Google Inc. under a BSD license. 2009-07-02 The InnoDB Team * ddl/ddl0ddl.c: Do not rollback the transaction if the table create fails. Let the user handle the transaction rollback. 2009-06-24 The InnoDB Team Embedded InnoDB 1.0.3.5325 released 2009-05-30 The InnoDB Team * api/api0api.c: Return a meaningful error code when creating a table if the table name, supplied by the user, is not in 'database/table_name' format instead of asserting. 2009-05-29 The InnoDB Team * api/api0api.c, configure.in: Add a function to return the API version number. The version number format is a 64 bit unsigned integer encoded as follows: | 16 bits reserverd | 16 bits current | 16 bits revision | 16 bits age | - If the library source code has changed at all since the last release, then revision will be incremented (`c:r:a' becomes `c:r+1:a'). - If any interfaces have been added, removed, or changed since the last update, current will be incremented, and revision will be set to 0. - If any interfaces have been added (but not changed or removed) since the last release, then age will be incremented. - If any interfaces have been changed or removed since the last release, then age will be set to 0. 2009-05-28 The InnoDB Team * fil/fil0fil.c, srv/srv0start.c: Change ib_database_create() to create the database sub-directory relative to the data_home_dir setting. 2009-05-27 The InnoDB Team * api/api0cfg.c, include/api0api.h: Change the ib_cfg_set*() functions to return ib_err_t instead of ib_bool_t. 2009-05-26 The InnoDB Team * api/api0api.c: Fix a bug in the 1.0.0 release were INTs were stored incorrectly. The sign flag was being set incorrectly when converting the value to the storage format. 2009-05-26 The InnoDB Team * api/api0cfg.c: Change the default value of "log_group_home_dir" from "log" to ".". 2009-05-25 The InnoDB Team * api/api0api.c, include/api0api.h: Introduce two new types: - IB_CHAR_ANYCHARSET - Fixed width column with any charset - IB_VARCHAR_ANYCHARSET - Variable length column, any charset 2009-05-20 The InnoDB Team * api/api0api.c, include/api0api.h: Add an API function to list all tables in the data dictionary 2009-05-19 The InnoDB Team * ddl/ddl0ddl.c, api/api0api.c: Add a function to drop a database. All the tables are first dropped and then the database. If the tables are in use then the referenced tables will be put in a background drop queue. The tables in the background drop queue will be dropped once the reference count for that table reaches 0. 2009-05-19 The InnoDB Team * btr/btr0btr.c, btr/btr0cur.c, lock/lock0lock.c, include/page0page.ic, include/lock0lock.h, include/dict0dict.h, include/page0page.h, include/dict0dict.ic, ibuf/ibuf0ibuf.c, page/page0zip.c, page/page0page.c: Write updates of PAGE_MAX_TRX_ID to the redo log and add debug assertions for checking that PAGE_MAX_TRX_ID is valid on leaf pages of secondary indexes and the insert buffer B-tree. This bug could cause failures in secondary index lookups in consistent reads right after crash recovery. 2009-05-18 The InnoDB Team * btr/btr0cur.c: Correctly estimate the space needed on the compressed page when performing an update by delete-and-insert. 2009-05-14 The InnoDB Team * include/srv0srv.h, srv/srv0srv.c, api/api0cfg.c: Remove unused variable. 2009-05-13 The InnoDB Team * dict/dict0dict.c: Fix Bug#44320 InnoDB: missing DB_ROLL_PTR in Table Monitor COLUMNS output 2009-04-29 The InnoDB Team * fil/fil0fil.c, include/fil0fil.h, include/mtr0mtr.h, log/log0recv.c: Fix Bug#41609 Crash recovery does not work for InnoDB temporary tables 2009-04-23 The InnoDB Team * api/api0api.c: Fix a bug that prevented creation of compressed tables. 2009-04-23 The InnoDB Team * api/api0api.c, include/api0api.h: Add a function to convert error codes to strings, similar to strerror(3). 2009-05-13 The InnoDB Team * row/row0sel.c: Add row caching when fetching rows with row_search_for_client(). This change will fetch more than one row and cache it in the row_prebuilt_t structure to reduce CPU and locking overhead. This port from the plugin branch is different because in Embedded InnoDB we cache the rows in InnoDB row format, in the plugin we cache the rows in the MySQL row format. 2009-05-13 The InnoDB Team * api/api0api.c: Make the insert row code more efficient, we do a shallow copy when inserting rows and cache the dtuple_t instance in the query graph rather than creating it for each insert. Aggressive inlining of some frequently called API functions. The ones that are called from within the API implementation. 2009-05-12 The InnoDB Team * api/api0api.c: Add row caching when fetching rows with row_search_for_client() 2009-05-12 The InnoDB Team * api/api0cfg.c, tests/Makefile.am, tests/ib_cfg.c: Fix a bug when retrieving a string configuration parameters with ib_cfg_get(), reported in http://forums.innodb.com/read.php?8,584,584#msg-584 2009-05-06 The InnoDB Team * innodb.h: Remove the dependency on the config.h file generated by autoconf and CMake. API code assumes C99 compiler to work out the integer width. We make an exception for VisualStudio by using the __int8 etc. instead of the standard aliases uint8_t etc. 2009-05-06 The InnoDB Team * innodb.h: Replace the public interface file api0api.h with innodb.h. 2009-05-05 The InnoDB Team * innodb.h api/api0api.c etc.: Allow users to set a callback function and the output stream to print the InnoDB error messages. Currently this callback function must be a drop-in replacement for fprintf(). 2009-04-23 The InnoDB Team * include/trx0types.h: Define the logical type names trx_id_t, roll_ptr_t, and undo_no_t and use them in place of dulint everywhere. 2009-04-21 The InnoDB Team Embedded InnoDB 1.0.0 released haildb-2.3.2/read/0000755000175000017500000000000011513177437014555 5ustar00pcrewspcrews00000000000000haildb-2.3.2/read/read0read.c0000644000175000017500000003727511513177357016567 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1997, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file read/read0read.c Cursor read Created 2/16/1997 Heikki Tuuri *******************************************************/ #include "read0read.h" #ifdef UNIV_NONINL #include "read0read.ic" #endif #include "srv0srv.h" #include "trx0sys.h" /* ------------------------------------------------------------------------------- FACT A: Cursor read view on a secondary index sees only committed versions ------- of the records in the secondary index or those versions of rows created by transaction which created a cursor before cursor was created even if transaction which created the cursor has changed that clustered index page. PROOF: We must show that read goes always to the clustered index record to see that record is visible in the cursor read view. Consider e.g. following table and SQL-clauses: create table t1(a int not null, b int, primary key(a), index(b)); insert into t1 values (1,1),(2,2); commit; Now consider that we have a cursor for a query select b from t1 where b >= 1; This query will use secondary key on the table t1. Now after the first fetch on this cursor if we do a update: update t1 set b = 5 where b = 2; Now second fetch of the cursor should not see record (2,5) instead it should see record (2,2). We also should show that if we have delete t1 where b = 5; we still can see record (2,2). When we access a secondary key record maximum transaction id is fetched from this record and this trx_id is compared to up_limit_id in the view. If trx_id in the record is greater or equal than up_limit_id in the view cluster record is accessed. Because trx_id of the creating transaction is stored when this view was created to the list of trx_ids not seen by this read view previous version of the record is requested to be built. This is build using clustered record. If the secondary key record is delete marked it's corresponding clustered record can be already be purged only if records trx_id < low_limit_no. Purge can't remove any record deleted by a transaction which was active when cursor was created. But, we still may have a deleted secondary key record but no clustered record. But, this is not a problem because this case is handled in row_sel_get_clust_rec() function which is called whenever we note that this read view does not see trx_id in the record. Thus, we see correct version. Q. E. D. ------------------------------------------------------------------------------- FACT B: Cursor read view on a clustered index sees only committed versions ------- of the records in the clustered index or those versions of rows created by transaction which created a cursor before cursor was created even if transaction which created the cursor has changed that clustered index page. PROOF: Consider e.g.following table and SQL-clauses: create table t1(a int not null, b int, primary key(a)); insert into t1 values (1),(2); commit; Now consider that we have a cursor for a query select a from t1 where a >= 1; This query will use clustered key on the table t1. Now after the first fetch on this cursor if we do a update: update t1 set a = 5 where a = 2; Now second fetch of the cursor should not see record (5) instead it should see record (2). We also should show that if we have execute delete t1 where a = 5; after the cursor is opened we still can see record (2). When accessing clustered record we always check if this read view sees trx_id stored to clustered record. By default we don't see any changes if record trx_id >= low_limit_id i.e. change was made transaction which started after transaction which created the cursor. If row was changed by the future transaction a previous version of the clustered record is created. Thus we see only committed version in this case. We see all changes made by committed transactions i.e. record trx_id < up_limit_id. In this case we don't need to do anything, we already see correct version of the record. We don't see any changes made by active transaction except creating transaction. We have stored trx_id of creating transaction to list of trx_ids when this view was created. Thus we can easily see if this record was changed by the creating transaction. Because we already have clustered record we can access roll_ptr. Using this roll_ptr we can fetch undo record. We can now check that undo_no of the undo record is less than undo_no of the trancaction which created a view when cursor was created. We see this clustered record only in case when record undo_no is less than undo_no in the view. If this is not true we build based on undo_rec previous version of the record. This record is found because purge can't remove records accessed by active transaction. Thus we see correct version. Q. E. D. ------------------------------------------------------------------------------- FACT C: Purge does not remove any delete marked row that is visible ------- to cursor view. TODO: proof this */ /*********************************************************************//** Creates a read view object. @return own: read view struct */ UNIV_INLINE read_view_t* read_view_create_low( /*=================*/ ulint n, /*!< in: number of cells in the trx_ids array */ mem_heap_t* heap) /*!< in: memory heap from which allocated */ { read_view_t* view; view = mem_heap_alloc(heap, sizeof(read_view_t)); view->n_trx_ids = n; view->trx_ids = mem_heap_alloc(heap, n * sizeof *view->trx_ids); return(view); } /*********************************************************************//** Makes a copy of the oldest existing read view, with the exception that also the creating trx of the oldest view is set as not visible in the 'copied' view. Opens a new view if no views currently exist. The view must be closed with ..._close. This is used in purge. @return own: read view struct */ UNIV_INTERN read_view_t* read_view_oldest_copy_or_open_new( /*==============================*/ trx_id_t cr_trx_id, /*!< in: trx_id of creating transaction, or ut_dulint_zero used in purge */ mem_heap_t* heap) /*!< in: memory heap from which allocated */ { read_view_t* old_view; read_view_t* view_copy; ibool needs_insert = TRUE; ulint insert_done = 0; ulint n; ulint i; ut_ad(mutex_own(&kernel_mutex)); old_view = UT_LIST_GET_LAST(trx_sys->view_list); if (old_view == NULL) { return(read_view_open_now(cr_trx_id, heap)); } n = old_view->n_trx_ids; if (!ut_dulint_is_zero(old_view->creator_trx_id)) { n++; } else { needs_insert = FALSE; } view_copy = read_view_create_low(n, heap); /* Insert the id of the creator in the right place of the descending array of ids, if needs_insert is TRUE: */ i = 0; while (i < n) { if (needs_insert && (i >= old_view->n_trx_ids || ut_dulint_cmp(old_view->creator_trx_id, read_view_get_nth_trx_id(old_view, i)) > 0)) { read_view_set_nth_trx_id(view_copy, i, old_view->creator_trx_id); needs_insert = FALSE; insert_done = 1; } else { read_view_set_nth_trx_id(view_copy, i, read_view_get_nth_trx_id( old_view, i - insert_done)); } i++; } view_copy->creator_trx_id = cr_trx_id; view_copy->low_limit_no = old_view->low_limit_no; view_copy->low_limit_id = old_view->low_limit_id; if (n > 0) { /* The last active transaction has the smallest id: */ view_copy->up_limit_id = read_view_get_nth_trx_id( view_copy, n - 1); } else { view_copy->up_limit_id = old_view->up_limit_id; } UT_LIST_ADD_LAST(view_list, trx_sys->view_list, view_copy); return(view_copy); } /*********************************************************************//** Opens a read view where exactly the transactions serialized before this point in time are seen in the view. @return own: read view struct */ UNIV_INTERN read_view_t* read_view_open_now( /*===============*/ trx_id_t cr_trx_id, /*!< in: trx_id of creating transaction, or ut_dulint_zero used in purge */ mem_heap_t* heap) /*!< in: memory heap from which allocated */ { read_view_t* view; trx_t* trx; ulint n; ut_ad(mutex_own(&kernel_mutex)); view = read_view_create_low(UT_LIST_GET_LEN(trx_sys->trx_list), heap); view->creator_trx_id = cr_trx_id; view->type = VIEW_NORMAL; view->undo_no = ut_dulint_zero; /* No future transactions should be visible in the view */ view->low_limit_no = trx_sys->max_trx_id; view->low_limit_id = view->low_limit_no; n = 0; trx = UT_LIST_GET_FIRST(trx_sys->trx_list); /* No active transaction should be visible, except cr_trx */ while (trx) { ut_ad(trx->magic_n == TRX_MAGIC_N); if (ut_dulint_cmp(trx->id, cr_trx_id) != 0 && (trx->conc_state == TRX_ACTIVE || trx->conc_state == TRX_PREPARED)) { read_view_set_nth_trx_id(view, n, trx->id); n++; /* NOTE that a transaction whose trx number is < trx_sys->max_trx_id can still be active, if it is in the middle of its commit! Note that when a transaction starts, we initialize trx->no to ut_dulint_max. */ if (ut_dulint_cmp(view->low_limit_no, trx->no) > 0) { view->low_limit_no = trx->no; } } trx = UT_LIST_GET_NEXT(trx_list, trx); } view->n_trx_ids = n; if (n > 0) { /* The last active transaction has the smallest id: */ view->up_limit_id = read_view_get_nth_trx_id(view, n - 1); } else { view->up_limit_id = view->low_limit_id; } UT_LIST_ADD_FIRST(view_list, trx_sys->view_list, view); return(view); } /*********************************************************************//** Closes a read view. */ UNIV_INTERN void read_view_close( /*============*/ read_view_t* view) /*!< in: read view */ { ut_ad(mutex_own(&kernel_mutex)); UT_LIST_REMOVE(view_list, trx_sys->view_list, view); } /*********************************************************************//** Closes a consistent read view for the client. This function is called at an SQL statement end if the trx isolation level is <= TRX_ISO_READ_COMMITTED. */ UNIV_INTERN void read_view_close_for_read_committed( /*===============================*/ trx_t* trx) /*!< in: trx which has a read view */ { ut_a(trx->global_read_view); mutex_enter(&kernel_mutex); read_view_close(trx->global_read_view); mem_heap_empty(trx->global_read_view_heap); trx->read_view = NULL; trx->global_read_view = NULL; mutex_exit(&kernel_mutex); } /*********************************************************************//** Prints a read view to ib_stream. */ UNIV_INTERN void read_view_print( /*============*/ const read_view_t* view) /*!< in: read view */ { ulint n_ids; ulint i; if (view->type == VIEW_HIGH_GRANULARITY) { ib_logger(ib_stream, "High-granularity read view undo_n:o %lu %lu\n", (ulong) ut_dulint_get_high(view->undo_no), (ulong) ut_dulint_get_low(view->undo_no)); } else { ib_logger(ib_stream, "Normal read view\n"); } ib_logger(ib_stream, "Read view low limit trx n:o %lu %lu\n", (ulong) ut_dulint_get_high(view->low_limit_no), (ulong) ut_dulint_get_low(view->low_limit_no)); ib_logger(ib_stream, "Read view up limit trx id " TRX_ID_FMT "\n", TRX_ID_PREP_PRINTF(view->up_limit_id)); ib_logger(ib_stream, "Read view low limit trx id " TRX_ID_FMT "\n", TRX_ID_PREP_PRINTF(view->low_limit_id)); ib_logger(ib_stream, "Read view individually stored trx ids:\n"); n_ids = view->n_trx_ids; for (i = 0; i < n_ids; i++) { ib_logger(ib_stream, "Read view trx id " TRX_ID_FMT "\n", TRX_ID_PREP_PRINTF( read_view_get_nth_trx_id(view, i))); } } /*********************************************************************//** Create a high-granularity consistent cursor view to be used in cursors. In this consistent read view modifications done by the creating transaction after the cursor is created or future transactions are not visible. */ UNIV_INTERN cursor_view_t* read_cursor_view_create( /*====================*/ trx_t* cr_trx) /*!< in: trx where cursor view is created */ { cursor_view_t* curview; read_view_t* view; mem_heap_t* heap; trx_t* trx; ulint n; ut_a(cr_trx); /* Use larger heap than in trx_create when creating a read_view because cursors are quite long. */ heap = mem_heap_create(512); curview = (cursor_view_t*) mem_heap_alloc(heap, sizeof(cursor_view_t)); curview->heap = heap; /* Drop cursor tables from consideration when evaluating the need of auto-commit */ curview->n_client_tables_in_use = cr_trx->n_client_tables_in_use; cr_trx->n_client_tables_in_use = 0; mutex_enter(&kernel_mutex); curview->read_view = read_view_create_low( UT_LIST_GET_LEN(trx_sys->trx_list), curview->heap); view = curview->read_view; view->creator_trx_id = cr_trx->id; view->type = VIEW_HIGH_GRANULARITY; view->undo_no = cr_trx->undo_no; /* No future transactions should be visible in the view */ view->low_limit_no = trx_sys->max_trx_id; view->low_limit_id = view->low_limit_no; n = 0; trx = UT_LIST_GET_FIRST(trx_sys->trx_list); /* No active transaction should be visible */ while (trx) { if (trx->conc_state == TRX_ACTIVE || trx->conc_state == TRX_PREPARED) { read_view_set_nth_trx_id(view, n, trx->id); n++; /* NOTE that a transaction whose trx number is < trx_sys->max_trx_id can still be active, if it is in the middle of its commit! Note that when a transaction starts, we initialize trx->no to ut_dulint_max. */ if (ut_dulint_cmp(view->low_limit_no, trx->no) > 0) { view->low_limit_no = trx->no; } } trx = UT_LIST_GET_NEXT(trx_list, trx); } view->n_trx_ids = n; if (n > 0) { /* The last active transaction has the smallest id: */ view->up_limit_id = read_view_get_nth_trx_id(view, n - 1); } else { view->up_limit_id = view->low_limit_id; } UT_LIST_ADD_FIRST(view_list, trx_sys->view_list, view); mutex_exit(&kernel_mutex); return(curview); } /*********************************************************************//** Close a given consistent cursor view and restore global read view back to a transaction read view. */ UNIV_INTERN void read_cursor_view_close( /*===================*/ trx_t* trx, /*!< in: trx */ cursor_view_t* curview)/*!< in: cursor view to be closed */ { ut_a(curview); ut_a(curview->read_view); ut_a(curview->heap); /* Add cursor's tables to the global count of active tables that belong to this transaction */ trx->n_client_tables_in_use += curview->n_client_tables_in_use; mutex_enter(&kernel_mutex); read_view_close(curview->read_view); trx->read_view = trx->global_read_view; mutex_exit(&kernel_mutex); mem_heap_free(curview->heap); } /*********************************************************************//** This function sets a given consistent cursor view to a transaction read view if given consistent cursor view is not NULL. Otherwise, function restores a global read view to a transaction read view. */ UNIV_INTERN void read_cursor_set( /*============*/ trx_t* trx, /*!< in: transaction where cursor is set */ cursor_view_t* curview)/*!< in: consistent cursor view to be set */ { ut_a(trx); mutex_enter(&kernel_mutex); if (UNIV_LIKELY(curview != NULL)) { trx->read_view = curview->read_view; } else { trx->read_view = trx->global_read_view; } mutex_exit(&kernel_mutex); } haildb-2.3.2/ha/0000755000175000017500000000000011513177437014232 5ustar00pcrewspcrews00000000000000haildb-2.3.2/ha/hash0hash.c0000644000175000017500000001164411513177357016254 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1997, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file ha/hash0hash.c The simple hash table utility Created 5/20/1997 Heikki Tuuri *******************************************************/ #include "hash0hash.h" #ifdef UNIV_NONINL #include "hash0hash.ic" #endif #include "mem0mem.h" #ifndef UNIV_HOTBACKUP /************************************************************//** Reserves the mutex for a fold value in a hash table. */ UNIV_INTERN void hash_mutex_enter( /*=============*/ hash_table_t* table, /*!< in: hash table */ ulint fold) /*!< in: fold */ { mutex_enter(hash_get_mutex(table, fold)); } /************************************************************//** Releases the mutex for a fold value in a hash table. */ UNIV_INTERN void hash_mutex_exit( /*============*/ hash_table_t* table, /*!< in: hash table */ ulint fold) /*!< in: fold */ { mutex_exit(hash_get_mutex(table, fold)); } /************************************************************//** Reserves all the mutexes of a hash table, in an ascending order. */ UNIV_INTERN void hash_mutex_enter_all( /*=================*/ hash_table_t* table) /*!< in: hash table */ { ulint i; for (i = 0; i < table->n_mutexes; i++) { mutex_enter(table->mutexes + i); } } /************************************************************//** Releases all the mutexes of a hash table. */ UNIV_INTERN void hash_mutex_exit_all( /*================*/ hash_table_t* table) /*!< in: hash table */ { ulint i; for (i = 0; i < table->n_mutexes; i++) { mutex_exit(table->mutexes + i); } } #endif /* !UNIV_HOTBACKUP */ /*************************************************************//** Creates a hash table with >= n array cells. The actual number of cells is chosen to be a prime number slightly bigger than n. @return own: created table */ UNIV_INTERN hash_table_t* hash_create( /*========*/ ulint n) /*!< in: number of array cells */ { hash_cell_t* array; ulint prime; hash_table_t* table; prime = ut_find_prime(n); table = mem_alloc(sizeof(hash_table_t)); array = ut_malloc(sizeof(hash_cell_t) * prime); table->array = array; table->n_cells = prime; #ifndef UNIV_HOTBACKUP # if defined UNIV_AHI_DEBUG || defined UNIV_DEBUG table->adaptive = FALSE; # endif /* UNIV_AHI_DEBUG || UNIV_DEBUG */ table->n_mutexes = 0; table->mutexes = NULL; table->heaps = NULL; #endif /* !UNIV_HOTBACKUP */ table->heap = NULL; ut_d(table->magic_n = HASH_TABLE_MAGIC_N); /* Initialize the cell array */ hash_table_clear(table); return(table); } /*************************************************************//** Frees a hash table. */ UNIV_INTERN void hash_table_free( /*============*/ hash_table_t* table) /*!< in, own: hash table */ { ut_ad(table); ut_ad(table->magic_n == HASH_TABLE_MAGIC_N); #ifndef UNIV_HOTBACKUP ut_a(table->mutexes == NULL); #endif /* !UNIV_HOTBACKUP */ ut_free(table->array); mem_free(table); } #ifndef UNIV_HOTBACKUP /*************************************************************//** Creates a mutex array to protect a hash table. */ UNIV_INTERN void hash_create_mutexes_func( /*=====================*/ hash_table_t* table, /*!< in: hash table */ #ifdef UNIV_SYNC_DEBUG ulint sync_level, /*!< in: latching order level of the mutexes: used in the debug version */ #endif /* UNIV_SYNC_DEBUG */ ulint n_mutexes) /*!< in: number of mutexes, must be a power of 2 */ { ulint i; ut_ad(table); ut_ad(table->magic_n == HASH_TABLE_MAGIC_N); ut_a(n_mutexes > 0); ut_a(ut_is_2pow(n_mutexes)); table->mutexes = mem_alloc(n_mutexes * sizeof(mutex_t)); for (i = 0; i < n_mutexes; i++) { mutex_create(table->mutexes + i, sync_level); } table->n_mutexes = n_mutexes; } /***************************************************************** Frees a mutex array created with hash_create_mutexes_func(). */ UNIV_INTERN void hash_free_mutexes_func( /*===================*/ hash_table_t* table) /*!< in,own: hash table */ { ulint i; for (i = 0; i < table->n_mutexes; i++) { mutex_free(&table->mutexes[i]); #ifdef UNIV_DEBUG memset(&table->mutexes[i], 0x0, sizeof(table->mutexes[i])); #endif } mem_free(table->mutexes); } #endif /* !UNIV_HOTBACKUP */ haildb-2.3.2/ha/ha0ha.c0000644000175000017500000002630311513177357015364 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1994, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /********************************************************************//** @file ha/ha0ha.c The hash table with external chains Created 8/22/1994 Heikki Tuuri *************************************************************************/ #include "ha0ha.h" #ifdef UNIV_NONINL #include "ha0ha.ic" #endif #ifdef UNIV_DEBUG # include "buf0buf.h" #endif /* UNIV_DEBUG */ #ifdef UNIV_SYNC_DEBUG # include "btr0sea.h" #endif /* UNIV_SYNC_DEBUG */ #include "page0page.h" /*************************************************************//** Creates a hash table with at least n array cells. The actual number of cells is chosen to be a prime number slightly bigger than n. @return own: created table */ UNIV_INTERN hash_table_t* ha_create_func( /*===========*/ ulint n, /*!< in: number of array cells */ #ifdef UNIV_SYNC_DEBUG ulint mutex_level, /*!< in: level of the mutexes in the latching order: this is used in the debug version */ #endif /* UNIV_SYNC_DEBUG */ ulint n_mutexes) /*!< in: number of mutexes to protect the hash table: must be a power of 2, or 0 */ { hash_table_t* table; #ifndef UNIV_HOTBACKUP ulint i; #endif /* !UNIV_HOTBACKUP */ ut_ad(ut_is_2pow(n_mutexes)); table = hash_create(n); #if defined UNIV_AHI_DEBUG || defined UNIV_DEBUG # ifndef UNIV_HOTBACKUP table->adaptive = TRUE; # endif /* !UNIV_HOTBACKUP */ #endif /* UNIV_AHI_DEBUG || UNIV_DEBUG */ /* Creating MEM_HEAP_BTR_SEARCH type heaps can potentially fail, but in practise it never should in this case, hence the asserts. */ if (n_mutexes == 0) { table->heap = mem_heap_create_in_btr_search( ut_min(4096, MEM_MAX_ALLOC_IN_BUF)); ut_a(table->heap); return(table); } #ifndef UNIV_HOTBACKUP hash_create_mutexes(table, n_mutexes, mutex_level); table->heaps = mem_alloc(n_mutexes * sizeof(void*)); for (i = 0; i < n_mutexes; i++) { table->heaps[i] = mem_heap_create_in_btr_search(4096); ut_a(table->heaps[i]); } #endif /* !UNIV_HOTBACKUP */ return(table); } /*************************************************************//** Empties a hash table and frees the memory heaps. */ UNIV_INTERN void ha_clear( /*=====*/ hash_table_t* table) /*!< in, own: hash table */ { ulint i; ulint n; ut_ad(table); ut_ad(table->magic_n == HASH_TABLE_MAGIC_N); #ifdef UNIV_SYNC_DEBUG ut_ad(rw_lock_own(&btr_search_latch, RW_LOCK_EXCLUSIVE)); #endif /* UNIV_SYNC_DEBUG */ #ifndef UNIV_HOTBACKUP /* Free the memory heaps. */ n = table->n_mutexes; for (i = 0; i < n; i++) { mem_heap_free(table->heaps[i]); } #endif /* !UNIV_HOTBACKUP */ /* Clear the hash table. */ n = hash_get_n_cells(table); for (i = 0; i < n; i++) { hash_get_nth_cell(table, i)->node = NULL; } } /*************************************************************//** Inserts an entry into a hash table. If an entry with the same fold number is found, its node is updated to point to the new data, and no new node is inserted. @return TRUE if succeed, FALSE if no more memory could be allocated */ UNIV_INTERN ibool ha_insert_for_fold_func( /*====================*/ hash_table_t* table, /*!< in: hash table */ ulint fold, /*!< in: folded value of data; if a node with the same fold value already exists, it is updated to point to the same data, and no new node is created! */ #if defined UNIV_AHI_DEBUG || defined UNIV_DEBUG buf_block_t* block, /*!< in: buffer block containing the data */ #endif /* UNIV_AHI_DEBUG || UNIV_DEBUG */ void* data) /*!< in: data, must not be NULL */ { hash_cell_t* cell; ha_node_t* node; ha_node_t* prev_node; ulint hash; ut_ad(data); ut_ad(table); ut_ad(table->magic_n == HASH_TABLE_MAGIC_N); #if defined UNIV_AHI_DEBUG || defined UNIV_DEBUG ut_a(block->frame == page_align(data)); #endif /* UNIV_AHI_DEBUG || UNIV_DEBUG */ ASSERT_HASH_MUTEX_OWN(table, fold); hash = hash_calc_hash(fold, table); cell = hash_get_nth_cell(table, hash); prev_node = cell->node; while (prev_node != NULL) { if (prev_node->fold == fold) { #if defined UNIV_AHI_DEBUG || defined UNIV_DEBUG # ifndef UNIV_HOTBACKUP if (table->adaptive) { buf_block_t* prev_block = prev_node->block; ut_a(prev_block->frame == page_align(prev_node->data)); ut_a(prev_block->n_pointers > 0); prev_block->n_pointers--; block->n_pointers++; } # endif /* !UNIV_HOTBACKUP */ prev_node->block = block; #endif /* UNIV_AHI_DEBUG || UNIV_DEBUG */ prev_node->data = data; return(TRUE); } prev_node = prev_node->next; } /* We have to allocate a new chain node */ node = mem_heap_alloc(hash_get_heap(table, fold), sizeof(ha_node_t)); if (node == NULL) { /* It was a btr search type memory heap and at the moment no more memory could be allocated: return */ ut_ad(hash_get_heap(table, fold)->type & MEM_HEAP_BTR_SEARCH); return(FALSE); } ha_node_set_data(node, block, data); #if defined UNIV_AHI_DEBUG || defined UNIV_DEBUG # ifndef UNIV_HOTBACKUP if (table->adaptive) { block->n_pointers++; } # endif /* !UNIV_HOTBACKUP */ #endif /* UNIV_AHI_DEBUG || UNIV_DEBUG */ node->fold = fold; node->next = NULL; prev_node = cell->node; if (prev_node == NULL) { cell->node = node; return(TRUE); } while (prev_node->next != NULL) { prev_node = prev_node->next; } prev_node->next = node; return(TRUE); } /***********************************************************//** Deletes a hash node. */ UNIV_INTERN void ha_delete_hash_node( /*================*/ hash_table_t* table, /*!< in: hash table */ ha_node_t* del_node) /*!< in: node to be deleted */ { ut_ad(table); ut_ad(table->magic_n == HASH_TABLE_MAGIC_N); #if defined UNIV_AHI_DEBUG || defined UNIV_DEBUG # ifndef UNIV_HOTBACKUP if (table->adaptive) { ut_a(del_node->block->frame = page_align(del_node->data)); ut_a(del_node->block->n_pointers > 0); del_node->block->n_pointers--; } # endif /* !UNIV_HOTBACKUP */ #endif /* UNIV_AHI_DEBUG || UNIV_DEBUG */ HASH_DELETE_AND_COMPACT(ha_node_t, next, table, del_node); } /*********************************************************//** Looks for an element when we know the pointer to the data, and updates the pointer to data, if found. */ UNIV_INTERN void ha_search_and_update_if_found_func( /*===============================*/ hash_table_t* table, /*!< in/out: hash table */ ulint fold, /*!< in: folded value of the searched data */ void* data, /*!< in: pointer to the data */ #if defined UNIV_AHI_DEBUG || defined UNIV_DEBUG buf_block_t* new_block,/*!< in: block containing new_data */ #endif /* UNIV_AHI_DEBUG || UNIV_DEBUG */ void* new_data)/*!< in: new pointer to the data */ { ha_node_t* node; ut_ad(table); ut_ad(table->magic_n == HASH_TABLE_MAGIC_N); ASSERT_HASH_MUTEX_OWN(table, fold); #if defined UNIV_AHI_DEBUG || defined UNIV_DEBUG ut_a(new_block->frame == page_align(new_data)); #endif /* UNIV_AHI_DEBUG || UNIV_DEBUG */ node = ha_search_with_data(table, fold, data); if (node) { #if defined UNIV_AHI_DEBUG || defined UNIV_DEBUG # ifndef UNIV_HOTBACKUP if (table->adaptive) { ut_a(node->block->n_pointers > 0); node->block->n_pointers--; new_block->n_pointers++; } # endif /* !UNIV_HOTBACKUP */ node->block = new_block; #endif /* UNIV_AHI_DEBUG || UNIV_DEBUG */ node->data = new_data; } } #ifndef UNIV_HOTBACKUP /*****************************************************************//** Removes from the chain determined by fold all nodes whose data pointer points to the page given. */ UNIV_INTERN void ha_remove_all_nodes_to_page( /*========================*/ hash_table_t* table, /*!< in: hash table */ ulint fold, /*!< in: fold value */ const page_t* page) /*!< in: buffer page */ { ha_node_t* node; ut_ad(table); ut_ad(table->magic_n == HASH_TABLE_MAGIC_N); ASSERT_HASH_MUTEX_OWN(table, fold); node = ha_chain_get_first(table, fold); while (node) { if (page_align(ha_node_get_data(node)) == page) { /* Remove the hash node */ ha_delete_hash_node(table, node); /* Start again from the first node in the chain because the deletion may compact the heap of nodes and move other nodes! */ node = ha_chain_get_first(table, fold); } else { node = ha_chain_get_next(node); } } #ifdef UNIV_DEBUG /* Check that all nodes really got deleted */ node = ha_chain_get_first(table, fold); while (node) { ut_a(page_align(ha_node_get_data(node)) != page); node = ha_chain_get_next(node); } #endif } /*************************************************************//** Validates a given range of the cells in hash table. @return TRUE if ok */ UNIV_INTERN ibool ha_validate( /*========*/ hash_table_t* table, /*!< in: hash table */ ulint start_index, /*!< in: start index */ ulint end_index) /*!< in: end index */ { hash_cell_t* cell; ha_node_t* node; ibool ok = TRUE; ulint i; ut_ad(table); ut_ad(table->magic_n == HASH_TABLE_MAGIC_N); ut_a(start_index <= end_index); ut_a(start_index < hash_get_n_cells(table)); ut_a(end_index < hash_get_n_cells(table)); for (i = start_index; i <= end_index; i++) { cell = hash_get_nth_cell(table, i); node = cell->node; while (node) { if (hash_calc_hash(node->fold, table) != i) { ut_print_timestamp(ib_stream); ib_logger(ib_stream, "InnoDB: Error: hash table node" " fold value %lu does not\n" "InnoDB: match the cell number %lu.\n", (ulong) node->fold, (ulong) i); ok = FALSE; } node = node->next; } } return(ok); } /*************************************************************//** Prints info of a hash table. */ UNIV_INTERN void ha_print_info( /*==========*/ ib_stream_t ib_stream, /*!< in: file where to print */ hash_table_t* table) /*!< in: hash table */ { #ifdef PRINT_USED_CELLS hash_cell_t* cell; ulint cells = 0; ulint i; #endif /* PRINT_USED_CELLS */ ulint n_bufs; ut_ad(table); ut_ad(table->magic_n == HASH_TABLE_MAGIC_N); #ifdef PRINT_USED_CELLS for (i = 0; i < hash_get_n_cells(table); i++) { cell = hash_get_nth_cell(table, i); if (cell->node) { cells++; } } #endif /* PRINT_USED_CELLS */ ib_logger(ib_stream, "Hash table size %lu", (ulong) hash_get_n_cells(table)); #ifdef PRINT_USED_CELLS ib_logger(ib_stream, ", used cells %lu", (ulong) cells); #endif /* PRINT_USED_CELLS */ if (table->heaps == NULL && table->heap != NULL) { /* This calculation is intended for the adaptive hash index: how many buffer frames we have reserved? */ n_bufs = UT_LIST_GET_LEN(table->heap->base) - 1; if (table->heap->free_block) { n_bufs++; } ib_logger(ib_stream, ", node heap has %lu buffer(s)\n", (ulong) n_bufs); } } #endif /* !UNIV_HOTBACKUP */ haildb-2.3.2/ha/ha0storage.c0000644000175000017500000001156411513177357016443 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 2007, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file ha/ha0storage.c Hash storage. Provides a data structure that stores chunks of data in its own storage, avoiding duplicates. Created September 22, 2007 Vasil Dimov *******************************************************/ #include "univ.i" #include "ha0storage.h" #include "hash0hash.h" #include "mem0mem.h" #include "ut0rnd.h" #ifdef UNIV_NONINL #include "ha0storage.ic" #endif /*******************************************************************//** Retrieves a data from a storage. If it is present, a pointer to the stored copy of data is returned, otherwise NULL is returned. */ static const void* ha_storage_get( /*===========*/ ha_storage_t* storage, /*!< in: hash storage */ const void* data, /*!< in: data to check for */ ulint data_len) /*!< in: data length */ { ha_storage_node_t* node; ulint fold; /* avoid repetitive calls to ut_fold_binary() in the HASH_SEARCH macro */ fold = ut_fold_binary(data, data_len); #define IS_FOUND \ node->data_len == data_len && memcmp(node->data, data, data_len) == 0 HASH_SEARCH( next, /* node->"next" */ storage->hash, /* the hash table */ fold, /* key */ ha_storage_node_t*, /* type of node->next */ node, /* auxiliary variable */ , /* assertion */ IS_FOUND); /* search criteria */ if (node == NULL) { return(NULL); } /* else */ return(node->data); } /*******************************************************************//** Copies data into the storage and returns a pointer to the copy. If the same data chunk is already present, then pointer to it is returned. Data chunks are considered to be equal if len1 == len2 and memcmp(data1, data2, len1) == 0. If "data" is not present (and thus data_len bytes need to be allocated) and the size of storage is going to become more than "memlim" then "data" is not added and NULL is returned. To disable this behavior "memlim" can be set to 0, which stands for "no limit". */ UNIV_INTERN const void* ha_storage_put_memlim( /*==================*/ ha_storage_t* storage, /*!< in/out: hash storage */ const void* data, /*!< in: data to store */ ulint data_len, /*!< in: data length */ ulint memlim) /*!< in: memory limit to obey */ { void* raw; ha_storage_node_t* node; const void* data_copy; ulint fold; /* check if data chunk is already present */ data_copy = ha_storage_get(storage, data, data_len); if (data_copy != NULL) { return(data_copy); } /* not present */ /* check if we are allowed to allocate data_len bytes */ if (memlim > 0 && ha_storage_get_size(storage) + data_len > memlim) { return(NULL); } /* we put the auxiliary node struct and the data itself in one continuous block */ raw = mem_heap_alloc(storage->heap, sizeof(ha_storage_node_t) + data_len); node = (ha_storage_node_t*) raw; data_copy = (byte*) raw + sizeof(*node); memcpy((byte*) raw + sizeof(*node), data, data_len); node->data_len = data_len; node->data = data_copy; /* avoid repetitive calls to ut_fold_binary() in the HASH_INSERT macro */ fold = ut_fold_binary(data, data_len); HASH_INSERT( ha_storage_node_t, /* type used in the hash chain */ next, /* node->"next" */ storage->hash, /* the hash table */ fold, /* key */ node); /* add this data to the hash */ /* the output should not be changed because it will spoil the hash table */ return(data_copy); } #ifdef UNIV_COMPILE_TEST_FUNCS void test_ha_storage() { ha_storage_t* storage; char buf[1024]; int i; const void* stored[256]; const void* p; storage = ha_storage_create(0, 0); for (i = 0; i < 256; i++) { memset(buf, i, sizeof(buf)); stored[i] = ha_storage_put(storage, buf, sizeof(buf)); } //ha_storage_empty(&storage); for (i = 255; i >= 0; i--) { memset(buf, i, sizeof(buf)); p = ha_storage_put(storage, buf, sizeof(buf)); if (p != stored[i]) { ib_logger(ib_stream, "ha_storage_put() returned %p " "instead of %p, i=%d\n", p, stored[i], i); return; } } ib_logger(ib_stream, "all ok\n"); ha_storage_free(storage); } #endif /* UNIV_COMPILE_TEST_FUNCS */ haildb-2.3.2/que/0000755000175000017500000000000011513177437014434 5ustar00pcrewspcrews00000000000000haildb-2.3.2/que/que0que.c0000644000175000017500000010277111513177357016176 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file que/que0que.c Query graph Created 5/27/1996 Heikki Tuuri *******************************************************/ #include "que0que.h" #ifdef UNIV_NONINL #include "que0que.ic" #endif #include "srv0que.h" #include "usr0sess.h" #include "trx0trx.h" #include "trx0roll.h" #include "row0undo.h" #include "row0ins.h" #include "row0upd.h" #include "row0sel.h" #include "row0purge.h" #include "dict0crea.h" #include "log0log.h" #include "eval0proc.h" #include "eval0eval.h" #include "pars0types.h" #define QUE_PARALLELIZE_LIMIT (64 * 256 * 256 * 256) #define QUE_ROUND_ROBIN_LIMIT (64 * 256 * 256 * 256) #define QUE_MAX_LOOPS_WITHOUT_CHECK 16 #ifdef UNIV_DEBUG /* If the following flag is set TRUE, the module will print trace info of SQL execution in the UNIV_SQL_DEBUG version */ UNIV_INTERN ibool que_trace_on = FALSE; #endif /* UNIV_DEBUG */ /* Short introduction to query graphs ================================== A query graph consists of nodes linked to each other in various ways. The execution starts at que_run_threads() which takes a que_thr_t parameter. que_thr_t contains two fields that control query graph execution: run_node and prev_node. run_node is the next node to execute and prev_node is the last node executed. Each node has a pointer to a 'next' statement, i.e., its brother, and a pointer to its parent node. The next pointer is NULL in the last statement of a block. Loop nodes contain a link to the first statement of the enclosed statement list. While the loop runs, que_thr_step() checks if execution to the loop node came from its parent or from one of the statement nodes in the loop. If it came from the parent of the loop node it starts executing the first statement node in the loop. If it came from one of the statement nodes in the loop, then it checks if the statement node has another statement node following it, and runs it if so. To signify loop ending, the loop statements (see e.g. while_step()) set que_thr_t->run_node to the loop node's parent node. This is noticed on the next call of que_thr_step() and execution proceeds to the node pointed to by the loop node's 'next' pointer. For example, the code: X := 1; WHILE X < 5 LOOP X := X + 1; X := X + 1; X := 5 will result in the following node hierarchy, with the X-axis indicating 'next' links and the Y-axis indicating parent/child links: A - W - A | | A - A A = assign_node_t, W = while_node_t. */ /* How a stored procedure containing COMMIT or ROLLBACK commands is executed? The commit or rollback can be seen as a subprocedure call. The problem is that if there are several query threads currently running within the transaction, their action could mess the commit or rollback operation. Or, at the least, the operation would be difficult to visualize and keep in control. Therefore the query thread requesting a commit or a rollback sends to the transaction a signal, which moves the transaction to TRX_QUE_SIGNALED state. All running query threads of the transaction will eventually notice that the transaction is now in this state and voluntarily suspend themselves. Only the last query thread which suspends itself will trigger handling of the signal. When the transaction starts to handle a rollback or commit signal, it builds a query graph which, when executed, will roll back or commit the incomplete transaction. The transaction is moved to the TRX_QUE_ROLLING_BACK or TRX_QUE_COMMITTING state. If specified, the SQL cursors opened by the transaction are closed. When the execution of the graph completes, it is like returning from a subprocedure: the query thread which requested the operation starts running again. */ /*************************************************************************//** Reset the variables. */ UNIV_INTERN void que_var_init(void) /*=============*/ { #ifdef UNIV_DEBUG que_trace_on = FALSE; #endif /* UNIV_DEBUG */ } /***********************************************************************//** Adds a query graph to the session's list of graphs. */ UNIV_INTERN void que_graph_publish( /*==============*/ que_t* graph, /*!< in: graph */ sess_t* sess) /*!< in: session */ { ut_ad(mutex_own(&kernel_mutex)); UT_LIST_ADD_LAST(graphs, sess->graphs, graph); } /***********************************************************************//** Creates a query graph fork node. @return own: fork node */ UNIV_INTERN que_fork_t* que_fork_create( /*============*/ que_t* graph, /*!< in: graph, if NULL then this fork node is assumed to be the graph root */ que_node_t* parent, /*!< in: parent node */ ulint fork_type, /*!< in: fork type */ mem_heap_t* heap) /*!< in: memory heap where created */ { que_fork_t* fork; ut_ad(heap); fork = mem_heap_alloc(heap, sizeof(que_fork_t)); fork->common.type = QUE_NODE_FORK; fork->n_active_thrs = 0; fork->state = QUE_FORK_COMMAND_WAIT; if (graph != NULL) { fork->graph = graph; } else { fork->graph = fork; } fork->common.parent = parent; fork->fork_type = fork_type; fork->caller = NULL; UT_LIST_INIT(fork->thrs); fork->sym_tab = NULL; fork->info = NULL; fork->heap = heap; return(fork); } /***********************************************************************//** Creates a query graph thread node. @return own: query thread node */ UNIV_INTERN que_thr_t* que_thr_create( /*===========*/ que_fork_t* parent, /*!< in: parent node, i.e., a fork node */ mem_heap_t* heap) /*!< in: memory heap where created */ { que_thr_t* thr; ut_ad(parent && heap); thr = mem_heap_alloc(heap, sizeof(que_thr_t)); thr->common.type = QUE_NODE_THR; thr->common.parent = parent; thr->magic_n = QUE_THR_MAGIC_N; thr->graph = parent->graph; thr->state = QUE_THR_COMMAND_WAIT; thr->is_active = FALSE; thr->run_node = NULL; thr->resource = 0; thr->lock_state = QUE_THR_LOCK_NOLOCK; UT_LIST_ADD_LAST(thrs, parent->thrs, thr); return(thr); } /**********************************************************************//** Moves a suspended query thread to the QUE_THR_RUNNING state and may release a single worker thread to execute it. This function should be used to end the wait state of a query thread waiting for a lock or a stored procedure completion. */ UNIV_INTERN void que_thr_end_wait( /*=============*/ que_thr_t* thr, /*!< in: query thread in the QUE_THR_LOCK_WAIT, or QUE_THR_PROCEDURE_WAIT, or QUE_THR_SIG_REPLY_WAIT state */ que_thr_t** next_thr) /*!< in/out: next query thread to run; if the value which is passed in is a pointer to a NULL pointer, then the calling function can start running a new query thread; if NULL is passed as the parameter, it is ignored */ { ibool was_active; ut_ad(mutex_own(&kernel_mutex)); ut_ad(thr); ut_ad((thr->state == QUE_THR_LOCK_WAIT) || (thr->state == QUE_THR_PROCEDURE_WAIT) || (thr->state == QUE_THR_SIG_REPLY_WAIT)); ut_ad(thr->run_node); thr->prev_node = thr->run_node; was_active = thr->is_active; que_thr_move_to_run_state(thr); if (was_active) { return; } if (next_thr && *next_thr == NULL) { *next_thr = thr; } else { ut_a(0); srv_que_task_enqueue_low(thr); } } /**********************************************************************//** Same as que_thr_end_wait, but no parameter next_thr available. */ UNIV_INTERN void que_thr_end_wait_no_next_thr( /*=========================*/ que_thr_t* thr) /*!< in: query thread in the QUE_THR_LOCK_WAIT, or QUE_THR_PROCEDURE_WAIT, or QUE_THR_SIG_REPLY_WAIT state */ { ibool was_active; ut_a(thr->state == QUE_THR_LOCK_WAIT); ut_ad(mutex_own(&kernel_mutex)); ut_ad(thr); ut_ad((thr->state == QUE_THR_LOCK_WAIT) || (thr->state == QUE_THR_PROCEDURE_WAIT) || (thr->state == QUE_THR_SIG_REPLY_WAIT)); was_active = thr->is_active; que_thr_move_to_run_state(thr); if (was_active) { return; } /* We let the OS thread (not just the query thread) to wait for the lock to be released: */ srv_release_user_thread_if_suspended(thr); /* srv_que_task_enqueue_low(thr); */ } /**********************************************************************//** Inits a query thread for a command. */ UNIV_INLINE void que_thr_init_command( /*=================*/ que_thr_t* thr) /*!< in: query thread */ { thr->run_node = thr; thr->prev_node = thr->common.parent; que_thr_move_to_run_state(thr); } /**********************************************************************//** Starts execution of a command in a query fork. Picks a query thread which is not in the QUE_THR_RUNNING state and moves it to that state. If none can be chosen, a situation which may arise in parallelized fetches, NULL is returned. @return a query thread of the graph moved to QUE_THR_RUNNING state, or NULL; the query thread should be executed by que_run_threads by the caller */ UNIV_INTERN que_thr_t* que_fork_start_command( /*===================*/ que_fork_t* fork) /*!< in: a query fork */ { que_thr_t* thr; que_thr_t* suspended_thr = NULL; que_thr_t* completed_thr = NULL; fork->state = QUE_FORK_ACTIVE; fork->last_sel_node = NULL; suspended_thr = NULL; completed_thr = NULL; /* Choose the query thread to run: usually there is just one thread, but in a parallelized select, which necessarily is non-scrollable, there may be several to choose from */ /* First we try to find a query thread in the QUE_THR_COMMAND_WAIT state. Then we try to find a query thread in the QUE_THR_SUSPENDED state, finally we try to find a query thread in the QUE_THR_COMPLETED state */ thr = UT_LIST_GET_FIRST(fork->thrs); /* We make a single pass over the thr list within which we note which threads are ready to run. */ while (thr) { switch (thr->state) { case QUE_THR_COMMAND_WAIT: /* We have to send the initial message to query thread to start it */ que_thr_init_command(thr); return(thr); case QUE_THR_SUSPENDED: /* In this case the execution of the thread was suspended: no initial message is needed because execution can continue from where it was left */ if (!suspended_thr) { suspended_thr = thr; } break; case QUE_THR_COMPLETED: if (!completed_thr) { completed_thr = thr; } break; case QUE_THR_LOCK_WAIT: ut_error; } thr = UT_LIST_GET_NEXT(thrs, thr); } if (suspended_thr) { thr = suspended_thr; que_thr_move_to_run_state(thr); } else if (completed_thr) { thr = completed_thr; que_thr_init_command(thr); } return(thr); } /**********************************************************************//** After signal handling is finished, returns control to a query graph error handling routine. (Currently, just returns the control to the root of the graph so that the graph can communicate an error message to the client.) */ UNIV_INTERN void que_fork_error_handle( /*==================*/ trx_t* trx __attribute__((unused)), /*!< in: trx */ que_t* fork) /*!< in: query graph which was run before signal handling started, NULL not allowed */ { que_thr_t* thr; ut_ad(mutex_own(&kernel_mutex)); ut_ad(trx->sess->state == SESS_ERROR); ut_ad(UT_LIST_GET_LEN(trx->reply_signals) == 0); ut_ad(UT_LIST_GET_LEN(trx->wait_thrs) == 0); thr = UT_LIST_GET_FIRST(fork->thrs); while (thr != NULL) { ut_ad(!thr->is_active); ut_ad(thr->state != QUE_THR_SIG_REPLY_WAIT); ut_ad(thr->state != QUE_THR_LOCK_WAIT); thr->run_node = thr; thr->prev_node = thr->child; thr->state = QUE_THR_COMPLETED; thr = UT_LIST_GET_NEXT(thrs, thr); } thr = UT_LIST_GET_FIRST(fork->thrs); que_thr_move_to_run_state(thr); ut_a(0); srv_que_task_enqueue_low(thr); } /****************************************************************//** Tests if all the query threads in the same fork have a given state. @return TRUE if all the query threads in the same fork were in the given state */ UNIV_INLINE ibool que_fork_all_thrs_in_state( /*=======================*/ que_fork_t* fork, /*!< in: query fork */ ulint state) /*!< in: state */ { que_thr_t* thr_node; thr_node = UT_LIST_GET_FIRST(fork->thrs); while (thr_node != NULL) { if (thr_node->state != state) { return(FALSE); } thr_node = UT_LIST_GET_NEXT(thrs, thr_node); } return(TRUE); } /**********************************************************************//** Calls que_graph_free_recursive for statements in a statement list. */ static void que_graph_free_stat_list( /*=====================*/ que_node_t* node) /*!< in: first query graph node in the list */ { while (node) { que_graph_free_recursive(node); node = que_node_get_next(node); } } /**********************************************************************//** Frees a query graph, but not the heap where it was created. Does not free explicit cursor declarations, they are freed in que_graph_free. */ UNIV_INTERN void que_graph_free_recursive( /*=====================*/ que_node_t* node) /*!< in: query graph node */ { que_fork_t* fork; que_thr_t* thr; undo_node_t* undo; sel_node_t* sel; ins_node_t* ins; upd_node_t* upd; tab_node_t* cre_tab; ind_node_t* cre_ind; purge_node_t* purge; if (node == NULL) { return; } switch (que_node_get_type(node)) { case QUE_NODE_FORK: fork = node; thr = UT_LIST_GET_FIRST(fork->thrs); while (thr) { que_graph_free_recursive(thr); thr = UT_LIST_GET_NEXT(thrs, thr); } break; case QUE_NODE_THR: thr = node; if (thr->magic_n != QUE_THR_MAGIC_N) { ib_logger(ib_stream, "que_thr struct appears corrupt;" " magic n %lu\n", (unsigned long) thr->magic_n); ut_error; } thr->magic_n = QUE_THR_MAGIC_FREED; que_graph_free_recursive(thr->child); break; case QUE_NODE_UNDO: undo = node; mem_heap_free(undo->heap); break; case QUE_NODE_SELECT: sel = node; sel_node_free_private(sel); break; case QUE_NODE_INSERT: ins = node; que_graph_free_recursive(ins->select); mem_heap_free(ins->entry_sys_heap); break; case QUE_NODE_PURGE: purge = node; mem_heap_free(purge->heap); break; case QUE_NODE_UPDATE: upd = node; if (upd->in_client_interface) { btr_pcur_free(upd->pcur); } que_graph_free_recursive(upd->cascade_node); if (upd->cascade_heap) { mem_heap_free(upd->cascade_heap); } que_graph_free_recursive(upd->select); mem_heap_free(upd->heap); break; case QUE_NODE_CREATE_TABLE: cre_tab = node; que_graph_free_recursive(cre_tab->tab_def); que_graph_free_recursive(cre_tab->col_def); que_graph_free_recursive(cre_tab->commit_node); mem_heap_free(cre_tab->heap); break; case QUE_NODE_CREATE_INDEX: cre_ind = node; que_graph_free_recursive(cre_ind->ind_def); que_graph_free_recursive(cre_ind->field_def); que_graph_free_recursive(cre_ind->commit_node); mem_heap_free(cre_ind->heap); break; case QUE_NODE_PROC: que_graph_free_stat_list(((proc_node_t*)node)->stat_list); break; case QUE_NODE_IF: que_graph_free_stat_list(((if_node_t*)node)->stat_list); que_graph_free_stat_list(((if_node_t*)node)->else_part); que_graph_free_stat_list(((if_node_t*)node)->elsif_list); break; case QUE_NODE_ELSIF: que_graph_free_stat_list(((elsif_node_t*)node)->stat_list); break; case QUE_NODE_WHILE: que_graph_free_stat_list(((while_node_t*)node)->stat_list); break; case QUE_NODE_FOR: que_graph_free_stat_list(((for_node_t*)node)->stat_list); break; case QUE_NODE_ASSIGNMENT: case QUE_NODE_EXIT: case QUE_NODE_RETURN: case QUE_NODE_COMMIT: case QUE_NODE_ROLLBACK: case QUE_NODE_LOCK: case QUE_NODE_FUNC: case QUE_NODE_ORDER: case QUE_NODE_ROW_PRINTF: case QUE_NODE_OPEN: case QUE_NODE_FETCH: /* No need to do anything */ break; default: ib_logger(ib_stream, "que_node struct appears corrupt; type %lu\n", (unsigned long) que_node_get_type(node)); ut_error; } } /**********************************************************************//** Frees a query graph. */ UNIV_INTERN void que_graph_free( /*===========*/ que_t* graph) /*!< in: query graph; we assume that the memory heap where this graph was created is private to this graph: if not, then use que_graph_free_recursive and free the heap afterwards! */ { ut_ad(graph); if (graph->sym_tab) { /* The following call frees dynamic memory allocated for variables etc. during execution. Frees also explicit cursor definitions. */ sym_tab_free_private(graph->sym_tab); } if (graph->info && graph->info->graph_owns_us) { pars_info_free(graph->info); } que_graph_free_recursive(graph); mem_heap_free(graph->heap); } /****************************************************************//** Performs an execution step on a thr node. @return query thread to run next, or NULL if none */ static que_thr_t* que_thr_node_step( /*==============*/ que_thr_t* thr) /*!< in: query thread where run_node must be the thread node itself */ { ut_ad(thr->run_node == thr); if (thr->prev_node == thr->common.parent) { /* If control to the node came from above, it is just passed on */ thr->run_node = thr->child; return(thr); } mutex_enter(&kernel_mutex); if (que_thr_peek_stop(thr)) { mutex_exit(&kernel_mutex); return(thr); } /* Thread execution completed */ thr->state = QUE_THR_COMPLETED; mutex_exit(&kernel_mutex); return(NULL); } /**********************************************************************//** Moves a thread from another state to the QUE_THR_RUNNING state. Increments the n_active_thrs counters of the query graph and transaction if thr was not active. ***NOTE***: This is the only functions in which such a transition is allowed to happen! */ UNIV_INTERN void que_thr_move_to_run_state( /*======================*/ que_thr_t* thr) /*!< in: an query thread */ { trx_t* trx; ut_ad(thr->state != QUE_THR_RUNNING); trx = thr_get_trx(thr); if (!thr->is_active) { (thr->graph)->n_active_thrs++; trx->n_active_thrs++; thr->is_active = TRUE; ut_ad((thr->graph)->n_active_thrs == 1); ut_ad(trx->n_active_thrs == 1); } thr->state = QUE_THR_RUNNING; } /**********************************************************************//** Decrements the query thread reference counts in the query graph and the transaction. May start signal handling, e.g., a rollback. *** NOTE ***: This and que_thr_stop_client are the only functions where the reference count can be decremented and this function may only be called from inside que_run_threads or que_thr_check_if_switch! These restrictions exist to make the rollback code easier to maintain. */ static void que_thr_dec_refer_count( /*====================*/ que_thr_t* thr, /*!< in: query thread */ que_thr_t** next_thr) /*!< in/out: next query thread to run; if the value which is passed in is a pointer to a NULL pointer, then the calling function can start running a new query thread */ { que_fork_t* fork; trx_t* trx; ulint fork_type; ibool stopped; fork = thr->common.parent; trx = thr_get_trx(thr); mutex_enter(&kernel_mutex); ut_a(thr->is_active); if (thr->state == QUE_THR_RUNNING) { stopped = que_thr_stop(thr); if (!stopped) { /* The reason for the thr suspension or wait was already canceled before we came here: continue running the thread */ /* ib_logger(ib_stream, "!!!!!!!! Wait already ended: continue thr\n"); */ if (next_thr && *next_thr == NULL) { /* Normally srv_suspend_user_thread resets the state to DB_SUCCESS before waiting, but in this case we have to do it here, otherwise nobody does it. */ trx->error_state = DB_SUCCESS; *next_thr = thr; } else { ut_error; srv_que_task_enqueue_low(thr); } mutex_exit(&kernel_mutex); return; } } ut_ad(fork->n_active_thrs == 1); ut_ad(trx->n_active_thrs == 1); fork->n_active_thrs--; trx->n_active_thrs--; thr->is_active = FALSE; if (trx->n_active_thrs > 0) { mutex_exit(&kernel_mutex); return; } fork_type = fork->fork_type; /* Check if all query threads in the same fork are completed */ if (que_fork_all_thrs_in_state(fork, QUE_THR_COMPLETED)) { switch (fork_type) { case QUE_FORK_ROLLBACK: /* This is really the undo graph used in rollback, no roll_node in this graph */ ut_ad(UT_LIST_GET_LEN(trx->signals) > 0); ut_ad(trx->handling_signals == TRUE); trx_finish_rollback_off_kernel(fork, trx, next_thr); break; case QUE_FORK_PURGE: case QUE_FORK_RECOVERY: case QUE_FORK_USER_INTERFACE: /* Do nothing */ break; default: ut_error; /* not used */ } } if (UT_LIST_GET_LEN(trx->signals) > 0 && trx->n_active_thrs == 0) { /* If the trx is signaled and its query thread count drops to zero, then we start processing a signal; from it we may get a new query thread to run */ trx_sig_start_handle(trx, next_thr); } if (trx->handling_signals && UT_LIST_GET_LEN(trx->signals) == 0) { trx_end_signal_handling(trx); } mutex_exit(&kernel_mutex); } /**********************************************************************//** Stops a query thread if graph or trx is in a state requiring it. The conditions are tested in the order (1) graph, (2) trx. The kernel mutex has to be reserved. @return TRUE if stopped */ UNIV_INTERN ibool que_thr_stop( /*=========*/ que_thr_t* thr) /*!< in: query thread */ { trx_t* trx; que_t* graph; ibool ret = TRUE; ut_ad(mutex_own(&kernel_mutex)); graph = thr->graph; trx = graph->trx; if (graph->state == QUE_FORK_COMMAND_WAIT) { thr->state = QUE_THR_SUSPENDED; } else if (trx->que_state == TRX_QUE_LOCK_WAIT) { UT_LIST_ADD_FIRST(trx_thrs, trx->wait_thrs, thr); thr->state = QUE_THR_LOCK_WAIT; } else if (trx->error_state != DB_SUCCESS && trx->error_state != DB_LOCK_WAIT) { thr->state = QUE_THR_COMPLETED; } else if (UT_LIST_GET_LEN(trx->signals) > 0 && graph->fork_type != QUE_FORK_ROLLBACK) { thr->state = QUE_THR_SUSPENDED; } else { ut_ad(graph->state == QUE_FORK_ACTIVE); ret = FALSE; } return(ret); } /**********************************************************************//** A patch for a client used to 'stop' a dummy query thread used in client select, when there is no error or lock wait. TODO: Currently only called from row0merge, needs to be removed. */ UNIV_INTERN void que_thr_stop_for_client_no_error( /*=============================*/ que_thr_t* thr, /*!< in: query thread */ trx_t* trx) /*!< in: transaction */ { ut_ad(thr->state == QUE_THR_RUNNING); ut_ad(thr->is_active == TRUE); ut_ad(trx->n_active_thrs == 1); ut_ad(thr->graph->n_active_thrs == 1); if (thr->magic_n != QUE_THR_MAGIC_N) { ib_logger(ib_stream, "que_thr struct appears corrupt; magic n %lu\n", (unsigned long) thr->magic_n); ut_error; } thr->state = QUE_THR_COMPLETED; thr->is_active = FALSE; (thr->graph)->n_active_thrs--; trx->n_active_thrs--; } /**********************************************************************//** Moves a thread from another state to the QUE_THR_RUNNING state. Increments the n_active_thrs counters of the query graph and transaction if thr was not active. */ UNIV_INTERN void que_thr_move_to_run_state_for_client( /*=================================*/ que_thr_t* thr, /*!< in: an query thread */ trx_t* trx) /*!< in: transaction */ { if (thr->magic_n != QUE_THR_MAGIC_N) { ib_logger(ib_stream, "que_thr struct appears corrupt; magic n %lu\n", (unsigned long) thr->magic_n); ut_error; } else if (!thr->is_active) { thr->graph->n_active_thrs++; trx->n_active_thrs++; thr->is_active = TRUE; } thr->state = QUE_THR_RUNNING; } /**********************************************************************//** The query thread is stopped and made inactive, except in the case where it was put to the lock wait state in lock0lock.c, but the lock has already been granted or the transaction chosen as a victim in deadlock resolution. */ UNIV_INTERN void que_thr_stop_client( /*================*/ que_thr_t* thr) /*!< in: query thread */ { trx_t* trx; trx = thr_get_trx(thr); mutex_enter(&kernel_mutex); if (thr->state == QUE_THR_RUNNING) { if (trx->error_state != DB_SUCCESS && trx->error_state != DB_LOCK_WAIT) { thr->state = QUE_THR_COMPLETED; } else { /* It must have been a lock wait but the lock was already released, or this transaction was chosen as a victim in selective deadlock resolution */ mutex_exit(&kernel_mutex); return; } } ut_ad(thr->is_active == TRUE); ut_ad(trx->n_active_thrs == 1); ut_ad(thr->graph->n_active_thrs == 1); thr->is_active = FALSE; (thr->graph)->n_active_thrs--; trx->n_active_thrs--; mutex_exit(&kernel_mutex); } /****************************************************************//** Get the first containing loop node (e.g. while_node_t or for_node_t) for the given node, or NULL if the node is not within a loop. @return containing loop node, or NULL. */ UNIV_INTERN que_node_t* que_node_get_containing_loop_node( /*==============================*/ que_node_t* node) /*!< in: node */ { ut_ad(node); for (;;) { ulint type; node = que_node_get_parent(node); if (!node) { break; } type = que_node_get_type(node); if ((type == QUE_NODE_FOR) || (type == QUE_NODE_WHILE)) { break; } } return(node); } /**********************************************************************//** Prints info of an SQL query graph node. */ UNIV_INTERN void que_node_print_info( /*================*/ que_node_t* node) /*!< in: query graph node */ { ulint type; const char* str; type = que_node_get_type(node); if (type == QUE_NODE_SELECT) { str = "SELECT"; } else if (type == QUE_NODE_INSERT) { str = "INSERT"; } else if (type == QUE_NODE_UPDATE) { str = "UPDATE"; } else if (type == QUE_NODE_WHILE) { str = "WHILE"; } else if (type == QUE_NODE_ASSIGNMENT) { str = "ASSIGNMENT"; } else if (type == QUE_NODE_IF) { str = "IF"; } else if (type == QUE_NODE_FETCH) { str = "FETCH"; } else if (type == QUE_NODE_OPEN) { str = "OPEN"; } else if (type == QUE_NODE_PROC) { str = "STORED PROCEDURE"; } else if (type == QUE_NODE_FUNC) { str = "FUNCTION"; } else if (type == QUE_NODE_LOCK) { str = "LOCK"; } else if (type == QUE_NODE_THR) { str = "QUERY THREAD"; } else if (type == QUE_NODE_COMMIT) { str = "COMMIT"; } else if (type == QUE_NODE_UNDO) { str = "UNDO ROW"; } else if (type == QUE_NODE_PURGE) { str = "PURGE ROW"; } else if (type == QUE_NODE_ROLLBACK) { str = "ROLLBACK"; } else if (type == QUE_NODE_CREATE_TABLE) { str = "CREATE TABLE"; } else if (type == QUE_NODE_CREATE_INDEX) { str = "CREATE INDEX"; } else if (type == QUE_NODE_FOR) { str = "FOR LOOP"; } else if (type == QUE_NODE_RETURN) { str = "RETURN"; } else if (type == QUE_NODE_EXIT) { str = "EXIT"; } else { str = "UNKNOWN NODE TYPE"; } ib_logger(ib_stream, "Node type %lu: %s, address %p\n", (ulong) type, str, (void*) node); } /**********************************************************************//** Performs an execution step on a query thread. @return query thread to run next: it may differ from the input parameter if, e.g., a subprocedure call is made */ UNIV_INLINE que_thr_t* que_thr_step( /*=========*/ que_thr_t* thr) /*!< in: query thread */ { que_node_t* node; que_thr_t* old_thr; trx_t* trx; ulint type; trx = thr_get_trx(thr); ut_ad(thr->state == QUE_THR_RUNNING); ut_a(trx->error_state == DB_SUCCESS); thr->resource++; node = thr->run_node; type = que_node_get_type(node); old_thr = thr; #ifdef UNIV_DEBUG if (que_trace_on) { ib_logger(ib_stream, "To execute: "); que_node_print_info(node); } #endif if (type & QUE_NODE_CONTROL_STAT) { if ((thr->prev_node != que_node_get_parent(node)) && que_node_get_next(thr->prev_node)) { /* The control statements, like WHILE, always pass the control to the next child statement if there is any child left */ thr->run_node = que_node_get_next(thr->prev_node); } else if (type == QUE_NODE_IF) { if_step(thr); } else if (type == QUE_NODE_FOR) { for_step(thr); } else if (type == QUE_NODE_PROC) { /* We can access trx->undo_no without reserving trx->undo_mutex, because there cannot be active query threads doing updating or inserting at the moment! */ if (thr->prev_node == que_node_get_parent(node)) { trx->last_sql_stat_start.least_undo_no = trx->undo_no; } proc_step(thr); } else if (type == QUE_NODE_WHILE) { while_step(thr); } else { ut_error; } } else if (type == QUE_NODE_ASSIGNMENT) { assign_step(thr); } else if (type == QUE_NODE_SELECT) { thr = row_sel_step(thr); } else if (type == QUE_NODE_INSERT) { thr = row_ins_step(thr); } else if (type == QUE_NODE_UPDATE) { thr = row_upd_step(thr); } else if (type == QUE_NODE_FETCH) { thr = fetch_step(thr); } else if (type == QUE_NODE_OPEN) { thr = open_step(thr); } else if (type == QUE_NODE_FUNC) { proc_eval_step(thr); } else if (type == QUE_NODE_LOCK) { ut_error; /* thr = que_lock_step(thr); */ } else if (type == QUE_NODE_THR) { thr = que_thr_node_step(thr); } else if (type == QUE_NODE_COMMIT) { thr = trx_commit_step(thr); } else if (type == QUE_NODE_UNDO) { thr = row_undo_step(thr); } else if (type == QUE_NODE_PURGE) { thr = row_purge_step(thr); } else if (type == QUE_NODE_RETURN) { thr = return_step(thr); } else if (type == QUE_NODE_EXIT) { thr = exit_step(thr); } else if (type == QUE_NODE_ROLLBACK) { thr = trx_rollback_step(thr); } else if (type == QUE_NODE_CREATE_TABLE) { thr = dict_create_table_step(thr); } else if (type == QUE_NODE_CREATE_INDEX) { thr = dict_create_index_step(thr); } else if (type == QUE_NODE_ROW_PRINTF) { thr = row_printf_step(thr); } else { ut_error; } if (type == QUE_NODE_EXIT) { old_thr->prev_node = que_node_get_containing_loop_node(node); } else { old_thr->prev_node = node; } if (thr) { ut_a(thr_get_trx(thr)->error_state == DB_SUCCESS); } return(thr); } /**********************************************************************//** Run a query thread until it finishes or encounters e.g. a lock wait. */ static void que_run_threads_low( /*================*/ que_thr_t* thr) /*!< in: query thread */ { que_thr_t* next_thr; ulint cumul_resource; ulint loop_count; ut_ad(thr->state == QUE_THR_RUNNING); ut_a(thr_get_trx(thr)->error_state == DB_SUCCESS); ut_ad(!mutex_own(&kernel_mutex)); /* cumul_resource counts how much resources the OS thread (NOT the query thread) has spent in this function */ loop_count = QUE_MAX_LOOPS_WITHOUT_CHECK; cumul_resource = 0; loop: /* Check that there is enough space in the log to accommodate possible log entries by this query step; if the operation can touch more than about 4 pages, checks must be made also within the query step! */ log_free_check(); /* Perform the actual query step: note that the query thread may change if, e.g., a subprocedure call is made */ /*-------------------------*/ next_thr = que_thr_step(thr); /*-------------------------*/ ut_a(!next_thr || (thr_get_trx(next_thr)->error_state == DB_SUCCESS)); loop_count++; if (next_thr != thr) { ut_a(next_thr == NULL); /* This can change next_thr to a non-NULL value if there was a lock wait that already completed. */ que_thr_dec_refer_count(thr, &next_thr); if (next_thr == NULL) { return; } loop_count = QUE_MAX_LOOPS_WITHOUT_CHECK; thr = next_thr; } goto loop; } /**********************************************************************//** Run a query thread. Handles lock waits. */ UNIV_INTERN void que_run_threads( /*============*/ que_thr_t* thr) /*!< in: query thread */ { loop: ut_a(thr_get_trx(thr)->error_state == DB_SUCCESS); que_run_threads_low(thr); mutex_enter(&kernel_mutex); switch (thr->state) { case QUE_THR_RUNNING: /* There probably was a lock wait, but it already ended before we came here: continue running thr */ mutex_exit(&kernel_mutex); goto loop; case QUE_THR_LOCK_WAIT: mutex_exit(&kernel_mutex); /* The ..._user_... function works also for InnoDB's internal threads. Let us wait that the lock wait ends. */ srv_suspend_user_thread(thr); if (thr_get_trx(thr)->error_state != DB_SUCCESS) { /* thr was chosen as a deadlock victim or there was a lock wait timeout */ que_thr_dec_refer_count(thr, NULL); return; } goto loop; case QUE_THR_COMPLETED: case QUE_THR_COMMAND_WAIT: /* Do nothing */ break; default: ut_error; } mutex_exit(&kernel_mutex); } /*********************************************************************//** Evaluate the given SQL. @return error code or DB_SUCCESS */ UNIV_INTERN ulint que_eval_sql( /*=========*/ pars_info_t* info, /*!< in: info struct, or NULL */ const char* sql, /*!< in: SQL string */ ibool reserve_dict_mutex, /*!< in: if TRUE, acquire/release dict_sys->mutex around call to pars_sql. */ trx_t* trx) /*!< in: trx */ { que_thr_t* thr; que_t* graph; ut_a(trx->error_state == DB_SUCCESS); if (reserve_dict_mutex) { mutex_enter(&dict_sys->mutex); } graph = pars_sql(info, sql); if (reserve_dict_mutex) { mutex_exit(&dict_sys->mutex); } ut_a(graph); graph->trx = trx; trx->graph = NULL; graph->fork_type = QUE_FORK_USER_INTERFACE; ut_a(thr = que_fork_start_command(graph)); que_run_threads(thr); que_graph_free(graph); return(trx->error_state); } haildb-2.3.2/Makefile.in0000644000175000017500000061521211513177417015714 0ustar00pcrewspcrews00000000000000# Makefile.in generated by automake 1.11.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, # 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, # Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ # Copyright (C) 2008, 2010 Oracle/Innobase Oy # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; version 2 of the License. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA VPATH = @srcdir@ pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ target_triplet = @target@ @WITH_ZIP_TRUE@am__append_1 = \ @WITH_ZIP_TRUE@ include/buf0buddy.h include/buf0buddy.ic \ @WITH_ZIP_TRUE@ include/page0zip.h include/page0zip.ic @WITH_ZIP_TRUE@am__append_2 = buf/buf0buddy.c page/page0zip.c check_PROGRAMS = $(am__EXEEXT_1) subdir = . DIST_COMMON = README $(am__configure_deps) $(am__noinst_HEADERS_DIST) \ $(include_HEADERS) $(srcdir)/Makefile.am $(srcdir)/Makefile.in \ $(srcdir)/config.h.in $(srcdir)/haildb.spec.in \ $(top_srcdir)/configure COPYING ChangeLog config/compile \ config/config.guess config/config.rpath config/config.sub \ config/depcomp config/install-sh config/ltmain.sh \ config/missing config/ylwrap pars/pars0grm.c ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/lib-prefix.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/m4/pandora_64bit.m4 \ $(top_srcdir)/m4/pandora_bison.m4 \ $(top_srcdir)/m4/pandora_canonical.m4 \ $(top_srcdir)/m4/pandora_check_compiler_version.m4 \ $(top_srcdir)/m4/pandora_check_cxx_standard.m4 \ $(top_srcdir)/m4/pandora_cinttypes.m4 \ $(top_srcdir)/m4/pandora_clock_gettime.m4 \ $(top_srcdir)/m4/pandora_cstdint.m4 \ $(top_srcdir)/m4/pandora_enable_dtrace.m4 \ $(top_srcdir)/m4/pandora_ensure_gcc_version.m4 \ $(top_srcdir)/m4/pandora_extensions.m4 \ $(top_srcdir)/m4/pandora_have_better_malloc.m4 \ $(top_srcdir)/m4/pandora_have_gcc_atomics.m4 \ $(top_srcdir)/m4/pandora_header_assert.m4 \ $(top_srcdir)/m4/pandora_header_stdcxx_98.m4 \ $(top_srcdir)/m4/pandora_libtool.m4 \ $(top_srcdir)/m4/pandora_optimize.m4 \ $(top_srcdir)/m4/pandora_platform.m4 \ $(top_srcdir)/m4/pandora_use_pipe.m4 \ $(top_srcdir)/m4/pandora_vc_build.m4 \ $(top_srcdir)/m4/pandora_version.m4 \ $(top_srcdir)/m4/pandora_visibility.m4 \ $(top_srcdir)/m4/pandora_warnings.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \ configure.lineno config.status.lineno mkinstalldirs = $(install_sh) -d CONFIG_HEADER = config.h CONFIG_CLEAN_FILES = haildb.spec CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(includedir)" LTLIBRARIES = $(lib_LTLIBRARIES) libhaildb_la_LIBADD = am__libhaildb_la_SOURCES_DIST = btr/btr0btr.c btr/btr0cur.c \ btr/btr0pcur.c btr/btr0sea.c buf/buf0buf.c buf/buf0flu.c \ buf/buf0lru.c buf/buf0rea.c data/data0data.c data/data0type.c \ dict/dict0boot.c dict/dict0crea.c dict/dict0dict.c \ dict/dict0load.c dict/dict0mem.c dyn/dyn0dyn.c \ eval/eval0eval.c eval/eval0proc.c fil/fil0fil.c fsp/fsp0fsp.c \ fut/fut0fut.c fut/fut0lst.c ha/ha0ha.c ha/ha0storage.c \ ha/hash0hash.c ibuf/ibuf0ibuf.c lock/lock0iter.c \ lock/lock0lock.c log/log0log.c log/log0recv.c mach/mach0data.c \ mem/mem0mem.c mtr/mtr0log.c mtr/mtr0mtr.c os/os0file.c \ os/os0proc.c os/os0sync.c os/os0thread.c page/page0cur.c \ page/page0page.c pars/lexyy.c pars/pars0grm.y pars/pars0opt.c \ pars/pars0pars.c pars/pars0sym.c que/que0que.c \ read/read0read.c rem/rem0cmp.c rem/rem0rec.c row/row0ext.c \ row/row0ins.c row/row0merge.c row/row0purge.c row/row0row.c \ row/row0sel.c row/row0uins.c row/row0umod.c row/row0undo.c \ row/row0upd.c row/row0vers.c row/row0prebuilt.c srv/srv0que.c \ srv/srv0srv.c srv/srv0start.c sync/sync0arr.c sync/sync0rw.c \ sync/sync0sync.c thr/thr0loc.c trx/trx0purge.c trx/trx0rec.c \ trx/trx0roll.c trx/trx0rseg.c trx/trx0sys.c trx/trx0trx.c \ trx/trx0undo.c usr/usr0sess.c ut/ut0byte.c ut/ut0dbg.c \ ut/ut0list.c ut/ut0mem.c ut/ut0rnd.c ut/ut0ut.c ut/ut0vec.c \ ut/ut0rbt.c api/api0api.c api/api0ucode.c ddl/ddl0ddl.c \ api/api0misc.c api/api0cfg.c api/api0status.c api/api0sql.c \ buf/buf0buddy.c page/page0zip.c am__dirstamp = $(am__leading_dot)dirstamp @WITH_ZIP_TRUE@am__objects_1 = buf/libhaildb_la-buf0buddy.lo \ @WITH_ZIP_TRUE@ page/libhaildb_la-page0zip.lo am_libhaildb_la_OBJECTS = btr/libhaildb_la-btr0btr.lo \ btr/libhaildb_la-btr0cur.lo btr/libhaildb_la-btr0pcur.lo \ btr/libhaildb_la-btr0sea.lo buf/libhaildb_la-buf0buf.lo \ buf/libhaildb_la-buf0flu.lo buf/libhaildb_la-buf0lru.lo \ buf/libhaildb_la-buf0rea.lo data/libhaildb_la-data0data.lo \ data/libhaildb_la-data0type.lo dict/libhaildb_la-dict0boot.lo \ dict/libhaildb_la-dict0crea.lo dict/libhaildb_la-dict0dict.lo \ dict/libhaildb_la-dict0load.lo dict/libhaildb_la-dict0mem.lo \ dyn/libhaildb_la-dyn0dyn.lo eval/libhaildb_la-eval0eval.lo \ eval/libhaildb_la-eval0proc.lo fil/libhaildb_la-fil0fil.lo \ fsp/libhaildb_la-fsp0fsp.lo fut/libhaildb_la-fut0fut.lo \ fut/libhaildb_la-fut0lst.lo ha/libhaildb_la-ha0ha.lo \ ha/libhaildb_la-ha0storage.lo ha/libhaildb_la-hash0hash.lo \ ibuf/libhaildb_la-ibuf0ibuf.lo lock/libhaildb_la-lock0iter.lo \ lock/libhaildb_la-lock0lock.lo log/libhaildb_la-log0log.lo \ log/libhaildb_la-log0recv.lo mach/libhaildb_la-mach0data.lo \ mem/libhaildb_la-mem0mem.lo mtr/libhaildb_la-mtr0log.lo \ mtr/libhaildb_la-mtr0mtr.lo os/libhaildb_la-os0file.lo \ os/libhaildb_la-os0proc.lo os/libhaildb_la-os0sync.lo \ os/libhaildb_la-os0thread.lo page/libhaildb_la-page0cur.lo \ page/libhaildb_la-page0page.lo pars/libhaildb_la-lexyy.lo \ pars/libhaildb_la-pars0grm.lo pars/libhaildb_la-pars0opt.lo \ pars/libhaildb_la-pars0pars.lo pars/libhaildb_la-pars0sym.lo \ que/libhaildb_la-que0que.lo read/libhaildb_la-read0read.lo \ rem/libhaildb_la-rem0cmp.lo rem/libhaildb_la-rem0rec.lo \ row/libhaildb_la-row0ext.lo row/libhaildb_la-row0ins.lo \ row/libhaildb_la-row0merge.lo row/libhaildb_la-row0purge.lo \ row/libhaildb_la-row0row.lo row/libhaildb_la-row0sel.lo \ row/libhaildb_la-row0uins.lo row/libhaildb_la-row0umod.lo \ row/libhaildb_la-row0undo.lo row/libhaildb_la-row0upd.lo \ row/libhaildb_la-row0vers.lo row/libhaildb_la-row0prebuilt.lo \ srv/libhaildb_la-srv0que.lo srv/libhaildb_la-srv0srv.lo \ srv/libhaildb_la-srv0start.lo sync/libhaildb_la-sync0arr.lo \ sync/libhaildb_la-sync0rw.lo sync/libhaildb_la-sync0sync.lo \ thr/libhaildb_la-thr0loc.lo trx/libhaildb_la-trx0purge.lo \ trx/libhaildb_la-trx0rec.lo trx/libhaildb_la-trx0roll.lo \ trx/libhaildb_la-trx0rseg.lo trx/libhaildb_la-trx0sys.lo \ trx/libhaildb_la-trx0trx.lo trx/libhaildb_la-trx0undo.lo \ usr/libhaildb_la-usr0sess.lo ut/libhaildb_la-ut0byte.lo \ ut/libhaildb_la-ut0dbg.lo ut/libhaildb_la-ut0list.lo \ ut/libhaildb_la-ut0mem.lo ut/libhaildb_la-ut0rnd.lo \ ut/libhaildb_la-ut0ut.lo ut/libhaildb_la-ut0vec.lo \ ut/libhaildb_la-ut0rbt.lo api/libhaildb_la-api0api.lo \ api/libhaildb_la-api0ucode.lo ddl/libhaildb_la-ddl0ddl.lo \ api/libhaildb_la-api0misc.lo api/libhaildb_la-api0cfg.lo \ api/libhaildb_la-api0status.lo api/libhaildb_la-api0sql.lo \ $(am__objects_1) libhaildb_la_OBJECTS = $(am_libhaildb_la_OBJECTS) AM_V_lt = $(am__v_lt_$(V)) am__v_lt_ = $(am__v_lt_$(AM_DEFAULT_VERBOSITY)) am__v_lt_0 = --silent libhaildb_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(libhaildb_la_CFLAGS) \ $(CFLAGS) $(libhaildb_la_LDFLAGS) $(LDFLAGS) -o $@ libtest0aux_la_LIBADD = am_libtest0aux_la_OBJECTS = tests/test0aux.lo libtest0aux_la_OBJECTS = $(am_libtest0aux_la_OBJECTS) am__EXEEXT_1 = tests/bug579934_open_index_by_name_segv$(EXEEXT) \ tests/ib_cfg$(EXEEXT) tests/ib_cursor$(EXEEXT) \ tests/ib_ddl$(EXEEXT) tests/ib_dict$(EXEEXT) \ tests/ib_drop$(EXEEXT) tests/ib_duplicate_key_name$(EXEEXT) \ tests/ib_logger$(EXEEXT) tests/ib_mt_drv$(EXEEXT) \ tests/ib_shutdown$(EXEEXT) tests/ib_status$(EXEEXT) \ tests/ib_panic$(EXEEXT) tests/ib_trx_is_interrupted$(EXEEXT) \ tests/ib_tablename$(EXEEXT) tests/ib_table_statistics$(EXEEXT) \ tests/ib_test1$(EXEEXT) tests/ib_client_compare$(EXEEXT) \ tests/ib_test2$(EXEEXT) tests/ib_test3$(EXEEXT) \ tests/ib_test5$(EXEEXT) tests/ib_types$(EXEEXT) \ tests/ib_update$(EXEEXT) tests_bug579934_open_index_by_name_segv_SOURCES = \ tests/bug579934_open_index_by_name_segv.c tests_bug579934_open_index_by_name_segv_OBJECTS = \ tests/bug579934_open_index_by_name_segv.$(OBJEXT) tests_bug579934_open_index_by_name_segv_LDADD = $(LDADD) tests_bug579934_open_index_by_name_segv_DEPENDENCIES = libhaildb.la \ libtest0aux.la tests_ib_cfg_SOURCES = tests/ib_cfg.c tests_ib_cfg_OBJECTS = tests/ib_cfg.$(OBJEXT) tests_ib_cfg_LDADD = $(LDADD) tests_ib_cfg_DEPENDENCIES = libhaildb.la libtest0aux.la tests_ib_client_compare_SOURCES = tests/ib_client_compare.c tests_ib_client_compare_OBJECTS = tests/ib_client_compare.$(OBJEXT) tests_ib_client_compare_LDADD = $(LDADD) tests_ib_client_compare_DEPENDENCIES = libhaildb.la libtest0aux.la tests_ib_cursor_SOURCES = tests/ib_cursor.c tests_ib_cursor_OBJECTS = tests/ib_cursor.$(OBJEXT) tests_ib_cursor_LDADD = $(LDADD) tests_ib_cursor_DEPENDENCIES = libhaildb.la libtest0aux.la tests_ib_ddl_SOURCES = tests/ib_ddl.c tests_ib_ddl_OBJECTS = tests/ib_ddl.$(OBJEXT) tests_ib_ddl_LDADD = $(LDADD) tests_ib_ddl_DEPENDENCIES = libhaildb.la libtest0aux.la tests_ib_dict_SOURCES = tests/ib_dict.c tests_ib_dict_OBJECTS = tests/ib_dict.$(OBJEXT) tests_ib_dict_LDADD = $(LDADD) tests_ib_dict_DEPENDENCIES = libhaildb.la libtest0aux.la tests_ib_drop_SOURCES = tests/ib_drop.c tests_ib_drop_OBJECTS = tests/ib_drop.$(OBJEXT) tests_ib_drop_LDADD = $(LDADD) tests_ib_drop_DEPENDENCIES = libhaildb.la libtest0aux.la tests_ib_duplicate_key_name_SOURCES = tests/ib_duplicate_key_name.c tests_ib_duplicate_key_name_OBJECTS = \ tests/ib_duplicate_key_name.$(OBJEXT) tests_ib_duplicate_key_name_LDADD = $(LDADD) tests_ib_duplicate_key_name_DEPENDENCIES = libhaildb.la libtest0aux.la tests_ib_logger_SOURCES = tests/ib_logger.c tests_ib_logger_OBJECTS = tests/ib_logger.$(OBJEXT) tests_ib_logger_LDADD = $(LDADD) tests_ib_logger_DEPENDENCIES = libhaildb.la libtest0aux.la am_tests_ib_mt_drv_OBJECTS = tests/ib_mt_base.$(OBJEXT) \ tests/ib_mt_drv.$(OBJEXT) tests/ib_mt_t1.$(OBJEXT) \ tests/ib_mt_t2.$(OBJEXT) tests_ib_mt_drv_OBJECTS = $(am_tests_ib_mt_drv_OBJECTS) tests_ib_mt_drv_LDADD = $(LDADD) tests_ib_mt_drv_DEPENDENCIES = libhaildb.la libtest0aux.la tests_ib_panic_SOURCES = tests/ib_panic.c tests_ib_panic_OBJECTS = tests/ib_panic.$(OBJEXT) tests_ib_panic_LDADD = $(LDADD) tests_ib_panic_DEPENDENCIES = libhaildb.la libtest0aux.la tests_ib_shutdown_SOURCES = tests/ib_shutdown.c tests_ib_shutdown_OBJECTS = tests/ib_shutdown.$(OBJEXT) tests_ib_shutdown_LDADD = $(LDADD) tests_ib_shutdown_DEPENDENCIES = libhaildb.la libtest0aux.la tests_ib_status_SOURCES = tests/ib_status.c tests_ib_status_OBJECTS = tests/ib_status.$(OBJEXT) tests_ib_status_LDADD = $(LDADD) tests_ib_status_DEPENDENCIES = libhaildb.la libtest0aux.la tests_ib_table_statistics_SOURCES = tests/ib_table_statistics.c tests_ib_table_statistics_OBJECTS = \ tests/ib_table_statistics.$(OBJEXT) tests_ib_table_statistics_LDADD = $(LDADD) tests_ib_table_statistics_DEPENDENCIES = libhaildb.la libtest0aux.la tests_ib_tablename_SOURCES = tests/ib_tablename.c tests_ib_tablename_OBJECTS = tests/ib_tablename.$(OBJEXT) tests_ib_tablename_LDADD = $(LDADD) tests_ib_tablename_DEPENDENCIES = libhaildb.la libtest0aux.la tests_ib_test1_SOURCES = tests/ib_test1.c tests_ib_test1_OBJECTS = tests/ib_test1.$(OBJEXT) tests_ib_test1_LDADD = $(LDADD) tests_ib_test1_DEPENDENCIES = libhaildb.la libtest0aux.la tests_ib_test2_SOURCES = tests/ib_test2.c tests_ib_test2_OBJECTS = tests/ib_test2.$(OBJEXT) tests_ib_test2_LDADD = $(LDADD) tests_ib_test2_DEPENDENCIES = libhaildb.la libtest0aux.la tests_ib_test3_SOURCES = tests/ib_test3.c tests_ib_test3_OBJECTS = tests/ib_test3.$(OBJEXT) tests_ib_test3_LDADD = $(LDADD) tests_ib_test3_DEPENDENCIES = libhaildb.la libtest0aux.la tests_ib_test5_SOURCES = tests/ib_test5.c tests_ib_test5_OBJECTS = tests/ib_test5.$(OBJEXT) tests_ib_test5_LDADD = $(LDADD) tests_ib_test5_DEPENDENCIES = libhaildb.la libtest0aux.la tests_ib_trx_is_interrupted_SOURCES = tests/ib_trx_is_interrupted.c tests_ib_trx_is_interrupted_OBJECTS = \ tests/ib_trx_is_interrupted.$(OBJEXT) tests_ib_trx_is_interrupted_LDADD = $(LDADD) tests_ib_trx_is_interrupted_DEPENDENCIES = libhaildb.la libtest0aux.la tests_ib_types_SOURCES = tests/ib_types.c tests_ib_types_OBJECTS = tests/ib_types.$(OBJEXT) tests_ib_types_LDADD = $(LDADD) tests_ib_types_DEPENDENCIES = libhaildb.la libtest0aux.la tests_ib_update_SOURCES = tests/ib_update.c tests_ib_update_OBJECTS = tests/ib_update.$(OBJEXT) tests_ib_update_LDADD = $(LDADD) tests_ib_update_DEPENDENCIES = libhaildb.la libtest0aux.la DEFAULT_INCLUDES = depcomp = $(SHELL) $(top_srcdir)/config/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_$(V)) am__v_CC_ = $(am__v_CC_$(AM_DEFAULT_VERBOSITY)) am__v_CC_0 = @echo " CC " $@; AM_V_at = $(am__v_at_$(V)) am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY)) am__v_at_0 = @ CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_$(V)) am__v_CCLD_ = $(am__v_CCLD_$(AM_DEFAULT_VERBOSITY)) am__v_CCLD_0 = @echo " CCLD " $@; YACCCOMPILE = $(YACC) $(YFLAGS) $(AM_YFLAGS) LTYACCCOMPILE = $(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(YACC) $(YFLAGS) $(AM_YFLAGS) AM_V_YACC = $(am__v_YACC_$(V)) am__v_YACC_ = $(am__v_YACC_$(AM_DEFAULT_VERBOSITY)) am__v_YACC_0 = @echo " YACC " $@; YLWRAP = $(top_srcdir)/config/ylwrap AM_V_GEN = $(am__v_GEN_$(V)) am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY)) am__v_GEN_0 = @echo " GEN " $@; SOURCES = $(libhaildb_la_SOURCES) $(libtest0aux_la_SOURCES) \ tests/bug579934_open_index_by_name_segv.c tests/ib_cfg.c \ tests/ib_client_compare.c tests/ib_cursor.c tests/ib_ddl.c \ tests/ib_dict.c tests/ib_drop.c tests/ib_duplicate_key_name.c \ tests/ib_logger.c $(tests_ib_mt_drv_SOURCES) tests/ib_panic.c \ tests/ib_shutdown.c tests/ib_status.c \ tests/ib_table_statistics.c tests/ib_tablename.c \ tests/ib_test1.c tests/ib_test2.c tests/ib_test3.c \ tests/ib_test5.c tests/ib_trx_is_interrupted.c \ tests/ib_types.c tests/ib_update.c DIST_SOURCES = $(am__libhaildb_la_SOURCES_DIST) \ $(libtest0aux_la_SOURCES) \ tests/bug579934_open_index_by_name_segv.c tests/ib_cfg.c \ tests/ib_client_compare.c tests/ib_cursor.c tests/ib_ddl.c \ tests/ib_dict.c tests/ib_drop.c tests/ib_duplicate_key_name.c \ tests/ib_logger.c $(tests_ib_mt_drv_SOURCES) tests/ib_panic.c \ tests/ib_shutdown.c tests/ib_status.c \ tests/ib_table_statistics.c tests/ib_tablename.c \ tests/ib_test1.c tests/ib_test2.c tests/ib_test3.c \ tests/ib_test5.c tests/ib_trx_is_interrupted.c \ tests/ib_types.c tests/ib_update.c am__noinst_HEADERS_DIST = include/btr0btr.h include/btr0btr.ic \ include/btr0cur.h include/btr0cur.ic include/btr0pcur.h \ include/btr0pcur.ic include/btr0sea.h include/btr0sea.ic \ include/btr0types.h include/buf0buf.h include/buf0buf.ic \ include/buf0flu.h include/buf0flu.ic include/buf0lru.h \ include/buf0lru.ic include/buf0rea.h include/buf0types.h \ include/data0data.h include/data0data.ic include/data0type.h \ include/data0type.ic include/data0types.h include/db0err.h \ include/dict0boot.h include/dict0boot.ic include/dict0crea.h \ include/dict0crea.ic include/dict0dict.h include/dict0dict.ic \ include/dict0load.h include/dict0load.ic include/dict0mem.h \ include/dict0mem.ic include/dict0types.h include/dyn0dyn.h \ include/dyn0dyn.ic include/eval0eval.h include/eval0eval.ic \ include/eval0proc.h include/eval0proc.ic include/fil0fil.h \ include/fsp0fsp.h include/fsp0fsp.ic include/fut0fut.h \ include/fsp0types.h include/fut0fut.ic include/fut0lst.h \ include/fut0lst.ic include/ha0ha.h include/ha0ha.ic \ include/ha0storage.h include/ha0storage.ic include/hash0hash.h \ include/hash0hash.ic include/ibuf0ibuf.h include/ibuf0ibuf.ic \ include/ibuf0types.h include/lock0iter.h include/lock0lock.h \ include/lock0lock.ic include/lock0priv.h include/lock0priv.ic \ include/lock0types.h include/log0log.h include/log0log.ic \ include/log0recv.h include/log0recv.ic include/mach0data.h \ include/mach0data.ic include/mem0dbg.h include/mem0dbg.ic \ include/mem0mem.h include/mem0mem.ic include/mtr0log.h \ include/mtr0log.ic include/mtr0mtr.h include/mtr0mtr.ic \ include/mtr0types.h include/os0file.h include/os0proc.h \ include/os0proc.ic include/os0sync.h include/os0sync.ic \ include/os0thread.h include/os0thread.ic include/page0cur.h \ include/page0cur.ic include/page0page.h include/page0page.ic \ include/page0types.h include/pars0grm.h include/pars0opt.h \ include/pars0opt.ic include/pars0pars.h include/pars0pars.ic \ include/pars0sym.h include/pars0sym.ic include/pars0types.h \ include/que0que.h include/que0que.ic include/que0types.h \ include/read0read.h include/read0read.ic include/read0types.h \ include/rem0cmp.h include/rem0cmp.ic include/rem0rec.h \ include/rem0rec.ic include/rem0types.h include/row0ext.h \ include/row0ext.ic include/row0ins.h include/row0ins.ic \ include/row0merge.h include/row0purge.h include/row0purge.ic \ include/row0row.h include/row0row.ic include/row0sel.h \ include/row0sel.ic include/row0types.h include/row0uins.h \ include/row0uins.ic include/row0umod.h include/row0umod.ic \ include/row0undo.h include/row0undo.ic include/row0upd.h \ include/row0upd.ic include/row0vers.h include/row0vers.ic \ include/srv0que.h include/row0prebuilt.h include/srv0srv.h \ include/srv0srv.ic include/srv0start.h include/sync0arr.h \ include/sync0arr.ic include/sync0rw.h include/sync0rw.ic \ include/sync0sync.h include/sync0sync.ic include/sync0types.h \ include/thr0loc.h include/thr0loc.ic include/trx0purge.h \ include/trx0purge.ic include/trx0rec.h include/trx0rec.ic \ include/trx0roll.h include/trx0roll.ic include/trx0rseg.h \ include/trx0rseg.ic include/trx0sys.h include/trx0sys.ic \ include/trx0trx.h include/trx0trx.ic include/trx0types.h \ include/trx0undo.h include/trx0undo.ic include/trx0xa.h \ include/univ.i include/usr0sess.h include/usr0sess.ic \ include/usr0types.h include/ut0byte.h include/ut0byte.ic \ include/ut0dbg.h include/ut0lst.h include/ut0mem.h \ include/ut0mem.ic include/ut0rnd.h include/ut0rnd.ic \ include/ut0sort.h include/ut0ut.h include/ut0ut.ic \ include/ut0vec.h include/ut0vec.ic include/ut0list.h \ include/ut0list.ic include/ut0rbt.h include/ddl0ddl.h \ include/api0misc.h include/api0ucode.h include/api0api.h \ tests/ib_mt_base.h tests/ib_mt_drv.h tests/test0aux.h \ include/buf0buddy.h include/buf0buddy.ic include/page0zip.h \ include/page0zip.ic HEADERS = $(include_HEADERS) $(noinst_HEADERS) ETAGS = etags CTAGS = ctags am__tty_colors = \ red=; grn=; lgn=; blu=; std= DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) distdir = $(PACKAGE)-$(VERSION) top_distdir = $(distdir) am__remove_distdir = \ { test ! -d "$(distdir)" \ || { find "$(distdir)" -type d ! -perm -200 -exec chmod u+w {} ';' \ && rm -fr "$(distdir)"; }; } DIST_ARCHIVES = $(distdir).tar.gz GZIP_ENV = --best distuninstallcheck_listfiles = find . -type f -print distcleancheck_listfiles = find . -type f -print ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_CFLAGS = @AM_CFLAGS@ AM_CPPFLAGS = @AM_CPPFLAGS@ AM_CXXFLAGS = @AM_CXXFLAGS@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AM_LDFLAGS = @AM_LDFLAGS@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ BETTER_MALLOC_LIBS = @BETTER_MALLOC_LIBS@ BOOSTSKIP_WARNINGS = @BOOSTSKIP_WARNINGS@ CC = @CC@ CC44 = @CC44@ CC45 = @CC45@ CCDEPMODE = @CCDEPMODE@ CC_4_2 = @CC_4_2@ CC_VERSION = @CC_VERSION@ CFLAGS = @CFLAGS@ CFLAG_VISIBILITY = @CFLAG_VISIBILITY@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CXX = @CXX@ CXX44 = @CXX44@ CXX45 = @CXX45@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CXX_4_2 = @CXX_4_2@ CXX_STANDARD = @CXX_STANDARD@ CXX_VERSION = @CXX_VERSION@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DOXYGEN = @DOXYGEN@ DPKG_GENSYMBOLS = @DPKG_GENSYMBOLS@ DSYMUTIL = @DSYMUTIL@ DTRACE = @DTRACE@ DTRACEFLAGS = @DTRACEFLAGS@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GCOV_LIBS = @GCOV_LIBS@ GREP = @GREP@ HAILDB_FULL_VERSION = @HAILDB_FULL_VERSION@ HAVE_VISIBILITY = @HAVE_VISIBILITY@ IB_API_VERSION = @IB_API_VERSION@ IB_API_VERSION_AGE = @IB_API_VERSION_AGE@ IB_API_VERSION_REVISION = @IB_API_VERSION_REVISION@ INNOBASE_SKIP_WARNINGS = @INNOBASE_SKIP_WARNINGS@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ ISAINFO = @ISAINFO@ LCOV = @LCOV@ LCOV_GENHTML = @LCOV_GENHTML@ LD = @LD@ LDFLAGS = @LDFLAGS@ LD_VERSION_SCRIPT = @LD_VERSION_SCRIPT@ LIBC_P = @LIBC_P@ LIBM = @LIBM@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAKEINFO = @MAKEINFO@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ NO_CONVERSION = @NO_CONVERSION@ NO_EFF_CXX = @NO_EFF_CXX@ NO_OLD_STYLE_CAST = @NO_OLD_STYLE_CAST@ NO_REDUNDANT_DECLS = @NO_REDUNDANT_DECLS@ NO_SHADOW = @NO_SHADOW@ NO_STRICT_ALIASING = @NO_STRICT_ALIASING@ NO_UNREACHED = @NO_UNREACHED@ NO_VISIBILITY = @NO_VISIBILITY@ NO_WERROR = @NO_WERROR@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PANDORA_HEX_VERSION = @PANDORA_HEX_VERSION@ PANDORA_OPTIMIZE_BITFIELD = @PANDORA_OPTIMIZE_BITFIELD@ PANDORA_RELEASE_COMMENT = @PANDORA_RELEASE_COMMENT@ PANDORA_RELEASE_ID = @PANDORA_RELEASE_ID@ PANDORA_RELEASE_VERSION = @PANDORA_RELEASE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PERL = @PERL@ PERMISSIVE_WARNINGS = @PERMISSIVE_WARNINGS@ PROTOSKIP_WARNINGS = @PROTOSKIP_WARNINGS@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SPHINXBUILD = @SPHINXBUILD@ STRIP = @STRIP@ TARGET_FREEBSD = @TARGET_FREEBSD@ TARGET_LINUX = @TARGET_LINUX@ TARGET_OSX = @TARGET_OSX@ TARGET_SOLARIS = @TARGET_SOLARIS@ TARGET_WINDOWS = @TARGET_WINDOWS@ VERSION = @VERSION@ YACC = @YACC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ lt_ECHO = @lt_ECHO@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target = @target@ target_alias = @target_alias@ target_cpu = @target_cpu@ target_os = @target_os@ target_vendor = @target_vendor@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ uname_prog = @uname_prog@ ACLOCAL_AMFLAGS = -I m4 --force include_HEADERS = haildb.h libhaildb_la_LDFLAGS = -version-info $(IB_API_VERSION) $(LD_VERSION_SCRIPT) libhaildb_la_CFLAGS = \ ${AM_CFLAGS} \ ${CFLAG_VISIBILITY} \ -DBUILDING_HAILDB noinst_HEADERS = include/btr0btr.h include/btr0btr.ic \ include/btr0cur.h include/btr0cur.ic include/btr0pcur.h \ include/btr0pcur.ic include/btr0sea.h include/btr0sea.ic \ include/btr0types.h include/buf0buf.h include/buf0buf.ic \ include/buf0flu.h include/buf0flu.ic include/buf0lru.h \ include/buf0lru.ic include/buf0rea.h include/buf0types.h \ include/data0data.h include/data0data.ic include/data0type.h \ include/data0type.ic include/data0types.h include/db0err.h \ include/dict0boot.h include/dict0boot.ic include/dict0crea.h \ include/dict0crea.ic include/dict0dict.h include/dict0dict.ic \ include/dict0load.h include/dict0load.ic include/dict0mem.h \ include/dict0mem.ic include/dict0types.h include/dyn0dyn.h \ include/dyn0dyn.ic include/eval0eval.h include/eval0eval.ic \ include/eval0proc.h include/eval0proc.ic include/fil0fil.h \ include/fsp0fsp.h include/fsp0fsp.ic include/fut0fut.h \ include/fsp0types.h include/fut0fut.ic include/fut0lst.h \ include/fut0lst.ic include/ha0ha.h include/ha0ha.ic \ include/ha0storage.h include/ha0storage.ic include/hash0hash.h \ include/hash0hash.ic include/ibuf0ibuf.h include/ibuf0ibuf.ic \ include/ibuf0types.h include/lock0iter.h include/lock0lock.h \ include/lock0lock.ic include/lock0priv.h include/lock0priv.ic \ include/lock0types.h include/log0log.h include/log0log.ic \ include/log0recv.h include/log0recv.ic include/mach0data.h \ include/mach0data.ic include/mem0dbg.h include/mem0dbg.ic \ include/mem0mem.h include/mem0mem.ic include/mtr0log.h \ include/mtr0log.ic include/mtr0mtr.h include/mtr0mtr.ic \ include/mtr0types.h include/os0file.h include/os0proc.h \ include/os0proc.ic include/os0sync.h include/os0sync.ic \ include/os0thread.h include/os0thread.ic include/page0cur.h \ include/page0cur.ic include/page0page.h include/page0page.ic \ include/page0types.h include/pars0grm.h include/pars0opt.h \ include/pars0opt.ic include/pars0pars.h include/pars0pars.ic \ include/pars0sym.h include/pars0sym.ic include/pars0types.h \ include/que0que.h include/que0que.ic include/que0types.h \ include/read0read.h include/read0read.ic include/read0types.h \ include/rem0cmp.h include/rem0cmp.ic include/rem0rec.h \ include/rem0rec.ic include/rem0types.h include/row0ext.h \ include/row0ext.ic include/row0ins.h include/row0ins.ic \ include/row0merge.h include/row0purge.h include/row0purge.ic \ include/row0row.h include/row0row.ic include/row0sel.h \ include/row0sel.ic include/row0types.h include/row0uins.h \ include/row0uins.ic include/row0umod.h include/row0umod.ic \ include/row0undo.h include/row0undo.ic include/row0upd.h \ include/row0upd.ic include/row0vers.h include/row0vers.ic \ include/srv0que.h include/row0prebuilt.h include/srv0srv.h \ include/srv0srv.ic include/srv0start.h include/sync0arr.h \ include/sync0arr.ic include/sync0rw.h include/sync0rw.ic \ include/sync0sync.h include/sync0sync.ic include/sync0types.h \ include/thr0loc.h include/thr0loc.ic include/trx0purge.h \ include/trx0purge.ic include/trx0rec.h include/trx0rec.ic \ include/trx0roll.h include/trx0roll.ic include/trx0rseg.h \ include/trx0rseg.ic include/trx0sys.h include/trx0sys.ic \ include/trx0trx.h include/trx0trx.ic include/trx0types.h \ include/trx0undo.h include/trx0undo.ic include/trx0xa.h \ include/univ.i include/usr0sess.h include/usr0sess.ic \ include/usr0types.h include/ut0byte.h include/ut0byte.ic \ include/ut0dbg.h include/ut0lst.h include/ut0mem.h \ include/ut0mem.ic include/ut0rnd.h include/ut0rnd.ic \ include/ut0sort.h include/ut0ut.h include/ut0ut.ic \ include/ut0vec.h include/ut0vec.ic include/ut0list.h \ include/ut0list.ic include/ut0rbt.h include/ddl0ddl.h \ include/api0misc.h include/api0ucode.h include/api0api.h \ tests/ib_mt_base.h tests/ib_mt_drv.h tests/test0aux.h \ $(am__append_1) lib_LTLIBRARIES = libhaildb.la libhaildb_la_SOURCES = btr/btr0btr.c btr/btr0cur.c btr/btr0pcur.c \ btr/btr0sea.c buf/buf0buf.c buf/buf0flu.c buf/buf0lru.c \ buf/buf0rea.c data/data0data.c data/data0type.c \ dict/dict0boot.c dict/dict0crea.c dict/dict0dict.c \ dict/dict0load.c dict/dict0mem.c dyn/dyn0dyn.c \ eval/eval0eval.c eval/eval0proc.c fil/fil0fil.c fsp/fsp0fsp.c \ fut/fut0fut.c fut/fut0lst.c ha/ha0ha.c ha/ha0storage.c \ ha/hash0hash.c ibuf/ibuf0ibuf.c lock/lock0iter.c \ lock/lock0lock.c log/log0log.c log/log0recv.c mach/mach0data.c \ mem/mem0mem.c mtr/mtr0log.c mtr/mtr0mtr.c os/os0file.c \ os/os0proc.c os/os0sync.c os/os0thread.c page/page0cur.c \ page/page0page.c pars/lexyy.c pars/pars0grm.y pars/pars0opt.c \ pars/pars0pars.c pars/pars0sym.c que/que0que.c \ read/read0read.c rem/rem0cmp.c rem/rem0rec.c row/row0ext.c \ row/row0ins.c row/row0merge.c row/row0purge.c row/row0row.c \ row/row0sel.c row/row0uins.c row/row0umod.c row/row0undo.c \ row/row0upd.c row/row0vers.c row/row0prebuilt.c srv/srv0que.c \ srv/srv0srv.c srv/srv0start.c sync/sync0arr.c sync/sync0rw.c \ sync/sync0sync.c thr/thr0loc.c trx/trx0purge.c trx/trx0rec.c \ trx/trx0roll.c trx/trx0rseg.c trx/trx0sys.c trx/trx0trx.c \ trx/trx0undo.c usr/usr0sess.c ut/ut0byte.c ut/ut0dbg.c \ ut/ut0list.c ut/ut0mem.c ut/ut0rnd.c ut/ut0ut.c ut/ut0vec.c \ ut/ut0rbt.c api/api0api.c api/api0ucode.c ddl/ddl0ddl.c \ api/api0misc.c api/api0cfg.c api/api0status.c api/api0sql.c \ $(am__append_2) EXTRA_DIST = \ .quickly \ config/autorun.sh \ config/pandora-plugin \ config/uncrustify.cfg \ CMakeLists.txt \ COPYING \ COPYING.Google \ COPYING.Percona \ COPYING.Sun_Microsystems \ config.h.cmake \ libhaildb.ver \ ${top_srcdir}/m4/*m4 \ pars/make_bison.sh \ pars/make_flex.sh \ pars/pars0grm.y \ pars/pars0lex.l \ tests/CMakeLists.examples \ tests/CMakeLists.txt \ tests/Makefile.examples \ tests/README \ tests/ib_compressed.c \ tests/ib_deadlock.c \ tests/ib_index.c \ tests/ib_mt_stress.c \ tests/ib_perf1.c \ tests/ib_recover.c \ tests/ib_search.c \ tests/ib_zip.c \ tests/run.sh \ win/innodb.def DISTCLEANFILES = \ config/top.h \ tests/ibdata1 \ tests/log/ib_logfile1 \ tests/log/ib_logfile0 # keep the following lists alphabetically sorted TEST_EXECUTABLES = \ tests/bug579934_open_index_by_name_segv \ tests/ib_cfg \ tests/ib_cursor \ tests/ib_ddl \ tests/ib_dict \ tests/ib_drop \ tests/ib_duplicate_key_name \ tests/ib_logger \ tests/ib_mt_drv \ tests/ib_shutdown \ tests/ib_status \ tests/ib_panic \ tests/ib_trx_is_interrupted \ tests/ib_tablename \ tests/ib_table_statistics \ tests/ib_test1 \ tests/ib_client_compare \ tests/ib_test2 \ tests/ib_test3 \ tests/ib_test5 \ tests/ib_types \ tests/ib_update TESTS_ENVIRONMENT = ${top_srcdir}/tests/run.sh LDADD = libhaildb.la libtest0aux.la check_LTLIBRARIES = libtest0aux.la tests_ib_mt_drv_SOURCES = \ tests/ib_mt_base.c \ tests/ib_mt_drv.c \ tests/ib_mt_t1.c \ tests/ib_mt_t2.c libtest0aux_la_SOURCES = tests/test0aux.c TESTS = $(check_PROGRAMS) TEST_STRESS_EXECUTABLES = \ ib_mt_stress \ ib_perf1 all: config.h $(MAKE) $(AM_MAKEFLAGS) all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj .y am--refresh: @: $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ echo ' cd $(srcdir) && $(AUTOMAKE) --foreign'; \ $(am__cd) $(srcdir) && $(AUTOMAKE) --foreign \ && exit 0; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ echo ' $(SHELL) ./config.status'; \ $(SHELL) ./config.status;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) $(SHELL) ./config.status --recheck $(top_srcdir)/configure: $(am__configure_deps) $(am__cd) $(srcdir) && $(AUTOCONF) $(ACLOCAL_M4): $(am__aclocal_m4_deps) $(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS) $(am__aclocal_m4_deps): config.h: stamp-h1 @if test ! -f $@; then \ rm -f stamp-h1; \ $(MAKE) $(AM_MAKEFLAGS) stamp-h1; \ else :; fi stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status @rm -f stamp-h1 cd $(top_builddir) && $(SHELL) ./config.status config.h $(srcdir)/config.h.in: $(am__configure_deps) ($(am__cd) $(top_srcdir) && $(AUTOHEADER)) rm -f stamp-h1 touch $@ distclean-hdr: -rm -f config.h stamp-h1 haildb.spec: $(top_builddir)/config.status $(srcdir)/haildb.spec.in cd $(top_builddir) && $(SHELL) ./config.status $@ clean-checkLTLIBRARIES: -test -z "$(check_LTLIBRARIES)" || rm -f $(check_LTLIBRARIES) @list='$(check_LTLIBRARIES)'; for p in $$list; do \ dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ test "$$dir" != "$$p" || dir=.; \ echo "rm -f \"$${dir}/so_locations\""; \ rm -f "$${dir}/so_locations"; \ done install-libLTLIBRARIES: $(lib_LTLIBRARIES) @$(NORMAL_INSTALL) test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)" @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ } uninstall-libLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ done clean-libLTLIBRARIES: -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ test "$$dir" != "$$p" || dir=.; \ echo "rm -f \"$${dir}/so_locations\""; \ rm -f "$${dir}/so_locations"; \ done btr/$(am__dirstamp): @$(MKDIR_P) btr @: > btr/$(am__dirstamp) btr/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) btr/$(DEPDIR) @: > btr/$(DEPDIR)/$(am__dirstamp) btr/libhaildb_la-btr0btr.lo: btr/$(am__dirstamp) \ btr/$(DEPDIR)/$(am__dirstamp) btr/libhaildb_la-btr0cur.lo: btr/$(am__dirstamp) \ btr/$(DEPDIR)/$(am__dirstamp) btr/libhaildb_la-btr0pcur.lo: btr/$(am__dirstamp) \ btr/$(DEPDIR)/$(am__dirstamp) btr/libhaildb_la-btr0sea.lo: btr/$(am__dirstamp) \ btr/$(DEPDIR)/$(am__dirstamp) buf/$(am__dirstamp): @$(MKDIR_P) buf @: > buf/$(am__dirstamp) buf/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) buf/$(DEPDIR) @: > buf/$(DEPDIR)/$(am__dirstamp) buf/libhaildb_la-buf0buf.lo: buf/$(am__dirstamp) \ buf/$(DEPDIR)/$(am__dirstamp) buf/libhaildb_la-buf0flu.lo: buf/$(am__dirstamp) \ buf/$(DEPDIR)/$(am__dirstamp) buf/libhaildb_la-buf0lru.lo: buf/$(am__dirstamp) \ buf/$(DEPDIR)/$(am__dirstamp) buf/libhaildb_la-buf0rea.lo: buf/$(am__dirstamp) \ buf/$(DEPDIR)/$(am__dirstamp) data/$(am__dirstamp): @$(MKDIR_P) data @: > data/$(am__dirstamp) data/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) data/$(DEPDIR) @: > data/$(DEPDIR)/$(am__dirstamp) data/libhaildb_la-data0data.lo: data/$(am__dirstamp) \ data/$(DEPDIR)/$(am__dirstamp) data/libhaildb_la-data0type.lo: data/$(am__dirstamp) \ data/$(DEPDIR)/$(am__dirstamp) dict/$(am__dirstamp): @$(MKDIR_P) dict @: > dict/$(am__dirstamp) dict/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) dict/$(DEPDIR) @: > dict/$(DEPDIR)/$(am__dirstamp) dict/libhaildb_la-dict0boot.lo: dict/$(am__dirstamp) \ dict/$(DEPDIR)/$(am__dirstamp) dict/libhaildb_la-dict0crea.lo: dict/$(am__dirstamp) \ dict/$(DEPDIR)/$(am__dirstamp) dict/libhaildb_la-dict0dict.lo: dict/$(am__dirstamp) \ dict/$(DEPDIR)/$(am__dirstamp) dict/libhaildb_la-dict0load.lo: dict/$(am__dirstamp) \ dict/$(DEPDIR)/$(am__dirstamp) dict/libhaildb_la-dict0mem.lo: dict/$(am__dirstamp) \ dict/$(DEPDIR)/$(am__dirstamp) dyn/$(am__dirstamp): @$(MKDIR_P) dyn @: > dyn/$(am__dirstamp) dyn/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) dyn/$(DEPDIR) @: > dyn/$(DEPDIR)/$(am__dirstamp) dyn/libhaildb_la-dyn0dyn.lo: dyn/$(am__dirstamp) \ dyn/$(DEPDIR)/$(am__dirstamp) eval/$(am__dirstamp): @$(MKDIR_P) eval @: > eval/$(am__dirstamp) eval/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) eval/$(DEPDIR) @: > eval/$(DEPDIR)/$(am__dirstamp) eval/libhaildb_la-eval0eval.lo: eval/$(am__dirstamp) \ eval/$(DEPDIR)/$(am__dirstamp) eval/libhaildb_la-eval0proc.lo: eval/$(am__dirstamp) \ eval/$(DEPDIR)/$(am__dirstamp) fil/$(am__dirstamp): @$(MKDIR_P) fil @: > fil/$(am__dirstamp) fil/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) fil/$(DEPDIR) @: > fil/$(DEPDIR)/$(am__dirstamp) fil/libhaildb_la-fil0fil.lo: fil/$(am__dirstamp) \ fil/$(DEPDIR)/$(am__dirstamp) fsp/$(am__dirstamp): @$(MKDIR_P) fsp @: > fsp/$(am__dirstamp) fsp/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) fsp/$(DEPDIR) @: > fsp/$(DEPDIR)/$(am__dirstamp) fsp/libhaildb_la-fsp0fsp.lo: fsp/$(am__dirstamp) \ fsp/$(DEPDIR)/$(am__dirstamp) fut/$(am__dirstamp): @$(MKDIR_P) fut @: > fut/$(am__dirstamp) fut/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) fut/$(DEPDIR) @: > fut/$(DEPDIR)/$(am__dirstamp) fut/libhaildb_la-fut0fut.lo: fut/$(am__dirstamp) \ fut/$(DEPDIR)/$(am__dirstamp) fut/libhaildb_la-fut0lst.lo: fut/$(am__dirstamp) \ fut/$(DEPDIR)/$(am__dirstamp) ha/$(am__dirstamp): @$(MKDIR_P) ha @: > ha/$(am__dirstamp) ha/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) ha/$(DEPDIR) @: > ha/$(DEPDIR)/$(am__dirstamp) ha/libhaildb_la-ha0ha.lo: ha/$(am__dirstamp) \ ha/$(DEPDIR)/$(am__dirstamp) ha/libhaildb_la-ha0storage.lo: ha/$(am__dirstamp) \ ha/$(DEPDIR)/$(am__dirstamp) ha/libhaildb_la-hash0hash.lo: ha/$(am__dirstamp) \ ha/$(DEPDIR)/$(am__dirstamp) ibuf/$(am__dirstamp): @$(MKDIR_P) ibuf @: > ibuf/$(am__dirstamp) ibuf/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) ibuf/$(DEPDIR) @: > ibuf/$(DEPDIR)/$(am__dirstamp) ibuf/libhaildb_la-ibuf0ibuf.lo: ibuf/$(am__dirstamp) \ ibuf/$(DEPDIR)/$(am__dirstamp) lock/$(am__dirstamp): @$(MKDIR_P) lock @: > lock/$(am__dirstamp) lock/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) lock/$(DEPDIR) @: > lock/$(DEPDIR)/$(am__dirstamp) lock/libhaildb_la-lock0iter.lo: lock/$(am__dirstamp) \ lock/$(DEPDIR)/$(am__dirstamp) lock/libhaildb_la-lock0lock.lo: lock/$(am__dirstamp) \ lock/$(DEPDIR)/$(am__dirstamp) log/$(am__dirstamp): @$(MKDIR_P) log @: > log/$(am__dirstamp) log/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) log/$(DEPDIR) @: > log/$(DEPDIR)/$(am__dirstamp) log/libhaildb_la-log0log.lo: log/$(am__dirstamp) \ log/$(DEPDIR)/$(am__dirstamp) log/libhaildb_la-log0recv.lo: log/$(am__dirstamp) \ log/$(DEPDIR)/$(am__dirstamp) mach/$(am__dirstamp): @$(MKDIR_P) mach @: > mach/$(am__dirstamp) mach/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) mach/$(DEPDIR) @: > mach/$(DEPDIR)/$(am__dirstamp) mach/libhaildb_la-mach0data.lo: mach/$(am__dirstamp) \ mach/$(DEPDIR)/$(am__dirstamp) mem/$(am__dirstamp): @$(MKDIR_P) mem @: > mem/$(am__dirstamp) mem/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) mem/$(DEPDIR) @: > mem/$(DEPDIR)/$(am__dirstamp) mem/libhaildb_la-mem0mem.lo: mem/$(am__dirstamp) \ mem/$(DEPDIR)/$(am__dirstamp) mtr/$(am__dirstamp): @$(MKDIR_P) mtr @: > mtr/$(am__dirstamp) mtr/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) mtr/$(DEPDIR) @: > mtr/$(DEPDIR)/$(am__dirstamp) mtr/libhaildb_la-mtr0log.lo: mtr/$(am__dirstamp) \ mtr/$(DEPDIR)/$(am__dirstamp) mtr/libhaildb_la-mtr0mtr.lo: mtr/$(am__dirstamp) \ mtr/$(DEPDIR)/$(am__dirstamp) os/$(am__dirstamp): @$(MKDIR_P) os @: > os/$(am__dirstamp) os/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) os/$(DEPDIR) @: > os/$(DEPDIR)/$(am__dirstamp) os/libhaildb_la-os0file.lo: os/$(am__dirstamp) \ os/$(DEPDIR)/$(am__dirstamp) os/libhaildb_la-os0proc.lo: os/$(am__dirstamp) \ os/$(DEPDIR)/$(am__dirstamp) os/libhaildb_la-os0sync.lo: os/$(am__dirstamp) \ os/$(DEPDIR)/$(am__dirstamp) os/libhaildb_la-os0thread.lo: os/$(am__dirstamp) \ os/$(DEPDIR)/$(am__dirstamp) page/$(am__dirstamp): @$(MKDIR_P) page @: > page/$(am__dirstamp) page/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) page/$(DEPDIR) @: > page/$(DEPDIR)/$(am__dirstamp) page/libhaildb_la-page0cur.lo: page/$(am__dirstamp) \ page/$(DEPDIR)/$(am__dirstamp) page/libhaildb_la-page0page.lo: page/$(am__dirstamp) \ page/$(DEPDIR)/$(am__dirstamp) pars/$(am__dirstamp): @$(MKDIR_P) pars @: > pars/$(am__dirstamp) pars/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) pars/$(DEPDIR) @: > pars/$(DEPDIR)/$(am__dirstamp) pars/libhaildb_la-lexyy.lo: pars/$(am__dirstamp) \ pars/$(DEPDIR)/$(am__dirstamp) pars/libhaildb_la-pars0grm.lo: pars/$(am__dirstamp) \ pars/$(DEPDIR)/$(am__dirstamp) pars/libhaildb_la-pars0opt.lo: pars/$(am__dirstamp) \ pars/$(DEPDIR)/$(am__dirstamp) pars/libhaildb_la-pars0pars.lo: pars/$(am__dirstamp) \ pars/$(DEPDIR)/$(am__dirstamp) pars/libhaildb_la-pars0sym.lo: pars/$(am__dirstamp) \ pars/$(DEPDIR)/$(am__dirstamp) que/$(am__dirstamp): @$(MKDIR_P) que @: > que/$(am__dirstamp) que/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) que/$(DEPDIR) @: > que/$(DEPDIR)/$(am__dirstamp) que/libhaildb_la-que0que.lo: que/$(am__dirstamp) \ que/$(DEPDIR)/$(am__dirstamp) read/$(am__dirstamp): @$(MKDIR_P) read @: > read/$(am__dirstamp) read/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) read/$(DEPDIR) @: > read/$(DEPDIR)/$(am__dirstamp) read/libhaildb_la-read0read.lo: read/$(am__dirstamp) \ read/$(DEPDIR)/$(am__dirstamp) rem/$(am__dirstamp): @$(MKDIR_P) rem @: > rem/$(am__dirstamp) rem/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) rem/$(DEPDIR) @: > rem/$(DEPDIR)/$(am__dirstamp) rem/libhaildb_la-rem0cmp.lo: rem/$(am__dirstamp) \ rem/$(DEPDIR)/$(am__dirstamp) rem/libhaildb_la-rem0rec.lo: rem/$(am__dirstamp) \ rem/$(DEPDIR)/$(am__dirstamp) row/$(am__dirstamp): @$(MKDIR_P) row @: > row/$(am__dirstamp) row/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) row/$(DEPDIR) @: > row/$(DEPDIR)/$(am__dirstamp) row/libhaildb_la-row0ext.lo: row/$(am__dirstamp) \ row/$(DEPDIR)/$(am__dirstamp) row/libhaildb_la-row0ins.lo: row/$(am__dirstamp) \ row/$(DEPDIR)/$(am__dirstamp) row/libhaildb_la-row0merge.lo: row/$(am__dirstamp) \ row/$(DEPDIR)/$(am__dirstamp) row/libhaildb_la-row0purge.lo: row/$(am__dirstamp) \ row/$(DEPDIR)/$(am__dirstamp) row/libhaildb_la-row0row.lo: row/$(am__dirstamp) \ row/$(DEPDIR)/$(am__dirstamp) row/libhaildb_la-row0sel.lo: row/$(am__dirstamp) \ row/$(DEPDIR)/$(am__dirstamp) row/libhaildb_la-row0uins.lo: row/$(am__dirstamp) \ row/$(DEPDIR)/$(am__dirstamp) row/libhaildb_la-row0umod.lo: row/$(am__dirstamp) \ row/$(DEPDIR)/$(am__dirstamp) row/libhaildb_la-row0undo.lo: row/$(am__dirstamp) \ row/$(DEPDIR)/$(am__dirstamp) row/libhaildb_la-row0upd.lo: row/$(am__dirstamp) \ row/$(DEPDIR)/$(am__dirstamp) row/libhaildb_la-row0vers.lo: row/$(am__dirstamp) \ row/$(DEPDIR)/$(am__dirstamp) row/libhaildb_la-row0prebuilt.lo: row/$(am__dirstamp) \ row/$(DEPDIR)/$(am__dirstamp) srv/$(am__dirstamp): @$(MKDIR_P) srv @: > srv/$(am__dirstamp) srv/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) srv/$(DEPDIR) @: > srv/$(DEPDIR)/$(am__dirstamp) srv/libhaildb_la-srv0que.lo: srv/$(am__dirstamp) \ srv/$(DEPDIR)/$(am__dirstamp) srv/libhaildb_la-srv0srv.lo: srv/$(am__dirstamp) \ srv/$(DEPDIR)/$(am__dirstamp) srv/libhaildb_la-srv0start.lo: srv/$(am__dirstamp) \ srv/$(DEPDIR)/$(am__dirstamp) sync/$(am__dirstamp): @$(MKDIR_P) sync @: > sync/$(am__dirstamp) sync/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) sync/$(DEPDIR) @: > sync/$(DEPDIR)/$(am__dirstamp) sync/libhaildb_la-sync0arr.lo: sync/$(am__dirstamp) \ sync/$(DEPDIR)/$(am__dirstamp) sync/libhaildb_la-sync0rw.lo: sync/$(am__dirstamp) \ sync/$(DEPDIR)/$(am__dirstamp) sync/libhaildb_la-sync0sync.lo: sync/$(am__dirstamp) \ sync/$(DEPDIR)/$(am__dirstamp) thr/$(am__dirstamp): @$(MKDIR_P) thr @: > thr/$(am__dirstamp) thr/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) thr/$(DEPDIR) @: > thr/$(DEPDIR)/$(am__dirstamp) thr/libhaildb_la-thr0loc.lo: thr/$(am__dirstamp) \ thr/$(DEPDIR)/$(am__dirstamp) trx/$(am__dirstamp): @$(MKDIR_P) trx @: > trx/$(am__dirstamp) trx/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) trx/$(DEPDIR) @: > trx/$(DEPDIR)/$(am__dirstamp) trx/libhaildb_la-trx0purge.lo: trx/$(am__dirstamp) \ trx/$(DEPDIR)/$(am__dirstamp) trx/libhaildb_la-trx0rec.lo: trx/$(am__dirstamp) \ trx/$(DEPDIR)/$(am__dirstamp) trx/libhaildb_la-trx0roll.lo: trx/$(am__dirstamp) \ trx/$(DEPDIR)/$(am__dirstamp) trx/libhaildb_la-trx0rseg.lo: trx/$(am__dirstamp) \ trx/$(DEPDIR)/$(am__dirstamp) trx/libhaildb_la-trx0sys.lo: trx/$(am__dirstamp) \ trx/$(DEPDIR)/$(am__dirstamp) trx/libhaildb_la-trx0trx.lo: trx/$(am__dirstamp) \ trx/$(DEPDIR)/$(am__dirstamp) trx/libhaildb_la-trx0undo.lo: trx/$(am__dirstamp) \ trx/$(DEPDIR)/$(am__dirstamp) usr/$(am__dirstamp): @$(MKDIR_P) usr @: > usr/$(am__dirstamp) usr/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) usr/$(DEPDIR) @: > usr/$(DEPDIR)/$(am__dirstamp) usr/libhaildb_la-usr0sess.lo: usr/$(am__dirstamp) \ usr/$(DEPDIR)/$(am__dirstamp) ut/$(am__dirstamp): @$(MKDIR_P) ut @: > ut/$(am__dirstamp) ut/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) ut/$(DEPDIR) @: > ut/$(DEPDIR)/$(am__dirstamp) ut/libhaildb_la-ut0byte.lo: ut/$(am__dirstamp) \ ut/$(DEPDIR)/$(am__dirstamp) ut/libhaildb_la-ut0dbg.lo: ut/$(am__dirstamp) \ ut/$(DEPDIR)/$(am__dirstamp) ut/libhaildb_la-ut0list.lo: ut/$(am__dirstamp) \ ut/$(DEPDIR)/$(am__dirstamp) ut/libhaildb_la-ut0mem.lo: ut/$(am__dirstamp) \ ut/$(DEPDIR)/$(am__dirstamp) ut/libhaildb_la-ut0rnd.lo: ut/$(am__dirstamp) \ ut/$(DEPDIR)/$(am__dirstamp) ut/libhaildb_la-ut0ut.lo: ut/$(am__dirstamp) \ ut/$(DEPDIR)/$(am__dirstamp) ut/libhaildb_la-ut0vec.lo: ut/$(am__dirstamp) \ ut/$(DEPDIR)/$(am__dirstamp) ut/libhaildb_la-ut0rbt.lo: ut/$(am__dirstamp) \ ut/$(DEPDIR)/$(am__dirstamp) api/$(am__dirstamp): @$(MKDIR_P) api @: > api/$(am__dirstamp) api/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) api/$(DEPDIR) @: > api/$(DEPDIR)/$(am__dirstamp) api/libhaildb_la-api0api.lo: api/$(am__dirstamp) \ api/$(DEPDIR)/$(am__dirstamp) api/libhaildb_la-api0ucode.lo: api/$(am__dirstamp) \ api/$(DEPDIR)/$(am__dirstamp) ddl/$(am__dirstamp): @$(MKDIR_P) ddl @: > ddl/$(am__dirstamp) ddl/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) ddl/$(DEPDIR) @: > ddl/$(DEPDIR)/$(am__dirstamp) ddl/libhaildb_la-ddl0ddl.lo: ddl/$(am__dirstamp) \ ddl/$(DEPDIR)/$(am__dirstamp) api/libhaildb_la-api0misc.lo: api/$(am__dirstamp) \ api/$(DEPDIR)/$(am__dirstamp) api/libhaildb_la-api0cfg.lo: api/$(am__dirstamp) \ api/$(DEPDIR)/$(am__dirstamp) api/libhaildb_la-api0status.lo: api/$(am__dirstamp) \ api/$(DEPDIR)/$(am__dirstamp) api/libhaildb_la-api0sql.lo: api/$(am__dirstamp) \ api/$(DEPDIR)/$(am__dirstamp) buf/libhaildb_la-buf0buddy.lo: buf/$(am__dirstamp) \ buf/$(DEPDIR)/$(am__dirstamp) page/libhaildb_la-page0zip.lo: page/$(am__dirstamp) \ page/$(DEPDIR)/$(am__dirstamp) libhaildb.la: $(libhaildb_la_OBJECTS) $(libhaildb_la_DEPENDENCIES) $(AM_V_CCLD)$(libhaildb_la_LINK) -rpath $(libdir) $(libhaildb_la_OBJECTS) $(libhaildb_la_LIBADD) $(LIBS) tests/$(am__dirstamp): @$(MKDIR_P) tests @: > tests/$(am__dirstamp) tests/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) tests/$(DEPDIR) @: > tests/$(DEPDIR)/$(am__dirstamp) tests/test0aux.lo: tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) libtest0aux.la: $(libtest0aux_la_OBJECTS) $(libtest0aux_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) $(libtest0aux_la_OBJECTS) $(libtest0aux_la_LIBADD) $(LIBS) clean-checkPROGRAMS: @list='$(check_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list tests/bug579934_open_index_by_name_segv.$(OBJEXT): \ tests/$(am__dirstamp) tests/$(DEPDIR)/$(am__dirstamp) tests/bug579934_open_index_by_name_segv$(EXEEXT): $(tests_bug579934_open_index_by_name_segv_OBJECTS) $(tests_bug579934_open_index_by_name_segv_DEPENDENCIES) tests/$(am__dirstamp) @rm -f tests/bug579934_open_index_by_name_segv$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tests_bug579934_open_index_by_name_segv_OBJECTS) $(tests_bug579934_open_index_by_name_segv_LDADD) $(LIBS) tests/ib_cfg.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/ib_cfg$(EXEEXT): $(tests_ib_cfg_OBJECTS) $(tests_ib_cfg_DEPENDENCIES) tests/$(am__dirstamp) @rm -f tests/ib_cfg$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tests_ib_cfg_OBJECTS) $(tests_ib_cfg_LDADD) $(LIBS) tests/ib_client_compare.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/ib_client_compare$(EXEEXT): $(tests_ib_client_compare_OBJECTS) $(tests_ib_client_compare_DEPENDENCIES) tests/$(am__dirstamp) @rm -f tests/ib_client_compare$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tests_ib_client_compare_OBJECTS) $(tests_ib_client_compare_LDADD) $(LIBS) tests/ib_cursor.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/ib_cursor$(EXEEXT): $(tests_ib_cursor_OBJECTS) $(tests_ib_cursor_DEPENDENCIES) tests/$(am__dirstamp) @rm -f tests/ib_cursor$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tests_ib_cursor_OBJECTS) $(tests_ib_cursor_LDADD) $(LIBS) tests/ib_ddl.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/ib_ddl$(EXEEXT): $(tests_ib_ddl_OBJECTS) $(tests_ib_ddl_DEPENDENCIES) tests/$(am__dirstamp) @rm -f tests/ib_ddl$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tests_ib_ddl_OBJECTS) $(tests_ib_ddl_LDADD) $(LIBS) tests/ib_dict.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/ib_dict$(EXEEXT): $(tests_ib_dict_OBJECTS) $(tests_ib_dict_DEPENDENCIES) tests/$(am__dirstamp) @rm -f tests/ib_dict$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tests_ib_dict_OBJECTS) $(tests_ib_dict_LDADD) $(LIBS) tests/ib_drop.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/ib_drop$(EXEEXT): $(tests_ib_drop_OBJECTS) $(tests_ib_drop_DEPENDENCIES) tests/$(am__dirstamp) @rm -f tests/ib_drop$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tests_ib_drop_OBJECTS) $(tests_ib_drop_LDADD) $(LIBS) tests/ib_duplicate_key_name.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/ib_duplicate_key_name$(EXEEXT): $(tests_ib_duplicate_key_name_OBJECTS) $(tests_ib_duplicate_key_name_DEPENDENCIES) tests/$(am__dirstamp) @rm -f tests/ib_duplicate_key_name$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tests_ib_duplicate_key_name_OBJECTS) $(tests_ib_duplicate_key_name_LDADD) $(LIBS) tests/ib_logger.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/ib_logger$(EXEEXT): $(tests_ib_logger_OBJECTS) $(tests_ib_logger_DEPENDENCIES) tests/$(am__dirstamp) @rm -f tests/ib_logger$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tests_ib_logger_OBJECTS) $(tests_ib_logger_LDADD) $(LIBS) tests/ib_mt_base.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/ib_mt_drv.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/ib_mt_t1.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/ib_mt_t2.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/ib_mt_drv$(EXEEXT): $(tests_ib_mt_drv_OBJECTS) $(tests_ib_mt_drv_DEPENDENCIES) tests/$(am__dirstamp) @rm -f tests/ib_mt_drv$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tests_ib_mt_drv_OBJECTS) $(tests_ib_mt_drv_LDADD) $(LIBS) tests/ib_panic.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/ib_panic$(EXEEXT): $(tests_ib_panic_OBJECTS) $(tests_ib_panic_DEPENDENCIES) tests/$(am__dirstamp) @rm -f tests/ib_panic$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tests_ib_panic_OBJECTS) $(tests_ib_panic_LDADD) $(LIBS) tests/ib_shutdown.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/ib_shutdown$(EXEEXT): $(tests_ib_shutdown_OBJECTS) $(tests_ib_shutdown_DEPENDENCIES) tests/$(am__dirstamp) @rm -f tests/ib_shutdown$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tests_ib_shutdown_OBJECTS) $(tests_ib_shutdown_LDADD) $(LIBS) tests/ib_status.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/ib_status$(EXEEXT): $(tests_ib_status_OBJECTS) $(tests_ib_status_DEPENDENCIES) tests/$(am__dirstamp) @rm -f tests/ib_status$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tests_ib_status_OBJECTS) $(tests_ib_status_LDADD) $(LIBS) tests/ib_table_statistics.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/ib_table_statistics$(EXEEXT): $(tests_ib_table_statistics_OBJECTS) $(tests_ib_table_statistics_DEPENDENCIES) tests/$(am__dirstamp) @rm -f tests/ib_table_statistics$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tests_ib_table_statistics_OBJECTS) $(tests_ib_table_statistics_LDADD) $(LIBS) tests/ib_tablename.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/ib_tablename$(EXEEXT): $(tests_ib_tablename_OBJECTS) $(tests_ib_tablename_DEPENDENCIES) tests/$(am__dirstamp) @rm -f tests/ib_tablename$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tests_ib_tablename_OBJECTS) $(tests_ib_tablename_LDADD) $(LIBS) tests/ib_test1.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/ib_test1$(EXEEXT): $(tests_ib_test1_OBJECTS) $(tests_ib_test1_DEPENDENCIES) tests/$(am__dirstamp) @rm -f tests/ib_test1$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tests_ib_test1_OBJECTS) $(tests_ib_test1_LDADD) $(LIBS) tests/ib_test2.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/ib_test2$(EXEEXT): $(tests_ib_test2_OBJECTS) $(tests_ib_test2_DEPENDENCIES) tests/$(am__dirstamp) @rm -f tests/ib_test2$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tests_ib_test2_OBJECTS) $(tests_ib_test2_LDADD) $(LIBS) tests/ib_test3.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/ib_test3$(EXEEXT): $(tests_ib_test3_OBJECTS) $(tests_ib_test3_DEPENDENCIES) tests/$(am__dirstamp) @rm -f tests/ib_test3$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tests_ib_test3_OBJECTS) $(tests_ib_test3_LDADD) $(LIBS) tests/ib_test5.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/ib_test5$(EXEEXT): $(tests_ib_test5_OBJECTS) $(tests_ib_test5_DEPENDENCIES) tests/$(am__dirstamp) @rm -f tests/ib_test5$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tests_ib_test5_OBJECTS) $(tests_ib_test5_LDADD) $(LIBS) tests/ib_trx_is_interrupted.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/ib_trx_is_interrupted$(EXEEXT): $(tests_ib_trx_is_interrupted_OBJECTS) $(tests_ib_trx_is_interrupted_DEPENDENCIES) tests/$(am__dirstamp) @rm -f tests/ib_trx_is_interrupted$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tests_ib_trx_is_interrupted_OBJECTS) $(tests_ib_trx_is_interrupted_LDADD) $(LIBS) tests/ib_types.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/ib_types$(EXEEXT): $(tests_ib_types_OBJECTS) $(tests_ib_types_DEPENDENCIES) tests/$(am__dirstamp) @rm -f tests/ib_types$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tests_ib_types_OBJECTS) $(tests_ib_types_LDADD) $(LIBS) tests/ib_update.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) tests/ib_update$(EXEEXT): $(tests_ib_update_OBJECTS) $(tests_ib_update_DEPENDENCIES) tests/$(am__dirstamp) @rm -f tests/ib_update$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tests_ib_update_OBJECTS) $(tests_ib_update_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) -rm -f api/libhaildb_la-api0api.$(OBJEXT) -rm -f api/libhaildb_la-api0api.lo -rm -f api/libhaildb_la-api0cfg.$(OBJEXT) -rm -f api/libhaildb_la-api0cfg.lo -rm -f api/libhaildb_la-api0misc.$(OBJEXT) -rm -f api/libhaildb_la-api0misc.lo -rm -f api/libhaildb_la-api0sql.$(OBJEXT) -rm -f api/libhaildb_la-api0sql.lo -rm -f api/libhaildb_la-api0status.$(OBJEXT) -rm -f api/libhaildb_la-api0status.lo -rm -f api/libhaildb_la-api0ucode.$(OBJEXT) -rm -f api/libhaildb_la-api0ucode.lo -rm -f btr/libhaildb_la-btr0btr.$(OBJEXT) -rm -f btr/libhaildb_la-btr0btr.lo -rm -f btr/libhaildb_la-btr0cur.$(OBJEXT) -rm -f btr/libhaildb_la-btr0cur.lo -rm -f btr/libhaildb_la-btr0pcur.$(OBJEXT) -rm -f btr/libhaildb_la-btr0pcur.lo -rm -f btr/libhaildb_la-btr0sea.$(OBJEXT) -rm -f btr/libhaildb_la-btr0sea.lo -rm -f buf/libhaildb_la-buf0buddy.$(OBJEXT) -rm -f buf/libhaildb_la-buf0buddy.lo -rm -f buf/libhaildb_la-buf0buf.$(OBJEXT) -rm -f buf/libhaildb_la-buf0buf.lo -rm -f buf/libhaildb_la-buf0flu.$(OBJEXT) -rm -f buf/libhaildb_la-buf0flu.lo -rm -f buf/libhaildb_la-buf0lru.$(OBJEXT) -rm -f buf/libhaildb_la-buf0lru.lo -rm -f buf/libhaildb_la-buf0rea.$(OBJEXT) -rm -f buf/libhaildb_la-buf0rea.lo -rm -f data/libhaildb_la-data0data.$(OBJEXT) -rm -f data/libhaildb_la-data0data.lo -rm -f data/libhaildb_la-data0type.$(OBJEXT) -rm -f data/libhaildb_la-data0type.lo -rm -f ddl/libhaildb_la-ddl0ddl.$(OBJEXT) -rm -f ddl/libhaildb_la-ddl0ddl.lo -rm -f dict/libhaildb_la-dict0boot.$(OBJEXT) -rm -f dict/libhaildb_la-dict0boot.lo -rm -f dict/libhaildb_la-dict0crea.$(OBJEXT) -rm -f dict/libhaildb_la-dict0crea.lo -rm -f dict/libhaildb_la-dict0dict.$(OBJEXT) -rm -f dict/libhaildb_la-dict0dict.lo -rm -f dict/libhaildb_la-dict0load.$(OBJEXT) -rm -f dict/libhaildb_la-dict0load.lo -rm -f dict/libhaildb_la-dict0mem.$(OBJEXT) -rm -f dict/libhaildb_la-dict0mem.lo -rm -f dyn/libhaildb_la-dyn0dyn.$(OBJEXT) -rm -f dyn/libhaildb_la-dyn0dyn.lo -rm -f eval/libhaildb_la-eval0eval.$(OBJEXT) -rm -f eval/libhaildb_la-eval0eval.lo -rm -f eval/libhaildb_la-eval0proc.$(OBJEXT) -rm -f eval/libhaildb_la-eval0proc.lo -rm -f fil/libhaildb_la-fil0fil.$(OBJEXT) -rm -f fil/libhaildb_la-fil0fil.lo -rm -f fsp/libhaildb_la-fsp0fsp.$(OBJEXT) -rm -f fsp/libhaildb_la-fsp0fsp.lo -rm -f fut/libhaildb_la-fut0fut.$(OBJEXT) -rm -f fut/libhaildb_la-fut0fut.lo -rm -f fut/libhaildb_la-fut0lst.$(OBJEXT) -rm -f fut/libhaildb_la-fut0lst.lo -rm -f ha/libhaildb_la-ha0ha.$(OBJEXT) -rm -f ha/libhaildb_la-ha0ha.lo -rm -f ha/libhaildb_la-ha0storage.$(OBJEXT) -rm -f ha/libhaildb_la-ha0storage.lo -rm -f ha/libhaildb_la-hash0hash.$(OBJEXT) -rm -f ha/libhaildb_la-hash0hash.lo -rm -f ibuf/libhaildb_la-ibuf0ibuf.$(OBJEXT) -rm -f ibuf/libhaildb_la-ibuf0ibuf.lo -rm -f lock/libhaildb_la-lock0iter.$(OBJEXT) -rm -f lock/libhaildb_la-lock0iter.lo -rm -f lock/libhaildb_la-lock0lock.$(OBJEXT) -rm -f lock/libhaildb_la-lock0lock.lo -rm -f log/libhaildb_la-log0log.$(OBJEXT) -rm -f log/libhaildb_la-log0log.lo -rm -f log/libhaildb_la-log0recv.$(OBJEXT) -rm -f log/libhaildb_la-log0recv.lo -rm -f mach/libhaildb_la-mach0data.$(OBJEXT) -rm -f mach/libhaildb_la-mach0data.lo -rm -f mem/libhaildb_la-mem0mem.$(OBJEXT) -rm -f mem/libhaildb_la-mem0mem.lo -rm -f mtr/libhaildb_la-mtr0log.$(OBJEXT) -rm -f mtr/libhaildb_la-mtr0log.lo -rm -f mtr/libhaildb_la-mtr0mtr.$(OBJEXT) -rm -f mtr/libhaildb_la-mtr0mtr.lo -rm -f os/libhaildb_la-os0file.$(OBJEXT) -rm -f os/libhaildb_la-os0file.lo -rm -f os/libhaildb_la-os0proc.$(OBJEXT) -rm -f os/libhaildb_la-os0proc.lo -rm -f os/libhaildb_la-os0sync.$(OBJEXT) -rm -f os/libhaildb_la-os0sync.lo -rm -f os/libhaildb_la-os0thread.$(OBJEXT) -rm -f os/libhaildb_la-os0thread.lo -rm -f page/libhaildb_la-page0cur.$(OBJEXT) -rm -f page/libhaildb_la-page0cur.lo -rm -f page/libhaildb_la-page0page.$(OBJEXT) -rm -f page/libhaildb_la-page0page.lo -rm -f page/libhaildb_la-page0zip.$(OBJEXT) -rm -f page/libhaildb_la-page0zip.lo -rm -f pars/libhaildb_la-lexyy.$(OBJEXT) -rm -f pars/libhaildb_la-lexyy.lo -rm -f pars/libhaildb_la-pars0grm.$(OBJEXT) -rm -f pars/libhaildb_la-pars0grm.lo -rm -f pars/libhaildb_la-pars0opt.$(OBJEXT) -rm -f pars/libhaildb_la-pars0opt.lo -rm -f pars/libhaildb_la-pars0pars.$(OBJEXT) -rm -f pars/libhaildb_la-pars0pars.lo -rm -f pars/libhaildb_la-pars0sym.$(OBJEXT) -rm -f pars/libhaildb_la-pars0sym.lo -rm -f que/libhaildb_la-que0que.$(OBJEXT) -rm -f que/libhaildb_la-que0que.lo -rm -f read/libhaildb_la-read0read.$(OBJEXT) -rm -f read/libhaildb_la-read0read.lo -rm -f rem/libhaildb_la-rem0cmp.$(OBJEXT) -rm -f rem/libhaildb_la-rem0cmp.lo -rm -f rem/libhaildb_la-rem0rec.$(OBJEXT) -rm -f rem/libhaildb_la-rem0rec.lo -rm -f row/libhaildb_la-row0ext.$(OBJEXT) -rm -f row/libhaildb_la-row0ext.lo -rm -f row/libhaildb_la-row0ins.$(OBJEXT) -rm -f row/libhaildb_la-row0ins.lo -rm -f row/libhaildb_la-row0merge.$(OBJEXT) -rm -f row/libhaildb_la-row0merge.lo -rm -f row/libhaildb_la-row0prebuilt.$(OBJEXT) -rm -f row/libhaildb_la-row0prebuilt.lo -rm -f row/libhaildb_la-row0purge.$(OBJEXT) -rm -f row/libhaildb_la-row0purge.lo -rm -f row/libhaildb_la-row0row.$(OBJEXT) -rm -f row/libhaildb_la-row0row.lo -rm -f row/libhaildb_la-row0sel.$(OBJEXT) -rm -f row/libhaildb_la-row0sel.lo -rm -f row/libhaildb_la-row0uins.$(OBJEXT) -rm -f row/libhaildb_la-row0uins.lo -rm -f row/libhaildb_la-row0umod.$(OBJEXT) -rm -f row/libhaildb_la-row0umod.lo -rm -f row/libhaildb_la-row0undo.$(OBJEXT) -rm -f row/libhaildb_la-row0undo.lo -rm -f row/libhaildb_la-row0upd.$(OBJEXT) -rm -f row/libhaildb_la-row0upd.lo -rm -f row/libhaildb_la-row0vers.$(OBJEXT) -rm -f row/libhaildb_la-row0vers.lo -rm -f srv/libhaildb_la-srv0que.$(OBJEXT) -rm -f srv/libhaildb_la-srv0que.lo -rm -f srv/libhaildb_la-srv0srv.$(OBJEXT) -rm -f srv/libhaildb_la-srv0srv.lo -rm -f srv/libhaildb_la-srv0start.$(OBJEXT) -rm -f srv/libhaildb_la-srv0start.lo -rm -f sync/libhaildb_la-sync0arr.$(OBJEXT) -rm -f sync/libhaildb_la-sync0arr.lo -rm -f sync/libhaildb_la-sync0rw.$(OBJEXT) -rm -f sync/libhaildb_la-sync0rw.lo -rm -f sync/libhaildb_la-sync0sync.$(OBJEXT) -rm -f sync/libhaildb_la-sync0sync.lo -rm -f tests/bug579934_open_index_by_name_segv.$(OBJEXT) -rm -f tests/ib_cfg.$(OBJEXT) -rm -f tests/ib_client_compare.$(OBJEXT) -rm -f tests/ib_cursor.$(OBJEXT) -rm -f tests/ib_ddl.$(OBJEXT) -rm -f tests/ib_dict.$(OBJEXT) -rm -f tests/ib_drop.$(OBJEXT) -rm -f tests/ib_duplicate_key_name.$(OBJEXT) -rm -f tests/ib_logger.$(OBJEXT) -rm -f tests/ib_mt_base.$(OBJEXT) -rm -f tests/ib_mt_drv.$(OBJEXT) -rm -f tests/ib_mt_t1.$(OBJEXT) -rm -f tests/ib_mt_t2.$(OBJEXT) -rm -f tests/ib_panic.$(OBJEXT) -rm -f tests/ib_shutdown.$(OBJEXT) -rm -f tests/ib_status.$(OBJEXT) -rm -f tests/ib_table_statistics.$(OBJEXT) -rm -f tests/ib_tablename.$(OBJEXT) -rm -f tests/ib_test1.$(OBJEXT) -rm -f tests/ib_test2.$(OBJEXT) -rm -f tests/ib_test3.$(OBJEXT) -rm -f tests/ib_test5.$(OBJEXT) -rm -f tests/ib_trx_is_interrupted.$(OBJEXT) -rm -f tests/ib_types.$(OBJEXT) -rm -f tests/ib_update.$(OBJEXT) -rm -f tests/test0aux.$(OBJEXT) -rm -f tests/test0aux.lo -rm -f thr/libhaildb_la-thr0loc.$(OBJEXT) -rm -f thr/libhaildb_la-thr0loc.lo -rm -f trx/libhaildb_la-trx0purge.$(OBJEXT) -rm -f trx/libhaildb_la-trx0purge.lo -rm -f trx/libhaildb_la-trx0rec.$(OBJEXT) -rm -f trx/libhaildb_la-trx0rec.lo -rm -f trx/libhaildb_la-trx0roll.$(OBJEXT) -rm -f trx/libhaildb_la-trx0roll.lo -rm -f trx/libhaildb_la-trx0rseg.$(OBJEXT) -rm -f trx/libhaildb_la-trx0rseg.lo -rm -f trx/libhaildb_la-trx0sys.$(OBJEXT) -rm -f trx/libhaildb_la-trx0sys.lo -rm -f trx/libhaildb_la-trx0trx.$(OBJEXT) -rm -f trx/libhaildb_la-trx0trx.lo -rm -f trx/libhaildb_la-trx0undo.$(OBJEXT) -rm -f trx/libhaildb_la-trx0undo.lo -rm -f usr/libhaildb_la-usr0sess.$(OBJEXT) -rm -f usr/libhaildb_la-usr0sess.lo -rm -f ut/libhaildb_la-ut0byte.$(OBJEXT) -rm -f ut/libhaildb_la-ut0byte.lo -rm -f ut/libhaildb_la-ut0dbg.$(OBJEXT) -rm -f ut/libhaildb_la-ut0dbg.lo -rm -f ut/libhaildb_la-ut0list.$(OBJEXT) -rm -f ut/libhaildb_la-ut0list.lo -rm -f ut/libhaildb_la-ut0mem.$(OBJEXT) -rm -f ut/libhaildb_la-ut0mem.lo -rm -f ut/libhaildb_la-ut0rbt.$(OBJEXT) -rm -f ut/libhaildb_la-ut0rbt.lo -rm -f ut/libhaildb_la-ut0rnd.$(OBJEXT) -rm -f ut/libhaildb_la-ut0rnd.lo -rm -f ut/libhaildb_la-ut0ut.$(OBJEXT) -rm -f ut/libhaildb_la-ut0ut.lo -rm -f ut/libhaildb_la-ut0vec.$(OBJEXT) -rm -f ut/libhaildb_la-ut0vec.lo distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@api/$(DEPDIR)/libhaildb_la-api0api.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@api/$(DEPDIR)/libhaildb_la-api0cfg.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@api/$(DEPDIR)/libhaildb_la-api0misc.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@api/$(DEPDIR)/libhaildb_la-api0sql.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@api/$(DEPDIR)/libhaildb_la-api0status.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@api/$(DEPDIR)/libhaildb_la-api0ucode.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@btr/$(DEPDIR)/libhaildb_la-btr0btr.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@btr/$(DEPDIR)/libhaildb_la-btr0cur.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@btr/$(DEPDIR)/libhaildb_la-btr0pcur.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@btr/$(DEPDIR)/libhaildb_la-btr0sea.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@buf/$(DEPDIR)/libhaildb_la-buf0buddy.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@buf/$(DEPDIR)/libhaildb_la-buf0buf.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@buf/$(DEPDIR)/libhaildb_la-buf0flu.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@buf/$(DEPDIR)/libhaildb_la-buf0lru.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@buf/$(DEPDIR)/libhaildb_la-buf0rea.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@data/$(DEPDIR)/libhaildb_la-data0data.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@data/$(DEPDIR)/libhaildb_la-data0type.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ddl/$(DEPDIR)/libhaildb_la-ddl0ddl.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@dict/$(DEPDIR)/libhaildb_la-dict0boot.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@dict/$(DEPDIR)/libhaildb_la-dict0crea.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@dict/$(DEPDIR)/libhaildb_la-dict0dict.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@dict/$(DEPDIR)/libhaildb_la-dict0load.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@dict/$(DEPDIR)/libhaildb_la-dict0mem.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@dyn/$(DEPDIR)/libhaildb_la-dyn0dyn.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@eval/$(DEPDIR)/libhaildb_la-eval0eval.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@eval/$(DEPDIR)/libhaildb_la-eval0proc.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@fil/$(DEPDIR)/libhaildb_la-fil0fil.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@fsp/$(DEPDIR)/libhaildb_la-fsp0fsp.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@fut/$(DEPDIR)/libhaildb_la-fut0fut.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@fut/$(DEPDIR)/libhaildb_la-fut0lst.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ha/$(DEPDIR)/libhaildb_la-ha0ha.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ha/$(DEPDIR)/libhaildb_la-ha0storage.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ha/$(DEPDIR)/libhaildb_la-hash0hash.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ibuf/$(DEPDIR)/libhaildb_la-ibuf0ibuf.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lock/$(DEPDIR)/libhaildb_la-lock0iter.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@lock/$(DEPDIR)/libhaildb_la-lock0lock.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@log/$(DEPDIR)/libhaildb_la-log0log.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@log/$(DEPDIR)/libhaildb_la-log0recv.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@mach/$(DEPDIR)/libhaildb_la-mach0data.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@mem/$(DEPDIR)/libhaildb_la-mem0mem.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@mtr/$(DEPDIR)/libhaildb_la-mtr0log.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@mtr/$(DEPDIR)/libhaildb_la-mtr0mtr.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@os/$(DEPDIR)/libhaildb_la-os0file.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@os/$(DEPDIR)/libhaildb_la-os0proc.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@os/$(DEPDIR)/libhaildb_la-os0sync.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@os/$(DEPDIR)/libhaildb_la-os0thread.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@page/$(DEPDIR)/libhaildb_la-page0cur.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@page/$(DEPDIR)/libhaildb_la-page0page.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@page/$(DEPDIR)/libhaildb_la-page0zip.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@pars/$(DEPDIR)/libhaildb_la-lexyy.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@pars/$(DEPDIR)/libhaildb_la-pars0grm.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@pars/$(DEPDIR)/libhaildb_la-pars0opt.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@pars/$(DEPDIR)/libhaildb_la-pars0pars.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@pars/$(DEPDIR)/libhaildb_la-pars0sym.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@que/$(DEPDIR)/libhaildb_la-que0que.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@read/$(DEPDIR)/libhaildb_la-read0read.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@rem/$(DEPDIR)/libhaildb_la-rem0cmp.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@rem/$(DEPDIR)/libhaildb_la-rem0rec.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@row/$(DEPDIR)/libhaildb_la-row0ext.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@row/$(DEPDIR)/libhaildb_la-row0ins.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@row/$(DEPDIR)/libhaildb_la-row0merge.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@row/$(DEPDIR)/libhaildb_la-row0prebuilt.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@row/$(DEPDIR)/libhaildb_la-row0purge.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@row/$(DEPDIR)/libhaildb_la-row0row.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@row/$(DEPDIR)/libhaildb_la-row0sel.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@row/$(DEPDIR)/libhaildb_la-row0uins.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@row/$(DEPDIR)/libhaildb_la-row0umod.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@row/$(DEPDIR)/libhaildb_la-row0undo.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@row/$(DEPDIR)/libhaildb_la-row0upd.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@row/$(DEPDIR)/libhaildb_la-row0vers.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@srv/$(DEPDIR)/libhaildb_la-srv0que.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@srv/$(DEPDIR)/libhaildb_la-srv0srv.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@srv/$(DEPDIR)/libhaildb_la-srv0start.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@sync/$(DEPDIR)/libhaildb_la-sync0arr.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@sync/$(DEPDIR)/libhaildb_la-sync0rw.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@sync/$(DEPDIR)/libhaildb_la-sync0sync.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/bug579934_open_index_by_name_segv.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/ib_cfg.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/ib_client_compare.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/ib_cursor.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/ib_ddl.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/ib_dict.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/ib_drop.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/ib_duplicate_key_name.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/ib_logger.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/ib_mt_base.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/ib_mt_drv.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/ib_mt_t1.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/ib_mt_t2.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/ib_panic.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/ib_shutdown.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/ib_status.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/ib_table_statistics.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/ib_tablename.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/ib_test1.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/ib_test2.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/ib_test3.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/ib_test5.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/ib_trx_is_interrupted.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/ib_types.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/ib_update.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/test0aux.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@thr/$(DEPDIR)/libhaildb_la-thr0loc.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@trx/$(DEPDIR)/libhaildb_la-trx0purge.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@trx/$(DEPDIR)/libhaildb_la-trx0rec.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@trx/$(DEPDIR)/libhaildb_la-trx0roll.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@trx/$(DEPDIR)/libhaildb_la-trx0rseg.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@trx/$(DEPDIR)/libhaildb_la-trx0sys.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@trx/$(DEPDIR)/libhaildb_la-trx0trx.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@trx/$(DEPDIR)/libhaildb_la-trx0undo.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@usr/$(DEPDIR)/libhaildb_la-usr0sess.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ut/$(DEPDIR)/libhaildb_la-ut0byte.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ut/$(DEPDIR)/libhaildb_la-ut0dbg.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ut/$(DEPDIR)/libhaildb_la-ut0list.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ut/$(DEPDIR)/libhaildb_la-ut0mem.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ut/$(DEPDIR)/libhaildb_la-ut0rbt.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ut/$(DEPDIR)/libhaildb_la-ut0rnd.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ut/$(DEPDIR)/libhaildb_la-ut0ut.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ut/$(DEPDIR)/libhaildb_la-ut0vec.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ @am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< btr/libhaildb_la-btr0btr.lo: btr/btr0btr.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT btr/libhaildb_la-btr0btr.lo -MD -MP -MF btr/$(DEPDIR)/libhaildb_la-btr0btr.Tpo -c -o btr/libhaildb_la-btr0btr.lo `test -f 'btr/btr0btr.c' || echo '$(srcdir)/'`btr/btr0btr.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) btr/$(DEPDIR)/libhaildb_la-btr0btr.Tpo btr/$(DEPDIR)/libhaildb_la-btr0btr.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='btr/btr0btr.c' object='btr/libhaildb_la-btr0btr.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o btr/libhaildb_la-btr0btr.lo `test -f 'btr/btr0btr.c' || echo '$(srcdir)/'`btr/btr0btr.c btr/libhaildb_la-btr0cur.lo: btr/btr0cur.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT btr/libhaildb_la-btr0cur.lo -MD -MP -MF btr/$(DEPDIR)/libhaildb_la-btr0cur.Tpo -c -o btr/libhaildb_la-btr0cur.lo `test -f 'btr/btr0cur.c' || echo '$(srcdir)/'`btr/btr0cur.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) btr/$(DEPDIR)/libhaildb_la-btr0cur.Tpo btr/$(DEPDIR)/libhaildb_la-btr0cur.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='btr/btr0cur.c' object='btr/libhaildb_la-btr0cur.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o btr/libhaildb_la-btr0cur.lo `test -f 'btr/btr0cur.c' || echo '$(srcdir)/'`btr/btr0cur.c btr/libhaildb_la-btr0pcur.lo: btr/btr0pcur.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT btr/libhaildb_la-btr0pcur.lo -MD -MP -MF btr/$(DEPDIR)/libhaildb_la-btr0pcur.Tpo -c -o btr/libhaildb_la-btr0pcur.lo `test -f 'btr/btr0pcur.c' || echo '$(srcdir)/'`btr/btr0pcur.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) btr/$(DEPDIR)/libhaildb_la-btr0pcur.Tpo btr/$(DEPDIR)/libhaildb_la-btr0pcur.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='btr/btr0pcur.c' object='btr/libhaildb_la-btr0pcur.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o btr/libhaildb_la-btr0pcur.lo `test -f 'btr/btr0pcur.c' || echo '$(srcdir)/'`btr/btr0pcur.c btr/libhaildb_la-btr0sea.lo: btr/btr0sea.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT btr/libhaildb_la-btr0sea.lo -MD -MP -MF btr/$(DEPDIR)/libhaildb_la-btr0sea.Tpo -c -o btr/libhaildb_la-btr0sea.lo `test -f 'btr/btr0sea.c' || echo '$(srcdir)/'`btr/btr0sea.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) btr/$(DEPDIR)/libhaildb_la-btr0sea.Tpo btr/$(DEPDIR)/libhaildb_la-btr0sea.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='btr/btr0sea.c' object='btr/libhaildb_la-btr0sea.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o btr/libhaildb_la-btr0sea.lo `test -f 'btr/btr0sea.c' || echo '$(srcdir)/'`btr/btr0sea.c buf/libhaildb_la-buf0buf.lo: buf/buf0buf.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT buf/libhaildb_la-buf0buf.lo -MD -MP -MF buf/$(DEPDIR)/libhaildb_la-buf0buf.Tpo -c -o buf/libhaildb_la-buf0buf.lo `test -f 'buf/buf0buf.c' || echo '$(srcdir)/'`buf/buf0buf.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) buf/$(DEPDIR)/libhaildb_la-buf0buf.Tpo buf/$(DEPDIR)/libhaildb_la-buf0buf.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='buf/buf0buf.c' object='buf/libhaildb_la-buf0buf.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o buf/libhaildb_la-buf0buf.lo `test -f 'buf/buf0buf.c' || echo '$(srcdir)/'`buf/buf0buf.c buf/libhaildb_la-buf0flu.lo: buf/buf0flu.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT buf/libhaildb_la-buf0flu.lo -MD -MP -MF buf/$(DEPDIR)/libhaildb_la-buf0flu.Tpo -c -o buf/libhaildb_la-buf0flu.lo `test -f 'buf/buf0flu.c' || echo '$(srcdir)/'`buf/buf0flu.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) buf/$(DEPDIR)/libhaildb_la-buf0flu.Tpo buf/$(DEPDIR)/libhaildb_la-buf0flu.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='buf/buf0flu.c' object='buf/libhaildb_la-buf0flu.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o buf/libhaildb_la-buf0flu.lo `test -f 'buf/buf0flu.c' || echo '$(srcdir)/'`buf/buf0flu.c buf/libhaildb_la-buf0lru.lo: buf/buf0lru.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT buf/libhaildb_la-buf0lru.lo -MD -MP -MF buf/$(DEPDIR)/libhaildb_la-buf0lru.Tpo -c -o buf/libhaildb_la-buf0lru.lo `test -f 'buf/buf0lru.c' || echo '$(srcdir)/'`buf/buf0lru.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) buf/$(DEPDIR)/libhaildb_la-buf0lru.Tpo buf/$(DEPDIR)/libhaildb_la-buf0lru.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='buf/buf0lru.c' object='buf/libhaildb_la-buf0lru.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o buf/libhaildb_la-buf0lru.lo `test -f 'buf/buf0lru.c' || echo '$(srcdir)/'`buf/buf0lru.c buf/libhaildb_la-buf0rea.lo: buf/buf0rea.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT buf/libhaildb_la-buf0rea.lo -MD -MP -MF buf/$(DEPDIR)/libhaildb_la-buf0rea.Tpo -c -o buf/libhaildb_la-buf0rea.lo `test -f 'buf/buf0rea.c' || echo '$(srcdir)/'`buf/buf0rea.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) buf/$(DEPDIR)/libhaildb_la-buf0rea.Tpo buf/$(DEPDIR)/libhaildb_la-buf0rea.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='buf/buf0rea.c' object='buf/libhaildb_la-buf0rea.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o buf/libhaildb_la-buf0rea.lo `test -f 'buf/buf0rea.c' || echo '$(srcdir)/'`buf/buf0rea.c data/libhaildb_la-data0data.lo: data/data0data.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT data/libhaildb_la-data0data.lo -MD -MP -MF data/$(DEPDIR)/libhaildb_la-data0data.Tpo -c -o data/libhaildb_la-data0data.lo `test -f 'data/data0data.c' || echo '$(srcdir)/'`data/data0data.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) data/$(DEPDIR)/libhaildb_la-data0data.Tpo data/$(DEPDIR)/libhaildb_la-data0data.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='data/data0data.c' object='data/libhaildb_la-data0data.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o data/libhaildb_la-data0data.lo `test -f 'data/data0data.c' || echo '$(srcdir)/'`data/data0data.c data/libhaildb_la-data0type.lo: data/data0type.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT data/libhaildb_la-data0type.lo -MD -MP -MF data/$(DEPDIR)/libhaildb_la-data0type.Tpo -c -o data/libhaildb_la-data0type.lo `test -f 'data/data0type.c' || echo '$(srcdir)/'`data/data0type.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) data/$(DEPDIR)/libhaildb_la-data0type.Tpo data/$(DEPDIR)/libhaildb_la-data0type.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='data/data0type.c' object='data/libhaildb_la-data0type.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o data/libhaildb_la-data0type.lo `test -f 'data/data0type.c' || echo '$(srcdir)/'`data/data0type.c dict/libhaildb_la-dict0boot.lo: dict/dict0boot.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT dict/libhaildb_la-dict0boot.lo -MD -MP -MF dict/$(DEPDIR)/libhaildb_la-dict0boot.Tpo -c -o dict/libhaildb_la-dict0boot.lo `test -f 'dict/dict0boot.c' || echo '$(srcdir)/'`dict/dict0boot.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) dict/$(DEPDIR)/libhaildb_la-dict0boot.Tpo dict/$(DEPDIR)/libhaildb_la-dict0boot.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='dict/dict0boot.c' object='dict/libhaildb_la-dict0boot.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o dict/libhaildb_la-dict0boot.lo `test -f 'dict/dict0boot.c' || echo '$(srcdir)/'`dict/dict0boot.c dict/libhaildb_la-dict0crea.lo: dict/dict0crea.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT dict/libhaildb_la-dict0crea.lo -MD -MP -MF dict/$(DEPDIR)/libhaildb_la-dict0crea.Tpo -c -o dict/libhaildb_la-dict0crea.lo `test -f 'dict/dict0crea.c' || echo '$(srcdir)/'`dict/dict0crea.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) dict/$(DEPDIR)/libhaildb_la-dict0crea.Tpo dict/$(DEPDIR)/libhaildb_la-dict0crea.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='dict/dict0crea.c' object='dict/libhaildb_la-dict0crea.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o dict/libhaildb_la-dict0crea.lo `test -f 'dict/dict0crea.c' || echo '$(srcdir)/'`dict/dict0crea.c dict/libhaildb_la-dict0dict.lo: dict/dict0dict.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT dict/libhaildb_la-dict0dict.lo -MD -MP -MF dict/$(DEPDIR)/libhaildb_la-dict0dict.Tpo -c -o dict/libhaildb_la-dict0dict.lo `test -f 'dict/dict0dict.c' || echo '$(srcdir)/'`dict/dict0dict.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) dict/$(DEPDIR)/libhaildb_la-dict0dict.Tpo dict/$(DEPDIR)/libhaildb_la-dict0dict.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='dict/dict0dict.c' object='dict/libhaildb_la-dict0dict.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o dict/libhaildb_la-dict0dict.lo `test -f 'dict/dict0dict.c' || echo '$(srcdir)/'`dict/dict0dict.c dict/libhaildb_la-dict0load.lo: dict/dict0load.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT dict/libhaildb_la-dict0load.lo -MD -MP -MF dict/$(DEPDIR)/libhaildb_la-dict0load.Tpo -c -o dict/libhaildb_la-dict0load.lo `test -f 'dict/dict0load.c' || echo '$(srcdir)/'`dict/dict0load.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) dict/$(DEPDIR)/libhaildb_la-dict0load.Tpo dict/$(DEPDIR)/libhaildb_la-dict0load.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='dict/dict0load.c' object='dict/libhaildb_la-dict0load.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o dict/libhaildb_la-dict0load.lo `test -f 'dict/dict0load.c' || echo '$(srcdir)/'`dict/dict0load.c dict/libhaildb_la-dict0mem.lo: dict/dict0mem.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT dict/libhaildb_la-dict0mem.lo -MD -MP -MF dict/$(DEPDIR)/libhaildb_la-dict0mem.Tpo -c -o dict/libhaildb_la-dict0mem.lo `test -f 'dict/dict0mem.c' || echo '$(srcdir)/'`dict/dict0mem.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) dict/$(DEPDIR)/libhaildb_la-dict0mem.Tpo dict/$(DEPDIR)/libhaildb_la-dict0mem.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='dict/dict0mem.c' object='dict/libhaildb_la-dict0mem.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o dict/libhaildb_la-dict0mem.lo `test -f 'dict/dict0mem.c' || echo '$(srcdir)/'`dict/dict0mem.c dyn/libhaildb_la-dyn0dyn.lo: dyn/dyn0dyn.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT dyn/libhaildb_la-dyn0dyn.lo -MD -MP -MF dyn/$(DEPDIR)/libhaildb_la-dyn0dyn.Tpo -c -o dyn/libhaildb_la-dyn0dyn.lo `test -f 'dyn/dyn0dyn.c' || echo '$(srcdir)/'`dyn/dyn0dyn.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) dyn/$(DEPDIR)/libhaildb_la-dyn0dyn.Tpo dyn/$(DEPDIR)/libhaildb_la-dyn0dyn.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='dyn/dyn0dyn.c' object='dyn/libhaildb_la-dyn0dyn.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o dyn/libhaildb_la-dyn0dyn.lo `test -f 'dyn/dyn0dyn.c' || echo '$(srcdir)/'`dyn/dyn0dyn.c eval/libhaildb_la-eval0eval.lo: eval/eval0eval.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT eval/libhaildb_la-eval0eval.lo -MD -MP -MF eval/$(DEPDIR)/libhaildb_la-eval0eval.Tpo -c -o eval/libhaildb_la-eval0eval.lo `test -f 'eval/eval0eval.c' || echo '$(srcdir)/'`eval/eval0eval.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) eval/$(DEPDIR)/libhaildb_la-eval0eval.Tpo eval/$(DEPDIR)/libhaildb_la-eval0eval.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='eval/eval0eval.c' object='eval/libhaildb_la-eval0eval.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o eval/libhaildb_la-eval0eval.lo `test -f 'eval/eval0eval.c' || echo '$(srcdir)/'`eval/eval0eval.c eval/libhaildb_la-eval0proc.lo: eval/eval0proc.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT eval/libhaildb_la-eval0proc.lo -MD -MP -MF eval/$(DEPDIR)/libhaildb_la-eval0proc.Tpo -c -o eval/libhaildb_la-eval0proc.lo `test -f 'eval/eval0proc.c' || echo '$(srcdir)/'`eval/eval0proc.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) eval/$(DEPDIR)/libhaildb_la-eval0proc.Tpo eval/$(DEPDIR)/libhaildb_la-eval0proc.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='eval/eval0proc.c' object='eval/libhaildb_la-eval0proc.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o eval/libhaildb_la-eval0proc.lo `test -f 'eval/eval0proc.c' || echo '$(srcdir)/'`eval/eval0proc.c fil/libhaildb_la-fil0fil.lo: fil/fil0fil.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT fil/libhaildb_la-fil0fil.lo -MD -MP -MF fil/$(DEPDIR)/libhaildb_la-fil0fil.Tpo -c -o fil/libhaildb_la-fil0fil.lo `test -f 'fil/fil0fil.c' || echo '$(srcdir)/'`fil/fil0fil.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) fil/$(DEPDIR)/libhaildb_la-fil0fil.Tpo fil/$(DEPDIR)/libhaildb_la-fil0fil.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='fil/fil0fil.c' object='fil/libhaildb_la-fil0fil.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o fil/libhaildb_la-fil0fil.lo `test -f 'fil/fil0fil.c' || echo '$(srcdir)/'`fil/fil0fil.c fsp/libhaildb_la-fsp0fsp.lo: fsp/fsp0fsp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT fsp/libhaildb_la-fsp0fsp.lo -MD -MP -MF fsp/$(DEPDIR)/libhaildb_la-fsp0fsp.Tpo -c -o fsp/libhaildb_la-fsp0fsp.lo `test -f 'fsp/fsp0fsp.c' || echo '$(srcdir)/'`fsp/fsp0fsp.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) fsp/$(DEPDIR)/libhaildb_la-fsp0fsp.Tpo fsp/$(DEPDIR)/libhaildb_la-fsp0fsp.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='fsp/fsp0fsp.c' object='fsp/libhaildb_la-fsp0fsp.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o fsp/libhaildb_la-fsp0fsp.lo `test -f 'fsp/fsp0fsp.c' || echo '$(srcdir)/'`fsp/fsp0fsp.c fut/libhaildb_la-fut0fut.lo: fut/fut0fut.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT fut/libhaildb_la-fut0fut.lo -MD -MP -MF fut/$(DEPDIR)/libhaildb_la-fut0fut.Tpo -c -o fut/libhaildb_la-fut0fut.lo `test -f 'fut/fut0fut.c' || echo '$(srcdir)/'`fut/fut0fut.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) fut/$(DEPDIR)/libhaildb_la-fut0fut.Tpo fut/$(DEPDIR)/libhaildb_la-fut0fut.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='fut/fut0fut.c' object='fut/libhaildb_la-fut0fut.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o fut/libhaildb_la-fut0fut.lo `test -f 'fut/fut0fut.c' || echo '$(srcdir)/'`fut/fut0fut.c fut/libhaildb_la-fut0lst.lo: fut/fut0lst.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT fut/libhaildb_la-fut0lst.lo -MD -MP -MF fut/$(DEPDIR)/libhaildb_la-fut0lst.Tpo -c -o fut/libhaildb_la-fut0lst.lo `test -f 'fut/fut0lst.c' || echo '$(srcdir)/'`fut/fut0lst.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) fut/$(DEPDIR)/libhaildb_la-fut0lst.Tpo fut/$(DEPDIR)/libhaildb_la-fut0lst.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='fut/fut0lst.c' object='fut/libhaildb_la-fut0lst.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o fut/libhaildb_la-fut0lst.lo `test -f 'fut/fut0lst.c' || echo '$(srcdir)/'`fut/fut0lst.c ha/libhaildb_la-ha0ha.lo: ha/ha0ha.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT ha/libhaildb_la-ha0ha.lo -MD -MP -MF ha/$(DEPDIR)/libhaildb_la-ha0ha.Tpo -c -o ha/libhaildb_la-ha0ha.lo `test -f 'ha/ha0ha.c' || echo '$(srcdir)/'`ha/ha0ha.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ha/$(DEPDIR)/libhaildb_la-ha0ha.Tpo ha/$(DEPDIR)/libhaildb_la-ha0ha.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ha/ha0ha.c' object='ha/libhaildb_la-ha0ha.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o ha/libhaildb_la-ha0ha.lo `test -f 'ha/ha0ha.c' || echo '$(srcdir)/'`ha/ha0ha.c ha/libhaildb_la-ha0storage.lo: ha/ha0storage.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT ha/libhaildb_la-ha0storage.lo -MD -MP -MF ha/$(DEPDIR)/libhaildb_la-ha0storage.Tpo -c -o ha/libhaildb_la-ha0storage.lo `test -f 'ha/ha0storage.c' || echo '$(srcdir)/'`ha/ha0storage.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ha/$(DEPDIR)/libhaildb_la-ha0storage.Tpo ha/$(DEPDIR)/libhaildb_la-ha0storage.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ha/ha0storage.c' object='ha/libhaildb_la-ha0storage.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o ha/libhaildb_la-ha0storage.lo `test -f 'ha/ha0storage.c' || echo '$(srcdir)/'`ha/ha0storage.c ha/libhaildb_la-hash0hash.lo: ha/hash0hash.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT ha/libhaildb_la-hash0hash.lo -MD -MP -MF ha/$(DEPDIR)/libhaildb_la-hash0hash.Tpo -c -o ha/libhaildb_la-hash0hash.lo `test -f 'ha/hash0hash.c' || echo '$(srcdir)/'`ha/hash0hash.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ha/$(DEPDIR)/libhaildb_la-hash0hash.Tpo ha/$(DEPDIR)/libhaildb_la-hash0hash.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ha/hash0hash.c' object='ha/libhaildb_la-hash0hash.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o ha/libhaildb_la-hash0hash.lo `test -f 'ha/hash0hash.c' || echo '$(srcdir)/'`ha/hash0hash.c ibuf/libhaildb_la-ibuf0ibuf.lo: ibuf/ibuf0ibuf.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT ibuf/libhaildb_la-ibuf0ibuf.lo -MD -MP -MF ibuf/$(DEPDIR)/libhaildb_la-ibuf0ibuf.Tpo -c -o ibuf/libhaildb_la-ibuf0ibuf.lo `test -f 'ibuf/ibuf0ibuf.c' || echo '$(srcdir)/'`ibuf/ibuf0ibuf.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ibuf/$(DEPDIR)/libhaildb_la-ibuf0ibuf.Tpo ibuf/$(DEPDIR)/libhaildb_la-ibuf0ibuf.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ibuf/ibuf0ibuf.c' object='ibuf/libhaildb_la-ibuf0ibuf.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o ibuf/libhaildb_la-ibuf0ibuf.lo `test -f 'ibuf/ibuf0ibuf.c' || echo '$(srcdir)/'`ibuf/ibuf0ibuf.c lock/libhaildb_la-lock0iter.lo: lock/lock0iter.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT lock/libhaildb_la-lock0iter.lo -MD -MP -MF lock/$(DEPDIR)/libhaildb_la-lock0iter.Tpo -c -o lock/libhaildb_la-lock0iter.lo `test -f 'lock/lock0iter.c' || echo '$(srcdir)/'`lock/lock0iter.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) lock/$(DEPDIR)/libhaildb_la-lock0iter.Tpo lock/$(DEPDIR)/libhaildb_la-lock0iter.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='lock/lock0iter.c' object='lock/libhaildb_la-lock0iter.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o lock/libhaildb_la-lock0iter.lo `test -f 'lock/lock0iter.c' || echo '$(srcdir)/'`lock/lock0iter.c lock/libhaildb_la-lock0lock.lo: lock/lock0lock.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT lock/libhaildb_la-lock0lock.lo -MD -MP -MF lock/$(DEPDIR)/libhaildb_la-lock0lock.Tpo -c -o lock/libhaildb_la-lock0lock.lo `test -f 'lock/lock0lock.c' || echo '$(srcdir)/'`lock/lock0lock.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) lock/$(DEPDIR)/libhaildb_la-lock0lock.Tpo lock/$(DEPDIR)/libhaildb_la-lock0lock.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='lock/lock0lock.c' object='lock/libhaildb_la-lock0lock.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o lock/libhaildb_la-lock0lock.lo `test -f 'lock/lock0lock.c' || echo '$(srcdir)/'`lock/lock0lock.c log/libhaildb_la-log0log.lo: log/log0log.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT log/libhaildb_la-log0log.lo -MD -MP -MF log/$(DEPDIR)/libhaildb_la-log0log.Tpo -c -o log/libhaildb_la-log0log.lo `test -f 'log/log0log.c' || echo '$(srcdir)/'`log/log0log.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) log/$(DEPDIR)/libhaildb_la-log0log.Tpo log/$(DEPDIR)/libhaildb_la-log0log.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='log/log0log.c' object='log/libhaildb_la-log0log.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o log/libhaildb_la-log0log.lo `test -f 'log/log0log.c' || echo '$(srcdir)/'`log/log0log.c log/libhaildb_la-log0recv.lo: log/log0recv.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT log/libhaildb_la-log0recv.lo -MD -MP -MF log/$(DEPDIR)/libhaildb_la-log0recv.Tpo -c -o log/libhaildb_la-log0recv.lo `test -f 'log/log0recv.c' || echo '$(srcdir)/'`log/log0recv.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) log/$(DEPDIR)/libhaildb_la-log0recv.Tpo log/$(DEPDIR)/libhaildb_la-log0recv.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='log/log0recv.c' object='log/libhaildb_la-log0recv.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o log/libhaildb_la-log0recv.lo `test -f 'log/log0recv.c' || echo '$(srcdir)/'`log/log0recv.c mach/libhaildb_la-mach0data.lo: mach/mach0data.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT mach/libhaildb_la-mach0data.lo -MD -MP -MF mach/$(DEPDIR)/libhaildb_la-mach0data.Tpo -c -o mach/libhaildb_la-mach0data.lo `test -f 'mach/mach0data.c' || echo '$(srcdir)/'`mach/mach0data.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) mach/$(DEPDIR)/libhaildb_la-mach0data.Tpo mach/$(DEPDIR)/libhaildb_la-mach0data.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='mach/mach0data.c' object='mach/libhaildb_la-mach0data.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o mach/libhaildb_la-mach0data.lo `test -f 'mach/mach0data.c' || echo '$(srcdir)/'`mach/mach0data.c mem/libhaildb_la-mem0mem.lo: mem/mem0mem.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT mem/libhaildb_la-mem0mem.lo -MD -MP -MF mem/$(DEPDIR)/libhaildb_la-mem0mem.Tpo -c -o mem/libhaildb_la-mem0mem.lo `test -f 'mem/mem0mem.c' || echo '$(srcdir)/'`mem/mem0mem.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) mem/$(DEPDIR)/libhaildb_la-mem0mem.Tpo mem/$(DEPDIR)/libhaildb_la-mem0mem.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='mem/mem0mem.c' object='mem/libhaildb_la-mem0mem.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o mem/libhaildb_la-mem0mem.lo `test -f 'mem/mem0mem.c' || echo '$(srcdir)/'`mem/mem0mem.c mtr/libhaildb_la-mtr0log.lo: mtr/mtr0log.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT mtr/libhaildb_la-mtr0log.lo -MD -MP -MF mtr/$(DEPDIR)/libhaildb_la-mtr0log.Tpo -c -o mtr/libhaildb_la-mtr0log.lo `test -f 'mtr/mtr0log.c' || echo '$(srcdir)/'`mtr/mtr0log.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) mtr/$(DEPDIR)/libhaildb_la-mtr0log.Tpo mtr/$(DEPDIR)/libhaildb_la-mtr0log.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='mtr/mtr0log.c' object='mtr/libhaildb_la-mtr0log.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o mtr/libhaildb_la-mtr0log.lo `test -f 'mtr/mtr0log.c' || echo '$(srcdir)/'`mtr/mtr0log.c mtr/libhaildb_la-mtr0mtr.lo: mtr/mtr0mtr.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT mtr/libhaildb_la-mtr0mtr.lo -MD -MP -MF mtr/$(DEPDIR)/libhaildb_la-mtr0mtr.Tpo -c -o mtr/libhaildb_la-mtr0mtr.lo `test -f 'mtr/mtr0mtr.c' || echo '$(srcdir)/'`mtr/mtr0mtr.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) mtr/$(DEPDIR)/libhaildb_la-mtr0mtr.Tpo mtr/$(DEPDIR)/libhaildb_la-mtr0mtr.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='mtr/mtr0mtr.c' object='mtr/libhaildb_la-mtr0mtr.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o mtr/libhaildb_la-mtr0mtr.lo `test -f 'mtr/mtr0mtr.c' || echo '$(srcdir)/'`mtr/mtr0mtr.c os/libhaildb_la-os0file.lo: os/os0file.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT os/libhaildb_la-os0file.lo -MD -MP -MF os/$(DEPDIR)/libhaildb_la-os0file.Tpo -c -o os/libhaildb_la-os0file.lo `test -f 'os/os0file.c' || echo '$(srcdir)/'`os/os0file.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) os/$(DEPDIR)/libhaildb_la-os0file.Tpo os/$(DEPDIR)/libhaildb_la-os0file.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='os/os0file.c' object='os/libhaildb_la-os0file.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o os/libhaildb_la-os0file.lo `test -f 'os/os0file.c' || echo '$(srcdir)/'`os/os0file.c os/libhaildb_la-os0proc.lo: os/os0proc.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT os/libhaildb_la-os0proc.lo -MD -MP -MF os/$(DEPDIR)/libhaildb_la-os0proc.Tpo -c -o os/libhaildb_la-os0proc.lo `test -f 'os/os0proc.c' || echo '$(srcdir)/'`os/os0proc.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) os/$(DEPDIR)/libhaildb_la-os0proc.Tpo os/$(DEPDIR)/libhaildb_la-os0proc.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='os/os0proc.c' object='os/libhaildb_la-os0proc.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o os/libhaildb_la-os0proc.lo `test -f 'os/os0proc.c' || echo '$(srcdir)/'`os/os0proc.c os/libhaildb_la-os0sync.lo: os/os0sync.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT os/libhaildb_la-os0sync.lo -MD -MP -MF os/$(DEPDIR)/libhaildb_la-os0sync.Tpo -c -o os/libhaildb_la-os0sync.lo `test -f 'os/os0sync.c' || echo '$(srcdir)/'`os/os0sync.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) os/$(DEPDIR)/libhaildb_la-os0sync.Tpo os/$(DEPDIR)/libhaildb_la-os0sync.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='os/os0sync.c' object='os/libhaildb_la-os0sync.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o os/libhaildb_la-os0sync.lo `test -f 'os/os0sync.c' || echo '$(srcdir)/'`os/os0sync.c os/libhaildb_la-os0thread.lo: os/os0thread.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT os/libhaildb_la-os0thread.lo -MD -MP -MF os/$(DEPDIR)/libhaildb_la-os0thread.Tpo -c -o os/libhaildb_la-os0thread.lo `test -f 'os/os0thread.c' || echo '$(srcdir)/'`os/os0thread.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) os/$(DEPDIR)/libhaildb_la-os0thread.Tpo os/$(DEPDIR)/libhaildb_la-os0thread.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='os/os0thread.c' object='os/libhaildb_la-os0thread.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o os/libhaildb_la-os0thread.lo `test -f 'os/os0thread.c' || echo '$(srcdir)/'`os/os0thread.c page/libhaildb_la-page0cur.lo: page/page0cur.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT page/libhaildb_la-page0cur.lo -MD -MP -MF page/$(DEPDIR)/libhaildb_la-page0cur.Tpo -c -o page/libhaildb_la-page0cur.lo `test -f 'page/page0cur.c' || echo '$(srcdir)/'`page/page0cur.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) page/$(DEPDIR)/libhaildb_la-page0cur.Tpo page/$(DEPDIR)/libhaildb_la-page0cur.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='page/page0cur.c' object='page/libhaildb_la-page0cur.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o page/libhaildb_la-page0cur.lo `test -f 'page/page0cur.c' || echo '$(srcdir)/'`page/page0cur.c page/libhaildb_la-page0page.lo: page/page0page.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT page/libhaildb_la-page0page.lo -MD -MP -MF page/$(DEPDIR)/libhaildb_la-page0page.Tpo -c -o page/libhaildb_la-page0page.lo `test -f 'page/page0page.c' || echo '$(srcdir)/'`page/page0page.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) page/$(DEPDIR)/libhaildb_la-page0page.Tpo page/$(DEPDIR)/libhaildb_la-page0page.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='page/page0page.c' object='page/libhaildb_la-page0page.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o page/libhaildb_la-page0page.lo `test -f 'page/page0page.c' || echo '$(srcdir)/'`page/page0page.c pars/libhaildb_la-lexyy.lo: pars/lexyy.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT pars/libhaildb_la-lexyy.lo -MD -MP -MF pars/$(DEPDIR)/libhaildb_la-lexyy.Tpo -c -o pars/libhaildb_la-lexyy.lo `test -f 'pars/lexyy.c' || echo '$(srcdir)/'`pars/lexyy.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) pars/$(DEPDIR)/libhaildb_la-lexyy.Tpo pars/$(DEPDIR)/libhaildb_la-lexyy.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='pars/lexyy.c' object='pars/libhaildb_la-lexyy.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o pars/libhaildb_la-lexyy.lo `test -f 'pars/lexyy.c' || echo '$(srcdir)/'`pars/lexyy.c pars/libhaildb_la-pars0grm.lo: pars/pars0grm.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT pars/libhaildb_la-pars0grm.lo -MD -MP -MF pars/$(DEPDIR)/libhaildb_la-pars0grm.Tpo -c -o pars/libhaildb_la-pars0grm.lo `test -f 'pars/pars0grm.c' || echo '$(srcdir)/'`pars/pars0grm.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) pars/$(DEPDIR)/libhaildb_la-pars0grm.Tpo pars/$(DEPDIR)/libhaildb_la-pars0grm.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='pars/pars0grm.c' object='pars/libhaildb_la-pars0grm.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o pars/libhaildb_la-pars0grm.lo `test -f 'pars/pars0grm.c' || echo '$(srcdir)/'`pars/pars0grm.c pars/libhaildb_la-pars0opt.lo: pars/pars0opt.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT pars/libhaildb_la-pars0opt.lo -MD -MP -MF pars/$(DEPDIR)/libhaildb_la-pars0opt.Tpo -c -o pars/libhaildb_la-pars0opt.lo `test -f 'pars/pars0opt.c' || echo '$(srcdir)/'`pars/pars0opt.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) pars/$(DEPDIR)/libhaildb_la-pars0opt.Tpo pars/$(DEPDIR)/libhaildb_la-pars0opt.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='pars/pars0opt.c' object='pars/libhaildb_la-pars0opt.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o pars/libhaildb_la-pars0opt.lo `test -f 'pars/pars0opt.c' || echo '$(srcdir)/'`pars/pars0opt.c pars/libhaildb_la-pars0pars.lo: pars/pars0pars.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT pars/libhaildb_la-pars0pars.lo -MD -MP -MF pars/$(DEPDIR)/libhaildb_la-pars0pars.Tpo -c -o pars/libhaildb_la-pars0pars.lo `test -f 'pars/pars0pars.c' || echo '$(srcdir)/'`pars/pars0pars.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) pars/$(DEPDIR)/libhaildb_la-pars0pars.Tpo pars/$(DEPDIR)/libhaildb_la-pars0pars.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='pars/pars0pars.c' object='pars/libhaildb_la-pars0pars.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o pars/libhaildb_la-pars0pars.lo `test -f 'pars/pars0pars.c' || echo '$(srcdir)/'`pars/pars0pars.c pars/libhaildb_la-pars0sym.lo: pars/pars0sym.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT pars/libhaildb_la-pars0sym.lo -MD -MP -MF pars/$(DEPDIR)/libhaildb_la-pars0sym.Tpo -c -o pars/libhaildb_la-pars0sym.lo `test -f 'pars/pars0sym.c' || echo '$(srcdir)/'`pars/pars0sym.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) pars/$(DEPDIR)/libhaildb_la-pars0sym.Tpo pars/$(DEPDIR)/libhaildb_la-pars0sym.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='pars/pars0sym.c' object='pars/libhaildb_la-pars0sym.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o pars/libhaildb_la-pars0sym.lo `test -f 'pars/pars0sym.c' || echo '$(srcdir)/'`pars/pars0sym.c que/libhaildb_la-que0que.lo: que/que0que.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT que/libhaildb_la-que0que.lo -MD -MP -MF que/$(DEPDIR)/libhaildb_la-que0que.Tpo -c -o que/libhaildb_la-que0que.lo `test -f 'que/que0que.c' || echo '$(srcdir)/'`que/que0que.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) que/$(DEPDIR)/libhaildb_la-que0que.Tpo que/$(DEPDIR)/libhaildb_la-que0que.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='que/que0que.c' object='que/libhaildb_la-que0que.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o que/libhaildb_la-que0que.lo `test -f 'que/que0que.c' || echo '$(srcdir)/'`que/que0que.c read/libhaildb_la-read0read.lo: read/read0read.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT read/libhaildb_la-read0read.lo -MD -MP -MF read/$(DEPDIR)/libhaildb_la-read0read.Tpo -c -o read/libhaildb_la-read0read.lo `test -f 'read/read0read.c' || echo '$(srcdir)/'`read/read0read.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) read/$(DEPDIR)/libhaildb_la-read0read.Tpo read/$(DEPDIR)/libhaildb_la-read0read.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='read/read0read.c' object='read/libhaildb_la-read0read.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o read/libhaildb_la-read0read.lo `test -f 'read/read0read.c' || echo '$(srcdir)/'`read/read0read.c rem/libhaildb_la-rem0cmp.lo: rem/rem0cmp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT rem/libhaildb_la-rem0cmp.lo -MD -MP -MF rem/$(DEPDIR)/libhaildb_la-rem0cmp.Tpo -c -o rem/libhaildb_la-rem0cmp.lo `test -f 'rem/rem0cmp.c' || echo '$(srcdir)/'`rem/rem0cmp.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) rem/$(DEPDIR)/libhaildb_la-rem0cmp.Tpo rem/$(DEPDIR)/libhaildb_la-rem0cmp.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='rem/rem0cmp.c' object='rem/libhaildb_la-rem0cmp.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o rem/libhaildb_la-rem0cmp.lo `test -f 'rem/rem0cmp.c' || echo '$(srcdir)/'`rem/rem0cmp.c rem/libhaildb_la-rem0rec.lo: rem/rem0rec.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT rem/libhaildb_la-rem0rec.lo -MD -MP -MF rem/$(DEPDIR)/libhaildb_la-rem0rec.Tpo -c -o rem/libhaildb_la-rem0rec.lo `test -f 'rem/rem0rec.c' || echo '$(srcdir)/'`rem/rem0rec.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) rem/$(DEPDIR)/libhaildb_la-rem0rec.Tpo rem/$(DEPDIR)/libhaildb_la-rem0rec.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='rem/rem0rec.c' object='rem/libhaildb_la-rem0rec.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o rem/libhaildb_la-rem0rec.lo `test -f 'rem/rem0rec.c' || echo '$(srcdir)/'`rem/rem0rec.c row/libhaildb_la-row0ext.lo: row/row0ext.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT row/libhaildb_la-row0ext.lo -MD -MP -MF row/$(DEPDIR)/libhaildb_la-row0ext.Tpo -c -o row/libhaildb_la-row0ext.lo `test -f 'row/row0ext.c' || echo '$(srcdir)/'`row/row0ext.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) row/$(DEPDIR)/libhaildb_la-row0ext.Tpo row/$(DEPDIR)/libhaildb_la-row0ext.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='row/row0ext.c' object='row/libhaildb_la-row0ext.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o row/libhaildb_la-row0ext.lo `test -f 'row/row0ext.c' || echo '$(srcdir)/'`row/row0ext.c row/libhaildb_la-row0ins.lo: row/row0ins.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT row/libhaildb_la-row0ins.lo -MD -MP -MF row/$(DEPDIR)/libhaildb_la-row0ins.Tpo -c -o row/libhaildb_la-row0ins.lo `test -f 'row/row0ins.c' || echo '$(srcdir)/'`row/row0ins.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) row/$(DEPDIR)/libhaildb_la-row0ins.Tpo row/$(DEPDIR)/libhaildb_la-row0ins.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='row/row0ins.c' object='row/libhaildb_la-row0ins.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o row/libhaildb_la-row0ins.lo `test -f 'row/row0ins.c' || echo '$(srcdir)/'`row/row0ins.c row/libhaildb_la-row0merge.lo: row/row0merge.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT row/libhaildb_la-row0merge.lo -MD -MP -MF row/$(DEPDIR)/libhaildb_la-row0merge.Tpo -c -o row/libhaildb_la-row0merge.lo `test -f 'row/row0merge.c' || echo '$(srcdir)/'`row/row0merge.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) row/$(DEPDIR)/libhaildb_la-row0merge.Tpo row/$(DEPDIR)/libhaildb_la-row0merge.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='row/row0merge.c' object='row/libhaildb_la-row0merge.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o row/libhaildb_la-row0merge.lo `test -f 'row/row0merge.c' || echo '$(srcdir)/'`row/row0merge.c row/libhaildb_la-row0purge.lo: row/row0purge.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT row/libhaildb_la-row0purge.lo -MD -MP -MF row/$(DEPDIR)/libhaildb_la-row0purge.Tpo -c -o row/libhaildb_la-row0purge.lo `test -f 'row/row0purge.c' || echo '$(srcdir)/'`row/row0purge.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) row/$(DEPDIR)/libhaildb_la-row0purge.Tpo row/$(DEPDIR)/libhaildb_la-row0purge.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='row/row0purge.c' object='row/libhaildb_la-row0purge.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o row/libhaildb_la-row0purge.lo `test -f 'row/row0purge.c' || echo '$(srcdir)/'`row/row0purge.c row/libhaildb_la-row0row.lo: row/row0row.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT row/libhaildb_la-row0row.lo -MD -MP -MF row/$(DEPDIR)/libhaildb_la-row0row.Tpo -c -o row/libhaildb_la-row0row.lo `test -f 'row/row0row.c' || echo '$(srcdir)/'`row/row0row.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) row/$(DEPDIR)/libhaildb_la-row0row.Tpo row/$(DEPDIR)/libhaildb_la-row0row.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='row/row0row.c' object='row/libhaildb_la-row0row.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o row/libhaildb_la-row0row.lo `test -f 'row/row0row.c' || echo '$(srcdir)/'`row/row0row.c row/libhaildb_la-row0sel.lo: row/row0sel.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT row/libhaildb_la-row0sel.lo -MD -MP -MF row/$(DEPDIR)/libhaildb_la-row0sel.Tpo -c -o row/libhaildb_la-row0sel.lo `test -f 'row/row0sel.c' || echo '$(srcdir)/'`row/row0sel.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) row/$(DEPDIR)/libhaildb_la-row0sel.Tpo row/$(DEPDIR)/libhaildb_la-row0sel.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='row/row0sel.c' object='row/libhaildb_la-row0sel.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o row/libhaildb_la-row0sel.lo `test -f 'row/row0sel.c' || echo '$(srcdir)/'`row/row0sel.c row/libhaildb_la-row0uins.lo: row/row0uins.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT row/libhaildb_la-row0uins.lo -MD -MP -MF row/$(DEPDIR)/libhaildb_la-row0uins.Tpo -c -o row/libhaildb_la-row0uins.lo `test -f 'row/row0uins.c' || echo '$(srcdir)/'`row/row0uins.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) row/$(DEPDIR)/libhaildb_la-row0uins.Tpo row/$(DEPDIR)/libhaildb_la-row0uins.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='row/row0uins.c' object='row/libhaildb_la-row0uins.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o row/libhaildb_la-row0uins.lo `test -f 'row/row0uins.c' || echo '$(srcdir)/'`row/row0uins.c row/libhaildb_la-row0umod.lo: row/row0umod.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT row/libhaildb_la-row0umod.lo -MD -MP -MF row/$(DEPDIR)/libhaildb_la-row0umod.Tpo -c -o row/libhaildb_la-row0umod.lo `test -f 'row/row0umod.c' || echo '$(srcdir)/'`row/row0umod.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) row/$(DEPDIR)/libhaildb_la-row0umod.Tpo row/$(DEPDIR)/libhaildb_la-row0umod.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='row/row0umod.c' object='row/libhaildb_la-row0umod.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o row/libhaildb_la-row0umod.lo `test -f 'row/row0umod.c' || echo '$(srcdir)/'`row/row0umod.c row/libhaildb_la-row0undo.lo: row/row0undo.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT row/libhaildb_la-row0undo.lo -MD -MP -MF row/$(DEPDIR)/libhaildb_la-row0undo.Tpo -c -o row/libhaildb_la-row0undo.lo `test -f 'row/row0undo.c' || echo '$(srcdir)/'`row/row0undo.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) row/$(DEPDIR)/libhaildb_la-row0undo.Tpo row/$(DEPDIR)/libhaildb_la-row0undo.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='row/row0undo.c' object='row/libhaildb_la-row0undo.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o row/libhaildb_la-row0undo.lo `test -f 'row/row0undo.c' || echo '$(srcdir)/'`row/row0undo.c row/libhaildb_la-row0upd.lo: row/row0upd.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT row/libhaildb_la-row0upd.lo -MD -MP -MF row/$(DEPDIR)/libhaildb_la-row0upd.Tpo -c -o row/libhaildb_la-row0upd.lo `test -f 'row/row0upd.c' || echo '$(srcdir)/'`row/row0upd.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) row/$(DEPDIR)/libhaildb_la-row0upd.Tpo row/$(DEPDIR)/libhaildb_la-row0upd.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='row/row0upd.c' object='row/libhaildb_la-row0upd.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o row/libhaildb_la-row0upd.lo `test -f 'row/row0upd.c' || echo '$(srcdir)/'`row/row0upd.c row/libhaildb_la-row0vers.lo: row/row0vers.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT row/libhaildb_la-row0vers.lo -MD -MP -MF row/$(DEPDIR)/libhaildb_la-row0vers.Tpo -c -o row/libhaildb_la-row0vers.lo `test -f 'row/row0vers.c' || echo '$(srcdir)/'`row/row0vers.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) row/$(DEPDIR)/libhaildb_la-row0vers.Tpo row/$(DEPDIR)/libhaildb_la-row0vers.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='row/row0vers.c' object='row/libhaildb_la-row0vers.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o row/libhaildb_la-row0vers.lo `test -f 'row/row0vers.c' || echo '$(srcdir)/'`row/row0vers.c row/libhaildb_la-row0prebuilt.lo: row/row0prebuilt.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT row/libhaildb_la-row0prebuilt.lo -MD -MP -MF row/$(DEPDIR)/libhaildb_la-row0prebuilt.Tpo -c -o row/libhaildb_la-row0prebuilt.lo `test -f 'row/row0prebuilt.c' || echo '$(srcdir)/'`row/row0prebuilt.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) row/$(DEPDIR)/libhaildb_la-row0prebuilt.Tpo row/$(DEPDIR)/libhaildb_la-row0prebuilt.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='row/row0prebuilt.c' object='row/libhaildb_la-row0prebuilt.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o row/libhaildb_la-row0prebuilt.lo `test -f 'row/row0prebuilt.c' || echo '$(srcdir)/'`row/row0prebuilt.c srv/libhaildb_la-srv0que.lo: srv/srv0que.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT srv/libhaildb_la-srv0que.lo -MD -MP -MF srv/$(DEPDIR)/libhaildb_la-srv0que.Tpo -c -o srv/libhaildb_la-srv0que.lo `test -f 'srv/srv0que.c' || echo '$(srcdir)/'`srv/srv0que.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) srv/$(DEPDIR)/libhaildb_la-srv0que.Tpo srv/$(DEPDIR)/libhaildb_la-srv0que.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='srv/srv0que.c' object='srv/libhaildb_la-srv0que.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o srv/libhaildb_la-srv0que.lo `test -f 'srv/srv0que.c' || echo '$(srcdir)/'`srv/srv0que.c srv/libhaildb_la-srv0srv.lo: srv/srv0srv.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT srv/libhaildb_la-srv0srv.lo -MD -MP -MF srv/$(DEPDIR)/libhaildb_la-srv0srv.Tpo -c -o srv/libhaildb_la-srv0srv.lo `test -f 'srv/srv0srv.c' || echo '$(srcdir)/'`srv/srv0srv.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) srv/$(DEPDIR)/libhaildb_la-srv0srv.Tpo srv/$(DEPDIR)/libhaildb_la-srv0srv.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='srv/srv0srv.c' object='srv/libhaildb_la-srv0srv.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o srv/libhaildb_la-srv0srv.lo `test -f 'srv/srv0srv.c' || echo '$(srcdir)/'`srv/srv0srv.c srv/libhaildb_la-srv0start.lo: srv/srv0start.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT srv/libhaildb_la-srv0start.lo -MD -MP -MF srv/$(DEPDIR)/libhaildb_la-srv0start.Tpo -c -o srv/libhaildb_la-srv0start.lo `test -f 'srv/srv0start.c' || echo '$(srcdir)/'`srv/srv0start.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) srv/$(DEPDIR)/libhaildb_la-srv0start.Tpo srv/$(DEPDIR)/libhaildb_la-srv0start.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='srv/srv0start.c' object='srv/libhaildb_la-srv0start.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o srv/libhaildb_la-srv0start.lo `test -f 'srv/srv0start.c' || echo '$(srcdir)/'`srv/srv0start.c sync/libhaildb_la-sync0arr.lo: sync/sync0arr.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT sync/libhaildb_la-sync0arr.lo -MD -MP -MF sync/$(DEPDIR)/libhaildb_la-sync0arr.Tpo -c -o sync/libhaildb_la-sync0arr.lo `test -f 'sync/sync0arr.c' || echo '$(srcdir)/'`sync/sync0arr.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) sync/$(DEPDIR)/libhaildb_la-sync0arr.Tpo sync/$(DEPDIR)/libhaildb_la-sync0arr.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sync/sync0arr.c' object='sync/libhaildb_la-sync0arr.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o sync/libhaildb_la-sync0arr.lo `test -f 'sync/sync0arr.c' || echo '$(srcdir)/'`sync/sync0arr.c sync/libhaildb_la-sync0rw.lo: sync/sync0rw.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT sync/libhaildb_la-sync0rw.lo -MD -MP -MF sync/$(DEPDIR)/libhaildb_la-sync0rw.Tpo -c -o sync/libhaildb_la-sync0rw.lo `test -f 'sync/sync0rw.c' || echo '$(srcdir)/'`sync/sync0rw.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) sync/$(DEPDIR)/libhaildb_la-sync0rw.Tpo sync/$(DEPDIR)/libhaildb_la-sync0rw.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sync/sync0rw.c' object='sync/libhaildb_la-sync0rw.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o sync/libhaildb_la-sync0rw.lo `test -f 'sync/sync0rw.c' || echo '$(srcdir)/'`sync/sync0rw.c sync/libhaildb_la-sync0sync.lo: sync/sync0sync.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT sync/libhaildb_la-sync0sync.lo -MD -MP -MF sync/$(DEPDIR)/libhaildb_la-sync0sync.Tpo -c -o sync/libhaildb_la-sync0sync.lo `test -f 'sync/sync0sync.c' || echo '$(srcdir)/'`sync/sync0sync.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) sync/$(DEPDIR)/libhaildb_la-sync0sync.Tpo sync/$(DEPDIR)/libhaildb_la-sync0sync.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sync/sync0sync.c' object='sync/libhaildb_la-sync0sync.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o sync/libhaildb_la-sync0sync.lo `test -f 'sync/sync0sync.c' || echo '$(srcdir)/'`sync/sync0sync.c thr/libhaildb_la-thr0loc.lo: thr/thr0loc.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT thr/libhaildb_la-thr0loc.lo -MD -MP -MF thr/$(DEPDIR)/libhaildb_la-thr0loc.Tpo -c -o thr/libhaildb_la-thr0loc.lo `test -f 'thr/thr0loc.c' || echo '$(srcdir)/'`thr/thr0loc.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) thr/$(DEPDIR)/libhaildb_la-thr0loc.Tpo thr/$(DEPDIR)/libhaildb_la-thr0loc.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='thr/thr0loc.c' object='thr/libhaildb_la-thr0loc.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o thr/libhaildb_la-thr0loc.lo `test -f 'thr/thr0loc.c' || echo '$(srcdir)/'`thr/thr0loc.c trx/libhaildb_la-trx0purge.lo: trx/trx0purge.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT trx/libhaildb_la-trx0purge.lo -MD -MP -MF trx/$(DEPDIR)/libhaildb_la-trx0purge.Tpo -c -o trx/libhaildb_la-trx0purge.lo `test -f 'trx/trx0purge.c' || echo '$(srcdir)/'`trx/trx0purge.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) trx/$(DEPDIR)/libhaildb_la-trx0purge.Tpo trx/$(DEPDIR)/libhaildb_la-trx0purge.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='trx/trx0purge.c' object='trx/libhaildb_la-trx0purge.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o trx/libhaildb_la-trx0purge.lo `test -f 'trx/trx0purge.c' || echo '$(srcdir)/'`trx/trx0purge.c trx/libhaildb_la-trx0rec.lo: trx/trx0rec.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT trx/libhaildb_la-trx0rec.lo -MD -MP -MF trx/$(DEPDIR)/libhaildb_la-trx0rec.Tpo -c -o trx/libhaildb_la-trx0rec.lo `test -f 'trx/trx0rec.c' || echo '$(srcdir)/'`trx/trx0rec.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) trx/$(DEPDIR)/libhaildb_la-trx0rec.Tpo trx/$(DEPDIR)/libhaildb_la-trx0rec.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='trx/trx0rec.c' object='trx/libhaildb_la-trx0rec.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o trx/libhaildb_la-trx0rec.lo `test -f 'trx/trx0rec.c' || echo '$(srcdir)/'`trx/trx0rec.c trx/libhaildb_la-trx0roll.lo: trx/trx0roll.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT trx/libhaildb_la-trx0roll.lo -MD -MP -MF trx/$(DEPDIR)/libhaildb_la-trx0roll.Tpo -c -o trx/libhaildb_la-trx0roll.lo `test -f 'trx/trx0roll.c' || echo '$(srcdir)/'`trx/trx0roll.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) trx/$(DEPDIR)/libhaildb_la-trx0roll.Tpo trx/$(DEPDIR)/libhaildb_la-trx0roll.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='trx/trx0roll.c' object='trx/libhaildb_la-trx0roll.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o trx/libhaildb_la-trx0roll.lo `test -f 'trx/trx0roll.c' || echo '$(srcdir)/'`trx/trx0roll.c trx/libhaildb_la-trx0rseg.lo: trx/trx0rseg.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT trx/libhaildb_la-trx0rseg.lo -MD -MP -MF trx/$(DEPDIR)/libhaildb_la-trx0rseg.Tpo -c -o trx/libhaildb_la-trx0rseg.lo `test -f 'trx/trx0rseg.c' || echo '$(srcdir)/'`trx/trx0rseg.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) trx/$(DEPDIR)/libhaildb_la-trx0rseg.Tpo trx/$(DEPDIR)/libhaildb_la-trx0rseg.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='trx/trx0rseg.c' object='trx/libhaildb_la-trx0rseg.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o trx/libhaildb_la-trx0rseg.lo `test -f 'trx/trx0rseg.c' || echo '$(srcdir)/'`trx/trx0rseg.c trx/libhaildb_la-trx0sys.lo: trx/trx0sys.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT trx/libhaildb_la-trx0sys.lo -MD -MP -MF trx/$(DEPDIR)/libhaildb_la-trx0sys.Tpo -c -o trx/libhaildb_la-trx0sys.lo `test -f 'trx/trx0sys.c' || echo '$(srcdir)/'`trx/trx0sys.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) trx/$(DEPDIR)/libhaildb_la-trx0sys.Tpo trx/$(DEPDIR)/libhaildb_la-trx0sys.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='trx/trx0sys.c' object='trx/libhaildb_la-trx0sys.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o trx/libhaildb_la-trx0sys.lo `test -f 'trx/trx0sys.c' || echo '$(srcdir)/'`trx/trx0sys.c trx/libhaildb_la-trx0trx.lo: trx/trx0trx.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT trx/libhaildb_la-trx0trx.lo -MD -MP -MF trx/$(DEPDIR)/libhaildb_la-trx0trx.Tpo -c -o trx/libhaildb_la-trx0trx.lo `test -f 'trx/trx0trx.c' || echo '$(srcdir)/'`trx/trx0trx.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) trx/$(DEPDIR)/libhaildb_la-trx0trx.Tpo trx/$(DEPDIR)/libhaildb_la-trx0trx.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='trx/trx0trx.c' object='trx/libhaildb_la-trx0trx.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o trx/libhaildb_la-trx0trx.lo `test -f 'trx/trx0trx.c' || echo '$(srcdir)/'`trx/trx0trx.c trx/libhaildb_la-trx0undo.lo: trx/trx0undo.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT trx/libhaildb_la-trx0undo.lo -MD -MP -MF trx/$(DEPDIR)/libhaildb_la-trx0undo.Tpo -c -o trx/libhaildb_la-trx0undo.lo `test -f 'trx/trx0undo.c' || echo '$(srcdir)/'`trx/trx0undo.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) trx/$(DEPDIR)/libhaildb_la-trx0undo.Tpo trx/$(DEPDIR)/libhaildb_la-trx0undo.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='trx/trx0undo.c' object='trx/libhaildb_la-trx0undo.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o trx/libhaildb_la-trx0undo.lo `test -f 'trx/trx0undo.c' || echo '$(srcdir)/'`trx/trx0undo.c usr/libhaildb_la-usr0sess.lo: usr/usr0sess.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT usr/libhaildb_la-usr0sess.lo -MD -MP -MF usr/$(DEPDIR)/libhaildb_la-usr0sess.Tpo -c -o usr/libhaildb_la-usr0sess.lo `test -f 'usr/usr0sess.c' || echo '$(srcdir)/'`usr/usr0sess.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) usr/$(DEPDIR)/libhaildb_la-usr0sess.Tpo usr/$(DEPDIR)/libhaildb_la-usr0sess.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='usr/usr0sess.c' object='usr/libhaildb_la-usr0sess.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o usr/libhaildb_la-usr0sess.lo `test -f 'usr/usr0sess.c' || echo '$(srcdir)/'`usr/usr0sess.c ut/libhaildb_la-ut0byte.lo: ut/ut0byte.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT ut/libhaildb_la-ut0byte.lo -MD -MP -MF ut/$(DEPDIR)/libhaildb_la-ut0byte.Tpo -c -o ut/libhaildb_la-ut0byte.lo `test -f 'ut/ut0byte.c' || echo '$(srcdir)/'`ut/ut0byte.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ut/$(DEPDIR)/libhaildb_la-ut0byte.Tpo ut/$(DEPDIR)/libhaildb_la-ut0byte.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ut/ut0byte.c' object='ut/libhaildb_la-ut0byte.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o ut/libhaildb_la-ut0byte.lo `test -f 'ut/ut0byte.c' || echo '$(srcdir)/'`ut/ut0byte.c ut/libhaildb_la-ut0dbg.lo: ut/ut0dbg.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT ut/libhaildb_la-ut0dbg.lo -MD -MP -MF ut/$(DEPDIR)/libhaildb_la-ut0dbg.Tpo -c -o ut/libhaildb_la-ut0dbg.lo `test -f 'ut/ut0dbg.c' || echo '$(srcdir)/'`ut/ut0dbg.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ut/$(DEPDIR)/libhaildb_la-ut0dbg.Tpo ut/$(DEPDIR)/libhaildb_la-ut0dbg.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ut/ut0dbg.c' object='ut/libhaildb_la-ut0dbg.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o ut/libhaildb_la-ut0dbg.lo `test -f 'ut/ut0dbg.c' || echo '$(srcdir)/'`ut/ut0dbg.c ut/libhaildb_la-ut0list.lo: ut/ut0list.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT ut/libhaildb_la-ut0list.lo -MD -MP -MF ut/$(DEPDIR)/libhaildb_la-ut0list.Tpo -c -o ut/libhaildb_la-ut0list.lo `test -f 'ut/ut0list.c' || echo '$(srcdir)/'`ut/ut0list.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ut/$(DEPDIR)/libhaildb_la-ut0list.Tpo ut/$(DEPDIR)/libhaildb_la-ut0list.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ut/ut0list.c' object='ut/libhaildb_la-ut0list.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o ut/libhaildb_la-ut0list.lo `test -f 'ut/ut0list.c' || echo '$(srcdir)/'`ut/ut0list.c ut/libhaildb_la-ut0mem.lo: ut/ut0mem.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT ut/libhaildb_la-ut0mem.lo -MD -MP -MF ut/$(DEPDIR)/libhaildb_la-ut0mem.Tpo -c -o ut/libhaildb_la-ut0mem.lo `test -f 'ut/ut0mem.c' || echo '$(srcdir)/'`ut/ut0mem.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ut/$(DEPDIR)/libhaildb_la-ut0mem.Tpo ut/$(DEPDIR)/libhaildb_la-ut0mem.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ut/ut0mem.c' object='ut/libhaildb_la-ut0mem.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o ut/libhaildb_la-ut0mem.lo `test -f 'ut/ut0mem.c' || echo '$(srcdir)/'`ut/ut0mem.c ut/libhaildb_la-ut0rnd.lo: ut/ut0rnd.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT ut/libhaildb_la-ut0rnd.lo -MD -MP -MF ut/$(DEPDIR)/libhaildb_la-ut0rnd.Tpo -c -o ut/libhaildb_la-ut0rnd.lo `test -f 'ut/ut0rnd.c' || echo '$(srcdir)/'`ut/ut0rnd.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ut/$(DEPDIR)/libhaildb_la-ut0rnd.Tpo ut/$(DEPDIR)/libhaildb_la-ut0rnd.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ut/ut0rnd.c' object='ut/libhaildb_la-ut0rnd.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o ut/libhaildb_la-ut0rnd.lo `test -f 'ut/ut0rnd.c' || echo '$(srcdir)/'`ut/ut0rnd.c ut/libhaildb_la-ut0ut.lo: ut/ut0ut.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT ut/libhaildb_la-ut0ut.lo -MD -MP -MF ut/$(DEPDIR)/libhaildb_la-ut0ut.Tpo -c -o ut/libhaildb_la-ut0ut.lo `test -f 'ut/ut0ut.c' || echo '$(srcdir)/'`ut/ut0ut.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ut/$(DEPDIR)/libhaildb_la-ut0ut.Tpo ut/$(DEPDIR)/libhaildb_la-ut0ut.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ut/ut0ut.c' object='ut/libhaildb_la-ut0ut.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o ut/libhaildb_la-ut0ut.lo `test -f 'ut/ut0ut.c' || echo '$(srcdir)/'`ut/ut0ut.c ut/libhaildb_la-ut0vec.lo: ut/ut0vec.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT ut/libhaildb_la-ut0vec.lo -MD -MP -MF ut/$(DEPDIR)/libhaildb_la-ut0vec.Tpo -c -o ut/libhaildb_la-ut0vec.lo `test -f 'ut/ut0vec.c' || echo '$(srcdir)/'`ut/ut0vec.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ut/$(DEPDIR)/libhaildb_la-ut0vec.Tpo ut/$(DEPDIR)/libhaildb_la-ut0vec.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ut/ut0vec.c' object='ut/libhaildb_la-ut0vec.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o ut/libhaildb_la-ut0vec.lo `test -f 'ut/ut0vec.c' || echo '$(srcdir)/'`ut/ut0vec.c ut/libhaildb_la-ut0rbt.lo: ut/ut0rbt.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT ut/libhaildb_la-ut0rbt.lo -MD -MP -MF ut/$(DEPDIR)/libhaildb_la-ut0rbt.Tpo -c -o ut/libhaildb_la-ut0rbt.lo `test -f 'ut/ut0rbt.c' || echo '$(srcdir)/'`ut/ut0rbt.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ut/$(DEPDIR)/libhaildb_la-ut0rbt.Tpo ut/$(DEPDIR)/libhaildb_la-ut0rbt.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ut/ut0rbt.c' object='ut/libhaildb_la-ut0rbt.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o ut/libhaildb_la-ut0rbt.lo `test -f 'ut/ut0rbt.c' || echo '$(srcdir)/'`ut/ut0rbt.c api/libhaildb_la-api0api.lo: api/api0api.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT api/libhaildb_la-api0api.lo -MD -MP -MF api/$(DEPDIR)/libhaildb_la-api0api.Tpo -c -o api/libhaildb_la-api0api.lo `test -f 'api/api0api.c' || echo '$(srcdir)/'`api/api0api.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) api/$(DEPDIR)/libhaildb_la-api0api.Tpo api/$(DEPDIR)/libhaildb_la-api0api.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='api/api0api.c' object='api/libhaildb_la-api0api.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o api/libhaildb_la-api0api.lo `test -f 'api/api0api.c' || echo '$(srcdir)/'`api/api0api.c api/libhaildb_la-api0ucode.lo: api/api0ucode.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT api/libhaildb_la-api0ucode.lo -MD -MP -MF api/$(DEPDIR)/libhaildb_la-api0ucode.Tpo -c -o api/libhaildb_la-api0ucode.lo `test -f 'api/api0ucode.c' || echo '$(srcdir)/'`api/api0ucode.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) api/$(DEPDIR)/libhaildb_la-api0ucode.Tpo api/$(DEPDIR)/libhaildb_la-api0ucode.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='api/api0ucode.c' object='api/libhaildb_la-api0ucode.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o api/libhaildb_la-api0ucode.lo `test -f 'api/api0ucode.c' || echo '$(srcdir)/'`api/api0ucode.c ddl/libhaildb_la-ddl0ddl.lo: ddl/ddl0ddl.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT ddl/libhaildb_la-ddl0ddl.lo -MD -MP -MF ddl/$(DEPDIR)/libhaildb_la-ddl0ddl.Tpo -c -o ddl/libhaildb_la-ddl0ddl.lo `test -f 'ddl/ddl0ddl.c' || echo '$(srcdir)/'`ddl/ddl0ddl.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) ddl/$(DEPDIR)/libhaildb_la-ddl0ddl.Tpo ddl/$(DEPDIR)/libhaildb_la-ddl0ddl.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ddl/ddl0ddl.c' object='ddl/libhaildb_la-ddl0ddl.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o ddl/libhaildb_la-ddl0ddl.lo `test -f 'ddl/ddl0ddl.c' || echo '$(srcdir)/'`ddl/ddl0ddl.c api/libhaildb_la-api0misc.lo: api/api0misc.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT api/libhaildb_la-api0misc.lo -MD -MP -MF api/$(DEPDIR)/libhaildb_la-api0misc.Tpo -c -o api/libhaildb_la-api0misc.lo `test -f 'api/api0misc.c' || echo '$(srcdir)/'`api/api0misc.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) api/$(DEPDIR)/libhaildb_la-api0misc.Tpo api/$(DEPDIR)/libhaildb_la-api0misc.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='api/api0misc.c' object='api/libhaildb_la-api0misc.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o api/libhaildb_la-api0misc.lo `test -f 'api/api0misc.c' || echo '$(srcdir)/'`api/api0misc.c api/libhaildb_la-api0cfg.lo: api/api0cfg.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT api/libhaildb_la-api0cfg.lo -MD -MP -MF api/$(DEPDIR)/libhaildb_la-api0cfg.Tpo -c -o api/libhaildb_la-api0cfg.lo `test -f 'api/api0cfg.c' || echo '$(srcdir)/'`api/api0cfg.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) api/$(DEPDIR)/libhaildb_la-api0cfg.Tpo api/$(DEPDIR)/libhaildb_la-api0cfg.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='api/api0cfg.c' object='api/libhaildb_la-api0cfg.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o api/libhaildb_la-api0cfg.lo `test -f 'api/api0cfg.c' || echo '$(srcdir)/'`api/api0cfg.c api/libhaildb_la-api0status.lo: api/api0status.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT api/libhaildb_la-api0status.lo -MD -MP -MF api/$(DEPDIR)/libhaildb_la-api0status.Tpo -c -o api/libhaildb_la-api0status.lo `test -f 'api/api0status.c' || echo '$(srcdir)/'`api/api0status.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) api/$(DEPDIR)/libhaildb_la-api0status.Tpo api/$(DEPDIR)/libhaildb_la-api0status.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='api/api0status.c' object='api/libhaildb_la-api0status.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o api/libhaildb_la-api0status.lo `test -f 'api/api0status.c' || echo '$(srcdir)/'`api/api0status.c api/libhaildb_la-api0sql.lo: api/api0sql.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT api/libhaildb_la-api0sql.lo -MD -MP -MF api/$(DEPDIR)/libhaildb_la-api0sql.Tpo -c -o api/libhaildb_la-api0sql.lo `test -f 'api/api0sql.c' || echo '$(srcdir)/'`api/api0sql.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) api/$(DEPDIR)/libhaildb_la-api0sql.Tpo api/$(DEPDIR)/libhaildb_la-api0sql.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='api/api0sql.c' object='api/libhaildb_la-api0sql.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o api/libhaildb_la-api0sql.lo `test -f 'api/api0sql.c' || echo '$(srcdir)/'`api/api0sql.c buf/libhaildb_la-buf0buddy.lo: buf/buf0buddy.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT buf/libhaildb_la-buf0buddy.lo -MD -MP -MF buf/$(DEPDIR)/libhaildb_la-buf0buddy.Tpo -c -o buf/libhaildb_la-buf0buddy.lo `test -f 'buf/buf0buddy.c' || echo '$(srcdir)/'`buf/buf0buddy.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) buf/$(DEPDIR)/libhaildb_la-buf0buddy.Tpo buf/$(DEPDIR)/libhaildb_la-buf0buddy.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='buf/buf0buddy.c' object='buf/libhaildb_la-buf0buddy.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o buf/libhaildb_la-buf0buddy.lo `test -f 'buf/buf0buddy.c' || echo '$(srcdir)/'`buf/buf0buddy.c page/libhaildb_la-page0zip.lo: page/page0zip.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -MT page/libhaildb_la-page0zip.lo -MD -MP -MF page/$(DEPDIR)/libhaildb_la-page0zip.Tpo -c -o page/libhaildb_la-page0zip.lo `test -f 'page/page0zip.c' || echo '$(srcdir)/'`page/page0zip.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) page/$(DEPDIR)/libhaildb_la-page0zip.Tpo page/$(DEPDIR)/libhaildb_la-page0zip.Plo @am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='page/page0zip.c' object='page/libhaildb_la-page0zip.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libhaildb_la_CFLAGS) $(CFLAGS) -c -o page/libhaildb_la-page0zip.lo `test -f 'page/page0zip.c' || echo '$(srcdir)/'`page/page0zip.c .y.c: $(AM_V_YACC)$(am__skipyacc) $(SHELL) $(YLWRAP) $< y.tab.c $@ y.tab.h $*.h y.output $*.output -- $(YACCCOMPILE) mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs -rm -rf api/.libs api/_libs -rm -rf btr/.libs btr/_libs -rm -rf buf/.libs buf/_libs -rm -rf data/.libs data/_libs -rm -rf ddl/.libs ddl/_libs -rm -rf dict/.libs dict/_libs -rm -rf dyn/.libs dyn/_libs -rm -rf eval/.libs eval/_libs -rm -rf fil/.libs fil/_libs -rm -rf fsp/.libs fsp/_libs -rm -rf fut/.libs fut/_libs -rm -rf ha/.libs ha/_libs -rm -rf ibuf/.libs ibuf/_libs -rm -rf lock/.libs lock/_libs -rm -rf log/.libs log/_libs -rm -rf mach/.libs mach/_libs -rm -rf mem/.libs mem/_libs -rm -rf mtr/.libs mtr/_libs -rm -rf os/.libs os/_libs -rm -rf page/.libs page/_libs -rm -rf pars/.libs pars/_libs -rm -rf que/.libs que/_libs -rm -rf read/.libs read/_libs -rm -rf rem/.libs rem/_libs -rm -rf row/.libs row/_libs -rm -rf srv/.libs srv/_libs -rm -rf sync/.libs sync/_libs -rm -rf tests/.libs tests/_libs -rm -rf thr/.libs thr/_libs -rm -rf trx/.libs trx/_libs -rm -rf usr/.libs usr/_libs -rm -rf ut/.libs ut/_libs distclean-libtool: -rm -f libtool config.lt install-includeHEADERS: $(include_HEADERS) @$(NORMAL_INSTALL) test -z "$(includedir)" || $(MKDIR_P) "$(DESTDIR)$(includedir)" @list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(includedir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(includedir)" || exit $$?; \ done uninstall-includeHEADERS: @$(NORMAL_UNINSTALL) @list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ test -n "$$files" || exit 0; \ echo " ( cd '$(DESTDIR)$(includedir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(includedir)" && rm -f $$files ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in files) print i; }; }'`; \ mkid -fID $$unique tags: TAGS TAGS: $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \ $(TAGS_FILES) $(LISP) set x; \ here=`pwd`; \ list='$(SOURCES) $(HEADERS) config.h.in $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in files) print i; }; }'`; \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: CTAGS CTAGS: $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \ $(TAGS_FILES) $(LISP) list='$(SOURCES) $(HEADERS) config.h.in $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in files) print i; }; }'`; \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags check-TESTS: $(TESTS) @failed=0; all=0; xfail=0; xpass=0; skip=0; \ srcdir=$(srcdir); export srcdir; \ list=' $(TESTS) '; \ $(am__tty_colors); \ if test -n "$$list"; then \ for tst in $$list; do \ if test -f ./$$tst; then dir=./; \ elif test -f $$tst; then dir=; \ else dir="$(srcdir)/"; fi; \ if $(TESTS_ENVIRONMENT) $${dir}$$tst; then \ all=`expr $$all + 1`; \ case " $(XFAIL_TESTS) " in \ *[\ \ ]$$tst[\ \ ]*) \ xpass=`expr $$xpass + 1`; \ failed=`expr $$failed + 1`; \ col=$$red; res=XPASS; \ ;; \ *) \ col=$$grn; res=PASS; \ ;; \ esac; \ elif test $$? -ne 77; then \ all=`expr $$all + 1`; \ case " $(XFAIL_TESTS) " in \ *[\ \ ]$$tst[\ \ ]*) \ xfail=`expr $$xfail + 1`; \ col=$$lgn; res=XFAIL; \ ;; \ *) \ failed=`expr $$failed + 1`; \ col=$$red; res=FAIL; \ ;; \ esac; \ else \ skip=`expr $$skip + 1`; \ col=$$blu; res=SKIP; \ fi; \ echo "$${col}$$res$${std}: $$tst"; \ done; \ if test "$$all" -eq 1; then \ tests="test"; \ All=""; \ else \ tests="tests"; \ All="All "; \ fi; \ if test "$$failed" -eq 0; then \ if test "$$xfail" -eq 0; then \ banner="$$All$$all $$tests passed"; \ else \ if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \ banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \ fi; \ else \ if test "$$xpass" -eq 0; then \ banner="$$failed of $$all $$tests failed"; \ else \ if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \ banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \ fi; \ fi; \ dashes="$$banner"; \ skipped=""; \ if test "$$skip" -ne 0; then \ if test "$$skip" -eq 1; then \ skipped="($$skip test was not run)"; \ else \ skipped="($$skip tests were not run)"; \ fi; \ test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \ dashes="$$skipped"; \ fi; \ report=""; \ if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \ report="Please report to $(PACKAGE_BUGREPORT)"; \ test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \ dashes="$$report"; \ fi; \ dashes=`echo "$$dashes" | sed s/./=/g`; \ if test "$$failed" -eq 0; then \ echo "$$grn$$dashes"; \ else \ echo "$$red$$dashes"; \ fi; \ echo "$$banner"; \ test -z "$$skipped" || echo "$$skipped"; \ test -z "$$report" || echo "$$report"; \ echo "$$dashes$$std"; \ test "$$failed" -eq 0; \ else :; fi distdir: $(DISTFILES) $(am__remove_distdir) test -d "$(distdir)" || mkdir "$(distdir)" @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done -test -n "$(am__skip_mode_fix)" \ || find "$(distdir)" -type d ! -perm -755 \ -exec chmod u+rwx,go+rx {} \; -o \ ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \ ! -type d ! -perm -400 -exec chmod a+r {} \; -o \ ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \ || chmod -R a+r "$(distdir)" dist-gzip: distdir tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz $(am__remove_distdir) dist-bzip2: distdir tardir=$(distdir) && $(am__tar) | bzip2 -9 -c >$(distdir).tar.bz2 $(am__remove_distdir) dist-lzma: distdir tardir=$(distdir) && $(am__tar) | lzma -9 -c >$(distdir).tar.lzma $(am__remove_distdir) dist-xz: distdir tardir=$(distdir) && $(am__tar) | xz -c >$(distdir).tar.xz $(am__remove_distdir) dist-tarZ: distdir tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z $(am__remove_distdir) dist-shar: distdir shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz $(am__remove_distdir) dist-zip: distdir -rm -f $(distdir).zip zip -rq $(distdir).zip $(distdir) $(am__remove_distdir) dist dist-all: distdir tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz $(am__remove_distdir) # This target untars the dist file and tries a VPATH configuration. Then # it guarantees that the distribution is self-contained by making another # tarfile. distcheck: dist case '$(DIST_ARCHIVES)' in \ *.tar.gz*) \ GZIP=$(GZIP_ENV) gzip -dc $(distdir).tar.gz | $(am__untar) ;;\ *.tar.bz2*) \ bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\ *.tar.lzma*) \ lzma -dc $(distdir).tar.lzma | $(am__untar) ;;\ *.tar.xz*) \ xz -dc $(distdir).tar.xz | $(am__untar) ;;\ *.tar.Z*) \ uncompress -c $(distdir).tar.Z | $(am__untar) ;;\ *.shar.gz*) \ GZIP=$(GZIP_ENV) gzip -dc $(distdir).shar.gz | unshar ;;\ *.zip*) \ unzip $(distdir).zip ;;\ esac chmod -R a-w $(distdir); chmod a+w $(distdir) mkdir $(distdir)/_build mkdir $(distdir)/_inst chmod a-w $(distdir) test -d $(distdir)/_build || exit 0; \ dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \ && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \ && am__cwd=`pwd` \ && $(am__cd) $(distdir)/_build \ && ../configure --srcdir=.. --prefix="$$dc_install_base" \ $(DISTCHECK_CONFIGURE_FLAGS) \ && $(MAKE) $(AM_MAKEFLAGS) \ && $(MAKE) $(AM_MAKEFLAGS) dvi \ && $(MAKE) $(AM_MAKEFLAGS) check \ && $(MAKE) $(AM_MAKEFLAGS) install \ && $(MAKE) $(AM_MAKEFLAGS) installcheck \ && $(MAKE) $(AM_MAKEFLAGS) uninstall \ && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \ distuninstallcheck \ && chmod -R a-w "$$dc_install_base" \ && ({ \ (cd ../.. && umask 077 && mkdir "$$dc_destdir") \ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \ distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \ } || { rm -rf "$$dc_destdir"; exit 1; }) \ && rm -rf "$$dc_destdir" \ && $(MAKE) $(AM_MAKEFLAGS) dist \ && rm -rf $(DIST_ARCHIVES) \ && $(MAKE) $(AM_MAKEFLAGS) distcleancheck \ && cd "$$am__cwd" \ || exit 1 $(am__remove_distdir) @(echo "$(distdir) archives ready for distribution: "; \ list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \ sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x' distuninstallcheck: @$(am__cd) '$(distuninstallcheck_dir)' \ && test `$(distuninstallcheck_listfiles) | wc -l` -le 1 \ || { echo "ERROR: files left after uninstall:" ; \ if test -n "$(DESTDIR)"; then \ echo " (check DESTDIR support)"; \ fi ; \ $(distuninstallcheck_listfiles) ; \ exit 1; } >&2 distcleancheck: distclean @if test '$(srcdir)' = . ; then \ echo "ERROR: distcleancheck can only run from a VPATH build" ; \ exit 1 ; \ fi @test `$(distcleancheck_listfiles) | wc -l` -eq 0 \ || { echo "ERROR: files left in build directory after distclean:" ; \ $(distcleancheck_listfiles) ; \ exit 1; } >&2 check-am: all-am $(MAKE) $(AM_MAKEFLAGS) $(check_LTLIBRARIES) $(check_PROGRAMS) $(MAKE) $(AM_MAKEFLAGS) check-TESTS check: check-am all-am: Makefile $(LTLIBRARIES) $(HEADERS) config.h installdirs: for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(includedir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ `test -z '$(STRIP)' || \ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) -rm -f api/$(DEPDIR)/$(am__dirstamp) -rm -f api/$(am__dirstamp) -rm -f btr/$(DEPDIR)/$(am__dirstamp) -rm -f btr/$(am__dirstamp) -rm -f buf/$(DEPDIR)/$(am__dirstamp) -rm -f buf/$(am__dirstamp) -rm -f data/$(DEPDIR)/$(am__dirstamp) -rm -f data/$(am__dirstamp) -rm -f ddl/$(DEPDIR)/$(am__dirstamp) -rm -f ddl/$(am__dirstamp) -rm -f dict/$(DEPDIR)/$(am__dirstamp) -rm -f dict/$(am__dirstamp) -rm -f dyn/$(DEPDIR)/$(am__dirstamp) -rm -f dyn/$(am__dirstamp) -rm -f eval/$(DEPDIR)/$(am__dirstamp) -rm -f eval/$(am__dirstamp) -rm -f fil/$(DEPDIR)/$(am__dirstamp) -rm -f fil/$(am__dirstamp) -rm -f fsp/$(DEPDIR)/$(am__dirstamp) -rm -f fsp/$(am__dirstamp) -rm -f fut/$(DEPDIR)/$(am__dirstamp) -rm -f fut/$(am__dirstamp) -rm -f ha/$(DEPDIR)/$(am__dirstamp) -rm -f ha/$(am__dirstamp) -rm -f ibuf/$(DEPDIR)/$(am__dirstamp) -rm -f ibuf/$(am__dirstamp) -rm -f lock/$(DEPDIR)/$(am__dirstamp) -rm -f lock/$(am__dirstamp) -rm -f log/$(DEPDIR)/$(am__dirstamp) -rm -f log/$(am__dirstamp) -rm -f mach/$(DEPDIR)/$(am__dirstamp) -rm -f mach/$(am__dirstamp) -rm -f mem/$(DEPDIR)/$(am__dirstamp) -rm -f mem/$(am__dirstamp) -rm -f mtr/$(DEPDIR)/$(am__dirstamp) -rm -f mtr/$(am__dirstamp) -rm -f os/$(DEPDIR)/$(am__dirstamp) -rm -f os/$(am__dirstamp) -rm -f page/$(DEPDIR)/$(am__dirstamp) -rm -f page/$(am__dirstamp) -rm -f pars/$(DEPDIR)/$(am__dirstamp) -rm -f pars/$(am__dirstamp) -rm -f que/$(DEPDIR)/$(am__dirstamp) -rm -f que/$(am__dirstamp) -rm -f read/$(DEPDIR)/$(am__dirstamp) -rm -f read/$(am__dirstamp) -rm -f rem/$(DEPDIR)/$(am__dirstamp) -rm -f rem/$(am__dirstamp) -rm -f row/$(DEPDIR)/$(am__dirstamp) -rm -f row/$(am__dirstamp) -rm -f srv/$(DEPDIR)/$(am__dirstamp) -rm -f srv/$(am__dirstamp) -rm -f sync/$(DEPDIR)/$(am__dirstamp) -rm -f sync/$(am__dirstamp) -rm -f tests/$(DEPDIR)/$(am__dirstamp) -rm -f tests/$(am__dirstamp) -rm -f thr/$(DEPDIR)/$(am__dirstamp) -rm -f thr/$(am__dirstamp) -rm -f trx/$(DEPDIR)/$(am__dirstamp) -rm -f trx/$(am__dirstamp) -rm -f usr/$(DEPDIR)/$(am__dirstamp) -rm -f usr/$(am__dirstamp) -rm -f ut/$(DEPDIR)/$(am__dirstamp) -rm -f ut/$(am__dirstamp) -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." -rm -f pars/pars0grm.c clean: clean-am clean-am: clean-checkLTLIBRARIES clean-checkPROGRAMS clean-generic \ clean-libLTLIBRARIES clean-libtool mostlyclean-am distclean: distclean-am -rm -f $(am__CONFIG_DISTCLEAN_FILES) -rm -rf api/$(DEPDIR) btr/$(DEPDIR) buf/$(DEPDIR) data/$(DEPDIR) ddl/$(DEPDIR) dict/$(DEPDIR) dyn/$(DEPDIR) eval/$(DEPDIR) fil/$(DEPDIR) fsp/$(DEPDIR) fut/$(DEPDIR) ha/$(DEPDIR) ibuf/$(DEPDIR) lock/$(DEPDIR) log/$(DEPDIR) mach/$(DEPDIR) mem/$(DEPDIR) mtr/$(DEPDIR) os/$(DEPDIR) page/$(DEPDIR) pars/$(DEPDIR) que/$(DEPDIR) read/$(DEPDIR) rem/$(DEPDIR) row/$(DEPDIR) srv/$(DEPDIR) sync/$(DEPDIR) tests/$(DEPDIR) thr/$(DEPDIR) trx/$(DEPDIR) usr/$(DEPDIR) ut/$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-hdr distclean-libtool distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-includeHEADERS install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-libLTLIBRARIES install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f $(am__CONFIG_DISTCLEAN_FILES) -rm -rf $(top_srcdir)/autom4te.cache -rm -rf api/$(DEPDIR) btr/$(DEPDIR) buf/$(DEPDIR) data/$(DEPDIR) ddl/$(DEPDIR) dict/$(DEPDIR) dyn/$(DEPDIR) eval/$(DEPDIR) fil/$(DEPDIR) fsp/$(DEPDIR) fut/$(DEPDIR) ha/$(DEPDIR) ibuf/$(DEPDIR) lock/$(DEPDIR) log/$(DEPDIR) mach/$(DEPDIR) mem/$(DEPDIR) mtr/$(DEPDIR) os/$(DEPDIR) page/$(DEPDIR) pars/$(DEPDIR) que/$(DEPDIR) read/$(DEPDIR) rem/$(DEPDIR) row/$(DEPDIR) srv/$(DEPDIR) sync/$(DEPDIR) tests/$(DEPDIR) thr/$(DEPDIR) trx/$(DEPDIR) usr/$(DEPDIR) ut/$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-includeHEADERS uninstall-libLTLIBRARIES .MAKE: all check-am install-am install-strip .PHONY: CTAGS GTAGS all all-am am--refresh check check-TESTS check-am \ clean clean-checkLTLIBRARIES clean-checkPROGRAMS clean-generic \ clean-libLTLIBRARIES clean-libtool ctags dist dist-all \ dist-bzip2 dist-gzip dist-lzma dist-shar dist-tarZ dist-xz \ dist-zip distcheck distclean distclean-compile \ distclean-generic distclean-hdr distclean-libtool \ distclean-tags distcleancheck distdir distuninstallcheck dvi \ dvi-am html html-am info info-am install install-am \ install-data install-data-am install-dvi install-dvi-am \ install-exec install-exec-am install-html install-html-am \ install-includeHEADERS install-info install-info-am \ install-libLTLIBRARIES install-man install-pdf install-pdf-am \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags uninstall uninstall-am uninstall-includeHEADERS \ uninstall-libLTLIBRARIES test: test-clean check # stress tests, these take longer to complete test-stress: all test-clean mkdir -p $(top_srcdir)/tests/t && \ cd $(top_srcdir)/tests/t && \ for test in $(TEST_STRESS_EXECUTABLES) ; do \ echo "Running $${test} ..." ; \ echo "" ; \ ../$${test} ; \ echo "" ; \ done test-clean: cd $(top_srcdir)/tests && \ rm -fr log/ dict_test/ t/ test/ ibdata1 # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: haildb-2.3.2/ibuf/0000755000175000017500000000000011513177437014567 5ustar00pcrewspcrews00000000000000haildb-2.3.2/ibuf/ibuf0ibuf.c0000644000175000017500000027711611513177357016625 0ustar00pcrewspcrews00000000000000/***************************************************************************** Copyright (c) 1997, 2010, Innobase Oy. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************/ /**************************************************//** @file ibuf/ibuf0ibuf.c Insert buffer Created 7/19/1997 Heikki Tuuri *******************************************************/ #include "ibuf0ibuf.h" /** Number of bits describing a single page */ #define IBUF_BITS_PER_PAGE 4 #if IBUF_BITS_PER_PAGE % 2 # error "IBUF_BITS_PER_PAGE must be an even number!" #endif /** The start address for an insert buffer bitmap page bitmap */ #define IBUF_BITMAP PAGE_DATA #ifdef UNIV_NONINL #include "ibuf0ibuf.ic" #endif #ifndef UNIV_HOTBACKUP #include "buf0buf.h" #include "buf0rea.h" #include "fsp0fsp.h" #include "trx0sys.h" #include "fil0fil.h" #include "thr0loc.h" #include "rem0rec.h" #include "btr0cur.h" #include "btr0pcur.h" #include "btr0btr.h" #include "sync0sync.h" #include "dict0boot.h" #include "fut0lst.h" #include "lock0lock.h" #include "log0recv.h" #include "que0que.h" /* STRUCTURE OF AN INSERT BUFFER RECORD In versions < 4.1.x: 1. The first field is the page number. 2. The second field is an array which stores type info for each subsequent field. We store the information which affects the ordering of records, and also the physical storage size of an SQL NULL value. E.g., for CHAR(10) it is 10 bytes. 3. Next we have the fields of the actual index record. In versions >= 4.1.x: Note that contary to what we planned in the 1990's, there will only be one insert buffer tree, and that is in the system tablespace of InnoDB. 1. The first field is the space id. 2. The second field is a one-byte marker (0) which differentiates records from the < 4.1.x storage format. 3. The third field is the page number. 4. The fourth field contains the type info, where we have also added 2 bytes to store the charset. In the compressed table format of 5.0.x we must add more information here so that we can build a dummy 'index' struct which 5.0.x can use in the binary search on the index page in the ibuf merge phase. 5. The rest of the fields contain the fields of the actual index record. In versions >= 5.0.3: The first byte of the fourth field is an additional marker (0) if the record is in the compact format. The presence of this marker can be detected by looking at the length of the field modulo DATA_NEW_ORDER_NULL_TYPE_BUF_SIZE. The high-order bit of the character set field in the type info is the "nullable" flag for the field. */ /* PREVENTING DEADLOCKS IN THE INSERT BUFFER SYSTEM If an OS thread performs any operation that brings in disk pages from non-system tablespaces into the buffer pool, or creates such a page there, then the operation may have as a side effect an insert buffer index tree compression. Thus, the tree latch of the insert buffer tree may be acquired in the x-mode, and also the file space latch of the system tablespace may be acquired in the x-mode. Also, an insert to an index in a non-system tablespace can have the same effect. How do we know this cannot lead to a deadlock of OS threads? There is a problem with the i\o-handler threads: they break the latching order because they own x-latches to pages which are on a lower level than the insert buffer tree latch, its page latches, and the tablespace latch an insert buffer operation can reserve. The solution is the following: Let all the tree and page latches connected with the insert buffer be later in the latching order than the fsp latch and fsp page latches. Insert buffer pages must be such that the insert buffer is never invoked when these pages are accessed as this would result in a recursion violating the latching order. We let a special i/o-handler thread take care of i/o to the insert buffer pages and the ibuf bitmap pages, as well as the fsp bitmap pages and the first inode page, which contains the inode of the ibuf tree: let us call all these ibuf pages. To prevent deadlocks, we do not let a read-ahead access both non-ibuf and ibuf pages. Then an i/o-handler for the insert buffer never needs to access recursively the insert buffer tree and thus obeys the latching order. On the other hand, other i/o-handlers for other tablespaces may require access to the insert buffer, but because all kinds of latches they need to access there are later in the latching order, no violation of the latching order occurs in this case, either. A problem is how to grow and contract an insert buffer tree. As it is later in the latching order than the fsp management, we have to reserve the fsp latch first, before adding or removing pages from the insert buffer tree. We let the insert buffer tree have its own file space management: a free list of pages linked to the tree root. To prevent recursive using of the insert buffer when adding pages to the tree, we must first load these pages to memory, obtaining a latch on them, and only after that add them to the free list of the insert buffer tree. More difficult is removing of pages from the free list. If there is an excess of pages in the free list of the ibuf tree, they might be needed if some thread reserves the fsp latch, intending to allocate more file space. So we do the following: if a thread reserves the fsp latch, we check the writer count field of the latch. If this field has value 1, it means that the thread did not own the latch before entering the fsp system, and the mtr of the thread contains no modifications to the fsp pages. Now we are free to reserve the ibuf latch, and check if there is an excess of pages in the free list. We can then, in a separate mini-transaction, take them out of the free list and free them to the fsp system. To avoid deadlocks in the ibuf system, we divide file pages into three levels: (1) non-ibuf pages, (2) ibuf tree pages and the pages in the ibuf tree free list, and (3) ibuf bitmap pages. No OS thread is allowed to access higher level pages if it has latches to lower level pages; even if the thread owns a B-tree latch it must not access the B-tree non-leaf pages if it has latches on lower level pages. Read-ahead is only allowed for level 1 and 2 pages. Dedicated i/o-handler threads handle exclusively level 1 i/o. A dedicated i/o handler thread handles exclusively level 2 i/o. However, if an OS thread does the i/o handling for itself, i.e., it uses synchronous aio, it can access any pages, as long as it obeys the access order rules. */ /** Buffer pool size per the maximum insert buffer size */ #define IBUF_POOL_SIZE_PER_MAX_SIZE 2 /** Table name for the insert buffer. */ #define IBUF_TABLE_NAME "SYS_IBUF_TABLE" /** Operations that can currently be buffered. */ UNIV_INTERN ibuf_use_t ibuf_use = IBUF_USE_INSERT; /** The insert buffer control structure */ UNIV_INTERN ibuf_t* ibuf = NULL; /** Counter for ibuf_should_try() */ UNIV_INTERN ulint ibuf_flush_count = 0; #ifdef UNIV_IBUF_COUNT_DEBUG /** Number of tablespaces in the ibuf_counts array */ #define IBUF_COUNT_N_SPACES 4 /** Number of pages within each tablespace in the ibuf_counts array */ #define IBUF_COUNT_N_PAGES 130000 /** Buffered entry counts for file pages, used in debugging */ static ulint ibuf_counts[IBUF_COUNT_N_SPACES][IBUF_COUNT_N_PAGES]; /******************************************************************//** Checks that the indexes to ibuf_counts[][] are within limits. */ UNIV_INLINE void ibuf_count_check( /*=============*/ ulint space_id, /*!< in: space identifier */ ulint page_no) /*!< in: page number */ { if (space_id < IBUF_COUNT_N_SPACES && page_no < IBUF_COUNT_N_PAGES) { return; } ib_logger(ib_stream, "InnoDB: UNIV_IBUF_COUNT_DEBUG limits space_id and page_no\n" "InnoDB: and breaks crash recovery.\n" "InnoDB: space_id=%lu, should be 0<=space_id<%lu\n" "InnoDB: page_no=%lu, should be 0<=page_no<%lu\n", (ulint) space_id, (ulint) IBUF_COUNT_N_SPACES, (ulint) page_no, (ulint) IBUF_COUNT_N_PAGES); ut_error; } #endif /** @name Offsets to the per-page bits in the insert buffer bitmap */ /* @{ */ #define IBUF_BITMAP_FREE 0 /*!< Bits indicating the amount of free space */ #define IBUF_BITMAP_BUFFERED 2 /*!< TRUE if there are buffered changes for the page */ #define IBUF_BITMAP_IBUF 3 /*!< TRUE if page is a part of the ibuf tree, excluding the root page, or is in the free list of the ibuf */ /* @} */ /** The mutex used to block pessimistic inserts to ibuf trees */ static mutex_t ibuf_pessimistic_insert_mutex; /** The mutex protecting the insert buffer structs */ static mutex_t ibuf_mutex; /** The mutex protecting the insert buffer bitmaps */ static mutex_t ibuf_bitmap_mutex; /** The area in pages from which contract looks for page numbers for merge */ #define IBUF_MERGE_AREA 8 /** Inside the merge area, pages which have at most 1 per this number less buffered entries compared to maximum volume that can buffered for a single page are merged along with the page whose buffer became full */ #define IBUF_MERGE_THRESHOLD 4 /** In ibuf_contract at most this number of pages is read to memory in one batch, in order to merge the entries for them in the insert buffer */ #define IBUF_MAX_N_PAGES_MERGED IBUF_MERGE_AREA /** If the combined size of the ibuf trees exceeds ibuf->max_size by this many pages, we start to contract it in connection to inserts there, using non-synchronous contract */ #define IBUF_CONTRACT_ON_INSERT_NON_SYNC 0 /** If the combined size of the ibuf trees exceeds ibuf->max_size by this many pages, we start to contract it in connection to inserts there, using synchronous contract */ #define IBUF_CONTRACT_ON_INSERT_SYNC 5 /** If the combined size of the ibuf trees exceeds ibuf->max_size by this many pages, we start to contract it synchronous contract, but do not insert */ #define IBUF_CONTRACT_DO_NOT_INSERT 10 /* TODO: how to cope with drop table if there are records in the insert buffer for the indexes of the table? Is there actually any problem, because ibuf merge is done to a page when it is read in, and it is still physically like the index page even if the index would have been dropped! So, there seems to be no problem. */ /******************************************************************//** Reset the variables. */ UNIV_INTERN void ibuf_var_init(void) /*===============*/ { ibuf = NULL; ibuf_flush_count = 0; #ifdef UNIV_IBUF_COUNT_DEBUG memset(ibuf_counts, 0x0, sizeof(ibuf_counts)); #endif memset(&ibuf_pessimistic_insert_mutex, 0x0, sizeof(ibuf_pessimistic_insert_mutex)); memset(&ibuf_mutex, 0x0, sizeof(ibuf_mutex)); memset(&ibuf_bitmap_mutex, 0x0, sizeof(ibuf_bitmap_mutex)); } /********************************************************************** Sets the flag in the current OS thread local storage denoting that it is inside an insert buffer routine. */ UNIV_INLINE void ibuf_enter(void) /*============*/ { ibool* ptr; ptr = thr_local_get_in_ibuf_field(); ut_ad(*ptr == FALSE); *ptr = TRUE; } /******************************************************************//** Sets the flag in the current OS thread local storage denoting that it is exiting an insert buffer routine. */ UNIV_INLINE void ibuf_exit(void) /*===========*/ { ibool* ptr; ptr = thr_local_get_in_ibuf_field(); ut_ad(*ptr == TRUE); *ptr = FALSE; } /******************************************************************//** Returns TRUE if the current OS thread is performing an insert buffer routine. For instance, a read-ahead of non-ibuf pages is forbidden by threads that are executing an insert buffer routine. @return TRUE if inside an insert buffer routine */ UNIV_INTERN ibool ibuf_inside(void) /*=============*/ { return(*thr_local_get_in_ibuf_field()); } /******************************************************************//** Gets the ibuf header page and x-latches it. @return insert buffer header page */ static page_t* ibuf_header_page_get( /*=================*/ mtr_t* mtr) /*!< in: mtr */ { buf_block_t* block; ut_ad(!ibuf_inside()); block = buf_page_get( IBUF_SPACE_ID, 0, FSP_IBUF_HEADER_PAGE_NO, RW_X_LATCH, mtr); buf_block_dbg_add_level(block, SYNC_IBUF_HEADER); return(buf_block_get_frame(block)); } /******************************************************************//** Gets the root page and x-latches it. @return insert buffer tree root page */ static page_t* ibuf_tree_root_get( /*===============*/ mtr_t* mtr) /*!< in: mtr */ { buf_block_t* block; ut_ad(ibuf_inside()); mtr_x_lock(dict_index_get_lock(ibuf->index), mtr); block = buf_page_get( IBUF_SPACE_ID, 0, FSP_IBUF_TREE_ROOT_PAGE_NO, RW_X_LATCH, mtr); buf_block_dbg_add_level(block, SYNC_TREE_NODE); return(buf_block_get_frame(block)); } #ifdef UNIV_IBUF_COUNT_DEBUG /******************************************************************//** Gets the ibuf count for a given page. @return number of entries in the insert buffer currently buffered for this page */ UNIV_INTERN ulint ibuf_count_get( /*===========*/ ulint space, /*!< in: space id */ ulint page_no)/*!< in: page number */ { ibuf_count_check(space, page_no); return(ibuf_counts[space][page_no]); } /******************************************************************//** Sets the ibuf count for a given page. */ static void ibuf_count_set( /*===========*/ ulint space, /*!< in: space id */ ulint page_no,/*!< in: page number */ ulint val) /*!< in: value to set */ { ibuf_count_check(space, page_no); ut_a(val < UNIV_PAGE_SIZE); ibuf_counts[space][page_no] = val; } #endif /******************************************************************//** Closes insert buffer and frees the data structures. */ UNIV_INTERN void ibuf_close(void) /*============*/ { mutex_free(&ibuf_pessimistic_insert_mutex); memset(&ibuf_pessimistic_insert_mutex, 0x0, sizeof(ibuf_pessimistic_insert_mutex)); mutex_free(&ibuf_mutex); memset(&ibuf_mutex, 0x0, sizeof(ibuf_mutex)); mutex_free(&ibuf_bitmap_mutex); memset(&ibuf_bitmap_mutex, 0x0, sizeof(ibuf_mutex)); mem_free(ibuf); ibuf = NULL; } /********************************************************************** Updates the size information of the ibuf, assuming the segment size has not changed. */ static void ibuf_size_update( /*=============*/ const page_t* root, /*!< in: ibuf tree root */ mtr_t* mtr) /*!< in: mtr */ { ut_ad(mutex_own(&ibuf_mutex)); ibuf->free_list_len = flst_get_len(root + PAGE_HEADER + PAGE_BTR_IBUF_FREE_LIST, mtr); ibuf->height = 1 + btr_page_get_level(root, mtr); /* the '1 +' is the ibuf header page */ ibuf->size = ibuf->seg_size - (1 + ibuf->free_list_len); ibuf->empty = page_get_n_recs(root) == 0; } /******************************************************************//** Creates the insert buffer data structure at a database startup and initializes the data structures for the insert buffer. */ UNIV_INTERN void ibuf_init_at_db_start(void) /*=======================*/ { page_t* root; mtr_t mtr; dict_table_t* table; mem_heap_t* heap; dict_index_t* index; ulint n_used; page_t* header_page; ulint error; ibuf = mem_alloc(sizeof(ibuf_t)); memset(ibuf, 0, sizeof(*ibuf)); /* Note that also a pessimistic delete can sometimes make a B-tree grow in size, as the references on the upper levels of the tree can change */ ibuf->max_size = buf_pool_get_curr_size() / UNIV_PAGE_SIZE / IBUF_POOL_SIZE_PER_MAX_SIZE; mutex_create(&ibuf_pessimistic_insert_mutex, SYNC_IBUF_PESS_INSERT_MUTEX); mutex_create(&ibuf_mutex, SYNC_IBUF_MUTEX); mutex_create(&ibuf_bitmap_mutex, SYNC_IBUF_BITMAP_MUTEX); mtr_start(&mtr); mutex_enter(&ibuf_mutex); mtr_x_lock(fil_space_get_latch(IBUF_SPACE_ID, NULL), &mtr); header_page = ibuf_header_page_get(&mtr); fseg_n_reserved_pages(header_page + IBUF_HEADER + IBUF_TREE_SEG_HEADER, &n_used, &mtr); ibuf_enter(); ut_ad(n_used >= 2); ibuf->seg_size = n_used; { buf_block_t* block; block = buf_page_get( IBUF_SPACE_ID, 0, FSP_IBUF_TREE_ROOT_PAGE_NO, RW_X_LATCH, &mtr); buf_block_dbg_add_level(block, SYNC_TREE_NODE); root = buf_block_get_frame(block); } ibuf_size_update(root, &mtr); mutex_exit(&ibuf_mutex); mtr_commit(&mtr); ibuf_exit(); heap = mem_heap_create(450); /* Use old-style record format for the insert buffer. */ table = dict_mem_table_create(IBUF_TABLE_NAME, IBUF_SPACE_ID, 1, 0); dict_mem_table_add_col(table, heap, "DUMMY_COLUMN", DATA_BINARY, 0, 0); table->id = ut_dulint_add(DICT_IBUF_ID_MIN, IBUF_SPACE_ID); dict_table_add_to_cache(table, heap); mem_heap_free(heap); index = dict_mem_index_create( IBUF_TABLE_NAME, "CLUST_IND", IBUF_SPACE_ID, DICT_CLUSTERED | DICT_UNIVERSAL | DICT_IBUF, 1); dict_mem_index_add_field(index, "DUMMY_COLUMN", 0); index->id = ut_dulint_add(DICT_IBUF_ID_MIN, IBUF_SPACE_ID); error = dict_index_add_to_cache(table, index, FSP_IBUF_TREE_ROOT_PAGE_NO, FALSE); ut_a(error == DB_SUCCESS); ibuf->index = dict_table_get_first_index(table); } #endif /* !UNIV_HOTBACKUP */ /*********************************************************************//** Initializes an ibuf bitmap page. */ UNIV_INTERN void ibuf_bitmap_page_init( /*==================*/ buf_block_t* block, /*!< in: bitmap page */ mtr_t* mtr) /*!< in: mtr */ { page_t* page; ulint byte_offset; ulint zip_size = buf_block_get_zip_size(block); ut_a(ut_is_2pow(zip_size)); page = buf_block_get_frame(block); fil_page_set_type(page, FIL_PAGE_IBUF_BITMAP); /* Write all zeros to the bitmap */ if (!zip_size) { byte_offset = UT_BITS_IN_BYTES(UNIV_PAGE_SIZE * IBUF_BITS_PER_PAGE); } else { byte_offset = UT_BITS_IN_BYTES(zip_size * IBUF_BITS_PER_PAGE); } memset(page + IBUF_BITMAP, 0, byte_offset); /* The remaining area (up to the page trailer) is uninitialized. */ #ifndef UNIV_HOTBACKUP mlog_write_initial_log_record(page, MLOG_IBUF_BITMAP_INIT, mtr); #endif /* !UNIV_HOTBACKUP */ } /*********************************************************************//** Parses a redo log record of an ibuf bitmap page init. @return end of log record or NULL */ UNIV_INTERN byte* ibuf_parse_bitmap_init( /*===================*/ byte* ptr, /*!< in: buffer */ byte* end_ptr __attribute__((unused)), /*!< in: buffer end */ buf_block_t* block, /*!< in: block or NULL */ mtr_t* mtr) /*!< in: mtr or NULL */ { ut_ad(ptr && end_ptr); if (block) { ibuf_bitmap_page_init(block, mtr); } return(ptr); } #ifndef UNIV_HOTBACKUP /********************************************************************//** Gets the desired bits for a given page from a bitmap page. @return value of bits */ UNIV_INLINE ulint ibuf_bitmap_page_get_bits( /*======================*/ const page_t* page, /*!< in: bitmap page */ ulint page_no,/*!< in: page whose bits to get */ ulint zip_size,/*!< in: compressed page size in bytes; 0 for uncompressed pages */ ulint bit, /*!< in: IBUF_BITMAP_FREE, IBUF_BITMAP_BUFFERED, ... */ mtr_t* mtr __attribute__((unused))) /*!< in: mtr containing an x-latch to the bitmap page */ { ulint byte_offset; ulint bit_offset; ulint map_byte; ulint value; ut_ad(bit < IBUF_BITS_PER_PAGE); #if IBUF_BITS_PER_PAGE % 2 # error "IBUF_BITS_PER_PAGE % 2 != 0" #endif ut_ad(ut_is_2pow(zip_size)); ut_ad(mtr_memo_contains_page(mtr, page, MTR_MEMO_PAGE_X_FIX)); if (!zip_size) { bit_offset = (page_no % UNIV_PAGE_SIZE) * IBUF_BITS_PER_PAGE + bit; } else { bit_offset = (page_no & (zip_size - 1)) * IBUF_BITS_PER_PAGE + bit; } byte_offset = bit_offset / 8; bit_offset = bit_offset % 8; ut_ad(byte_offset + IBUF_BITMAP < UNIV_PAGE_SIZE); map_byte = mach_read_from_1(page + IBUF_BITMAP + byte_offset); value = ut_bit_get_nth(map_byte, bit_offset); if (bit == IBUF_BITMAP_FREE) { ut_ad(bit_offset + 1 < 8); value = value * 2 + ut_bit_get_nth(map_byte, bit_offset + 1); } return(value); } /********************************************************************//** Sets the desired bit for a given page in a bitmap page. */ static void ibuf_bitmap_page_set_bits( /*======================*/ page_t* page, /*!< in: bitmap page */ ulint page_no,/*!< in: page whose bits to set */ ulint zip_size,/*!< in: compressed page size in bytes; 0 for uncompressed pages */ ulint bit, /*!< in: IBUF_BITMAP_FREE, IBUF_BITMAP_BUFFERED, ... */ ulint val, /*!< in: value to set */ mtr_t* mtr) /*!< in: mtr containing an x-latch to the bitmap page */ { ulint byte_offset; ulint bit_offset; ulint map_byte; ut_ad(bit < IBUF_BITS_PER_PAGE); #if IBUF_BITS_PER_PAGE % 2 # error "IBUF_BITS_PER_PAGE % 2 != 0" #endif ut_ad(ut_is_2pow(zip_size)); ut_ad(mtr_memo_contains_page(mtr, page, MTR_MEMO_PAGE_X_FIX)); #ifdef UNIV_IBUF_COUNT_DEBUG ut_a((bit != IBUF_BITMAP_BUFFERED) || (val != FALSE) || (0 == ibuf_count_get(page_get_space_id(page), page_no))); #endif if (!zip_size) { bit_offset = (page_no % UNIV_PAGE_SIZE) * IBUF_BITS_PER_PAGE + bit; } else { bit_offset = (page_no & (zip_size - 1)) * IBUF_BITS_PER_PAGE + bit; } byte_offset = bit_offset / 8; bit_offset = bit_offset % 8; ut_ad(byte_offset + IBUF_BITMAP < UNIV_PAGE_SIZE); map_byte = mach_read_from_1(page + IBUF_BITMAP + byte_offset); if (bit == IBUF_BITMAP_FREE) { ut_ad(bit_offset + 1 < 8); ut_ad(val <= 3); map_byte = ut_bit_set_nth(map_byte, bit_offset, val / 2); map_byte = ut_bit_set_nth(map_byte, bit_offset + 1, val % 2); } else { ut_ad(val <= 1); map_byte = ut_bit_set_nth(map_byte, bit_offset, val); } mlog_write_ulint(page + IBUF_BITMAP + byte_offset, map_byte, MLOG_1BYTE, mtr); } /********************************************************************//** Calculates the bitmap page number for a given page number. @return the bitmap page number where the file page is mapped */ UNIV_INLINE ulint ibuf_bitmap_page_no_calc( /*=====================*/ ulint zip_size, /*!< in: compressed page size in bytes; 0 for uncompressed pages */ ulint page_no) /*!< in: tablespace page number */ { ut_ad(ut_is_2pow(zip_size)); if (!zip_size) { return(FSP_IBUF_BITMAP_OFFSET + (page_no & ~(UNIV_PAGE_SIZE - 1))); } else { return(FSP_IBUF_BITMAP_OFFSET + (page_no & ~(zip_size - 1))); } } /********************************************************************//** Gets the ibuf bitmap page where the bits describing a given file page are stored. @return bitmap page where the file page is mapped, that is, the bitmap page containing the descriptor bits for the file page; the bitmap page is x-latched */ static page_t* ibuf_bitmap_get_map_page_func( /*==========================*/ ulint space, /*!< in: space id of the file page */ ulint page_no,/*!< in: page number of the file page */ ulint zip_size,/*!< in: compressed page size in bytes; 0 for uncompressed pages */ const char* file, /*!< in: file name */ ulint line, /*!< in: line where called */ mtr_t* mtr) /*!< in: mtr */ { buf_block_t* block; block = buf_page_get_gen(space, zip_size, ibuf_bitmap_page_no_calc(zip_size, page_no), RW_X_LATCH, NULL, BUF_GET, file, line, mtr); buf_block_dbg_add_level(block, SYNC_IBUF_BITMAP); return(buf_block_get_frame(block)); } /********************************************************************//** Gets the ibuf bitmap page where the bits describing a given file page are stored. @return bitmap page where the file page is mapped, that is, the bitmap page containing the descriptor bits for the file page; the bitmap page is x-latched @param space in: space id of the file page @param page_no in: page number of the file page @param zip_size in: compressed page size in bytes; 0 for uncompressed pages @param mtr in: mini-transaction */ #define ibuf_bitmap_get_map_page(space, page_no, zip_size, mtr) \ ibuf_bitmap_get_map_page_func(space, page_no, zip_size, \ __FILE__, __LINE__, mtr) /************************************************************************//** Sets the free bits of the page in the ibuf bitmap. This is done in a separate mini-transaction, hence this operation does not restrict further work to only ibuf bitmap operations, which would result if the latch to the bitmap page were kept. */ UNIV_INLINE void ibuf_set_free_bits_low( /*===================*/ ulint zip_size,/*!< in: compressed page size in bytes; 0 for uncompressed pages */ const buf_block_t* block, /*!< in: index page; free bits are set if the index is non-clustered and page level is 0 */ ulint val, /*!< in: value to set: < 4 */ mtr_t* mtr) /*!< in/out: mtr */ { page_t* bitmap_page; ulint space; ulint page_no; if (!page_is_leaf(buf_block_get_frame(block))) { return; } space = buf_block_get_space(block); page_no = buf_block_get_page_no(block); bitmap_page = ibuf_bitmap_get_map_page(space, page_no, zip_size, mtr); #ifdef UNIV_IBUF_DEBUG # if 0 ib_logger(ib_stream, "Setting space %lu page %lu free bits to %lu should be %lu\n", space, page_no, val, ibuf_index_page_calc_free(zip_size, block)); # endif ut_a(val <= ibuf_index_page_calc_free(zip_size, block)); #endif /* UNIV_IBUF_DEBUG */ ibuf_bitmap_page_set_bits(bitmap_page, page_no, zip_size, IBUF_BITMAP_FREE, val, mtr); } /************************************************************************//** Sets the free bit of the page in the ibuf bitmap. This is done in a separate mini-transaction, hence this operation does not restrict further work to only ibuf bitmap operations, which would result if the latch to the bitmap page were kept. */ UNIV_INTERN void ibuf_set_free_bits_func( /*====================*/ buf_block_t* block, /*!< in: index page of a non-clustered index; free bit is reset if page level is 0 */ #ifdef UNIV_IBUF_DEBUG ulint max_val,/*!< in: ULINT_UNDEFINED or a maximum value which the bits must have before setting; this is for debugging */ #endif /* UNIV_IBUF_DEBUG */ ulint val) /*!< in: value to set: < 4 */ { mtr_t mtr; page_t* page; page_t* bitmap_page; ulint space; ulint page_no; ulint zip_size; page = buf_block_get_frame(block); if (!page_is_leaf(page)) { return; } mtr_start(&mtr); space = buf_block_get_space(block); page_no = buf_block_get_page_no(block); zip_size = buf_block_get_zip_size(block); bitmap_page = ibuf_bitmap_get_map_page(space, page_no, zip_size, &mtr); #ifdef UNIV_IBUF_DEBUG if (max_val != ULINT_UNDEFINED) { ulint old_val; old_val = ibuf_bitmap_page_get_bits( bitmap_page, page_no, zip_size, IBUF_BITMAP_FREE, &mtr); # if 0 if (old_val != max_val) { ib_logger(ib_stream, "Ibuf: page %lu old val %lu max val %lu\n", page_get_page_no(page), old_val, max_val); } # endif ut_a(old_val <= max_val); } # if 0 ib_logger(ib_stream, "Setting page no %lu free bits to %lu should be %lu\n", page_get_page_no(page), val, ibuf_index_page_calc_free(zip_size, block)); # endif ut_a(val <= ibuf_index_page_calc_free(zip_size, block)); #endif /* UNIV_IBUF_DEBUG */ ibuf_bitmap_page_set_bits(bitmap_page, page_no, zip_size, IBUF_BITMAP_FREE, val, &mtr); mtr_commit(&mtr); } /************************************************************************//** Resets the free bits of the page in the ibuf bitmap. This is done in a separate mini-transaction, hence this operation does not restrict further work to only ibuf bitmap operations, which would result if the latch to the bitmap page were kept. NOTE: The free bits in the insert buffer bitmap must never exceed the free space on a page. It is safe to decrement or reset the bits in the bitmap in a mini-transaction that is committed before the mini-transaction that affects the free space. */ UNIV_INTERN void ibuf_reset_free_bits( /*=================*/ buf_block_t* block) /*!< in: index page; free bits are set to 0 if the index is a non-clustered non-unique, and page level is 0 */ { ibuf_set_free_bits(block, 0, ULINT_UNDEFINED); } /**********************************************************************//** Updates the free bits for an uncompressed page to reflect the present state. Does this in the mtr given, which means that the latching order rules virtually prevent any further operations for this OS thread until mtr is committed. NOTE: The free bits in the insert buffer bitmap must never exceed the free space on a page. It is safe to set the free bits in the same mini-transaction that updated the page. */ UNIV_INTERN void ibuf_update_free_bits_low( /*======================*/ const buf_block_t* block, /*!< in: index page */ ulint max_ins_size, /*!< in: value of maximum insert size with reorganize before the latest operation performed to the page */ mtr_t* mtr) /*!< in/out: mtr */ { ulint before; ulint after; ut_a(!buf_block_get_page_zip(block)); before = ibuf_index_page_calc_free_bits(0, max_ins_size); after = ibuf_index_page_calc_free(0, block); /* This approach cannot be used on compressed pages, since the computed value of "before" often does not match the current state of the bitmap. This is because the free space may increase or decrease when a compressed page is reorganized. */ if (before != after) { ibuf_set_free_bits_low(0, block, after, mtr); } } /**********************************************************************//** Updates the free bits for a compressed page to reflect the present state. Does this in the mtr given, which means that the latching order rules virtually prevent any further operations for this OS thread until mtr is committed. NOTE: The free bits in the insert buffer bitmap must never exceed the free space on a page. It is safe to set the free bits in the same mini-transaction that updated the page. */ UNIV_INTERN void ibuf_update_free_bits_zip( /*======================*/ buf_block_t* block, /*!< in/out: index page */ mtr_t* mtr) /*!< in/out: mtr */ { page_t* bitmap_page; ulint space; ulint page_no; ulint zip_size; ulint after; space = buf_block_get_space(block); page_no = buf_block_get_page_no(block); zip_size = buf_block_get_zip_size(block); ut_a(page_is_leaf(buf_block_get_frame(block))); ut_a(zip_size); bitmap_page = ibuf_bitmap_get_map_page(space, page_no, zip_size, mtr); after = ibuf_index_page_calc_free_zip(zip_size, block); if (after == 0) { /* We move the page to the front of the buffer pool LRU list: the purpose of this is to prevent those pages to which we cannot make inserts using the insert buffer from slipping out of the buffer pool */ buf_page_make_young(&block->page); } ibuf_bitmap_page_set_bits(bitmap_page, page_no, zip_size, IBUF_BITMAP_FREE, after, mtr); } /**********************************************************************//** Updates the free bits for the two pages to reflect the present state. Does this in the mtr given, which means that the latching order rules virtually prevent any further operations until mtr is committed. NOTE: The free bits in the insert buffer bitmap must never exceed the free space on a page. It is safe to set the free bits in the same mini-transaction that updated the pages. */ UNIV_INTERN void ibuf_update_free_bits_for_two_pages_low( /*====================================*/ ulint zip_size,/*!< in: compressed page size in bytes; 0 for uncompressed pages */ buf_block_t* block1, /*!< in: index page */ buf_block_t* block2, /*!< in: index page */ mtr_t* mtr) /*!< in: mtr */ { ulint state; /* As we have to x-latch two random bitmap pages, we have to acquire the bitmap mutex to prevent a deadlock with a similar operation performed by another OS thread. */ mutex_enter(&ibuf_bitmap_mutex); state = ibuf_index_page_calc_free(zip_size, block1); ibuf_set_free_bits_low(zip_size, block1, state, mtr); state = ibuf_index_page_calc_free(zip_size, block2); ibuf_set_free_bits_low(zip_size, block2, state, mtr); mutex_exit(&ibuf_bitmap_mutex); } /**********************************************************************//** Returns TRUE if the page is one of the fixed address ibuf pages. @return TRUE if a fixed address ibuf i/o page */ UNIV_INLINE ibool ibuf_fixed_addr_page( /*=================*/ ulint space, /*!< in: space id */ ulint zip_size,/*!< in: compressed page size in bytes; 0 for uncompressed pages */ ulint page_no)/*!< in: page number */ { return((space == IBUF_SPACE_ID && page_no == IBUF_TREE_ROOT_PAGE_NO) || ibuf_bitmap_page(zip_size, page_no)); } /***********************************************************************//** Checks if a page is a level 2 or 3 page in the ibuf hierarchy of pages. Must not be called when recv_no_ibuf_operations==TRUE. @return TRUE if level 2 or level 3 page */ UNIV_INTERN ibool ibuf_page( /*======*/ ulint space, /*!< in: space id */ ulint zip_size,/*!< in: compressed page size in bytes, or 0 */ ulint page_no,/*!< in: page number */ mtr_t* mtr) /*!< in: mtr which will contain an x-latch to the bitmap page if the page is not one of the fixed address ibuf pages, or NULL, in which case a new transaction is created. */ { ibool ret; mtr_t local_mtr; page_t* bitmap_page; ut_ad(!recv_no_ibuf_operations); if (ibuf_fixed_addr_page(space, zip_size, page_no)) { return(TRUE); } else if (space != IBUF_SPACE_ID) { return(FALSE); } ut_ad(fil_space_get_type(IBUF_SPACE_ID) == FIL_TABLESPACE); if (mtr == NULL) { mtr = &local_mtr; mtr_start(mtr); } bitmap_page = ibuf_bitmap_get_map_page(space, page_no, zip_size, mtr); ret = ibuf_bitmap_page_get_bits(bitmap_page, page_no, zip_size, IBUF_BITMAP_IBUF, mtr); if (mtr == &local_mtr) { mtr_commit(mtr); } return(ret); } /********************************************************************//** Returns the page number field of an ibuf record. @return page number */ static ulint ibuf_rec_get_page_no( /*=================*/ const rec_t* rec) /*!< in: ibuf record */ { const byte* field; ulint len; ut_ad(ibuf_inside()); ut_ad(rec_get_n_fields_old(rec) > 2); field = rec_get_nth_field_old(rec, 1, &len); if (len == 1) { /* This is of the >= 4.1.x record format */ ut_a(trx_sys_multiple_tablespace_format); field = rec_get_nth_field_old(rec, 2, &len); } else { ut_a(trx_doublewrite_must_reset_space_ids); ut_a(!trx_sys_multiple_tablespace_format); field = rec_get_nth_field_old(rec, 0, &len); } ut_a(len == 4); return(mach_read_from_4(field)); } /********************************************************************//** Returns the space id field of an ibuf record. For < 4.1.x format records returns 0. @return space id */ static ulint ibuf_rec_get_space( /*===============*/ const rec_t* rec) /*!< in: ibuf record */ { const byte* field; ulint len; ut_ad(ibuf_inside()); ut_ad(rec_get_n_fields_old(rec) > 2); field = rec_get_nth_field_old(rec, 1, &len); if (len == 1) { /* This is of the >= 4.1.x record format */ ut_a(trx_sys_multiple_tablespace_format); field = rec_get_nth_field_old(rec, 0, &len); ut_a(len == 4); return(mach_read_from_4(field)); } ut_a(trx_doublewrite_must_reset_space_ids); ut_a(!trx_sys_multiple_tablespace_format); return(0); } /********************************************************************//** Creates a dummy index for inserting a record to a non-clustered index. @return dummy index */ static dict_index_t* ibuf_dummy_index_create( /*====================*/ ulint n, /*!< in: number of fields */ ibool comp) /*!< in: TRUE=use compact record format */ { dict_table_t* table; dict_index_t* index; table = dict_mem_table_create("IBUF_DUMMY", DICT_HDR_SPACE, n, comp ? DICT_TF_COMPACT : 0); index = dict_mem_index_create("IBUF_DUMMY", "IBUF_DUMMY", DICT_HDR_SPACE, 0, n); index->table = table; /* avoid ut_ad(index->cached) in dict_index_get_n_unique_in_tree */ index->cached = TRUE; return(index); } /********************************************************************//** Add a column to the dummy index */ static void ibuf_dummy_index_add_col( /*=====================*/ dict_index_t* index, /*!< in: dummy index */ const dtype_t* type, /*!< in: the data type of the column */ ulint len) /*!< in: length of the column */ { ulint i = index->table->n_def; dict_mem_table_add_col(index->table, NULL, NULL, dtype_get_mtype(type), dtype_get_prtype(type), dtype_get_len(type)); dict_index_add_col(index, index->table, dict_table_get_nth_col(index->table, i), len); } /********************************************************************//** Deallocates a dummy index for inserting a record to a non-clustered index. */ static void ibuf_dummy_index_free( /*==================*/ dict_index_t* index) /*!< in, own: dummy index */ { dict_table_t* table = index->table; dict_mem_index_free(index); dict_mem_table_free(table); } /*********************************************************************//** Builds the entry to insert into a non-clustered index when we have the corresponding record in an ibuf index. NOTE that as we copy pointers to fields in ibuf_rec, the caller must hold a latch to the ibuf_rec page as long as the entry is used! @return own: entry to insert to a non-clustered index */ UNIV_INLINE dtuple_t* ibuf_build_entry_pre_4_1_x( /*=======================*/ const rec_t* ibuf_rec, /*!< in: record in an insert buffer */ mem_heap_t* heap, /*!< in: heap where built */ dict_index_t** pindex) /*!< out, own: dummy index that describes the entry */ { ulint i; ulint len; const byte* types; dtuple_t* tuple; ulint n_fields; ut_a(trx_doublewrite_must_reset_space_ids); ut_a(!trx_sys_multiple_tablespace_format); n_fields = rec_get_n_fields_old(ibuf_rec) - 2; tuple = dtuple_create(heap, n_fields); types = rec_get_nth_field_old(ibuf_rec, 1, &len); ut_a(len == n_fields * DATA_ORDER_NULL_TYPE_BUF_SIZE); for (i = 0; i < n_fields; i++) { const byte* data; dfield_t* field; field = dtuple_get_nth_field(tuple, i); data = rec_get_nth_field_old(ibuf_rec, i + 2, &len); dfield_set_data(field, data, len); dtype_read_for_order_and_null_size( dfield_get_type(field), types + i * DATA_ORDER_NULL_TYPE_BUF_SIZE); } *pindex = ibuf_dummy_index_create(n_fields, FALSE); return(tuple); } /*********************************************************************//** Builds the entry to insert into a non-clustered index when we have the corresponding record in an ibuf index. NOTE that as we copy pointers to fields in ibuf_rec, the caller must hold a latch to the ibuf_rec page as long as the entry is used! @return own: entry to insert to a non-clustered index */ static dtuple_t* ibuf_build_entry_from_ibuf_rec( /*===========================*/ const rec_t* ibuf_rec, /*!< in: record in an insert buffer */ mem_heap_t* heap, /*!< in: heap where built */ dict_index_t** pindex) /*!< out, own: dummy index that describes the entry */ { dtuple_t* tuple; dfield_t* field; ulint n_fields; const byte* types; const byte* data; ulint len; ulint i; dict_index_t* index; data = rec_get_nth_field_old(ibuf_rec, 1, &len); if (len > 1) { /* This a < 4.1.x format record */ return(ibuf_build_entry_pre_4_1_x(ibuf_rec, heap, pindex)); } /* This a >= 4.1.x format record */ ut_a(trx_sys_multiple_tablespace_format); ut_a(*data == 0); ut_a(rec_get_n_fields_old(ibuf_rec) > 4); n_fields = rec_get_n_fields_old(ibuf_rec) - 4; tuple = dtuple_create(heap, n_fields); types = rec_get_nth_field_old(ibuf_rec, 3, &len); ut_a(len % DATA_NEW_ORDER_NULL_TYPE_BUF_SIZE <= 1); index = ibuf_dummy_index_create( n_fields, len % DATA_NEW_ORDER_NULL_TYPE_BUF_SIZE); if (len % DATA_NEW_ORDER_NULL_TYPE_BUF_SIZE) { /* compact record format */ len--; ut_a(*types == 0); types++; } ut_a(len == n_fields * DATA_NEW_ORDER_NULL_TYPE_BUF_SIZE); for (i = 0; i < n_fields; i++) { field = dtuple_get_nth_field(tuple, i); data = rec_get_nth_field_old(ibuf_rec, i + 4, &len); dfield_set_data(field, data, len); dtype_new_read_for_order_and_null_size( dfield_get_type(field), types + i * DATA_NEW_ORDER_NULL_TYPE_BUF_SIZE); ibuf_dummy_index_add_col(index, dfield_get_type(field), len); } /* Prevent an ut_ad() failure in page_zip_write_rec() by adding system columns to the dummy table pointed to by the dummy secondary index. The insert buffer is only used for secondary indexes, whose records never contain any system columns, such as DB_TRX_ID. */ ut_d(dict_table_add_system_columns(index->table, index->table->heap)); *pindex = index; return(tuple); } /********************************************************************//** Returns the space taken by a stored non-clustered index entry if converted to an index record. @return size of index record in bytes + an upper limit of the space taken in the page directory */ static ulint ibuf_rec_get_volume( /*================*/ const rec_t* ibuf_rec)/*!< in: ibuf record */ { dtype_t dtype; ibool new_format = FALSE; ulint data_size = 0; ulint n_fields; const byte* types; const byte* data; ulint len; ulint i; ulint comp; ut_ad(ibuf_inside()); ut_ad(rec_get_n_fields_old(ibuf_rec) > 2); data = rec_get_nth_field_old(ibuf_rec, 1, &len); if (len > 1) { /* < 4.1.x format record */ ut_a(trx_doublewrite_must_reset_space_ids); ut_a(!trx_sys_multiple_tablespace_format); n_fields = rec_get_n_fields_old(ibuf_rec) - 2; types = rec_get_nth_field_old(ibuf_rec, 1, &len); ut_ad(len == n_fields * DATA_ORDER_NULL_TYPE_BUF_SIZE); comp = 0; } else { /* >= 4.1.x format record */ ut_a(trx_sys_multiple_tablespace_format); ut_a(*data == 0); types = rec_get_nth_field_old(ibuf_rec, 3, &len); comp = len % DATA_NEW_ORDER_NULL_TYPE_BUF_SIZE; ut_a(comp <= 1); if (comp) { /* compact record format */ ulint volume; dict_index_t* dummy_index; mem_heap_t* heap = mem_heap_create(500); dtuple_t* entry = ibuf_build_entry_from_ibuf_rec( ibuf_rec, heap, &dummy_index); volume = rec_get_converted_size(dummy_index, entry, 0); ibuf_dummy_index_free(dummy_index); mem_heap_free(heap); return(volume + page_dir_calc_reserved_space(1)); } n_fields = rec_get_n_fields_old(ibuf_rec) - 4; new_format = TRUE; } for (i = 0; i < n_fields; i++) { if (new_format) { data = rec_get_nth_field_old(ibuf_rec, i + 4, &len); dtype_new_read_for_order_and_null_size( &dtype, types + i * DATA_NEW_ORDER_NULL_TYPE_BUF_SIZE); } else { data = rec_get_nth_field_old(ibuf_rec, i + 2, &len); dtype_read_for_order_and_null_size( &dtype, types + i * DATA_ORDER_NULL_TYPE_BUF_SIZE); } if (len == UNIV_SQL_NULL) { data_size += dtype_get_sql_null_size(&dtype, comp); } else { data_size += len; } } return(data_size + rec_get_converted_extra_size(data_size, n_fields, 0) + page_dir_calc_reserved_space(1)); } /*********************************************************************//** Builds the tuple to insert to an ibuf tree when we have an entry for a non-clustered index. NOTE that the original entry must be kept because we copy pointers to its fields. @return own: entry to insert into an ibuf index tree */ static dtuple_t* ibuf_entry_build( /*=============*/ dict_index_t* index, /*!< in: non-clustered index */ const dtuple_t* entry, /*!< in: entry for a non-clustered index */ ulint space, /*!< in: space id */ ulint page_no,/*!< in: index page number where entry should be inserted */ mem_heap_t* heap) /*!< in: heap into which to build */ { dtuple_t* tuple; dfield_t* field; const dfield_t* entry_field; ulint n_fields; byte* buf; byte* buf2; ulint i; /* Starting from 4.1.x, we have to build a tuple whose (1) first field is the space id, (2) the second field a single marker byte (0) to tell that this is a new format record, (3) the third contains the page number, and (4) the fourth contains the relevent type information of each data field; the length of this field % DATA_NEW_ORDER_NULL_TYPE_BUF_SIZE is (a) 0 for b-trees in the old format, and (b) 1 for b-trees in the compact format, the first byte of the field being the marker (0); (5) and the rest of the fields are copied from entry. All fields in the tuple are ordered like the type binary in our insert buffer tree. */ n_fields = dtuple_get_n_fields(entry); tuple = dtuple_create(heap, n_fields + 4); /* Store the space id in tuple */ field = dtuple_get_nth_field(tuple, 0); buf = mem_heap_alloc(heap, 4); mach_write_to_4(buf, space); dfield_set_data(field, buf, 4); /* Store the marker byte field in tuple */ field = dtuple_get_nth_field(tuple, 1); buf = mem_heap_alloc(heap, 1); /* We set the marker byte zero */ mach_write_to_1(buf, 0); dfield_set_data(field, buf, 1); /* Store the page number in tuple */ field = dtuple_get_nth_field(tuple, 2); buf = mem_heap_alloc(heap, 4); mach_write_to_4(buf, page_no); dfield_set_data(field, buf, 4); /* Store the type info in buf2, and add the fields from entry to tuple */ buf2 = mem_heap_alloc(heap, n_fields * DATA_NEW_ORDER_NULL_TYPE_BUF_SIZE + dict_table_is_comp(index->table)); if (dict_table_is_comp(index->table)) { *buf2++ = 0; /* write the compact format indicator */ } for (i = 0; i < n_fields; i++) { ulint fixed_len; const dict_field_t* ifield; /* We add 4 below because we have the 4 extra fields at the start of an ibuf record */ field = dtuple_get_nth_field(tuple, i + 4); entry_field = dtuple_get_nth_field(entry, i); dfield_copy(field, entry_field); ifield = dict_index_get_nth_field(index, i); /* Prefix index columns of fixed-length columns are of fixed length. However, in the function call below, dfield_get_type(entry_field) contains the fixed length of the column in the clustered index. Replace it with the fixed length of the secondary index column. */ fixed_len = ifield->fixed_len; #ifdef UNIV_DEBUG if (fixed_len) { /* dict_index_add_col() should guarantee these */ ut_ad(fixed_len <= (ulint) dfield_get_type(entry_field)->len); if (ifield->prefix_len) { ut_ad(ifield->prefix_len == fixed_len); } else { ut_ad(fixed_len == (ulint) dfield_get_type(entry_field)->len); } } #endif /* UNIV_DEBUG */ dtype_new_store_for_order_and_null_size( buf2 + i * DATA_NEW_ORDER_NULL_TYPE_BUF_SIZE, dfield_get_type(entry_field), fixed_len); } /* Store the type info in buf2 to field 3 of tuple */ field = dtuple_get_nth_field(tuple, 3); if (dict_table_is_comp(index->table)) { buf2--; } dfield_set_data(field, buf2, n_fields * DATA_NEW_ORDER_NULL_TYPE_BUF_SIZE + dict_table_is_comp(index->table)); /* Set all the types in the new tuple binary */ dtuple_set_types_binary(tuple, n_fields + 4); return(tuple); } /*********************************************************************//** Builds a search tuple used to search buffered inserts for an index page. This is for < 4.1.x format records @return own: search tuple */ static dtuple_t* ibuf_search_tuple_build( /*====================*/ ulint space, /*!< in: space id */ ulint page_no,/*!< in: index page number */ mem_heap_t* heap) /*!< in: heap into which to build */ { dtuple_t* tuple; dfield_t* field; byte* buf; ut_a(space == 0); ut_a(trx_doublewrite_must_reset_space_ids); ut_a(!trx_sys_multiple_tablespace_format); tuple = dtuple_create(heap, 1); /* Store the page number in tuple */ field = dtuple_get_nth_field(tuple, 0); buf = mem_heap_alloc(heap, 4); mach_write_to_4(buf, page_no); dfield_set_data(field, buf, 4); dtuple_set_types_binary(tuple, 1); return(tuple); } /*********************************************************************//** Builds a search tuple used to search buffered inserts for an index page. This is for >= 4.1.x format records. @return own: search tuple */ static dtuple_t* ibuf_new_search_tuple_build( /*========================*/ ulint space, /*!< in: space id */ ulint page_no,/*!< in: index page number */ mem_heap_t* heap) /*!< in: heap into which to build */ { dtuple_t* tuple; dfield_t* field; byte* buf; ut_a(trx_sys_multiple_tablespace_format); tuple = dtuple_create(heap, 3); /* Store the space id in tuple */ field = dtuple_get_nth_field(tuple, 0); buf = mem_heap_alloc(heap, 4); mach_write_to_4(buf, space); dfield_set_data(field, buf, 4); /* Store the new format record marker byte */ field = dtuple_get_nth_field(tuple, 1); buf = mem_heap_alloc(heap, 1); mach_write_to_1(buf, 0); dfield_set_data(field, buf, 1); /* Store the page number in tuple */ field = dtuple_get_nth_field(tuple, 2); buf = mem_heap_alloc(heap, 4); mach_write_to_4(buf, page_no); dfield_set_data(field, buf, 4); dtuple_set_types_binary(tuple, 3); return(tuple); } /*********************************************************************//** Checks if there are enough pages in the free list of the ibuf tree that we dare to start a pessimistic insert to the insert buffer. @return TRUE if enough free pages in list */ UNIV_INLINE ibool ibuf_data_enough_free_for_insert(void) /*==================================*/ { ut_ad(mutex_own(&ibuf_mutex)); /* We want a big margin of free pages, because a B-tree can sometimes grow in size also if records are deleted from it, as the node pointers can change, and we must make sure that we are able to delete the inserts buffered for pages that we read to the buffer pool, without any risk of running out of free space in the insert buffer. */ return(ibuf->free_list_len >= (ibuf->size / 2) + 3 * ibuf->height); } /*********************************************************************//** Checks if there are enough pages in the free list of the ibuf tree that we should remove them and free to the file space management. @return TRUE if enough free pages in list */ UNIV_INLINE ibool ibuf_data_too_much_free(void) /*=========================*/ { ut_ad(mutex_own(&ibuf_mutex)); return(ibuf->free_list_len >= 3 + (ibuf->size / 2) + 3 * ibuf->height); } /*********************************************************************//** Allocates a new page from the ibuf file segment and adds it to the free list. @return DB_SUCCESS, or DB_STRONG_FAIL if no space left */ static ulint ibuf_add_free_page(void) /*====================*/ { mtr_t mtr; page_t* header_page; ulint flags; ulint zip_size; ulint page_no; page_t* page; page_t* root; page_t* bitmap_page; mtr_start(&mtr); /* Acquire the fsp latch before the ibuf header, obeying the latching order */ mtr_x_lock(fil_space_get_latch(IBUF_SPACE_ID, &flags), &mtr); zip_size = dict_table_flags_to_zip_size(flags); header_page = ibuf_header_page_get(&mtr); /* Allocate a new page: NOTE that if the page has been a part of a non-clustered index which has subsequently been dropped, then the page may have buffered inserts in the insert buffer, and these should be deleted from there. These get deleted when the page allocation creates the page in buffer. Thus the call below may end up calling the insert buffer routines and, as we yet have no latches to insert buffer tree pages, these routines can run without a risk of a deadlock. This is the reason why we created a special ibuf header page apart from the ibuf tree. */ page_no = fseg_alloc_free_page( header_page + IBUF_HEADER + IBUF_TREE_SEG_HEADER, 0, FSP_UP, &mtr); if (page_no == FIL_NULL) { mtr_commit(&mtr); return(DB_STRONG_FAIL); } { buf_block_t* block; block = buf_page_get( IBUF_SPACE_ID, 0, page_no, RW_X_LATCH, &mtr); buf_block_dbg_add_level(block, SYNC_TREE_NODE_NEW); page = buf_block_get_frame(block); } ibuf_enter(); mutex_enter(&ibuf_mutex); root = ibuf_tree_root_get(&mtr); /* Add the page to the free list and update the ibuf size data */ flst_add_last(root + PAGE_HEADER + PAGE_BTR_IBUF_FREE_LIST, page + PAGE_HEADER + PAGE_BTR_IBUF_FREE_LIST_NODE, &mtr); mlog_write_ulint(page + FIL_PAGE_TYPE, FIL_PAGE_IBUF_FREE_LIST, MLOG_2BYTES, &mtr); ibuf->seg_size++; ibuf->free_list_len++; /* Set the bit indicating that this page is now an ibuf tree page (level 2 page) */ bitmap_page = ibuf_bitmap_get_map_page( IBUF_SPACE_ID, page_no, zip_size, &mtr); ibuf_bitmap_page_set_bits( bitmap_page, page_no, zip_size, IBUF_BITMAP_IBUF, TRUE, &mtr); mtr_commit(&mtr); mutex_exit(&ibuf_mutex); ibuf_exit(); return(DB_SUCCESS); } /*********************************************************************//** Removes a page from the free list and frees it to the fsp system. */ static void ibuf_remove_free_page(void) /*=======================*/ { mtr_t mtr; mtr_t mtr2; page_t* header_page; ulint flags; ulint zip_size; ulint page_no; page_t* page; page_t* root; page_t* bitmap_page; mtr_start(&mtr); /* Acquire the fsp latch before the ibuf header, obeying the latching order */ mtr_x_lock(fil_space_get_latch(IBUF_SPACE_ID, &flags), &mtr); zip_size = dict_table_flags_to_zip_size(flags); header_page = ibuf_header_page_get(&mtr); /* Prevent pessimistic inserts to insert buffer trees for a while */ mutex_enter(&ibuf_pessimistic_insert_mutex); ibuf_enter(); mutex_enter(&ibuf_mutex); if (!ibuf_data_too_much_free()) { mutex_exit(&ibuf_mutex); ibuf_exit(); mutex_exit(&ibuf_pessimistic_insert_mutex); mtr_commit(&mtr); return; } mtr_start(&mtr2); root = ibuf_tree_root_get(&mtr2); page_no = flst_get_last(root + PAGE_HEADER + PAGE_BTR_IBUF_FREE_LIST, &mtr2).page; /* NOTE that we must release the latch on the ibuf tree root because in fseg_free_page we access level 1 pages, and the root is a level 2 page. */ mtr_commit(&mtr2); mutex_exit(&ibuf_mutex); ibuf_exit(); /* Since pessimistic inserts were prevented, we know that the page is still in the free list. NOTE that also deletes may take pages from the free list, but they take them from the start, and the free list was so long that they cannot have taken the last page from it. */ fseg_free_page(header_page + IBUF_HEADER + IBUF_TREE_SEG_HEADER, IBUF_SPACE_ID, page_no, &mtr); #ifdef UNIV_DEBUG_FILE_ACCESSES buf_page_reset_file_page_was_freed(IBUF_SPACE_ID, page_no); #endif ibuf_enter(); mutex_enter(&ibuf_mutex); root = ibuf_tree_root_get(&mtr); ut_ad(page_no == flst_get_last(root + PAGE_HEADER + PAGE_BTR_IBUF_FREE_LIST, &mtr).page); { buf_block_t* block; block = buf_page_get( IBUF_SPACE_ID, 0, page_no, RW_X_LATCH, &mtr); buf_block_dbg_add_level(block, SYNC_TREE_NODE); page = buf_block_get_frame(block); } /* Remove the page from the free list and update the ibuf size data */ flst_remove(root + PAGE_HEADER + PAGE_BTR_IBUF_FREE_LIST, page + PAGE_HEADER + PAGE_BTR_IBUF_FREE_LIST_NODE, &mtr); ibuf->seg_size--; ibuf->free_list_len--; mutex_exit(&ibuf_pessimistic_insert_mutex); /* Set the bit indicating that this page is no more an ibuf tree page (level 2 page) */ bitmap_page = ibuf_bitmap_get_map_page( IBUF_SPACE_ID, page_no, zip_size, &mtr); ibuf_bitmap_page_set_bits( bitmap_page, page_no, zip_size, IBUF_BITMAP_IBUF, FALSE, &mtr); #ifdef UNIV_DEBUG_FILE_ACCESSES buf_page_set_file_page_was_freed(IBUF_SPACE_ID, page_no); #endif mtr_commit(&mtr); mutex_exit(&ibuf_mutex); ibuf_exit(); } /***********************************************************************//** Frees excess pages from the ibuf free list. This function is called when an OS thread calls fsp services to allocate a new file segment, or a new page to a file segment, and the thread did not own the fsp latch before this call. */ UNIV_INTERN void ibuf_free_excess_pages(void) /*========================*/ { ulint i; #ifdef UNIV_SYNC_DEBUG ut_ad(rw_lock_own(fil_space_get_latch(IBUF_SPACE_ID, NULL), RW_LOCK_EX)); #endif /* UNIV_SYNC_DEBUG */ ut_ad(rw_lock_get_x_lock_count( fil_space_get_latch(IBUF_SPACE_ID, NULL)) == 1); ut_ad(!ibuf_inside()); /* NOTE: We require that the thread did not own the latch before, because then we know that we can obey the correct latching order for ibuf latches */ if (!ibuf) { /* Not yet initialized; not sure if this is possible, but does no harm to check for it. */ return; } /* Free at most a few pages at a time, so that we do not delay the requested service too much */ for (i = 0; i < 4; i++) { mutex_enter(&ibuf_mutex); if (!ibuf_data_too_much_free()) { mutex_exit(&ibuf_mutex); return; } mutex_exit(&ibuf_mutex); ibuf_remove_free_page(); } } /*********************************************************************//** Reads page numbers from a leaf in an ibuf tree. @return a lower limit for the combined volume of records which will be merged */ static ulint ibuf_get_merge_page_nos( /*====================*/ ibool contract,/*!< in: TRUE if this function is called to contract the tree, FALSE if this is called when a single page becomes full and we look if it pays to read also nearby pages */ rec_t* rec, /*!< in: record from which we read up and down in the chain of records */ ulint* space_ids,/*!< in/out: space id's of the pages */ ib_int64_t* space_versions,/*!< in/out: tablespace version timestamps; used to prevent reading in old pages after DISCARD + IMPORT tablespace */ ulint* page_nos,/*!< in/out: buffer for at least IBUF_MAX_N_PAGES_MERGED many page numbers; the page numbers are in an ascending order */ ulint* n_stored)/*!< out: number of page numbers stored to page_nos in this function */ { ulint prev_page_no; ulint prev_space_id; ulint first_page_no; ulint first_space_id; ulint rec_page_no; ulint rec_space_id; ulint sum_volumes; ulint volume_for_page; ulint rec_volume; ulint limit; ulint n_pages; *n_stored = 0; limit = ut_min(IBUF_MAX_N_PAGES_MERGED, buf_pool->curr_size / 4); if (page_rec_is_supremum(rec)) { rec = page_rec_get_prev(rec); } if (page_rec_is_infimum(rec)) { rec = page_rec_get_next(rec); } if (page_rec_is_supremum(rec)) { return(0); } first_page_no = ibuf_rec_get_page_no(rec); first_space_id = ibuf_rec_get_space(rec); n_pages = 0; prev_page_no = 0; prev_space_id = 0; /* Go backwards from the first rec until we reach the border of the 'merge area', or the page start or the limit of storeable pages is reached */ while (!page_rec_is_infimum(rec) && UNIV_LIKELY(n_pages < limit)) { rec_page_no = ibuf_rec_get_page_no(rec); rec_space_id = ibuf_rec_get_space(rec); if (rec_space_id != first_space_id || (rec_page_no / IBUF_MERGE_AREA) != (first_page_no / IBUF_MERGE_AREA)) { break; } if (rec_page_no != prev_page_no || rec_space_id != prev_space_id) { n_pages++; } prev_page_no = rec_page_no; prev_space_id = rec_space_id; rec = page_rec_get_prev(rec); } rec = page_rec_get_next(rec); /* At the loop start there is no prev page; we mark this with a pair of space id, page no (0, 0) for which there can never be entries in the insert buffer */ prev_page_no = 0; prev_space_id = 0; sum_volumes = 0; volume_for_page = 0; while (*n_stored < limit) { if (page_rec_is_supremum(rec)) { /* When no more records available, mark this with another 'impossible' pair of space id, page no */ rec_page_no = 1; rec_space_id = 0; } else { rec_page_no = ibuf_rec_get_page_no(rec); rec_space_id = ibuf_rec_get_space(rec); ut_ad(rec_page_no > IBUF_TREE_ROOT_PAGE_NO); } #ifdef UNIV_IBUF_DEBUG ut_a(*n_stored < IBUF_MAX_N_PAGES_MERGED); #endif if ((rec_space_id != prev_space_id || rec_page_no != prev_page_no) && (prev_space_id != 0 || prev_page_no != 0)) { if ((prev_page_no == first_page_no && prev_space_id == first_space_id) || contract || (volume_for_page > ((IBUF_MERGE_THRESHOLD - 1) * 4 * UNIV_PAGE_SIZE / IBUF_PAGE_SIZE_PER_FREE_SPACE) / IBUF_MERGE_THRESHOLD)) { space_ids[*n_stored] = prev_space_id; space_versions[*n_stored] = fil_space_get_version(prev_space_id); page_nos[*n_stored] = prev_page_no; (*n_stored)++; sum_volumes += volume_for_page; } if (rec_space_id != first_space_id || rec_page_no / IBUF_MERGE_AREA != first_page_no / IBUF_MERGE_AREA) { break; } volume_for_page = 0; } if (rec_page_no == 1 && rec_space_id == 0) { /* Supremum record */ break; } rec_volume = ibuf_rec_get_volume(rec); volume_for_page += rec_volume; prev_page_no = rec_page_no; prev_space_id = rec_space_id; rec = page_rec_get_next(rec); } #ifdef UNIV_IBUF_DEBUG ut_a(*n_stored <= IBUF_MAX_N_PAGES_MERGED); #endif #if 0 ib_logger(ib_stream, "Ibuf merge batch %lu pages %lu volume\n", *n_stored, sum_volumes); #endif return(sum_volumes); } /*********************************************************************//** Contracts insert buffer trees by reading pages to the buffer pool. @return a lower limit for the combined size in bytes of entries which will be merged from ibuf trees to the pages read, 0 if ibuf is empty */ static ulint ibuf_contract_ext( /*==============*/ ulint* n_pages,/*!< out: number of pages to which merged */ ibool sync) /*!< in: TRUE if the caller wants to wait for the issued read with the highest tablespace address to complete */ { btr_pcur_t pcur; ulint page_nos[IBUF_MAX_N_PAGES_MERGED]; ulint space_ids[IBUF_MAX_N_PAGES_MERGED]; ib_int64_t space_versions[IBUF_MAX_N_PAGES_MERGED]; ulint n_stored; ulint sum_sizes; mtr_t mtr; *n_pages = 0; ut_ad(!ibuf_inside()); mutex_enter(&ibuf_mutex); if (ibuf->empty) { ibuf_is_empty: mutex_exit(&ibuf_mutex); return(0); } mtr_start(&mtr); ibuf_enter(); /* Open a cursor to a randomly chosen leaf of the tree, at a random position within the leaf */ btr_pcur_open_at_rnd_pos(ibuf->index, BTR_SEARCH_LEAF, &pcur, &mtr); if (page_get_n_recs(btr_pcur_get_page(&pcur)) == 0) { /* When the ibuf tree is emptied completely, the last record is removed using an optimistic delete and ibuf_size_update is not called, causing ibuf->empty to remain FALSE. If we do not reset it to TRUE here then database shutdown will hang in the loop in ibuf_contract_for_n_pages. */ ibuf->empty = TRUE; ibuf_exit(); mtr_commit(&mtr); btr_pcur_close(&pcur); goto ibuf_is_empty; } mutex_exit(&ibuf_mutex); sum_sizes = ibuf_get_merge_page_nos(TRUE, btr_pcur_get_rec(&pcur), space_ids, space_versions, page_nos, &n_stored); #if 0 /* defined UNIV_IBUF_DEBUG */ ib_logger(ib_stream, "Ibuf contract sync %lu pages %lu volume %lu\n", sync, n_stored, sum_sizes); #endif ibuf_exit(); mtr_commit(&mtr); btr_pcur_close(&pcur); buf_read_ibuf_merge_pages(sync, space_ids, space_versions, page_nos, n_stored); *n_pages = n_stored; return(sum_sizes + 1); } /*********************************************************************//** Contracts insert buffer trees by reading pages to the buffer pool. @return a lower limit for the combined size in bytes of entries which will be merged from ibuf trees to the pages read, 0 if ibuf is empty */ UNIV_INTERN ulint ibuf_contract( /*==========*/ ibool sync) /*!< in: TRUE if the caller wants to wait for the issued read with the highest tablespace address to complete */ { ulint n_pages; return(ibuf_contract_ext(&n_pages, sync)); } /*********************************************************************//** Contracts insert buffer trees by reading pages to the buffer pool. @return a lower limit for the combined size in bytes of entries which will be merged from ibuf trees to the pages read, 0 if ibuf is empty */ UNIV_INTERN ulint ibuf_contract_for_n_pages( /*======================*/ ibool sync, /*!< in: TRUE if the caller wants to wait for the issued read with the highest tablespace address to complete */ ulint n_pages)/*!< in: try to read at least this many pages to the buffer pool and merge the ibuf contents to them */ { ulint sum_bytes = 0; ulint sum_pages = 0; ulint n_bytes; ulint n_pag2; while (sum_pages < n_pages) { n_bytes = ibuf_contract_ext(&n_pag2, sync); if (n_bytes == 0) { return(sum_bytes); } sum_bytes += n_bytes; sum_pages += n_pag2; } return(sum_bytes); } /*********************************************************************//** Contract insert buffer trees after insert if they are too big. */ UNIV_INLINE void ibuf_contract_after_insert( /*=======================*/ ulint entry_size) /*!< in: size of a record which was inserted into an ibuf tree */ { ibool sync; ulint sum_sizes; ulint size; mutex_enter(&ibuf_mutex); if (ibuf->size < ibuf->max_size + IBUF_CONTRACT_ON_INSERT_NON_SYNC) { mutex_exit(&ibuf_mutex); return; } sync = FALSE; if (ibuf->size >= ibuf->max_size + IBUF_CONTRACT_ON_INSERT_SYNC) { sync = TRUE; } mutex_exit(&ibuf_mutex); /* Contract at least entry_size many bytes */ sum_sizes = 0; size = 1; while ((size > 0) && (sum_sizes < entry_size)) { size = ibuf_contract(sync); sum_sizes += size; } } /*********************************************************************//** Gets an upper limit for the combined size of entries buffered in the insert buffer for a given page. @return upper limit for the volume of buffered inserts for the index page, in bytes; UNIV_PAGE_SIZE, if the entries for the index page span several pages in the insert buffer */ static ulint ibuf_get_volume_buffered( /*=====================*/ btr_pcur_t* pcur, /*!< in: pcur positioned at a place in an insert buffer tree where we would insert an entry for the index page whose number is page_no, latch mode has to be BTR_MODIFY_PREV or BTR_MODIFY_TREE */ ulint space, /*!< in: space id */ ulint page_no,/*!< in: page number of an index page */ mtr_t* mtr) /*!< in: mtr */ { ulint volume; rec_t* rec; page_t* page; ulint prev_page_no; page_t* prev_page; ulint next_page_no; page_t* next_page; ut_a(trx_sys_multiple_tablespace_format); ut_ad((pcur->latch_mode == BTR_MODIFY_PREV) || (pcur->latch_mode == BTR_MODIFY_TREE)); /* Count the volume of records earlier in the alphabetical order than pcur */ volume = 0; rec = btr_pcur_get_rec(pcur); page = page_align(rec); if (page_rec_is_supremum(rec)) { rec = page_rec_get_prev(rec); } for (;;) { if (page_rec_is_infimum(rec)) { break; } if (page_no != ibuf_rec_get_page_no(rec) || space != ibuf_rec_get_space(rec)) { goto count_later; } volume += ibuf_rec_get_volume(rec); rec = page_rec_get_prev(rec); } /* Look at the previous page */ prev_page_no = btr_page_get_prev(page, mtr); if (prev_page_no == FIL_NULL) { goto count_later; } { buf_block_t* block; block = buf_page_get( IBUF_SPACE_ID, 0, prev_page_no, RW_X_LATCH, mtr); buf_block_dbg_add_level(block, SYNC_TREE_NODE); prev_page = buf_block_get_frame(block); } #ifdef UNIV_BTR_DEBUG ut_a(btr_page_get_next(prev_page, mtr) == page_get_page_no(page)); #endif /* UNIV_BTR_DEBUG */ rec = page_get_supremum_rec(prev_page); rec = page_rec_get_prev(rec); for (;;) { if (page_rec_is_infimum(rec)) { /* We cannot go to yet a previous page, because we do not have the x-latch on it, and cannot acquire one because of the latching order: we have to give up */ return(UNIV_PAGE_SIZE); } if (page_no != ibuf_rec_get_page_no(rec) || space != ibuf_rec_get_space(rec)) { goto count_later; } volume += ibuf_rec_get_volume(rec); rec = page_rec_get_prev(rec); } count_later: rec = btr_pcur_get_rec(pcur); if (!page_rec_is_supremum(rec)) { rec = page_rec_get_next(rec); } for (;;) { if (page_rec_is_supremum(rec)) { break; } if (page_no != ibuf_rec_get_page_no(rec) || space != ibuf_rec_get_space(rec)) { return(volume); } volume += ibuf_rec_get_volume(rec); rec = page_rec_get_next(rec); } /* Look at the next page */ next_page_no = btr_page_get_next(page, mtr); if (next_page_no == FIL_NULL) { return(volume); } { buf_block_t* block; block = buf_page_get( IBUF_SPACE_ID, 0, next_page_no, RW_X_LATCH, mtr); buf_block_dbg_add_level(block, SYNC_TREE_NODE); next_page = buf_block_get_frame(block); } #ifdef UNIV_BTR_DEBUG ut_a(btr_page_get_prev(next_page, mtr) == page_get_page_no(page)); #endif /* UNIV_BTR_DEBUG */ rec = page_get_infimum_rec(next_page); rec = page_rec_get_next(rec); for (;;) { if (page_rec_is_supremum(rec)) { /* We give up */ return(UNIV_PAGE_SIZE); } if (page_no != ibuf_rec_get_page_no(rec) || space != ibuf_rec_get_space(rec)) { return(volume); } volume += ibuf_rec_get_volume(rec); rec = page_rec_get_next(rec); } } /*********************************************************************//** Reads the biggest tablespace id from the high end of the insert buffer tree and updates the counter in fil_system. */ UNIV_INTERN void ibuf_update_max_tablespace_id(void) /*===============================*/ { ulint max_space_id; const rec_t* rec; const byte* field; ulint len; btr_pcur_t pcur; mtr_t mtr; ut_a(!dict_table_is_comp(ibuf->index->table)); ibuf_enter(); mtr_start(&mtr); btr_pcur_open_at_index_side( FALSE, ibuf->index, BTR_SEARCH_LEAF, &pcur, TRUE, &mtr); btr_pcur_move_to_prev(&pcur, &mtr); if (btr_pcur_is_before_first_on_page(&pcur)) { /* The tree is empty */ max_space_id = 0; } else { rec = btr_pcur_get_rec(&pcur); field = rec_get_nth_field_old(rec, 0, &len); ut_a(len == 4); max_space_id = mach_read_from_4(field); } mtr_commit(&mtr); ibuf_exit(); /* printf("Maximum space id in insert buffer %lu\n", max_space_id); */ fil_set_max_space_id_if_bigger(max_space_id); } /*********************************************************************//** Makes an index insert to the insert buffer, instead of directly to the disk page, if this is possible. @return DB_SUCCESS, DB_FAIL, DB_STRONG_FAIL */ static ulint ibuf_insert_low( /*============*/ ulint mode, /*!< in: BTR_MODIFY_PREV or BTR_MODIFY_TREE */ const dtuple_t* entry, /*!< in: index entry to insert */ ulint entry_size, /*!< in: rec_get_converted_size(index, entry) */ dict_index_t* index, /*!< in: index where to insert; must not be unique or clustered */ ulint space, /*!< in: space id where to insert */ ulint zip_size,/*!< in: compressed page size in bytes, or 0 */ ulint page_no,/*!< in: page number where to insert */ que_thr_t* thr) /*!< in: query thread */ { big_rec_t* dummy_big_rec; btr_pcur_t pcur; btr_cur_t* cursor; dtuple_t* ibuf_entry; mem_heap_t* heap; ulint buffered; rec_t* ins_rec; ibool old_bit_value; page_t* bitmap_page; page_t* root; ulint err; ibool do_merge; ulint space_ids[IBUF_MAX_N_PAGES_MERGED]; ib_int64_t space_versions[IBUF_MAX_N_PAGES_MERGED]; ulint page_nos[IBUF_MAX_N_PAGES_MERGED]; ulint n_stored; ulint bits; mtr_t mtr; mtr_t bitmap_mtr; ut_a(!dict_index_is_clust(index)); ut_ad(dtuple_check_typed(entry)); ut_ad(ut_is_2pow(zip_size)); ut_a(trx_sys_multiple_tablespace_format); do_merge = FALSE; mutex_enter(&ibuf_mutex); if (ibuf->size >= ibuf->max_size + IBUF_CONTRACT_DO_NOT_INSERT) { /* Insert buffer is now too big, contract it but do not try to insert */ mutex_exit(&ibuf_mutex); #ifdef UNIV_IBUF_DEBUG ib_logger(ib_stream, "Ibuf too big\n"); #endif /* Use synchronous contract (== TRUE) */ ibuf_contract(TRUE); return(DB_STRONG_FAIL); } mutex_exit(&ibuf_mutex); if (mode == BTR_MODIFY_TREE) { mutex_enter(&ibuf_pessimistic_insert_mutex); ibuf_enter(); mutex_enter(&ibuf_mutex); while (!ibuf_data_enough_free_for_insert()) { mutex_exit(&ibuf_mutex); ibuf_exit(); mutex_exit(&ibuf_pessimistic_insert_mutex); err = ibuf_add_free_page(); if (err == DB_STRONG_FAIL) { return(err); } mutex_enter(&ibuf_pessimistic_insert_mutex); ibuf_enter(); mutex_enter(&ibuf_mutex); } } else { ibuf_enter(); } heap = mem_heap_create(512); /* Build the entry which contains the space id and the page number as the first fields and the type information for other fields, and which will be inserted to the insert buffer. */ ibuf_entry = ibuf_entry_build(index, entry, space, page_no, heap); /* Open a cursor to the insert buffer tree to calculate if we can add the new entry to it without exceeding the free space limit for the page. */ mtr_start(&mtr); btr_pcur_open(ibuf->index, ibuf_entry, PAGE_CUR_LE, mode, &pcur, &mtr); /* Find out the volume of already buffered inserts for the same index page */ buffered = ibuf_get_volume_buffered(&pcur, space, page_no, &mtr); #ifdef UNIV_IBUF_COUNT_DEBUG ut_a((buffered == 0) || ibuf_count_get(space, page_no)); #endif mtr_start(&bitmap_mtr); bitmap_page = ibuf_bitmap_get_map_page(space, page_no, zip_size, &bitmap_mtr); /* We check if the index page is suitable for buffered entries */ if (buf_page_peek(space, page_no) || lock_rec_expl_exist_on_page(space, page_no)) { err = DB_STRONG_FAIL; mtr_commit(&bitmap_mtr); goto function_exit; } bits = ibuf_bitmap_page_get_bits(bitmap_page, page_no, zip_size, IBUF_BITMAP_FREE, &bitmap_mtr); if (buffered + entry_size + page_dir_calc_reserved_space(1) > ibuf_index_page_calc_free_from_bits(zip_size, bits)) { mtr_commit(&bitmap_mtr); /* It may not fit */ err = DB_STRONG_FAIL; do_merge = TRUE; ibuf_get_merge_page_nos(FALSE, btr_pcur_get_rec(&pcur), space_ids, space_versions, page_nos, &n_stored); goto function_exit; } /* Set the bitmap bit denoting that the insert buffer contains buffered entries for this index page, if the bit is not set yet */ old_bit_value = ibuf_bitmap_page_get_bits( bitmap_page, page_no, zip_size, IBUF_BITMAP_BUFFERED, &bitmap_mtr); if (!old_bit_value) { ibuf_bitmap_page_set_bits(bitmap_page, page_no, zip_size, IBUF_BITMAP_BUFFERED, TRUE, &bitmap_mtr); } mtr_commit(&bitmap_mtr); cursor = btr_pcur_get_btr_cur(&pcur); if (mode == BTR_MODIFY_PREV) { err = btr_cur_optimistic_insert(BTR_NO_LOCKING_FLAG, cursor, ibuf_entry, &ins_rec, &dummy_big_rec, 0, thr, &mtr); if (err == DB_SUCCESS) { /* Update the page max trx id field */ page_update_max_trx_id(btr_cur_get_block(cursor), NULL, thr_get_trx(thr)->id, &mtr); } } else { ut_ad(mode == BTR_MODIFY_TREE); /* We acquire an x-latch to the root page before the insert, because a pessimistic insert releases the tree x-latch, which would cause the x-latching of the root after that to break the latching order. */ root = ibuf_tree_root_get(&mtr); err = btr_cur_pessimistic_insert(BTR_NO_LOCKING_FLAG | BTR_NO_UNDO_LOG_FLAG, cursor, ibuf_entry, &ins_rec, &dummy_big_rec, 0, thr, &mtr); if (err == DB_SUCCESS) { /* Update the page max trx id field */ page_update_max_trx_id(btr_cur_get_block(cursor), NULL, thr_get_trx(thr)->id, &mtr); } ibuf_size_update(root, &mtr); } function_exit: #ifdef UNIV_IBUF_COUNT_DEBUG if (err == DB_SUCCESS) { ib_logger(ib_stream, "Incrementing ibuf count of space %lu page %lu\n" "from %lu by 1\n", space, page_no, ibuf_count_get(space, page_no)); ibuf_count_set(space, page_no, ibuf_count_get(space, page_no) + 1); } #endif if (mode == BTR_MODIFY_TREE) { mutex_exit(&ibuf_mutex); mutex_exit(&ibuf_pessimistic_insert_mutex); } mtr_commit(&mtr); btr_pcur_close(&pcur); ibuf_exit(); mem_heap_free(heap); if (err == DB_SUCCESS) { mutex_enter(&ibuf_mutex); ibuf->empty = FALSE; ibuf->n_inserts++; mutex_exit(&ibuf_mutex); if (mode == BTR_MODIFY_TREE) { ibuf_contract_after_insert(entry_size); } } if (do_merge) { #ifdef UNIV_IBUF_DEBUG ut_a(n_stored <= IBUF_MAX_N_PAGES_MERGED); #endif buf_read_ibuf_merge_pages(FALSE, space_ids, space_versions, page_nos, n_stored); } return(err); } /*********************************************************************//** Makes an index insert to the insert buffer, instead of directly to the disk page, if this is possible. Does not do insert if the index is clustered or unique. @return TRUE if success */ UNIV_INTERN ibool ibuf_insert( /*========*/ const dtuple_t* entry, /*!< in: index entry to insert */ dict_index_t* index, /*!< in: index where to insert */ ulint space, /*!< in: space id where to insert */ ulint zip_size,/*!< in: compressed page size in bytes, or 0 */ ulint page_no,/*!< in: page number where to insert */ que_thr_t* thr) /*!< in: query thread */ { ulint err; ulint entry_size; ut_a(trx_sys_multiple_tablespace_format); ut_ad(dtuple_check_typed(entry)); ut_ad(ut_is_2pow(zip_size)); ut_a(!dict_index_is_clust(index)); switch (UNIV_EXPECT(ibuf_use, IBUF_USE_INSERT)) { case IBUF_USE_NONE: return(FALSE); case IBUF_USE_INSERT: goto do_insert; case IBUF_USE_COUNT: break; } ut_error; /* unknown value of ibuf_use */ do_insert: entry_size = rec_get_converted_size(index, entry, 0); if (entry_size >= (page_get_free_space_of_empty(dict_table_is_comp(index->table)) / 2)) { return(FALSE); } err = ibuf_insert_low(BTR_MODIFY_PREV, entry, entry_size, index, space, zip_size, page_no, thr); if (err == DB_FAIL) { err = ibuf_insert_low(BTR_MODIFY_TREE, entry, entry_size, index, space, zip_size, page_no, thr); } if (err == DB_SUCCESS) { #ifdef UNIV_IBUF_DEBUG /* ib_logger(ib_stream, "Ibuf insert for page no %lu of index %s\n", page_no, index->name); */ #endif return(TRUE); } else { ut_a(err == DB_STRONG_FAIL); return(FALSE); } } /********************************************************************//** During merge, inserts to an index page a secondary index entry extracted from the insert buffer. */ static void ibuf_insert_to_index_page( /*======================*/ dtuple_t* entry, /*!< in: buffered entry to insert */ buf_block_t* block, /*!< in/out: index page where the buffered entry should be placed */ dict_index_t* index, /*!< in: record descriptor */ mtr_t* mtr) /*!< in: mtr */ { page_cur_t page_cur; ulint low_match; page_t* page = buf_block_get_frame(block); rec_t* rec; page_t* bitmap_page; ulint old_bits; ut_ad(ibuf_inside()); ut_ad(dtuple_check_typed(entry)); if (UNIV_UNLIKELY(dict_table_is_comp(index->table) != (ibool)!!page_is_comp(page))) { ib_logger(ib_stream, "InnoDB: Trying to insert a record from" " the insert buffer to an index page\n" "InnoDB: but the 'compact' flag does not match!\n"); goto dump; } rec = page_rec_get_next(page_get_infimum_rec(page)); if (UNIV_UNLIKELY(rec_get_n_fields(rec, index) != dtuple_get_n_fields(entry))) { ib_logger(ib_stream, "InnoDB: Trying to insert a record from" " the insert buffer to an index page\n" "InnoDB: but the number of fields does not match!\n"); dump: buf_page_print(page, 0); dtuple_print(ib_stream, entry); ib_logger(ib_stream, "InnoDB: The table where where" " this index record belongs\n" "InnoDB: is now probably corrupt." " Please run CHECK TABLE on\n" "InnoDB: your tables.\n" "InnoDB: Submit a detailed bug report, check the " "InnoDB website for details"); return; } low_match = page_cur_search(block, index, entry, PAGE_CUR_LE, &page_cur); if (low_match == dtuple_get_n_fields(entry)) { page_zip_des_t* page_zip; rec = page_cur_get_rec(&page_cur); page_zip = buf_block_get_page_zip(block); btr_cur_del_unmark_for_ibuf(rec, page_zip, mtr); } else { rec = page_cur_tuple_insert(&page_cur, entry, index, 0, mtr); if (UNIV_LIKELY(rec != NULL)) { return; } /* If the record did not fit, reorganize */ btr_page_reorganize(block, index, mtr); page_cur_search(block, index, entry, PAGE_CUR_LE, &page_cur); /* This time the record must fit */ if (UNIV_UNLIKELY (!page_cur_tuple_insert(&page_cur, entry, index, 0, mtr))) { ulint space; ulint page_no; ulint zip_size; ut_print_timestamp(ib_stream); ib_logger(ib_stream, " InnoDB: Error: Insert buffer insert" " fails; page free %lu," " dtuple size %lu\n", (ulong) page_get_max_insert_size( page, 1), (ulong) rec_get_converted_size( index, entry, 0)); ib_logger(ib_stream, "InnoDB: Cannot insert index record "); dtuple_print(ib_stream, entry); ib_logger(ib_stream, "\nInnoDB: The table where" " this index record belongs\n" "InnoDB: is now probably corrupt." " Please run CHECK TABLE on\n" "InnoDB: that table.\n"); space = page_get_space_id(page); zip_size = buf_block_get_zip_size(block); page_no = page_get_page_no(page); bitmap_page = ibuf_bitmap_get_map_page( space, page_no, zip_size, mtr); old_bits = ibuf_bitmap_page_get_bits( bitmap_page, page_no, zip_size, IBUF_BITMAP_FREE, mtr); ib_logger(ib_stream, "InnoDB: space %lu, page %lu," " zip_size %lu, bitmap bits %lu\n", (ulong) space, (ulong) page_no, (ulong) zip_size, (ulong) old_bits); ib_logger(ib_stream, "InnoDB: Submit a detailed bug report, check" "the InnoDB website for details"); } } } /*********************************************************************//** Deletes from ibuf the record on which pcur is positioned. If we have to resort to a pessimistic delete, this function commits mtr and closes the cursor. @return TRUE if mtr was committed and pcur closed in this operation */ static ibool ibuf_delete_rec( /*============*/ ulint space, /*!< in: space id */ ulint page_no,/*!< in: index page number where the record should belong */ btr_pcur_t* pcur, /*!< in: pcur positioned on the record to delete, having latch mode BTR_MODIFY_LEAF */ const dtuple_t* search_tuple, /*!< in: search tuple for entries of page_no */ mtr_t* mtr) /*!< in: mtr */ { ibool success; page_t* root; ulint err; ut_ad(ibuf_inside()); ut_ad(page_rec_is_user_rec(btr_pcur_get_rec(pcur))); ut_ad(ibuf_rec_get_page_no(btr_pcur_get_rec(pcur)) == page_no); ut_ad(ibuf_rec_get_space(btr_pcur_get_rec(pcur)) == space); success = btr_cur_optimistic_delete(btr_pcur_get_btr_cur(pcur), mtr); if (success) { #ifdef UNIV_IBUF_COUNT_DEBUG ib_logger(ib_stream, "Decrementing ibuf count of space %lu page %lu\n" "from %lu by 1\n", space, page_no, ibuf_count_get(space, page_no)); ibuf_count_set(space, page_no, ibuf_count_get(space, page_no) - 1); #endif return(FALSE); } ut_ad(page_rec_is_user_rec(btr_pcur_get_rec(pcur))); ut_ad(ibuf_rec_get_page_no(btr_pcur_get_rec(pcur)) == page_no); ut_ad(ibuf_rec_get_space(btr_pcur_get_rec(pcur)) == space); /* We have to resort to a pessimistic delete from ibuf */ btr_pcur_store_position(pcur, mtr); btr_pcur_commit_specify_mtr(pcur, mtr); mutex_enter(&ibuf_mutex); mtr_start(mtr); success = btr_pcur_restore_position(BTR_MODIFY_TREE, pcur, mtr); if (!success) { if (fil_space_get_flags(space) == ULINT_UNDEFINED) { /* The tablespace has been dropped. It is possible that another thread has deleted the insert buffer entry. Do not complain. */ goto commit_and_exit; } ib_logger(ib_stream, "InnoDB: ERROR: Submit the output to InnoDB." "Check the InnoDB website for details.\n" "InnoDB: ibuf cursor restoration fails!\n" "InnoDB: ibuf record inserted to page %lu\n", (ulong) page_no); rec_print_old(ib_stream, btr_pcur_get_rec(pcur)); rec_print_old(ib_stream, pcur->old_rec); dtuple_print(ib_stream, search_tuple); rec_print_old(ib_stream, page_rec_get_next(btr_pcur_get_rec(pcur))); btr_pcur_commit_specify_mtr(pcur, mtr); ib_logger(ib_stream, "InnoDB: Validating insert buffer tree:\n"); if (!btr_validate_index(ibuf->index, NULL)) { ut_error; } ib_logger(ib_stream, "InnoDB: ibuf tree ok\n"); goto func_exit; } root = ibuf_tree_root_get(mtr); btr_cur_pessimistic_delete(&err, TRUE, btr_pcur_get_btr_cur(pcur), RB_NONE, mtr); ut_a(err == DB_SUCCESS); #ifdef UNIV_IBUF_COUNT_DEBUG ibuf_count_set(space, page_no, ibuf_count_get(space, page_no) - 1); #endif ibuf_size_update(root, mtr); commit_and_exit: btr_pcur_commit_specify_mtr(pcur, mtr); func_exit: btr_pcur_close(pcur); mutex_exit(&ibuf_mutex); return(TRUE); } /*********************************************************************//** When an index page is read from a disk to the buffer pool, this function inserts to the page the possible index entries buffered in the insert buffer. The entries are deleted from the insert buffer. If the page is not read, but created in the buffer pool, this function deletes its buffered entries from the insert buffer; there can exist entries for such a page if the page belonged to an index which subsequently was dropped. */ UNIV_INTERN void ibuf_merge_or_delete_for_page( /*==========================*/ buf_block_t* block, /*!< in: if page has been read from disk, pointer to the page x-latched, else NULL */ ulint space, /*!< in: space id of the index page */ ulint page_no,/*!< in: page number of the index page */ ulint zip_size,/*!< in: compressed page size in bytes, or 0 */ ibool update_ibuf_bitmap)/*!< in: normally this is set to TRUE, but if we have deleted or are deleting the tablespace, then we naturally do not want to update a non-existent bitmap page */ { mem_heap_t* heap; btr_pcur_t pcur; dtuple_t* search_tuple; ulint n_inserts; #ifdef UNIV_IBUF_DEBUG ulint volume; #endif page_zip_des_t* page_zip = NULL; ibool tablespace_being_deleted = FALSE; ibool corruption_noticed = FALSE; mtr_t mtr; ut_ad(!block || buf_block_get_space(block) == space); ut_ad(!block || buf_block_get_page_no(block) == page_no); ut_ad(!block || buf_block_get_zip_size(block) == zip_size); if (srv_force_recovery >= IB_RECOVERY_NO_IBUF_MERGE || trx_sys_hdr_page(space, page_no)) { return; } /* We cannot refer to zip_size in the following, because zip_size is passed as ULINT_UNDEFINED (it is unknown) when buf_read_ibuf_merge_pages() is merging (discarding) changes for a dropped tablespace. When block != NULL or update_ibuf_bitmap is specified, the zip_size must be known. That is why we will repeat the check below, with zip_size in place of 0. Passing zip_size as 0 assumes that the uncompressed page size always is a power-of-2 multiple of the compressed page size. */ if (ibuf_fixed_addr_page(space, 0, page_no) || fsp_descr_page(0, page_no)) { return; } if (UNIV_LIKELY(update_ibuf_bitmap)) { ut_a(ut_is_2pow(zip_size)); if (ibuf_fixed_addr_page(space, zip_size, page_no) || fsp_descr_page(zip_size, page_no)) { return; } /* If the following returns FALSE, we get the counter incremented, and must decrement it when we leave this function. When the counter is > 0, that prevents tablespace from being dropped. */ tablespace_being_deleted = fil_inc_pending_ibuf_merges(space); if (UNIV_UNLIKELY(tablespace_being_deleted)) { /* Do not try to read the bitmap page from space; just delete the ibuf records for the page */ block = NULL; update_ibuf_bitmap = FALSE; } else { page_t* bitmap_page; mtr_start(&mtr); bitmap_page = ibuf_bitmap_get_map_page( space, page_no, zip_size, &mtr); if (!ibuf_bitmap_page_get_bits(bitmap_page, page_no, zip_size, IBUF_BITMAP_BUFFERED, &mtr)) { /* No inserts buffered for this page */ mtr_commit(&mtr); if (!tablespace_being_deleted) { fil_decr_pending_ibuf_merges(space); } return; } mtr_commit(&mtr); } } else if (block && (ibuf_fixed_addr_page(space, zip_size, page_no) || fsp_descr_page(zip_size, page_no))) { return; } ibuf_enter(); heap = mem_heap_create(512); if (!trx_sys_multiple_tablespace_format) { ut_a(trx_doublewrite_must_reset_space_ids); search_tuple = ibuf_search_tuple_build(space, page_no, heap); } else { search_tuple = ibuf_new_search_tuple_build(space, page_no, heap); } if (block) { /* Move the ownership of the x-latch on the page to this OS thread, so that we can acquire a second x-latch on it. This is needed for the insert operations to the index page to pass the debug checks. */ rw_lock_x_lock_move_ownership(&(block->lock)); page_zip = buf_block_get_page_zip(block); if (UNIV_UNLIKELY(fil_page_get_type(block->frame) != FIL_PAGE_INDEX) || UNIV_UNLIKELY(!page_is_leaf(block->frame))) { page_t* bitmap_page; corruption_noticed = TRUE; ut_print_timestamp(ib_stream); mtr_start(&mtr); ib_logger(ib_stream, " InnoDB: Dump of the ibuf bitmap page:\n"); bitmap_page = ibuf_bitmap_get_map_page(space, page_no, zip_size, &mtr); buf_page_print(bitmap_page, 0); mtr_commit(&mtr); ib_logger(ib_stream, "\nInnoDB: Dump of the page:\n"); buf_page_print(block->frame, 0); ib_logger(ib_stream, "InnoDB: Error: corruption in the tablespace." " Bitmap shows insert\n" "InnoDB: buffer records to page n:o %lu" " though the page\n" "InnoDB: type is %lu, which is" " not an index leaf page!\n" "InnoDB: We try to resolve the problem" " by skipping the insert buffer\n" "InnoDB: merge for this page." " Please run CHECK TABLE on your tables\n" "InnoDB: to determine if they are corrupt" " after this.\n\n" "InnoDB: Please submit a detailed bug report, " "check InnoDB website for details", (ulong) page_no, (ulong) fil_page_get_type(block->frame)); } } n_inserts = 0; #ifdef UNIV_IBUF_DEBUG volume = 0; #endif loop: mtr_start(&mtr); if (block) { ibool success; success = buf_page_get_known_nowait( RW_X_LATCH, block, BUF_KEEP_OLD, __FILE__, __LINE__, &mtr); ut_a(success); buf_block_dbg_add_level(block, SYNC_TREE_NODE); } /* Position pcur in the insert buffer at the first entry for this index page */ btr_pcur_open_on_user_rec( ibuf->index, search_tuple, PAGE_CUR_GE, BTR_MODIFY_LEAF, &pcur, &mtr); if (!btr_pcur_is_on_user_rec(&pcur)) { ut_ad(btr_pcur_is_after_last_in_tree(&pcur, &mtr)); goto reset_bit; } for (;;) { rec_t* rec; ut_ad(btr_pcur_is_on_user_rec(&pcur)); rec = btr_pcur_get_rec(&pcur); /* Check if the entry is for this index page */ if (ibuf_rec_get_page_no(rec) != page_no || ibuf_rec_get_space(rec) != space) { if (block) { page_header_reset_last_insert( block->frame, page_zip, &mtr); } goto reset_bit; } if (UNIV_UNLIKELY(corruption_noticed)) { ib_logger(ib_stream, "InnoDB: Discarding record\n "); rec_print_old(ib_stream, rec); ib_logger(ib_stream, "\nInnoDB: from the insert buffer!\n\n"); } else if (block) { /* Now we have at pcur a record which should be inserted to the index page; NOTE that the call below copies pointers to fields in rec, and we must keep the latch to the rec page until the insertion is finished! */ dtuple_t* entry; trx_id_t max_trx_id; dict_index_t* dummy_index; max_trx_id = page_get_max_trx_id(page_align(rec)); page_update_max_trx_id(block, page_zip, max_trx_id, &mtr); entry = ibuf_build_entry_from_ibuf_rec( rec, heap, &dummy_index); #ifdef UNIV_IBUF_DEBUG volume += rec_get_converted_size(dummy_index, entry, 0) + page_dir_calc_reserved_space(1); ut_a(volume <= 4 * UNIV_PAGE_SIZE / IBUF_PAGE_SIZE_PER_FREE_SPACE); #endif ibuf_insert_to_index_page(entry, block, dummy_index, &mtr); ibuf_dummy_index_free(dummy_index); } n_inserts++; /* Delete the record from ibuf */ if (ibuf_delete_rec(space, page_no, &pcur, search_tuple, &mtr)) { /* Deletion was pessimistic and mtr was committed: we start from the beginning again */ goto loop; } else if (btr_pcur_is_after_last_on_page(&pcur)) { mtr_commit(&mtr); btr_pcur_close(&pcur); goto loop; } } reset_bit: #ifdef UNIV_IBUF_COUNT_DEBUG if (ibuf_count_get(space, page_no) > 0) { /* btr_print_tree(ibuf_data->index->tree, 100); ibuf_print(); */ } #endif if (UNIV_LIKELY(update_ibuf_bitmap)) { page_t* bitmap_page; bitmap_page = ibuf_bitmap_get_map_page( space, page_no, zip_size, &mtr); ibuf_bitmap_page_set_bits( bitmap_page, page_no, zip_size, IBUF_BITMAP_BUFFERED, FALSE, &mtr); if (block) { ulint old_bits = ibuf_bitmap_page_get_bits( bitmap_page, page_no, zip_size, IBUF_BITMAP_FREE, &mtr); ulint new_bits = ibuf_index_page_calc_free( zip_size, block); if (old_bits != new_bits) { ibuf_bitmap_page_set_bits( bitmap_page, page_no, zip_size, IBUF_BITMAP_FREE, new_bits, &mtr); } } } mtr_commit(&mtr); btr_pcur_close(&pcur); mem_heap_free(heap); /* Protect our statistics keeping from race conditions */ mutex_enter(&ibuf_mutex); ibuf->n_merges++; ibuf->n_merged_recs += n_inserts; mutex_exit(&ibuf_mutex); if (update_ibuf_bitmap && !tablespace_being_deleted) { fil_decr_pending_ibuf_merges(space); } ibuf_exit(); #ifdef UNIV_IBUF_COUNT_DEBUG ut_a(ibuf_count_get(space, page_no) == 0); #endif } /*********************************************************************//** Deletes all entries in the insert buffer for a given space id. This is used in DISCARD TABLESPACE and IMPORT TABLESPACE. NOTE: this does not update the page free bitmaps in the space. The space will become CORRUPT when you call this function! */ UNIV_INTERN void ibuf_delete_for_discarded_space( /*============================*/ ulint space) /*!< in: space id */ { mem_heap_t* heap; btr_pcur_t pcur; dtuple_t* search_tuple; rec_t* ibuf_rec; ulint page_no; ibool closed; ulint n_inserts; mtr_t mtr; heap = mem_heap_create(512); /* Use page number 0 to build the search tuple so that we get the cursor positioned at the first entry for this space id */ search_tuple = ibuf_new_search_tuple_build(space, 0, heap); n_inserts = 0; loop: ibuf_enter(); mtr_start(&mtr); /* Position pcur in the insert buffer at the first entry for the space */ btr_pcur_open_on_user_rec( ibuf->index, search_tuple, PAGE_CUR_GE, BTR_MODIFY_LEAF, &pcur, &mtr); if (!btr_pcur_is_on_user_rec(&pcur)) { ut_ad(btr_pcur_is_after_last_in_tree(&pcur, &mtr)); goto leave_loop; } for (;;) { ut_ad(btr_pcur_is_on_user_rec(&pcur)); ibuf_rec = btr_pcur_get_rec(&pcur); /* Check if the entry is for this space */ if (ibuf_rec_get_space(ibuf_rec) != space) { goto leave_loop; } page_no = ibuf_rec_get_page_no(ibuf_rec); n_inserts++; /* Delete the record from ibuf */ closed = ibuf_delete_rec(space, page_no, &pcur, search_tuple, &mtr); if (closed) { /* Deletion was pessimistic and mtr was committed: we start from the beginning again */ ibuf_exit(); goto loop; } if (btr_pcur_is_after_last_on_page(&pcur)) { mtr_commit(&mtr); btr_pcur_close(&pcur); ibuf_exit(); goto loop; } } leave_loop: mtr_commit(&mtr); btr_pcur_close(&pcur); /* Protect our statistics keeping from race conditions */ mutex_enter(&ibuf_mutex); ibuf->n_merges++; ibuf->n_merged_recs += n_inserts; mutex_exit(&ibuf_mutex); ibuf_exit(); mem_heap_free(heap); } /******************************************************************//** Looks if the insert buffer is empty. @return TRUE if empty */ UNIV_INTERN ibool ibuf_is_empty(void) /*===============*/ { ibool is_empty; const page_t* root; mtr_t mtr; ibuf_enter(); mutex_enter(&ibuf_mutex); mtr_start(&mtr); root = ibuf_tree_root_get(&mtr); if (page_get_n_recs(root) == 0) { is_empty = TRUE; if (ibuf->empty == FALSE) { ib_logger(ib_stream, "InnoDB: Warning: insert buffer tree is empty" " but the data struct does not\n" "InnoDB: know it. This condition is legal" " if the master thread has not yet\n" "InnoDB: run to completion.\n"); } } else { ut_a(ibuf->empty == FALSE); is_empty = FALSE; } mtr_commit(&mtr); mutex_exit(&ibuf_mutex); ibuf_exit(); return(is_empty); } /******************************************************************//** Prints info of ibuf. */ UNIV_INTERN void ibuf_print( /*=======*/ ib_stream_t ib_stream) /*!< in: stream where to print */ { #ifdef UNIV_IBUF_COUNT_DEBUG ulint i; ulint j; #endif mutex_enter(&ibuf_mutex); ib_logger(ib_stream, "Ibuf: size %lu, free list len %lu, seg size %lu,\n" "%lu inserts, %lu merged recs, %lu merges\n", (ulong) ibuf->size, (ulong) ibuf->free_list_len, (ulong) ibuf->seg_size, (ulong) ibuf->n_inserts, (ulong) ibuf->n_merged_recs, (ulong) ibuf->n_merges); #ifdef UNIV_IBUF_COUNT_DEBUG for (i = 0; i < IBUF_COUNT_N_SPACES; i++) { for (j = 0; j < IBUF_COUNT_N_PAGES; j++) { ulint count = ibuf_count_get(i, j); if (count > 0) { ib_logger(ib_stream, "Ibuf count for space/page %lu/%lu" " is %lu\n", (ulong) i, (ulong) j, (ulong) count); } } } #endif /* UNIV_IBUF_COUNT_DEBUG */ mutex_exit(&ibuf_mutex); } #endif /* !UNIV_HOTBACKUP */ haildb-2.3.2/config/0000755000175000017500000000000011513177437015107 5ustar00pcrewspcrews00000000000000haildb-2.3.2/config/config.rpath0000755000175000017500000004364711513177357017436 0ustar00pcrewspcrews00000000000000#! /bin/sh # Output a system dependent set of variables, describing how to set the # run time search path of shared libraries in an executable. # # Copyright 1996-2007 Free Software Foundation, Inc. # Taken from GNU libtool, 2001 # Originally by Gordon Matzigkeit , 1996 # # This file is free software; the Free Software Foundation gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. # # The first argument passed to this file is the canonical host specification, # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM # or # CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM # The environment variables CC, GCC, LDFLAGS, LD, with_gnu_ld # should be set by the caller. # # The set of defined variables is at the end of this script. # Known limitations: # - On IRIX 6.5 with CC="cc", the run time search patch must not be longer # than 256 bytes, otherwise the compiler driver will dump core. The only # known workaround is to choose shorter directory names for the build # directory and/or the installation directory. # All known linkers require a `.a' archive for static linking (except MSVC, # which needs '.lib'). libext=a shrext=.so host="$1" host_cpu=`echo "$host" | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` host_vendor=`echo "$host" | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` host_os=`echo "$host" | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` # Code taken from libtool.m4's _LT_CC_BASENAME. for cc_temp in $CC""; do case $cc_temp in compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; \-*) ;; *) break;; esac done cc_basename=`echo "$cc_temp" | sed -e 's%^.*/%%'` # Code taken from libtool.m4's AC_LIBTOOL_PROG_COMPILER_PIC. wl= if test "$GCC" = yes; then wl='-Wl,' else case "$host_os" in aix*) wl='-Wl,' ;; darwin*) case $cc_basename in xlc*) wl='-Wl,' ;; esac ;; mingw* | cygwin* | pw32* | os2*) ;; hpux9* | hpux10* | hpux11*) wl='-Wl,' ;; irix5* | irix6* | nonstopux*) wl='-Wl,' ;; newsos6) ;; linux* | k*bsd*-gnu) case $cc_basename in icc* | ecc*) wl='-Wl,' ;; pgcc | pgf77 | pgf90) wl='-Wl,' ;; ccc*) wl='-Wl,' ;; como) wl='-lopt=' ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) wl='-Wl,' ;; esac ;; esac ;; osf3* | osf4* | osf5*) wl='-Wl,' ;; rdos*) ;; solaris*) wl='-Wl,' ;; sunos4*) wl='-Qoption ld ' ;; sysv4 | sysv4.2uw2* | sysv4.3*) wl='-Wl,' ;; sysv4*MP*) ;; sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) wl='-Wl,' ;; unicos*) wl='-Wl,' ;; uts4*) ;; esac fi # Code taken from libtool.m4's AC_LIBTOOL_PROG_LD_SHLIBS. hardcode_libdir_flag_spec= hardcode_libdir_separator= hardcode_direct=no hardcode_minus_L=no case "$host_os" in cygwin* | mingw* | pw32*) # FIXME: the MSVC++ port hasn't been tested in a loooong time # When not using gcc, we currently assume that we are using # Microsoft Visual C++. if test "$GCC" != yes; then with_gnu_ld=no fi ;; interix*) # we just hope/assume this is gcc and not c89 (= MSVC++) with_gnu_ld=yes ;; openbsd*) with_gnu_ld=no ;; esac ld_shlibs=yes if test "$with_gnu_ld" = yes; then # Set some defaults for GNU ld with shared library support. These # are reset later if shared libraries are not supported. Putting them # here allows them to be overridden if necessary. # Unlike libtool, we use -rpath here, not --rpath, since the documented # option of GNU ld is called -rpath, not --rpath. hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' case "$host_os" in aix3* | aix4* | aix5*) # On AIX/PPC, the GNU linker is very broken if test "$host_cpu" != ia64; then ld_shlibs=no fi ;; amigaos*) hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes # Samuel A. Falvo II reports # that the semantics of dynamic libraries on AmigaOS, at least up # to version 4, is to share data among multiple programs linked # with the same dynamic library. Since this doesn't match the # behavior of shared libraries on other platforms, we cannot use # them. ld_shlibs=no ;; beos*) if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then : else ld_shlibs=no fi ;; cygwin* | mingw* | pw32*) # hardcode_libdir_flag_spec is actually meaningless, as there is # no search path for DLLs. hardcode_libdir_flag_spec='-L$libdir' if $LD --help 2>&1 | grep 'auto-import' > /dev/null; then : else ld_shlibs=no fi ;; interix[3-9]*) hardcode_direct=no hardcode_libdir_flag_spec='${wl}-rpath,$libdir' ;; gnu* | linux* | k*bsd*-gnu) if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then : else ld_shlibs=no fi ;; netbsd*) ;; solaris*) if $LD -v 2>&1 | grep 'BFD 2\.8' > /dev/null; then ld_shlibs=no elif $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then : else ld_shlibs=no fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) case `$LD -v 2>&1` in *\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*) ld_shlibs=no ;; *) if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then hardcode_libdir_flag_spec='`test -z "$SCOABSPATH" && echo ${wl}-rpath,$libdir`' else ld_shlibs=no fi ;; esac ;; sunos4*) hardcode_direct=yes ;; *) if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then : else ld_shlibs=no fi ;; esac if test "$ld_shlibs" = no; then hardcode_libdir_flag_spec= fi else case "$host_os" in aix3*) # Note: this linker hardcodes the directories in LIBPATH if there # are no directories specified by -L. hardcode_minus_L=yes if test "$GCC" = yes; then # Neither direct hardcoding nor static linking is supported with a # broken collect2. hardcode_direct=unsupported fi ;; aix4* | aix5*) if test "$host_cpu" = ia64; then # On IA64, the linker does run time linking by default, so we don't # have to do anything special. aix_use_runtimelinking=no else aix_use_runtimelinking=no # Test if we are trying to use run time linking or normal # AIX style linking. If -brtl is somewhere in LDFLAGS, we # need to do runtime linking. case $host_os in aix4.[23]|aix4.[23].*|aix5*) for ld_flag in $LDFLAGS; do if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then aix_use_runtimelinking=yes break fi done ;; esac fi hardcode_direct=yes hardcode_libdir_separator=':' if test "$GCC" = yes; then case $host_os in aix4.[012]|aix4.[012].*) collect2name=`${CC} -print-prog-name=collect2` if test -f "$collect2name" && \ strings "$collect2name" | grep resolve_lib_name >/dev/null then # We have reworked collect2 : else # We have old collect2 hardcode_direct=unsupported hardcode_minus_L=yes hardcode_libdir_flag_spec='-L$libdir' hardcode_libdir_separator= fi ;; esac fi # Begin _LT_AC_SYS_LIBPATH_AIX. echo 'int main () { return 0; }' > conftest.c ${CC} ${LDFLAGS} conftest.c -o conftest aix_libpath=`dump -H conftest 2>/dev/null | sed -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } }'` if test -z "$aix_libpath"; then aix_libpath=`dump -HX64 conftest 2>/dev/null | sed -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } }'` fi if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib" fi rm -f conftest.c conftest # End _LT_AC_SYS_LIBPATH_AIX. if test "$aix_use_runtimelinking" = yes; then hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath" else if test "$host_cpu" = ia64; then hardcode_libdir_flag_spec='${wl}-R $libdir:/usr/lib:/lib' else hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath" fi fi ;; amigaos*) hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes # see comment about different semantics on the GNU ld section ld_shlibs=no ;; bsdi[45]*) ;; cygwin* | mingw* | pw32*) # When not using gcc, we currently assume that we are using # Microsoft Visual C++. # hardcode_libdir_flag_spec is actually meaningless, as there is # no search path for DLLs. hardcode_libdir_flag_spec=' ' libext=lib ;; darwin* | rhapsody*) hardcode_direct=no if test "$GCC" = yes ; then : else case $cc_basename in xlc*) ;; *) ld_shlibs=no ;; esac fi ;; dgux*) hardcode_libdir_flag_spec='-L$libdir' ;; freebsd1*) ld_shlibs=no ;; freebsd2.2*) hardcode_libdir_flag_spec='-R$libdir' hardcode_direct=yes ;; freebsd2*) hardcode_direct=yes hardcode_minus_L=yes ;; freebsd* | dragonfly*) hardcode_libdir_flag_spec='-R$libdir' hardcode_direct=yes ;; hpux9*) hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' hardcode_libdir_separator=: hardcode_direct=yes # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. hardcode_minus_L=yes ;; hpux10*) if test "$with_gnu_ld" = no; then hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' hardcode_libdir_separator=: hardcode_direct=yes # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. hardcode_minus_L=yes fi ;; hpux11*) if test "$with_gnu_ld" = no; then hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' hardcode_libdir_separator=: case $host_cpu in hppa*64*|ia64*) hardcode_direct=no ;; *) hardcode_direct=yes # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. hardcode_minus_L=yes ;; esac fi ;; irix5* | irix6* | nonstopux*) hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' hardcode_libdir_separator=: ;; netbsd*) hardcode_libdir_flag_spec='-R$libdir' hardcode_direct=yes ;; newsos6) hardcode_direct=yes hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' hardcode_libdir_separator=: ;; openbsd*) if test -f /usr/libexec/ld.so; then hardcode_direct=yes if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then hardcode_libdir_flag_spec='${wl}-rpath,$libdir' else case "$host_os" in openbsd[01].* | openbsd2.[0-7] | openbsd2.[0-7].*) hardcode_libdir_flag_spec='-R$libdir' ;; *) hardcode_libdir_flag_spec='${wl}-rpath,$libdir' ;; esac fi else ld_shlibs=no fi ;; os2*) hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes ;; osf3*) hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' hardcode_libdir_separator=: ;; osf4* | osf5*) if test "$GCC" = yes; then hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' else # Both cc and cxx compiler support -rpath directly hardcode_libdir_flag_spec='-rpath $libdir' fi hardcode_libdir_separator=: ;; solaris*) hardcode_libdir_flag_spec='-R$libdir' ;; sunos4*) hardcode_libdir_flag_spec='-L$libdir' hardcode_direct=yes hardcode_minus_L=yes ;; sysv4) case $host_vendor in sni) hardcode_direct=yes # is this really true??? ;; siemens) hardcode_direct=no ;; motorola) hardcode_direct=no #Motorola manual says yes, but my tests say they lie ;; esac ;; sysv4.3*) ;; sysv4*MP*) if test -d /usr/nec; then ld_shlibs=yes fi ;; sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*) ;; sysv5* | sco3.2v5* | sco5v6*) hardcode_libdir_flag_spec='`test -z "$SCOABSPATH" && echo ${wl}-R,$libdir`' hardcode_libdir_separator=':' ;; uts4*) hardcode_libdir_flag_spec='-L$libdir' ;; *) ld_shlibs=no ;; esac fi # Check dynamic linker characteristics # Code taken from libtool.m4's AC_LIBTOOL_SYS_DYNAMIC_LINKER. # Unlike libtool.m4, here we don't care about _all_ names of the library, but # only about the one the linker finds when passed -lNAME. This is the last # element of library_names_spec in libtool.m4, or possibly two of them if the # linker has special search rules. library_names_spec= # the last element of library_names_spec in libtool.m4 libname_spec='lib$name' case "$host_os" in aix3*) library_names_spec='$libname.a' ;; aix4* | aix5*) library_names_spec='$libname$shrext' ;; amigaos*) library_names_spec='$libname.a' ;; beos*) library_names_spec='$libname$shrext' ;; bsdi[45]*) library_names_spec='$libname$shrext' ;; cygwin* | mingw* | pw32*) shrext=.dll library_names_spec='$libname.dll.a $libname.lib' ;; darwin* | rhapsody*) shrext=.dylib library_names_spec='$libname$shrext' ;; dgux*) library_names_spec='$libname$shrext' ;; freebsd1*) ;; freebsd* | dragonfly*) case "$host_os" in freebsd[123]*) library_names_spec='$libname$shrext$versuffix' ;; *) library_names_spec='$libname$shrext' ;; esac ;; gnu*) library_names_spec='$libname$shrext' ;; hpux9* | hpux10* | hpux11*) case $host_cpu in ia64*) shrext=.so ;; hppa*64*) shrext=.sl ;; *) shrext=.sl ;; esac library_names_spec='$libname$shrext' ;; interix[3-9]*) library_names_spec='$libname$shrext' ;; irix5* | irix6* | nonstopux*) library_names_spec='$libname$shrext' case "$host_os" in irix5* | nonstopux*) libsuff= shlibsuff= ;; *) case $LD in *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") libsuff= shlibsuff= ;; *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") libsuff=32 shlibsuff=N32 ;; *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") libsuff=64 shlibsuff=64 ;; *) libsuff= shlibsuff= ;; esac ;; esac ;; linux*oldld* | linux*aout* | linux*coff*) ;; linux* | k*bsd*-gnu) library_names_spec='$libname$shrext' ;; knetbsd*-gnu) library_names_spec='$libname$shrext' ;; netbsd*) library_names_spec='$libname$shrext' ;; newsos6) library_names_spec='$libname$shrext' ;; nto-qnx*) library_names_spec='$libname$shrext' ;; openbsd*) library_names_spec='$libname$shrext$versuffix' ;; os2*) libname_spec='$name' shrext=.dll library_names_spec='$libname.a' ;; osf3* | osf4* | osf5*) library_names_spec='$libname$shrext' ;; rdos*) ;; solaris*) library_names_spec='$libname$shrext' ;; sunos4*) library_names_spec='$libname$shrext$versuffix' ;; sysv4 | sysv4.3*) library_names_spec='$libname$shrext' ;; sysv4*MP*) library_names_spec='$libname$shrext' ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) library_names_spec='$libname$shrext' ;; uts4*) library_names_spec='$libname$shrext' ;; esac sed_quote_subst='s/\(["`$\\]\)/\\\1/g' escaped_wl=`echo "X$wl" | sed -e 's/^X//' -e "$sed_quote_subst"` shlibext=`echo "$shrext" | sed -e 's,^\.,,'` escaped_libname_spec=`echo "X$libname_spec" | sed -e 's/^X//' -e "$sed_quote_subst"` escaped_library_names_spec=`echo "X$library_names_spec" | sed -e 's/^X//' -e "$sed_quote_subst"` escaped_hardcode_libdir_flag_spec=`echo "X$hardcode_libdir_flag_spec" | sed -e 's/^X//' -e "$sed_quote_subst"` LC_ALL=C sed -e 's/^\([a-zA-Z0-9_]*\)=/acl_cv_\1=/' <. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # This file is maintained in Automake, please report # bugs to or send patches to # . case $1 in '') echo "$0: No command. Try \`$0 --help' for more information." 1>&2 exit 1; ;; -h | --h*) cat <<\EOF Usage: compile [--help] [--version] PROGRAM [ARGS] Wrapper for compilers which do not understand `-c -o'. Remove `-o dest.o' from ARGS, run PROGRAM with the remaining arguments, and rename the output as expected. If you are trying to build a whole package this is not the right script to run: please start by reading the file `INSTALL'. Report bugs to . EOF exit $? ;; -v | --v*) echo "compile $scriptversion" exit $? ;; esac ofile= cfile= eat= for arg do if test -n "$eat"; then eat= else case $1 in -o) # configure might choose to run compile as `compile cc -o foo foo.c'. # So we strip `-o arg' only if arg is an object. eat=1 case $2 in *.o | *.obj) ofile=$2 ;; *) set x "$@" -o "$2" shift ;; esac ;; *.c) cfile=$1 set x "$@" "$1" shift ;; *) set x "$@" "$1" shift ;; esac fi shift done if test -z "$ofile" || test -z "$cfile"; then # If no `-o' option was seen then we might have been invoked from a # pattern rule where we don't need one. That is ok -- this is a # normal compilation that the losing compiler can handle. If no # `.c' file was seen then we are probably linking. That is also # ok. exec "$@" fi # Name of file we expect compiler to create. cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'` # Create the lock directory. # Note: use `[/\\:.-]' here to ensure that we don't use the same name # that we are using for the .o file. Also, base the name on the expected # object file name, since that is what matters with a parallel build. lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d while true; do if mkdir "$lockdir" >/dev/null 2>&1; then break fi sleep 1 done # FIXME: race condition here if user kills between mkdir and trap. trap "rmdir '$lockdir'; exit 1" 1 2 15 # Run the compile. "$@" ret=$? if test -f "$cofile"; then test "$cofile" = "$ofile" || mv "$cofile" "$ofile" elif test -f "${cofile}bj"; then test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile" fi rmdir "$lockdir" exit $ret # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC" # time-stamp-end: "; # UTC" # End: haildb-2.3.2/config/install-sh0000755000175000017500000003253711513177417017123 0ustar00pcrewspcrews00000000000000#!/bin/sh # install - install a program, script, or datafile scriptversion=2009-04-28.21; # UTC # This originates from X11R5 (mit/util/scripts/install.sh), which was # later released in X11R6 (xc/config/util/install.sh) with the # following copyright and license. # # Copyright (C) 1994 X Consortium # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN # AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- # TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Except as contained in this notice, the name of the X Consortium shall not # be used in advertising or otherwise to promote the sale, use or other deal- # ings in this Software without prior written authorization from the X Consor- # tium. # # # FSF changes to this file are in the public domain. # # Calling this script install-sh is preferred over install.sh, to prevent # `make' implicit rules from creating a file called install from it # when there is no Makefile. # # This script is compatible with the BSD install script, but was written # from scratch. nl=' ' IFS=" "" $nl" # set DOITPROG to echo to test this script # Don't use :- since 4.3BSD and earlier shells don't like it. doit=${DOITPROG-} if test -z "$doit"; then doit_exec=exec else doit_exec=$doit fi # Put in absolute file names if you don't have them in your path; # or use environment vars. chgrpprog=${CHGRPPROG-chgrp} chmodprog=${CHMODPROG-chmod} chownprog=${CHOWNPROG-chown} cmpprog=${CMPPROG-cmp} cpprog=${CPPROG-cp} mkdirprog=${MKDIRPROG-mkdir} mvprog=${MVPROG-mv} rmprog=${RMPROG-rm} stripprog=${STRIPPROG-strip} posix_glob='?' initialize_posix_glob=' test "$posix_glob" != "?" || { if (set -f) 2>/dev/null; then posix_glob= else posix_glob=: fi } ' posix_mkdir= # Desired mode of installed file. mode=0755 chgrpcmd= chmodcmd=$chmodprog chowncmd= mvcmd=$mvprog rmcmd="$rmprog -f" stripcmd= src= dst= dir_arg= dst_arg= copy_on_change=false no_target_directory= usage="\ Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE or: $0 [OPTION]... SRCFILES... DIRECTORY or: $0 [OPTION]... -t DIRECTORY SRCFILES... or: $0 [OPTION]... -d DIRECTORIES... In the 1st form, copy SRCFILE to DSTFILE. In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. In the 4th, create DIRECTORIES. Options: --help display this help and exit. --version display version info and exit. -c (ignored) -C install only if different (preserve the last data modification time) -d create directories instead of installing files. -g GROUP $chgrpprog installed files to GROUP. -m MODE $chmodprog installed files to MODE. -o USER $chownprog installed files to USER. -s $stripprog installed files. -t DIRECTORY install into DIRECTORY. -T report an error if DSTFILE is a directory. Environment variables override the default commands: CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG " while test $# -ne 0; do case $1 in -c) ;; -C) copy_on_change=true;; -d) dir_arg=true;; -g) chgrpcmd="$chgrpprog $2" shift;; --help) echo "$usage"; exit $?;; -m) mode=$2 case $mode in *' '* | *' '* | *' '* | *'*'* | *'?'* | *'['*) echo "$0: invalid mode: $mode" >&2 exit 1;; esac shift;; -o) chowncmd="$chownprog $2" shift;; -s) stripcmd=$stripprog;; -t) dst_arg=$2 shift;; -T) no_target_directory=true;; --version) echo "$0 $scriptversion"; exit $?;; --) shift break;; -*) echo "$0: invalid option: $1" >&2 exit 1;; *) break;; esac shift done if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then # When -d is used, all remaining arguments are directories to create. # When -t is used, the destination is already specified. # Otherwise, the last argument is the destination. Remove it from $@. for arg do if test -n "$dst_arg"; then # $@ is not empty: it contains at least $arg. set fnord "$@" "$dst_arg" shift # fnord fi shift # arg dst_arg=$arg done fi if test $# -eq 0; then if test -z "$dir_arg"; then echo "$0: no input file specified." >&2 exit 1 fi # It's OK to call `install-sh -d' without argument. # This can happen when creating conditional directories. exit 0 fi if test -z "$dir_arg"; then trap '(exit $?); exit' 1 2 13 15 # Set umask so as not to create temps with too-generous modes. # However, 'strip' requires both read and write access to temps. case $mode in # Optimize common cases. *644) cp_umask=133;; *755) cp_umask=22;; *[0-7]) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw='% 200' fi cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; *) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw=,u+rw fi cp_umask=$mode$u_plus_rw;; esac fi for src do # Protect names starting with `-'. case $src in -*) src=./$src;; esac if test -n "$dir_arg"; then dst=$src dstdir=$dst test -d "$dstdir" dstdir_status=$? else # Waiting for this to be detected by the "$cpprog $src $dsttmp" command # might cause directories to be created, which would be especially bad # if $src (and thus $dsttmp) contains '*'. if test ! -f "$src" && test ! -d "$src"; then echo "$0: $src does not exist." >&2 exit 1 fi if test -z "$dst_arg"; then echo "$0: no destination specified." >&2 exit 1 fi dst=$dst_arg # Protect names starting with `-'. case $dst in -*) dst=./$dst;; esac # If destination is a directory, append the input filename; won't work # if double slashes aren't ignored. if test -d "$dst"; then if test -n "$no_target_directory"; then echo "$0: $dst_arg: Is a directory" >&2 exit 1 fi dstdir=$dst dst=$dstdir/`basename "$src"` dstdir_status=0 else # Prefer dirname, but fall back on a substitute if dirname fails. dstdir=` (dirname "$dst") 2>/dev/null || expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$dst" : 'X\(//\)[^/]' \| \ X"$dst" : 'X\(//\)$' \| \ X"$dst" : 'X\(/\)' \| . 2>/dev/null || echo X"$dst" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q' ` test -d "$dstdir" dstdir_status=$? fi fi obsolete_mkdir_used=false if test $dstdir_status != 0; then case $posix_mkdir in '') # Create intermediate dirs using mode 755 as modified by the umask. # This is like FreeBSD 'install' as of 1997-10-28. umask=`umask` case $stripcmd.$umask in # Optimize common cases. *[2367][2367]) mkdir_umask=$umask;; .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; *[0-7]) mkdir_umask=`expr $umask + 22 \ - $umask % 100 % 40 + $umask % 20 \ - $umask % 10 % 4 + $umask % 2 `;; *) mkdir_umask=$umask,go-w;; esac # With -d, create the new directory with the user-specified mode. # Otherwise, rely on $mkdir_umask. if test -n "$dir_arg"; then mkdir_mode=-m$mode else mkdir_mode= fi posix_mkdir=false case $umask in *[123567][0-7][0-7]) # POSIX mkdir -p sets u+wx bits regardless of umask, which # is incompatible with FreeBSD 'install' when (umask & 300) != 0. ;; *) tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0 if (umask $mkdir_umask && exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1 then if test -z "$dir_arg" || { # Check for POSIX incompatibilities with -m. # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or # other-writeable bit of parent directory when it shouldn't. # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. ls_ld_tmpdir=`ls -ld "$tmpdir"` case $ls_ld_tmpdir in d????-?r-*) different_mode=700;; d????-?--*) different_mode=755;; *) false;; esac && $mkdirprog -m$different_mode -p -- "$tmpdir" && { ls_ld_tmpdir_1=`ls -ld "$tmpdir"` test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" } } then posix_mkdir=: fi rmdir "$tmpdir/d" "$tmpdir" else # Remove any dirs left behind by ancient mkdir implementations. rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null fi trap '' 0;; esac;; esac if $posix_mkdir && ( umask $mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" ) then : else # The umask is ridiculous, or mkdir does not conform to POSIX, # or it failed possibly due to a race condition. Create the # directory the slow way, step by step, checking for races as we go. case $dstdir in /*) prefix='/';; -*) prefix='./';; *) prefix='';; esac eval "$initialize_posix_glob" oIFS=$IFS IFS=/ $posix_glob set -f set fnord $dstdir shift $posix_glob set +f IFS=$oIFS prefixes= for d do test -z "$d" && continue prefix=$prefix$d if test -d "$prefix"; then prefixes= else if $posix_mkdir; then (umask=$mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break # Don't fail if two instances are running concurrently. test -d "$prefix" || exit 1 else case $prefix in *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; *) qprefix=$prefix;; esac prefixes="$prefixes '$qprefix'" fi fi prefix=$prefix/ done if test -n "$prefixes"; then # Don't fail if two instances are running concurrently. (umask $mkdir_umask && eval "\$doit_exec \$mkdirprog $prefixes") || test -d "$dstdir" || exit 1 obsolete_mkdir_used=true fi fi fi if test -n "$dir_arg"; then { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 else # Make a couple of temp file names in the proper directory. dsttmp=$dstdir/_inst.$$_ rmtmp=$dstdir/_rm.$$_ # Trap to clean up those temp files at exit. trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 # Copy the file name to the temp name. (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") && # and set any options; do chmod last to preserve setuid bits. # # If any of these fail, we abort the whole thing. If we want to # ignore errors from any of these, just make sure not to ignore # errors from the above "$doit $cpprog $src $dsttmp" command. # { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && # If -C, don't bother to copy if it wouldn't change the file. if $copy_on_change && old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && eval "$initialize_posix_glob" && $posix_glob set -f && set X $old && old=:$2:$4:$5:$6 && set X $new && new=:$2:$4:$5:$6 && $posix_glob set +f && test "$old" = "$new" && $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 then rm -f "$dsttmp" else # Rename the file to the real destination. $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || # The rename failed, perhaps because mv can't rename something else # to itself, or perhaps because mv is so ancient that it does not # support -f. { # Now remove or move aside any old file at destination location. # We try this two ways since rm can't unlink itself on some # systems and the destination file might be busy for other # reasons. In this case, the final cleanup might fail but the new # file should still install successfully. { test ! -f "$dst" || $doit $rmcmd -f "$dst" 2>/dev/null || { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } } || { echo "$0: cannot unlink or rename $dst" >&2 (exit 1); exit 1 } } && # Now rename the file to the real destination. $doit $mvcmd "$dsttmp" "$dst" } fi || exit 1 trap '' 0 fi done # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC" # time-stamp-end: "; # UTC" # End: haildb-2.3.2/config/ylwrap0000755000175000017500000001404311513177417016353 0ustar00pcrewspcrews00000000000000#! /bin/sh # ylwrap - wrapper for lex/yacc invocations. scriptversion=2009-04-28.21; # UTC # Copyright (C) 1996, 1997, 1998, 1999, 2001, 2002, 2003, 2004, 2005, # 2007, 2009 Free Software Foundation, Inc. # # Written by Tom Tromey . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # This file is maintained in Automake, please report # bugs to or send patches to # . case "$1" in '') echo "$0: No files given. Try \`$0 --help' for more information." 1>&2 exit 1 ;; --basedir) basedir=$2 shift 2 ;; -h|--h*) cat <<\EOF Usage: ylwrap [--help|--version] INPUT [OUTPUT DESIRED]... -- PROGRAM [ARGS]... Wrapper for lex/yacc invocations, renaming files as desired. INPUT is the input file OUTPUT is one file PROG generates DESIRED is the file we actually want instead of OUTPUT PROGRAM is program to run ARGS are passed to PROG Any number of OUTPUT,DESIRED pairs may be used. Report bugs to . EOF exit $? ;; -v|--v*) echo "ylwrap $scriptversion" exit $? ;; esac # The input. input="$1" shift case "$input" in [\\/]* | ?:[\\/]*) # Absolute path; do nothing. ;; *) # Relative path. Make it absolute. input="`pwd`/$input" ;; esac pairlist= while test "$#" -ne 0; do if test "$1" = "--"; then shift break fi pairlist="$pairlist $1" shift done # The program to run. prog="$1" shift # Make any relative path in $prog absolute. case "$prog" in [\\/]* | ?:[\\/]*) ;; *[\\/]*) prog="`pwd`/$prog" ;; esac # FIXME: add hostname here for parallel makes that run commands on # other machines. But that might take us over the 14-char limit. dirname=ylwrap$$ trap "cd '`pwd`'; rm -rf $dirname > /dev/null 2>&1" 1 2 3 15 mkdir $dirname || exit 1 cd $dirname case $# in 0) "$prog" "$input" ;; *) "$prog" "$@" "$input" ;; esac ret=$? if test $ret -eq 0; then set X $pairlist shift first=yes # Since DOS filename conventions don't allow two dots, # the DOS version of Bison writes out y_tab.c instead of y.tab.c # and y_tab.h instead of y.tab.h. Test to see if this is the case. y_tab_nodot="no" if test -f y_tab.c || test -f y_tab.h; then y_tab_nodot="yes" fi # The directory holding the input. input_dir=`echo "$input" | sed -e 's,\([\\/]\)[^\\/]*$,\1,'` # Quote $INPUT_DIR so we can use it in a regexp. # FIXME: really we should care about more than `.' and `\'. input_rx=`echo "$input_dir" | sed 's,\\\\,\\\\\\\\,g;s,\\.,\\\\.,g'` while test "$#" -ne 0; do from="$1" # Handle y_tab.c and y_tab.h output by DOS if test $y_tab_nodot = "yes"; then if test $from = "y.tab.c"; then from="y_tab.c" else if test $from = "y.tab.h"; then from="y_tab.h" fi fi fi if test -f "$from"; then # If $2 is an absolute path name, then just use that, # otherwise prepend `../'. case "$2" in [\\/]* | ?:[\\/]*) target="$2";; *) target="../$2";; esac # We do not want to overwrite a header file if it hasn't # changed. This avoid useless recompilations. However the # parser itself (the first file) should always be updated, # because it is the destination of the .y.c rule in the # Makefile. Divert the output of all other files to a temporary # file so we can compare them to existing versions. if test $first = no; then realtarget="$target" target="tmp-`echo $target | sed s/.*[\\/]//g`" fi # Edit out `#line' or `#' directives. # # We don't want the resulting debug information to point at # an absolute srcdir; it is better for it to just mention the # .y file with no path. # # We want to use the real output file name, not yy.lex.c for # instance. # # We want the include guards to be adjusted too. FROM=`echo "$from" | sed \ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'\ -e 's/[^ABCDEFGHIJKLMNOPQRSTUVWXYZ]/_/g'` TARGET=`echo "$2" | sed \ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'\ -e 's/[^ABCDEFGHIJKLMNOPQRSTUVWXYZ]/_/g'` sed -e "/^#/!b" -e "s,$input_rx,," -e "s,$from,$2," \ -e "s,$FROM,$TARGET," "$from" >"$target" || ret=$? # Check whether header files must be updated. if test $first = no; then if test -f "$realtarget" && cmp -s "$realtarget" "$target"; then echo "$2" is unchanged rm -f "$target" else echo updating "$2" mv -f "$target" "$realtarget" fi fi else # A missing file is only an error for the first file. This # is a blatant hack to let us support using "yacc -d". If -d # is not specified, we don't want an error when the header # file is "missing". if test $first = yes; then ret=1 fi fi shift shift first=no done else ret=$? fi # Remove the directory. cd .. rm -rf $dirname exit $ret # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC" # time-stamp-end: "; # UTC" # End: haildb-2.3.2/config/autorun.sh0000755000175000017500000000777111513177357017160 0ustar00pcrewspcrews00000000000000#!/bin/sh # # Copyright (C) 2006 Jan Kneschke # Copyright (C) 2009 Sun Microsystems, Inc. # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # Run this to generate all the initial makefiles, etc. die() { echo "$@"; exit 1; } # --force means overwrite ltmain.sh script if it already exists LIBTOOLIZE_FLAGS=" --automake --copy --force" # --add-missing instructs automake to install missing auxiliary files # and --force to overwrite them if they already exist AUTOMAKE_FLAGS="--add-missing --copy --force --foreign" ACLOCAL_FLAGS="-I m4" ARGV0=$0 ARGS="$@" run() { echo "$ARGV0: running \`$@' $ARGS" $@ $ARGS } # Try to locate a program by using which, and verify that the file is an # executable locate_binary() { for f in $@ do file=`which $f 2>/dev/null | grep -v '^no '` if test -n "$file" -a -x "$file"; then echo $file return 0 fi done echo "" return 1 } if test -f config/pre_hook.sh then . config/pre_hook.sh fi # Try to detect the supported binaries if the user didn't # override that by pushing the environment variable if test x$LIBTOOLIZE = x; then LIBTOOLIZE=`locate_binary glibtoolize libtoolize-1.5 libtoolize` if test x$LIBTOOLIZE = x; then die "Did not find a supported libtoolize" fi fi if test x$ACLOCAL = x; then ACLOCAL=`locate_binary aclocal-1.11 aclocal-1.10 aclocal-1.9 aclocal19 aclocal` if test x$ACLOCAL = x; then die "Did not find a supported aclocal" fi fi if test x$AUTOMAKE = x; then AUTOMAKE=`locate_binary automake-1.11 automake-1.10 automake-1.9 automake19 automake` if test x$AUTOMAKE = x; then die "Did not find a supported automake" fi fi if test x$AUTOCONF = x; then AUTOCONF=`locate_binary autoconf-2.59 autoconf259 autoconf` if test x$AUTOCONF = x; then die "Did not find a supported autoconf" fi fi if test x$AUTOHEADER = x; then AUTOHEADER=`locate_binary autoheader-2.59 autoheader259 autoheader` if test x$AUTOHEADER = x; then die "Did not find a supported autoheader" fi fi run $LIBTOOLIZE $LIBTOOLIZE_FLAGS || die "Can't execute libtoolize" run $ACLOCAL $ACLOCAL_FLAGS || die "Can't execute aclocal" run $AUTOHEADER || die "Can't execute autoheader" run $AUTOMAKE $AUTOMAKE_FLAGS || die "Can't execute automake" run $AUTOCONF || die "Can't execute autoconf" if test -f config/post_hook.sh then . config/post_hook.sh fi echo "---" echo "Configured with the following tools:" echo " * `$LIBTOOLIZE --version | head -1`" echo " * `$ACLOCAL --version | head -1`" echo " * `$AUTOHEADER --version | head -1`" echo " * `$AUTOMAKE --version | head -1`" echo " * `$AUTOCONF --version | head -1`" echo "---" haildb-2.3.2/config/config.sub0000755000175000017500000010344511513177417017077 0ustar00pcrewspcrews00000000000000#! /bin/sh # Configuration validation subroutine script. # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, # 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 # Free Software Foundation, Inc. timestamp='2010-01-22' # This file is (in principle) common to ALL GNU software. # The presence of a machine in this file suggests that SOME GNU software # can handle that machine. It does not imply ALL GNU software can. # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA # 02110-1301, USA. # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # Please send patches to . Submit a context # diff and a properly formatted GNU ChangeLog entry. # # Configuration subroutine to validate and canonicalize a configuration type. # Supply the specified configuration type as an argument. # If it is invalid, we print an error message on stderr and exit with code 1. # Otherwise, we print the canonical config type on stdout and succeed. # You can get the latest version of this script from: # http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD # This file is supposed to be the same for all GNU packages # and recognize all the CPU types, system types and aliases # that are meaningful with *any* GNU software. # Each package is responsible for reporting which valid configurations # it does not support. The user should be able to distinguish # a failure to support a valid configuration from a meaningless # configuration. # The goal of this file is to map all the various variations of a given # machine specification into a single specification in the form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM # or in some cases, the newer four-part form: # CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM # It is wrong to echo any other type of specification. me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] CPU-MFR-OPSYS $0 [OPTION] ALIAS Canonicalize a configuration name. Operation modes: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.sub ($timestamp) Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" exit 1 ;; *local*) # First pass through any local machine types. echo $1 exit ;; * ) break ;; esac done case $# in 0) echo "$me: missing argument$help" >&2 exit 1;; 1) ;; *) echo "$me: too many arguments$help" >&2 exit 1;; esac # Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). # Here we must recognize all the valid KERNEL-OS combinations. maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` case $maybe_os in nto-qnx* | linux-gnu* | linux-dietlibc | linux-newlib* | linux-uclibc* | \ uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | \ kopensolaris*-gnu* | \ storm-chaos* | os2-emx* | rtmk-nova*) os=-$maybe_os basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` ;; *) basic_machine=`echo $1 | sed 's/-[^-]*$//'` if [ $basic_machine != $1 ] then os=`echo $1 | sed 's/.*-/-/'` else os=; fi ;; esac ### Let's recognize common machines as not being operating systems so ### that things like config.sub decstation-3100 work. We also ### recognize some manufacturers as not being operating systems, so we ### can provide default operating systems below. case $os in -sun*os*) # Prevent following clause from handling this invalid input. ;; -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ -apple | -axis | -knuth | -cray | -microblaze) os= basic_machine=$1 ;; -bluegene*) os=-cnk ;; -sim | -cisco | -oki | -wec | -winbond) os= basic_machine=$1 ;; -scout) ;; -wrs) os=-vxworks basic_machine=$1 ;; -chorusos*) os=-chorusos basic_machine=$1 ;; -chorusrdb) os=-chorusrdb basic_machine=$1 ;; -hiux*) os=-hiuxwe2 ;; -sco6) os=-sco5v6 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco5) os=-sco3.2v5 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco4) os=-sco3.2v4 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco3.2.[4-9]*) os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco3.2v[4-9]*) # Don't forget version if it is 3.2v4 or newer. basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco5v6*) # Don't forget version if it is 3.2v4 or newer. basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco*) os=-sco3.2v2 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -udk*) basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -isc) os=-isc2.2 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -clix*) basic_machine=clipper-intergraph ;; -isc*) basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -lynx*) os=-lynxos ;; -ptx*) basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` ;; -windowsnt*) os=`echo $os | sed -e 's/windowsnt/winnt/'` ;; -psos*) os=-psos ;; -mint | -mint[0-9]*) basic_machine=m68k-atari os=-mint ;; esac # Decode aliases for certain CPU-COMPANY combinations. case $basic_machine in # Recognize the basic CPU types without company name. # Some are omitted here because they have special meanings below. 1750a | 580 \ | a29k \ | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ | am33_2.0 \ | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr | avr32 \ | bfin \ | c4x | clipper \ | d10v | d30v | dlx | dsp16xx \ | fido | fr30 | frv \ | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ | i370 | i860 | i960 | ia64 \ | ip2k | iq2000 \ | lm32 \ | m32c | m32r | m32rle | m68000 | m68k | m88k \ | maxq | mb | microblaze | mcore | mep | metag \ | mips | mipsbe | mipseb | mipsel | mipsle \ | mips16 \ | mips64 | mips64el \ | mips64octeon | mips64octeonel \ | mips64orion | mips64orionel \ | mips64r5900 | mips64r5900el \ | mips64vr | mips64vrel \ | mips64vr4100 | mips64vr4100el \ | mips64vr4300 | mips64vr4300el \ | mips64vr5000 | mips64vr5000el \ | mips64vr5900 | mips64vr5900el \ | mipsisa32 | mipsisa32el \ | mipsisa32r2 | mipsisa32r2el \ | mipsisa64 | mipsisa64el \ | mipsisa64r2 | mipsisa64r2el \ | mipsisa64sb1 | mipsisa64sb1el \ | mipsisa64sr71k | mipsisa64sr71kel \ | mipstx39 | mipstx39el \ | mn10200 | mn10300 \ | moxie \ | mt \ | msp430 \ | nios | nios2 \ | ns16k | ns32k \ | or32 \ | pdp10 | pdp11 | pj | pjl \ | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \ | pyramid \ | rx \ | score \ | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ | sh64 | sh64le \ | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ | spu | strongarm \ | tahoe | thumb | tic4x | tic80 | tron \ | ubicom32 \ | v850 | v850e \ | we32k \ | x86 | xc16x | xscale | xscalee[bl] | xstormy16 | xtensa \ | z8k | z80) basic_machine=$basic_machine-unknown ;; m6811 | m68hc11 | m6812 | m68hc12 | picochip) # Motorola 68HC11/12. basic_machine=$basic_machine-unknown os=-none ;; m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) ;; ms1) basic_machine=mt-unknown ;; # We use `pc' rather than `unknown' # because (1) that's what they normally are, and # (2) the word "unknown" tends to confuse beginning users. i*86 | x86_64) basic_machine=$basic_machine-pc ;; # Object if more than one company name word. *-*-*) echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 exit 1 ;; # Recognize the basic CPU types with company name. 580-* \ | a29k-* \ | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \ | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ | avr-* | avr32-* \ | bfin-* | bs2000-* \ | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \ | clipper-* | craynv-* | cydra-* \ | d10v-* | d30v-* | dlx-* \ | elxsi-* \ | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ | h8300-* | h8500-* \ | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ | i*86-* | i860-* | i960-* | ia64-* \ | ip2k-* | iq2000-* \ | lm32-* \ | m32c-* | m32r-* | m32rle-* \ | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ | m88110-* | m88k-* | maxq-* | mcore-* | metag-* | microblaze-* \ | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ | mips16-* \ | mips64-* | mips64el-* \ | mips64octeon-* | mips64octeonel-* \ | mips64orion-* | mips64orionel-* \ | mips64r5900-* | mips64r5900el-* \ | mips64vr-* | mips64vrel-* \ | mips64vr4100-* | mips64vr4100el-* \ | mips64vr4300-* | mips64vr4300el-* \ | mips64vr5000-* | mips64vr5000el-* \ | mips64vr5900-* | mips64vr5900el-* \ | mipsisa32-* | mipsisa32el-* \ | mipsisa32r2-* | mipsisa32r2el-* \ | mipsisa64-* | mipsisa64el-* \ | mipsisa64r2-* | mipsisa64r2el-* \ | mipsisa64sb1-* | mipsisa64sb1el-* \ | mipsisa64sr71k-* | mipsisa64sr71kel-* \ | mipstx39-* | mipstx39el-* \ | mmix-* \ | mt-* \ | msp430-* \ | nios-* | nios2-* \ | none-* | np1-* | ns16k-* | ns32k-* \ | orion-* \ | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \ | pyramid-* \ | romp-* | rs6000-* | rx-* \ | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ | sparclite-* \ | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | strongarm-* | sv1-* | sx?-* \ | tahoe-* | thumb-* \ | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ | tile-* | tilegx-* \ | tron-* \ | ubicom32-* \ | v850-* | v850e-* | vax-* \ | we32k-* \ | x86-* | x86_64-* | xc16x-* | xps100-* | xscale-* | xscalee[bl]-* \ | xstormy16-* | xtensa*-* \ | ymp-* \ | z8k-* | z80-*) ;; # Recognize the basic CPU types without company name, with glob match. xtensa*) basic_machine=$basic_machine-unknown ;; # Recognize the various machine names and aliases which stand # for a CPU type and a company and sometimes even an OS. 386bsd) basic_machine=i386-unknown os=-bsd ;; 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) basic_machine=m68000-att ;; 3b*) basic_machine=we32k-att ;; a29khif) basic_machine=a29k-amd os=-udi ;; abacus) basic_machine=abacus-unknown ;; adobe68k) basic_machine=m68010-adobe os=-scout ;; alliant | fx80) basic_machine=fx80-alliant ;; altos | altos3068) basic_machine=m68k-altos ;; am29k) basic_machine=a29k-none os=-bsd ;; amd64) basic_machine=x86_64-pc ;; amd64-*) basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'` ;; amdahl) basic_machine=580-amdahl os=-sysv ;; amiga | amiga-*) basic_machine=m68k-unknown ;; amigaos | amigados) basic_machine=m68k-unknown os=-amigaos ;; amigaunix | amix) basic_machine=m68k-unknown os=-sysv4 ;; apollo68) basic_machine=m68k-apollo os=-sysv ;; apollo68bsd) basic_machine=m68k-apollo os=-bsd ;; aros) basic_machine=i386-pc os=-aros ;; aux) basic_machine=m68k-apple os=-aux ;; balance) basic_machine=ns32k-sequent os=-dynix ;; blackfin) basic_machine=bfin-unknown os=-linux ;; blackfin-*) basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'` os=-linux ;; bluegene*) basic_machine=powerpc-ibm os=-cnk ;; c90) basic_machine=c90-cray os=-unicos ;; cegcc) basic_machine=arm-unknown os=-cegcc ;; convex-c1) basic_machine=c1-convex os=-bsd ;; convex-c2) basic_machine=c2-convex os=-bsd ;; convex-c32) basic_machine=c32-convex os=-bsd ;; convex-c34) basic_machine=c34-convex os=-bsd ;; convex-c38) basic_machine=c38-convex os=-bsd ;; cray | j90) basic_machine=j90-cray os=-unicos ;; craynv) basic_machine=craynv-cray os=-unicosmp ;; cr16) basic_machine=cr16-unknown os=-elf ;; crds | unos) basic_machine=m68k-crds ;; crisv32 | crisv32-* | etraxfs*) basic_machine=crisv32-axis ;; cris | cris-* | etrax*) basic_machine=cris-axis ;; crx) basic_machine=crx-unknown os=-elf ;; da30 | da30-*) basic_machine=m68k-da30 ;; decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) basic_machine=mips-dec ;; decsystem10* | dec10*) basic_machine=pdp10-dec os=-tops10 ;; decsystem20* | dec20*) basic_machine=pdp10-dec os=-tops20 ;; delta | 3300 | motorola-3300 | motorola-delta \ | 3300-motorola | delta-motorola) basic_machine=m68k-motorola ;; delta88) basic_machine=m88k-motorola os=-sysv3 ;; dicos) basic_machine=i686-pc os=-dicos ;; djgpp) basic_machine=i586-pc os=-msdosdjgpp ;; dpx20 | dpx20-*) basic_machine=rs6000-bull os=-bosx ;; dpx2* | dpx2*-bull) basic_machine=m68k-bull os=-sysv3 ;; ebmon29k) basic_machine=a29k-amd os=-ebmon ;; elxsi) basic_machine=elxsi-elxsi os=-bsd ;; encore | umax | mmax) basic_machine=ns32k-encore ;; es1800 | OSE68k | ose68k | ose | OSE) basic_machine=m68k-ericsson os=-ose ;; fx2800) basic_machine=i860-alliant ;; genix) basic_machine=ns32k-ns ;; gmicro) basic_machine=tron-gmicro os=-sysv ;; go32) basic_machine=i386-pc os=-go32 ;; h3050r* | hiux*) basic_machine=hppa1.1-hitachi os=-hiuxwe2 ;; h8300hms) basic_machine=h8300-hitachi os=-hms ;; h8300xray) basic_machine=h8300-hitachi os=-xray ;; h8500hms) basic_machine=h8500-hitachi os=-hms ;; harris) basic_machine=m88k-harris os=-sysv3 ;; hp300-*) basic_machine=m68k-hp ;; hp300bsd) basic_machine=m68k-hp os=-bsd ;; hp300hpux) basic_machine=m68k-hp os=-hpux ;; hp3k9[0-9][0-9] | hp9[0-9][0-9]) basic_machine=hppa1.0-hp ;; hp9k2[0-9][0-9] | hp9k31[0-9]) basic_machine=m68000-hp ;; hp9k3[2-9][0-9]) basic_machine=m68k-hp ;; hp9k6[0-9][0-9] | hp6[0-9][0-9]) basic_machine=hppa1.0-hp ;; hp9k7[0-79][0-9] | hp7[0-79][0-9]) basic_machine=hppa1.1-hp ;; hp9k78[0-9] | hp78[0-9]) # FIXME: really hppa2.0-hp basic_machine=hppa1.1-hp ;; hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) # FIXME: really hppa2.0-hp basic_machine=hppa1.1-hp ;; hp9k8[0-9][13679] | hp8[0-9][13679]) basic_machine=hppa1.1-hp ;; hp9k8[0-9][0-9] | hp8[0-9][0-9]) basic_machine=hppa1.0-hp ;; hppa-next) os=-nextstep3 ;; hppaosf) basic_machine=hppa1.1-hp os=-osf ;; hppro) basic_machine=hppa1.1-hp os=-proelf ;; i370-ibm* | ibm*) basic_machine=i370-ibm ;; # I'm not sure what "Sysv32" means. Should this be sysv3.2? i*86v32) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-sysv32 ;; i*86v4*) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-sysv4 ;; i*86v) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-sysv ;; i*86sol2) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-solaris2 ;; i386mach) basic_machine=i386-mach os=-mach ;; i386-vsta | vsta) basic_machine=i386-unknown os=-vsta ;; iris | iris4d) basic_machine=mips-sgi case $os in -irix*) ;; *) os=-irix4 ;; esac ;; isi68 | isi) basic_machine=m68k-isi os=-sysv ;; m68knommu) basic_machine=m68k-unknown os=-linux ;; m68knommu-*) basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'` os=-linux ;; m88k-omron*) basic_machine=m88k-omron ;; magnum | m3230) basic_machine=mips-mips os=-sysv ;; merlin) basic_machine=ns32k-utek os=-sysv ;; microblaze) basic_machine=microblaze-xilinx ;; mingw32) basic_machine=i386-pc os=-mingw32 ;; mingw32ce) basic_machine=arm-unknown os=-mingw32ce ;; miniframe) basic_machine=m68000-convergent ;; *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) basic_machine=m68k-atari os=-mint ;; mips3*-*) basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` ;; mips3*) basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown ;; monitor) basic_machine=m68k-rom68k os=-coff ;; morphos) basic_machine=powerpc-unknown os=-morphos ;; msdos) basic_machine=i386-pc os=-msdos ;; ms1-*) basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'` ;; mvs) basic_machine=i370-ibm os=-mvs ;; ncr3000) basic_machine=i486-ncr os=-sysv4 ;; netbsd386) basic_machine=i386-unknown os=-netbsd ;; netwinder) basic_machine=armv4l-rebel os=-linux ;; news | news700 | news800 | news900) basic_machine=m68k-sony os=-newsos ;; news1000) basic_machine=m68030-sony os=-newsos ;; news-3600 | risc-news) basic_machine=mips-sony os=-newsos ;; necv70) basic_machine=v70-nec os=-sysv ;; next | m*-next ) basic_machine=m68k-next case $os in -nextstep* ) ;; -ns2*) os=-nextstep2 ;; *) os=-nextstep3 ;; esac ;; nh3000) basic_machine=m68k-harris os=-cxux ;; nh[45]000) basic_machine=m88k-harris os=-cxux ;; nindy960) basic_machine=i960-intel os=-nindy ;; mon960) basic_machine=i960-intel os=-mon960 ;; nonstopux) basic_machine=mips-compaq os=-nonstopux ;; np1) basic_machine=np1-gould ;; nsr-tandem) basic_machine=nsr-tandem ;; op50n-* | op60c-*) basic_machine=hppa1.1-oki os=-proelf ;; openrisc | openrisc-*) basic_machine=or32-unknown ;; os400) basic_machine=powerpc-ibm os=-os400 ;; OSE68000 | ose68000) basic_machine=m68000-ericsson os=-ose ;; os68k) basic_machine=m68k-none os=-os68k ;; pa-hitachi) basic_machine=hppa1.1-hitachi os=-hiuxwe2 ;; paragon) basic_machine=i860-intel os=-osf ;; parisc) basic_machine=hppa-unknown os=-linux ;; parisc-*) basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'` os=-linux ;; pbd) basic_machine=sparc-tti ;; pbb) basic_machine=m68k-tti ;; pc532 | pc532-*) basic_machine=ns32k-pc532 ;; pc98) basic_machine=i386-pc ;; pc98-*) basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentium | p5 | k5 | k6 | nexgen | viac3) basic_machine=i586-pc ;; pentiumpro | p6 | 6x86 | athlon | athlon_*) basic_machine=i686-pc ;; pentiumii | pentium2 | pentiumiii | pentium3) basic_machine=i686-pc ;; pentium4) basic_machine=i786-pc ;; pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentiumpro-* | p6-* | 6x86-* | athlon-*) basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentium4-*) basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pn) basic_machine=pn-gould ;; power) basic_machine=power-ibm ;; ppc) basic_machine=powerpc-unknown ;; ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ppcle | powerpclittle | ppc-le | powerpc-little) basic_machine=powerpcle-unknown ;; ppcle-* | powerpclittle-*) basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ppc64) basic_machine=powerpc64-unknown ;; ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ppc64le | powerpc64little | ppc64-le | powerpc64-little) basic_machine=powerpc64le-unknown ;; ppc64le-* | powerpc64little-*) basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ps2) basic_machine=i386-ibm ;; pw32) basic_machine=i586-unknown os=-pw32 ;; rdos) basic_machine=i386-pc os=-rdos ;; rom68k) basic_machine=m68k-rom68k os=-coff ;; rm[46]00) basic_machine=mips-siemens ;; rtpc | rtpc-*) basic_machine=romp-ibm ;; s390 | s390-*) basic_machine=s390-ibm ;; s390x | s390x-*) basic_machine=s390x-ibm ;; sa29200) basic_machine=a29k-amd os=-udi ;; sb1) basic_machine=mipsisa64sb1-unknown ;; sb1el) basic_machine=mipsisa64sb1el-unknown ;; sde) basic_machine=mipsisa32-sde os=-elf ;; sei) basic_machine=mips-sei os=-seiux ;; sequent) basic_machine=i386-sequent ;; sh) basic_machine=sh-hitachi os=-hms ;; sh5el) basic_machine=sh5le-unknown ;; sh64) basic_machine=sh64-unknown ;; sparclite-wrs | simso-wrs) basic_machine=sparclite-wrs os=-vxworks ;; sps7) basic_machine=m68k-bull os=-sysv2 ;; spur) basic_machine=spur-unknown ;; st2000) basic_machine=m68k-tandem ;; stratus) basic_machine=i860-stratus os=-sysv4 ;; sun2) basic_machine=m68000-sun ;; sun2os3) basic_machine=m68000-sun os=-sunos3 ;; sun2os4) basic_machine=m68000-sun os=-sunos4 ;; sun3os3) basic_machine=m68k-sun os=-sunos3 ;; sun3os4) basic_machine=m68k-sun os=-sunos4 ;; sun4os3) basic_machine=sparc-sun os=-sunos3 ;; sun4os4) basic_machine=sparc-sun os=-sunos4 ;; sun4sol2) basic_machine=sparc-sun os=-solaris2 ;; sun3 | sun3-*) basic_machine=m68k-sun ;; sun4) basic_machine=sparc-sun ;; sun386 | sun386i | roadrunner) basic_machine=i386-sun ;; sv1) basic_machine=sv1-cray os=-unicos ;; symmetry) basic_machine=i386-sequent os=-dynix ;; t3e) basic_machine=alphaev5-cray os=-unicos ;; t90) basic_machine=t90-cray os=-unicos ;; tic54x | c54x*) basic_machine=tic54x-unknown os=-coff ;; tic55x | c55x*) basic_machine=tic55x-unknown os=-coff ;; tic6x | c6x*) basic_machine=tic6x-unknown os=-coff ;; # This must be matched before tile*. tilegx*) basic_machine=tilegx-unknown os=-linux-gnu ;; tile*) basic_machine=tile-unknown os=-linux-gnu ;; tx39) basic_machine=mipstx39-unknown ;; tx39el) basic_machine=mipstx39el-unknown ;; toad1) basic_machine=pdp10-xkl os=-tops20 ;; tower | tower-32) basic_machine=m68k-ncr ;; tpf) basic_machine=s390x-ibm os=-tpf ;; udi29k) basic_machine=a29k-amd os=-udi ;; ultra3) basic_machine=a29k-nyu os=-sym1 ;; v810 | necv810) basic_machine=v810-nec os=-none ;; vaxv) basic_machine=vax-dec os=-sysv ;; vms) basic_machine=vax-dec os=-vms ;; vpp*|vx|vx-*) basic_machine=f301-fujitsu ;; vxworks960) basic_machine=i960-wrs os=-vxworks ;; vxworks68) basic_machine=m68k-wrs os=-vxworks ;; vxworks29k) basic_machine=a29k-wrs os=-vxworks ;; w65*) basic_machine=w65-wdc os=-none ;; w89k-*) basic_machine=hppa1.1-winbond os=-proelf ;; xbox) basic_machine=i686-pc os=-mingw32 ;; xps | xps100) basic_machine=xps100-honeywell ;; ymp) basic_machine=ymp-cray os=-unicos ;; z8k-*-coff) basic_machine=z8k-unknown os=-sim ;; z80-*-coff) basic_machine=z80-unknown os=-sim ;; none) basic_machine=none-none os=-none ;; # Here we handle the default manufacturer of certain CPU types. It is in # some cases the only manufacturer, in others, it is the most popular. w89k) basic_machine=hppa1.1-winbond ;; op50n) basic_machine=hppa1.1-oki ;; op60c) basic_machine=hppa1.1-oki ;; romp) basic_machine=romp-ibm ;; mmix) basic_machine=mmix-knuth ;; rs6000) basic_machine=rs6000-ibm ;; vax) basic_machine=vax-dec ;; pdp10) # there are many clones, so DEC is not a safe bet basic_machine=pdp10-unknown ;; pdp11) basic_machine=pdp11-dec ;; we32k) basic_machine=we32k-att ;; sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele) basic_machine=sh-unknown ;; sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v) basic_machine=sparc-sun ;; cydra) basic_machine=cydra-cydrome ;; orion) basic_machine=orion-highlevel ;; orion105) basic_machine=clipper-highlevel ;; mac | mpw | mac-mpw) basic_machine=m68k-apple ;; pmac | pmac-mpw) basic_machine=powerpc-apple ;; *-unknown) # Make sure to match an already-canonicalized machine name. ;; *) echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 exit 1 ;; esac # Here we canonicalize certain aliases for manufacturers. case $basic_machine in *-digital*) basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` ;; *-commodore*) basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` ;; *) ;; esac # Decode manufacturer-specific aliases for certain operating systems. if [ x"$os" != x"" ] then case $os in # First match some system type aliases # that might get confused with valid system types. # -solaris* is a basic system type, with this one exception. -auroraux) os=-auroraux ;; -solaris1 | -solaris1.*) os=`echo $os | sed -e 's|solaris1|sunos4|'` ;; -solaris) os=-solaris2 ;; -svr4*) os=-sysv4 ;; -unixware*) os=-sysv4.2uw ;; -gnu/linux*) os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` ;; # First accept the basic system types. # The portable systems comes first. # Each alternative MUST END IN A *, to match a version number. # -sysv* is not here because it comes later, after sysvr4. -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \ | -sym* | -kopensolaris* \ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ | -aos* | -aros* \ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \ | -openbsd* | -solidbsd* \ | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ | -chorusos* | -chorusrdb* | -cegcc* \ | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ | -mingw32* | -linux-gnu* | -linux-newlib* | -linux-uclibc* \ | -uxpv* | -beos* | -mpeix* | -udk* \ | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es*) # Remember, each alternative MUST END IN *, to match a version number. ;; -qnx*) case $basic_machine in x86-* | i*86-*) ;; *) os=-nto$os ;; esac ;; -nto-qnx*) ;; -nto*) os=`echo $os | sed -e 's|nto|nto-qnx|'` ;; -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \ | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) ;; -mac*) os=`echo $os | sed -e 's|mac|macos|'` ;; -linux-dietlibc) os=-linux-dietlibc ;; -linux*) os=`echo $os | sed -e 's|linux|linux-gnu|'` ;; -sunos5*) os=`echo $os | sed -e 's|sunos5|solaris2|'` ;; -sunos6*) os=`echo $os | sed -e 's|sunos6|solaris3|'` ;; -opened*) os=-openedition ;; -os400*) os=-os400 ;; -wince*) os=-wince ;; -osfrose*) os=-osfrose ;; -osf*) os=-osf ;; -utek*) os=-bsd ;; -dynix*) os=-bsd ;; -acis*) os=-aos ;; -atheos*) os=-atheos ;; -syllable*) os=-syllable ;; -386bsd) os=-bsd ;; -ctix* | -uts*) os=-sysv ;; -nova*) os=-rtmk-nova ;; -ns2 ) os=-nextstep2 ;; -nsk*) os=-nsk ;; # Preserve the version number of sinix5. -sinix5.*) os=`echo $os | sed -e 's|sinix|sysv|'` ;; -sinix*) os=-sysv4 ;; -tpf*) os=-tpf ;; -triton*) os=-sysv3 ;; -oss*) os=-sysv3 ;; -svr4) os=-sysv4 ;; -svr3) os=-sysv3 ;; -sysvr4) os=-sysv4 ;; # This must come after -sysvr4. -sysv*) ;; -ose*) os=-ose ;; -es1800*) os=-ose ;; -xenix) os=-xenix ;; -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) os=-mint ;; -aros*) os=-aros ;; -kaos*) os=-kaos ;; -zvmoe) os=-zvmoe ;; -dicos*) os=-dicos ;; -nacl*) ;; -none) ;; *) # Get rid of the `-' at the beginning of $os. os=`echo $os | sed 's/[^-]*-//'` echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 exit 1 ;; esac else # Here we handle the default operating systems that come with various machines. # The value should be what the vendor currently ships out the door with their # machine or put another way, the most popular os provided with the machine. # Note that if you're going to try to match "-MANUFACTURER" here (say, # "-sun"), then you have to tell the case statement up towards the top # that MANUFACTURER isn't an operating system. Otherwise, code above # will signal an error saying that MANUFACTURER isn't an operating # system, and we'll never get to this point. case $basic_machine in score-*) os=-elf ;; spu-*) os=-elf ;; *-acorn) os=-riscix1.2 ;; arm*-rebel) os=-linux ;; arm*-semi) os=-aout ;; c4x-* | tic4x-*) os=-coff ;; # This must come before the *-dec entry. pdp10-*) os=-tops20 ;; pdp11-*) os=-none ;; *-dec | vax-*) os=-ultrix4.2 ;; m68*-apollo) os=-domain ;; i386-sun) os=-sunos4.0.2 ;; m68000-sun) os=-sunos3 # This also exists in the configure program, but was not the # default. # os=-sunos4 ;; m68*-cisco) os=-aout ;; mep-*) os=-elf ;; mips*-cisco) os=-elf ;; mips*-*) os=-elf ;; or32-*) os=-coff ;; *-tti) # must be before sparc entry or we get the wrong os. os=-sysv3 ;; sparc-* | *-sun) os=-sunos4.1.1 ;; *-be) os=-beos ;; *-haiku) os=-haiku ;; *-ibm) os=-aix ;; *-knuth) os=-mmixware ;; *-wec) os=-proelf ;; *-winbond) os=-proelf ;; *-oki) os=-proelf ;; *-hp) os=-hpux ;; *-hitachi) os=-hiux ;; i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) os=-sysv ;; *-cbm) os=-amigaos ;; *-dg) os=-dgux ;; *-dolphin) os=-sysv3 ;; m68k-ccur) os=-rtu ;; m88k-omron*) os=-luna ;; *-next ) os=-nextstep ;; *-sequent) os=-ptx ;; *-crds) os=-unos ;; *-ns) os=-genix ;; i370-*) os=-mvs ;; *-next) os=-nextstep3 ;; *-gould) os=-sysv ;; *-highlevel) os=-bsd ;; *-encore) os=-bsd ;; *-sgi) os=-irix ;; *-siemens) os=-sysv4 ;; *-masscomp) os=-rtu ;; f30[01]-fujitsu | f700-fujitsu) os=-uxpv ;; *-rom68k) os=-coff ;; *-*bug) os=-coff ;; *-apple) os=-macos ;; *-atari*) os=-mint ;; *) os=-none ;; esac fi # Here we handle the case where we know the os, and the CPU type, but not the # manufacturer. We pick the logical manufacturer. vendor=unknown case $basic_machine in *-unknown) case $os in -riscix*) vendor=acorn ;; -sunos*) vendor=sun ;; -cnk*|-aix*) vendor=ibm ;; -beos*) vendor=be ;; -hpux*) vendor=hp ;; -mpeix*) vendor=hp ;; -hiux*) vendor=hitachi ;; -unos*) vendor=crds ;; -dgux*) vendor=dg ;; -luna*) vendor=omron ;; -genix*) vendor=ns ;; -mvs* | -opened*) vendor=ibm ;; -os400*) vendor=ibm ;; -ptx*) vendor=sequent ;; -tpf*) vendor=ibm ;; -vxsim* | -vxworks* | -windiss*) vendor=wrs ;; -aux*) vendor=apple ;; -hms*) vendor=hitachi ;; -mpw* | -macos*) vendor=apple ;; -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) vendor=atari ;; -vos*) vendor=stratus ;; esac basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` ;; esac echo $basic_machine$os exit # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: haildb-2.3.2/config/depcomp0000755000175000017500000004426711513177417016477 0ustar00pcrewspcrews00000000000000#! /bin/sh # depcomp - compile a program generating dependencies as side-effects scriptversion=2009-04-28.21; # UTC # Copyright (C) 1999, 2000, 2003, 2004, 2005, 2006, 2007, 2009 Free # Software Foundation, Inc. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # This program 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 General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # Originally written by Alexandre Oliva . case $1 in '') echo "$0: No command. Try \`$0 --help' for more information." 1>&2 exit 1; ;; -h | --h*) cat <<\EOF Usage: depcomp [--help] [--version] PROGRAM [ARGS] Run PROGRAMS ARGS to compile a file, generating dependencies as side-effects. Environment variables: depmode Dependency tracking mode. source Source file read by `PROGRAMS ARGS'. object Object file output by `PROGRAMS ARGS'. DEPDIR directory where to store dependencies. depfile Dependency file to output. tmpdepfile Temporary file to use when outputing dependencies. libtool Whether libtool is used (yes/no). Report bugs to . EOF exit $? ;; -v | --v*) echo "depcomp $scriptversion" exit $? ;; esac if test -z "$depmode" || test -z "$source" || test -z "$object"; then echo "depcomp: Variables source, object and depmode must be set" 1>&2 exit 1 fi # Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po. depfile=${depfile-`echo "$object" | sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`} tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`} rm -f "$tmpdepfile" # Some modes work just like other modes, but use different flags. We # parameterize here, but still list the modes in the big case below, # to make depend.m4 easier to write. Note that we *cannot* use a case # here, because this file can only contain one case statement. if test "$depmode" = hp; then # HP compiler uses -M and no extra arg. gccflag=-M depmode=gcc fi if test "$depmode" = dashXmstdout; then # This is just like dashmstdout with a different argument. dashmflag=-xM depmode=dashmstdout fi cygpath_u="cygpath -u -f -" if test "$depmode" = msvcmsys; then # This is just like msvisualcpp but w/o cygpath translation. # Just convert the backslash-escaped backslashes to single forward # slashes to satisfy depend.m4 cygpath_u="sed s,\\\\\\\\,/,g" depmode=msvisualcpp fi case "$depmode" in gcc3) ## gcc 3 implements dependency tracking that does exactly what ## we want. Yay! Note: for some reason libtool 1.4 doesn't like ## it if -MD -MP comes after the -MF stuff. Hmm. ## Unfortunately, FreeBSD c89 acceptance of flags depends upon ## the command line argument order; so add the flags where they ## appear in depend2.am. Note that the slowdown incurred here ## affects only configure: in makefiles, %FASTDEP% shortcuts this. for arg do case $arg in -c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;; *) set fnord "$@" "$arg" ;; esac shift # fnord shift # $arg done "$@" stat=$? if test $stat -eq 0; then : else rm -f "$tmpdepfile" exit $stat fi mv "$tmpdepfile" "$depfile" ;; gcc) ## There are various ways to get dependency output from gcc. Here's ## why we pick this rather obscure method: ## - Don't want to use -MD because we'd like the dependencies to end ## up in a subdir. Having to rename by hand is ugly. ## (We might end up doing this anyway to support other compilers.) ## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like ## -MM, not -M (despite what the docs say). ## - Using -M directly means running the compiler twice (even worse ## than renaming). if test -z "$gccflag"; then gccflag=-MD, fi "$@" -Wp,"$gccflag$tmpdepfile" stat=$? if test $stat -eq 0; then : else rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" echo "$object : \\" > "$depfile" alpha=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz ## The second -e expression handles DOS-style file names with drive letters. sed -e 's/^[^:]*: / /' \ -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile" ## This next piece of magic avoids the `deleted header file' problem. ## The problem is that when a header file which appears in a .P file ## is deleted, the dependency causes make to die (because there is ## typically no way to rebuild the header). We avoid this by adding ## dummy dependencies for each header file. Too bad gcc doesn't do ## this for us directly. tr ' ' ' ' < "$tmpdepfile" | ## Some versions of gcc put a space before the `:'. On the theory ## that the space means something, we add a space to the output as ## well. ## Some versions of the HPUX 10.20 sed can't process this invocation ## correctly. Breaking it into two sed invocations is a workaround. sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; hp) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; sgi) if test "$libtool" = yes; then "$@" "-Wp,-MDupdate,$tmpdepfile" else "$@" -MDupdate "$tmpdepfile" fi stat=$? if test $stat -eq 0; then : else rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files echo "$object : \\" > "$depfile" # Clip off the initial element (the dependent). Don't try to be # clever and replace this with sed code, as IRIX sed won't handle # lines with more than a fixed number of characters (4096 in # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines; # the IRIX cc adds comments like `#:fec' to the end of the # dependency line. tr ' ' ' ' < "$tmpdepfile" \ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' | \ tr ' ' ' ' >> "$depfile" echo >> "$depfile" # The second pass generates a dummy entry for each header file. tr ' ' ' ' < "$tmpdepfile" \ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \ >> "$depfile" else # The sourcefile does not contain any dependencies, so just # store a dummy comment line, to avoid errors with the Makefile # "include basename.Plo" scheme. echo "#dummy" > "$depfile" fi rm -f "$tmpdepfile" ;; aix) # The C for AIX Compiler uses -M and outputs the dependencies # in a .u file. In older versions, this file always lives in the # current directory. Also, the AIX compiler puts `$object:' at the # start of each line; $object doesn't have directory information. # Version 6 uses the directory in both cases. dir=`echo "$object" | sed -e 's|/[^/]*$|/|'` test "x$dir" = "x$object" && dir= base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'` if test "$libtool" = yes; then tmpdepfile1=$dir$base.u tmpdepfile2=$base.u tmpdepfile3=$dir.libs/$base.u "$@" -Wc,-M else tmpdepfile1=$dir$base.u tmpdepfile2=$dir$base.u tmpdepfile3=$dir$base.u "$@" -M fi stat=$? if test $stat -eq 0; then : else rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" do test -f "$tmpdepfile" && break done if test -f "$tmpdepfile"; then # Each line is of the form `foo.o: dependent.h'. # Do two passes, one to just change these to # `$object: dependent.h' and one to simply `dependent.h:'. sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile" # That's a tab and a space in the []. sed -e 's,^.*\.[a-z]*:[ ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile" else # The sourcefile does not contain any dependencies, so just # store a dummy comment line, to avoid errors with the Makefile # "include basename.Plo" scheme. echo "#dummy" > "$depfile" fi rm -f "$tmpdepfile" ;; icc) # Intel's C compiler understands `-MD -MF file'. However on # icc -MD -MF foo.d -c -o sub/foo.o sub/foo.c # ICC 7.0 will fill foo.d with something like # foo.o: sub/foo.c # foo.o: sub/foo.h # which is wrong. We want: # sub/foo.o: sub/foo.c # sub/foo.o: sub/foo.h # sub/foo.c: # sub/foo.h: # ICC 7.1 will output # foo.o: sub/foo.c sub/foo.h # and will wrap long lines using \ : # foo.o: sub/foo.c ... \ # sub/foo.h ... \ # ... "$@" -MD -MF "$tmpdepfile" stat=$? if test $stat -eq 0; then : else rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" # Each line is of the form `foo.o: dependent.h', # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'. # Do two passes, one to just change these to # `$object: dependent.h' and one to simply `dependent.h:'. sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process this invocation # correctly. Breaking it into two sed invocations is a workaround. sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; hp2) # The "hp" stanza above does not work with aCC (C++) and HP's ia64 # compilers, which have integrated preprocessors. The correct option # to use with these is +Maked; it writes dependencies to a file named # 'foo.d', which lands next to the object file, wherever that # happens to be. # Much of this is similar to the tru64 case; see comments there. dir=`echo "$object" | sed -e 's|/[^/]*$|/|'` test "x$dir" = "x$object" && dir= base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'` if test "$libtool" = yes; then tmpdepfile1=$dir$base.d tmpdepfile2=$dir.libs/$base.d "$@" -Wc,+Maked else tmpdepfile1=$dir$base.d tmpdepfile2=$dir$base.d "$@" +Maked fi stat=$? if test $stat -eq 0; then : else rm -f "$tmpdepfile1" "$tmpdepfile2" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" do test -f "$tmpdepfile" && break done if test -f "$tmpdepfile"; then sed -e "s,^.*\.[a-z]*:,$object:," "$tmpdepfile" > "$depfile" # Add `dependent.h:' lines. sed -ne '2,${ s/^ *// s/ \\*$// s/$/:/ p }' "$tmpdepfile" >> "$depfile" else echo "#dummy" > "$depfile" fi rm -f "$tmpdepfile" "$tmpdepfile2" ;; tru64) # The Tru64 compiler uses -MD to generate dependencies as a side # effect. `cc -MD -o foo.o ...' puts the dependencies into `foo.o.d'. # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put # dependencies in `foo.d' instead, so we check for that too. # Subdirectories are respected. dir=`echo "$object" | sed -e 's|/[^/]*$|/|'` test "x$dir" = "x$object" && dir= base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'` if test "$libtool" = yes; then # With Tru64 cc, shared objects can also be used to make a # static library. This mechanism is used in libtool 1.4 series to # handle both shared and static libraries in a single compilation. # With libtool 1.4, dependencies were output in $dir.libs/$base.lo.d. # # With libtool 1.5 this exception was removed, and libtool now # generates 2 separate objects for the 2 libraries. These two # compilations output dependencies in $dir.libs/$base.o.d and # in $dir$base.o.d. We have to check for both files, because # one of the two compilations can be disabled. We should prefer # $dir$base.o.d over $dir.libs/$base.o.d because the latter is # automatically cleaned when .libs/ is deleted, while ignoring # the former would cause a distcleancheck panic. tmpdepfile1=$dir.libs/$base.lo.d # libtool 1.4 tmpdepfile2=$dir$base.o.d # libtool 1.5 tmpdepfile3=$dir.libs/$base.o.d # libtool 1.5 tmpdepfile4=$dir.libs/$base.d # Compaq CCC V6.2-504 "$@" -Wc,-MD else tmpdepfile1=$dir$base.o.d tmpdepfile2=$dir$base.d tmpdepfile3=$dir$base.d tmpdepfile4=$dir$base.d "$@" -MD fi stat=$? if test $stat -eq 0; then : else rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4" do test -f "$tmpdepfile" && break done if test -f "$tmpdepfile"; then sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile" # That's a tab and a space in the []. sed -e 's,^.*\.[a-z]*:[ ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile" else echo "#dummy" > "$depfile" fi rm -f "$tmpdepfile" ;; #nosideeffect) # This comment above is used by automake to tell side-effect # dependency tracking mechanisms from slower ones. dashmstdout) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout, regardless of -o. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi # Remove `-o $object'. IFS=" " for arg do case $arg in -o) shift ;; $object) shift ;; *) set fnord "$@" "$arg" shift # fnord shift # $arg ;; esac done test -z "$dashmflag" && dashmflag=-M # Require at least two characters before searching for `:' # in the target name. This is to cope with DOS-style filenames: # a dependency such as `c:/foo/bar' could be seen as target `c' otherwise. "$@" $dashmflag | sed 's:^[ ]*[^: ][^:][^:]*\:[ ]*:'"$object"'\: :' > "$tmpdepfile" rm -f "$depfile" cat < "$tmpdepfile" > "$depfile" tr ' ' ' ' < "$tmpdepfile" | \ ## Some versions of the HPUX 10.20 sed can't process this invocation ## correctly. Breaking it into two sed invocations is a workaround. sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; dashXmstdout) # This case only exists to satisfy depend.m4. It is never actually # run, as this mode is specially recognized in the preamble. exit 1 ;; makedepend) "$@" || exit $? # Remove any Libtool call if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi # X makedepend shift cleared=no eat=no for arg do case $cleared in no) set ""; shift cleared=yes ;; esac if test $eat = yes; then eat=no continue fi case "$arg" in -D*|-I*) set fnord "$@" "$arg"; shift ;; # Strip any option that makedepend may not understand. Remove # the object too, otherwise makedepend will parse it as a source file. -arch) eat=yes ;; -*|$object) ;; *) set fnord "$@" "$arg"; shift ;; esac done obj_suffix=`echo "$object" | sed 's/^.*\././'` touch "$tmpdepfile" ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@" rm -f "$depfile" cat < "$tmpdepfile" > "$depfile" sed '1,2d' "$tmpdepfile" | tr ' ' ' ' | \ ## Some versions of the HPUX 10.20 sed can't process this invocation ## correctly. Breaking it into two sed invocations is a workaround. sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" "$tmpdepfile".bak ;; cpp) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi # Remove `-o $object'. IFS=" " for arg do case $arg in -o) shift ;; $object) shift ;; *) set fnord "$@" "$arg" shift # fnord shift # $arg ;; esac done "$@" -E | sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' | sed '$ s: \\$::' > "$tmpdepfile" rm -f "$depfile" echo "$object : \\" > "$depfile" cat < "$tmpdepfile" >> "$depfile" sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; msvisualcpp) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi IFS=" " for arg do case "$arg" in -o) shift ;; $object) shift ;; "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI") set fnord "$@" shift shift ;; *) set fnord "$@" "$arg" shift shift ;; esac done "$@" -E 2>/dev/null | sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile" rm -f "$depfile" echo "$object : \\" > "$depfile" sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s:: \1 \\:p' >> "$depfile" echo " " >> "$depfile" sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile" rm -f "$tmpdepfile" ;; msvcmsys) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; none) exec "$@" ;; *) echo "Unknown depmode $depmode" 1>&2 exit 1 ;; esac exit 0 # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC" # time-stamp-end: "; # UTC" # End: haildb-2.3.2/config/ltmain.sh0000755000175000017500000073341511513177375016750 0ustar00pcrewspcrews00000000000000# Generated from ltmain.m4sh. # ltmain.sh (GNU libtool) 2.2.6b # Written by Gordon Matzigkeit , 1996 # Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006, 2007 2008 Free Software Foundation, Inc. # This is free software; see the source for copying conditions. There is NO # warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # GNU Libtool is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # As a special exception to the GNU General Public License, # if you distribute this file as part of a program or library that # is built using GNU Libtool, you may include this file under the # same distribution terms that you use for the rest of that program. # # GNU Libtool 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 # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNU Libtool; see the file COPYING. If not, a copy # can be downloaded from http://www.gnu.org/licenses/gpl.html, # or obtained by writing to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # Usage: $progname [OPTION]... [MODE-ARG]... # # Provide generalized library-building support services. # # --config show all configuration variables # --debug enable verbose shell tracing # -n, --dry-run display commands without modifying any files # --features display basic configuration information and exit # --mode=MODE use operation mode MODE # --preserve-dup-deps don't remove duplicate dependency libraries # --quiet, --silent don't print informational messages # --tag=TAG use configuration variables from tag TAG # -v, --verbose print informational messages (default) # --version print version information # -h, --help print short or long help message # # MODE must be one of the following: # # clean remove files from the build directory # compile compile a source file into a libtool object # execute automatically set library path, then run a program # finish complete the installation of libtool libraries # install install libraries or executables # link create a library or an executable # uninstall remove libraries from an installed directory # # MODE-ARGS vary depending on the MODE. # Try `$progname --help --mode=MODE' for a more detailed description of MODE. # # When reporting a bug, please describe a test case to reproduce it and # include the following information: # # host-triplet: $host # shell: $SHELL # compiler: $LTCC # compiler flags: $LTCFLAGS # linker: $LD (gnu? $with_gnu_ld) # $progname: (GNU libtool) 2.2.6b Debian-2.2.6b-2ubuntu1 # automake: $automake_version # autoconf: $autoconf_version # # Report bugs to . PROGRAM=ltmain.sh PACKAGE=libtool VERSION="2.2.6b Debian-2.2.6b-2ubuntu1" TIMESTAMP="" package_revision=1.3017 # Be Bourne compatible if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then emulate sh NULLCMD=: # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac fi BIN_SH=xpg4; export BIN_SH # for Tru64 DUALCASE=1; export DUALCASE # for MKS sh # NLS nuisances: We save the old values to restore during execute mode. # Only set LANG and LC_ALL to C if already set. # These must not be set unconditionally because not all systems understand # e.g. LANG=C (notably SCO). lt_user_locale= lt_safe_locale= for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES do eval "if test \"\${$lt_var+set}\" = set; then save_$lt_var=\$$lt_var $lt_var=C export $lt_var lt_user_locale=\"$lt_var=\\\$save_\$lt_var; \$lt_user_locale\" lt_safe_locale=\"$lt_var=C; \$lt_safe_locale\" fi" done $lt_unset CDPATH : ${CP="cp -f"} : ${ECHO="echo"} : ${EGREP="/bin/grep -E"} : ${FGREP="/bin/grep -F"} : ${GREP="/bin/grep"} : ${LN_S="ln -s"} : ${MAKE="make"} : ${MKDIR="mkdir"} : ${MV="mv -f"} : ${RM="rm -f"} : ${SED="/bin/sed"} : ${SHELL="${CONFIG_SHELL-/bin/sh}"} : ${Xsed="$SED -e 1s/^X//"} # Global variables: EXIT_SUCCESS=0 EXIT_FAILURE=1 EXIT_MISMATCH=63 # $? = 63 is used to indicate version mismatch to missing. EXIT_SKIP=77 # $? = 77 is used to indicate a skipped test to automake. exit_status=$EXIT_SUCCESS # Make sure IFS has a sensible default lt_nl=' ' IFS=" $lt_nl" dirname="s,/[^/]*$,," basename="s,^.*/,," # func_dirname_and_basename file append nondir_replacement # perform func_basename and func_dirname in a single function # call: # dirname: Compute the dirname of FILE. If nonempty, # add APPEND to the result, otherwise set result # to NONDIR_REPLACEMENT. # value returned in "$func_dirname_result" # basename: Compute filename of FILE. # value retuned in "$func_basename_result" # Implementation must be kept synchronized with func_dirname # and func_basename. For efficiency, we do not delegate to # those functions but instead duplicate the functionality here. func_dirname_and_basename () { # Extract subdirectory from the argument. func_dirname_result=`$ECHO "X${1}" | $Xsed -e "$dirname"` if test "X$func_dirname_result" = "X${1}"; then func_dirname_result="${3}" else func_dirname_result="$func_dirname_result${2}" fi func_basename_result=`$ECHO "X${1}" | $Xsed -e "$basename"` } # Generated shell functions inserted here. # Work around backward compatibility issue on IRIX 6.5. On IRIX 6.4+, sh # is ksh but when the shell is invoked as "sh" and the current value of # the _XPG environment variable is not equal to 1 (one), the special # positional parameter $0, within a function call, is the name of the # function. progpath="$0" # The name of this program: # In the unlikely event $progname began with a '-', it would play havoc with # func_echo (imagine progname=-n), so we prepend ./ in that case: func_dirname_and_basename "$progpath" progname=$func_basename_result case $progname in -*) progname=./$progname ;; esac # Make sure we have an absolute path for reexecution: case $progpath in [\\/]*|[A-Za-z]:\\*) ;; *[\\/]*) progdir=$func_dirname_result progdir=`cd "$progdir" && pwd` progpath="$progdir/$progname" ;; *) save_IFS="$IFS" IFS=: for progdir in $PATH; do IFS="$save_IFS" test -x "$progdir/$progname" && break done IFS="$save_IFS" test -n "$progdir" || progdir=`pwd` progpath="$progdir/$progname" ;; esac # Sed substitution that helps us do robust quoting. It backslashifies # metacharacters that are still active within double-quoted strings. Xsed="${SED}"' -e 1s/^X//' sed_quote_subst='s/\([`"$\\]\)/\\\1/g' # Same as above, but do not quote variable references. double_quote_subst='s/\(["`\\]\)/\\\1/g' # Re-`\' parameter expansions in output of double_quote_subst that were # `\'-ed in input to the same. If an odd number of `\' preceded a '$' # in input to double_quote_subst, that '$' was protected from expansion. # Since each input `\' is now two `\'s, look for any number of runs of # four `\'s followed by two `\'s and then a '$'. `\' that '$'. bs='\\' bs2='\\\\' bs4='\\\\\\\\' dollar='\$' sed_double_backslash="\ s/$bs4/&\\ /g s/^$bs2$dollar/$bs&/ s/\\([^$bs]\\)$bs2$dollar/\\1$bs2$bs$dollar/g s/\n//g" # Standard options: opt_dry_run=false opt_help=false opt_quiet=false opt_verbose=false opt_warning=: # func_echo arg... # Echo program name prefixed message, along with the current mode # name if it has been set yet. func_echo () { $ECHO "$progname${mode+: }$mode: $*" } # func_verbose arg... # Echo program name prefixed message in verbose mode only. func_verbose () { $opt_verbose && func_echo ${1+"$@"} # A bug in bash halts the script if the last line of a function # fails when set -e is in force, so we need another command to # work around that: : } # func_error arg... # Echo program name prefixed message to standard error. func_error () { $ECHO "$progname${mode+: }$mode: "${1+"$@"} 1>&2 } # func_warning arg... # Echo program name prefixed warning message to standard error. func_warning () { $opt_warning && $ECHO "$progname${mode+: }$mode: warning: "${1+"$@"} 1>&2 # bash bug again: : } # func_fatal_error arg... # Echo program name prefixed message to standard error, and exit. func_fatal_error () { func_error ${1+"$@"} exit $EXIT_FAILURE } # func_fatal_help arg... # Echo program name prefixed message to standard error, followed by # a help hint, and exit. func_fatal_help () { func_error ${1+"$@"} func_fatal_error "$help" } help="Try \`$progname --help' for more information." ## default # func_grep expression filename # Check whether EXPRESSION matches any line of FILENAME, without output. func_grep () { $GREP "$1" "$2" >/dev/null 2>&1 } # func_mkdir_p directory-path # Make sure the entire path to DIRECTORY-PATH is available. func_mkdir_p () { my_directory_path="$1" my_dir_list= if test -n "$my_directory_path" && test "$opt_dry_run" != ":"; then # Protect directory names starting with `-' case $my_directory_path in -*) my_directory_path="./$my_directory_path" ;; esac # While some portion of DIR does not yet exist... while test ! -d "$my_directory_path"; do # ...make a list in topmost first order. Use a colon delimited # list incase some portion of path contains whitespace. my_dir_list="$my_directory_path:$my_dir_list" # If the last portion added has no slash in it, the list is done case $my_directory_path in */*) ;; *) break ;; esac # ...otherwise throw away the child directory and loop my_directory_path=`$ECHO "X$my_directory_path" | $Xsed -e "$dirname"` done my_dir_list=`$ECHO "X$my_dir_list" | $Xsed -e 's,:*$,,'` save_mkdir_p_IFS="$IFS"; IFS=':' for my_dir in $my_dir_list; do IFS="$save_mkdir_p_IFS" # mkdir can fail with a `File exist' error if two processes # try to create one of the directories concurrently. Don't # stop in that case! $MKDIR "$my_dir" 2>/dev/null || : done IFS="$save_mkdir_p_IFS" # Bail out if we (or some other process) failed to create a directory. test -d "$my_directory_path" || \ func_fatal_error "Failed to create \`$1'" fi } # func_mktempdir [string] # Make a temporary directory that won't clash with other running # libtool processes, and avoids race conditions if possible. If # given, STRING is the basename for that directory. func_mktempdir () { my_template="${TMPDIR-/tmp}/${1-$progname}" if test "$opt_dry_run" = ":"; then # Return a directory name, but don't create it in dry-run mode my_tmpdir="${my_template}-$$" else # If mktemp works, use that first and foremost my_tmpdir=`mktemp -d "${my_template}-XXXXXXXX" 2>/dev/null` if test ! -d "$my_tmpdir"; then # Failing that, at least try and use $RANDOM to avoid a race my_tmpdir="${my_template}-${RANDOM-0}$$" save_mktempdir_umask=`umask` umask 0077 $MKDIR "$my_tmpdir" umask $save_mktempdir_umask fi # If we're not in dry-run mode, bomb out on failure test -d "$my_tmpdir" || \ func_fatal_error "cannot create temporary directory \`$my_tmpdir'" fi $ECHO "X$my_tmpdir" | $Xsed } # func_quote_for_eval arg # Aesthetically quote ARG to be evaled later. # This function returns two values: FUNC_QUOTE_FOR_EVAL_RESULT # is double-quoted, suitable for a subsequent eval, whereas # FUNC_QUOTE_FOR_EVAL_UNQUOTED_RESULT has merely all characters # which are still active within double quotes backslashified. func_quote_for_eval () { case $1 in *[\\\`\"\$]*) func_quote_for_eval_unquoted_result=`$ECHO "X$1" | $Xsed -e "$sed_quote_subst"` ;; *) func_quote_for_eval_unquoted_result="$1" ;; esac case $func_quote_for_eval_unquoted_result in # Double-quote args containing shell metacharacters to delay # word splitting, command substitution and and variable # expansion for a subsequent eval. # Many Bourne shells cannot handle close brackets correctly # in scan sets, so we specify it separately. *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") func_quote_for_eval_result="\"$func_quote_for_eval_unquoted_result\"" ;; *) func_quote_for_eval_result="$func_quote_for_eval_unquoted_result" esac } # func_quote_for_expand arg # Aesthetically quote ARG to be evaled later; same as above, # but do not quote variable references. func_quote_for_expand () { case $1 in *[\\\`\"]*) my_arg=`$ECHO "X$1" | $Xsed \ -e "$double_quote_subst" -e "$sed_double_backslash"` ;; *) my_arg="$1" ;; esac case $my_arg in # Double-quote args containing shell metacharacters to delay # word splitting and command substitution for a subsequent eval. # Many Bourne shells cannot handle close brackets correctly # in scan sets, so we specify it separately. *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") my_arg="\"$my_arg\"" ;; esac func_quote_for_expand_result="$my_arg" } # func_show_eval cmd [fail_exp] # Unless opt_silent is true, then output CMD. Then, if opt_dryrun is # not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP # is given, then evaluate it. func_show_eval () { my_cmd="$1" my_fail_exp="${2-:}" ${opt_silent-false} || { func_quote_for_expand "$my_cmd" eval "func_echo $func_quote_for_expand_result" } if ${opt_dry_run-false}; then :; else eval "$my_cmd" my_status=$? if test "$my_status" -eq 0; then :; else eval "(exit $my_status); $my_fail_exp" fi fi } # func_show_eval_locale cmd [fail_exp] # Unless opt_silent is true, then output CMD. Then, if opt_dryrun is # not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP # is given, then evaluate it. Use the saved locale for evaluation. func_show_eval_locale () { my_cmd="$1" my_fail_exp="${2-:}" ${opt_silent-false} || { func_quote_for_expand "$my_cmd" eval "func_echo $func_quote_for_expand_result" } if ${opt_dry_run-false}; then :; else eval "$lt_user_locale $my_cmd" my_status=$? eval "$lt_safe_locale" if test "$my_status" -eq 0; then :; else eval "(exit $my_status); $my_fail_exp" fi fi } # func_version # Echo version message to standard output and exit. func_version () { $SED -n '/^# '$PROGRAM' (GNU /,/# warranty; / { s/^# // s/^# *$// s/\((C)\)[ 0-9,-]*\( [1-9][0-9]*\)/\1\2/ p }' < "$progpath" exit $? } # func_usage # Echo short help message to standard output and exit. func_usage () { $SED -n '/^# Usage:/,/# -h/ { s/^# // s/^# *$// s/\$progname/'$progname'/ p }' < "$progpath" $ECHO $ECHO "run \`$progname --help | more' for full usage" exit $? } # func_help # Echo long help message to standard output and exit. func_help () { $SED -n '/^# Usage:/,/# Report bugs to/ { s/^# // s/^# *$// s*\$progname*'$progname'* s*\$host*'"$host"'* s*\$SHELL*'"$SHELL"'* s*\$LTCC*'"$LTCC"'* s*\$LTCFLAGS*'"$LTCFLAGS"'* s*\$LD*'"$LD"'* s/\$with_gnu_ld/'"$with_gnu_ld"'/ s/\$automake_version/'"`(automake --version) 2>/dev/null |$SED 1q`"'/ s/\$autoconf_version/'"`(autoconf --version) 2>/dev/null |$SED 1q`"'/ p }' < "$progpath" exit $? } # func_missing_arg argname # Echo program name prefixed message to standard error and set global # exit_cmd. func_missing_arg () { func_error "missing argument for $1" exit_cmd=exit } exit_cmd=: # Check that we have a working $ECHO. if test "X$1" = X--no-reexec; then # Discard the --no-reexec flag, and continue. shift elif test "X$1" = X--fallback-echo; then # Avoid inline document here, it may be left over : elif test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t'; then # Yippee, $ECHO works! : else # Restart under the correct shell, and then maybe $ECHO will work. exec $SHELL "$progpath" --no-reexec ${1+"$@"} fi if test "X$1" = X--fallback-echo; then # used as fallback echo shift cat </dev/null 2>&1; then taglist="$taglist $tagname" # Evaluate the configuration. Be careful to quote the path # and the sed script, to avoid splitting on whitespace, but # also don't use non-portable quotes within backquotes within # quotes we have to do it in 2 steps: extractedcf=`$SED -n -e "$sed_extractcf" < "$progpath"` eval "$extractedcf" else func_error "ignoring unknown tag $tagname" fi ;; esac } # Parse options once, thoroughly. This comes as soon as possible in # the script to make things like `libtool --version' happen quickly. { # Shorthand for --mode=foo, only valid as the first argument case $1 in clean|clea|cle|cl) shift; set dummy --mode clean ${1+"$@"}; shift ;; compile|compil|compi|comp|com|co|c) shift; set dummy --mode compile ${1+"$@"}; shift ;; execute|execut|execu|exec|exe|ex|e) shift; set dummy --mode execute ${1+"$@"}; shift ;; finish|finis|fini|fin|fi|f) shift; set dummy --mode finish ${1+"$@"}; shift ;; install|instal|insta|inst|ins|in|i) shift; set dummy --mode install ${1+"$@"}; shift ;; link|lin|li|l) shift; set dummy --mode link ${1+"$@"}; shift ;; uninstall|uninstal|uninsta|uninst|unins|unin|uni|un|u) shift; set dummy --mode uninstall ${1+"$@"}; shift ;; esac # Parse non-mode specific arguments: while test "$#" -gt 0; do opt="$1" shift case $opt in --config) func_config ;; --debug) preserve_args="$preserve_args $opt" func_echo "enabling shell trace mode" opt_debug='set -x' $opt_debug ;; -dlopen) test "$#" -eq 0 && func_missing_arg "$opt" && break execute_dlfiles="$execute_dlfiles $1" shift ;; --dry-run | -n) opt_dry_run=: ;; --features) func_features ;; --finish) mode="finish" ;; --mode) test "$#" -eq 0 && func_missing_arg "$opt" && break case $1 in # Valid mode arguments: clean) ;; compile) ;; execute) ;; finish) ;; install) ;; link) ;; relink) ;; uninstall) ;; # Catch anything else as an error *) func_error "invalid argument for $opt" exit_cmd=exit break ;; esac mode="$1" shift ;; --preserve-dup-deps) opt_duplicate_deps=: ;; --quiet|--silent) preserve_args="$preserve_args $opt" opt_silent=: ;; --verbose| -v) preserve_args="$preserve_args $opt" opt_silent=false ;; --tag) test "$#" -eq 0 && func_missing_arg "$opt" && break preserve_args="$preserve_args $opt $1" func_enable_tag "$1" # tagname is set here shift ;; # Separate optargs to long options: -dlopen=*|--mode=*|--tag=*) func_opt_split "$opt" set dummy "$func_opt_split_opt" "$func_opt_split_arg" ${1+"$@"} shift ;; -\?|-h) func_usage ;; --help) opt_help=: ;; --version) func_version ;; -*) func_fatal_help "unrecognized option \`$opt'" ;; *) nonopt="$opt" break ;; esac done case $host in *cygwin* | *mingw* | *pw32* | *cegcc*) # don't eliminate duplications in $postdeps and $predeps opt_duplicate_compiler_generated_deps=: ;; *) opt_duplicate_compiler_generated_deps=$opt_duplicate_deps ;; esac # Having warned about all mis-specified options, bail out if # anything was wrong. $exit_cmd $EXIT_FAILURE } # func_check_version_match # Ensure that we are using m4 macros, and libtool script from the same # release of libtool. func_check_version_match () { if test "$package_revision" != "$macro_revision"; then if test "$VERSION" != "$macro_version"; then if test -z "$macro_version"; then cat >&2 <<_LT_EOF $progname: Version mismatch error. This is $PACKAGE $VERSION, but the $progname: definition of this LT_INIT comes from an older release. $progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION $progname: and run autoconf again. _LT_EOF else cat >&2 <<_LT_EOF $progname: Version mismatch error. This is $PACKAGE $VERSION, but the $progname: definition of this LT_INIT comes from $PACKAGE $macro_version. $progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION $progname: and run autoconf again. _LT_EOF fi else cat >&2 <<_LT_EOF $progname: Version mismatch error. This is $PACKAGE $VERSION, revision $package_revision, $progname: but the definition of this LT_INIT comes from revision $macro_revision. $progname: You should recreate aclocal.m4 with macros from revision $package_revision $progname: of $PACKAGE $VERSION and run autoconf again. _LT_EOF fi exit $EXIT_MISMATCH fi } ## ----------- ## ## Main. ## ## ----------- ## $opt_help || { # Sanity checks first: func_check_version_match if test "$build_libtool_libs" != yes && test "$build_old_libs" != yes; then func_fatal_configuration "not configured to build any kind of library" fi test -z "$mode" && func_fatal_error "error: you must specify a MODE." # Darwin sucks eval std_shrext=\"$shrext_cmds\" # Only execute mode is allowed to have -dlopen flags. if test -n "$execute_dlfiles" && test "$mode" != execute; then func_error "unrecognized option \`-dlopen'" $ECHO "$help" 1>&2 exit $EXIT_FAILURE fi # Change the help message to a mode-specific one. generic_help="$help" help="Try \`$progname --help --mode=$mode' for more information." } # func_lalib_p file # True iff FILE is a libtool `.la' library or `.lo' object file. # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_lalib_p () { test -f "$1" && $SED -e 4q "$1" 2>/dev/null \ | $GREP "^# Generated by .*$PACKAGE" > /dev/null 2>&1 } # func_lalib_unsafe_p file # True iff FILE is a libtool `.la' library or `.lo' object file. # This function implements the same check as func_lalib_p without # resorting to external programs. To this end, it redirects stdin and # closes it afterwards, without saving the original file descriptor. # As a safety measure, use it only where a negative result would be # fatal anyway. Works if `file' does not exist. func_lalib_unsafe_p () { lalib_p=no if test -f "$1" && test -r "$1" && exec 5<&0 <"$1"; then for lalib_p_l in 1 2 3 4 do read lalib_p_line case "$lalib_p_line" in \#\ Generated\ by\ *$PACKAGE* ) lalib_p=yes; break;; esac done exec 0<&5 5<&- fi test "$lalib_p" = yes } # func_ltwrapper_script_p file # True iff FILE is a libtool wrapper script # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_ltwrapper_script_p () { func_lalib_p "$1" } # func_ltwrapper_executable_p file # True iff FILE is a libtool wrapper executable # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_ltwrapper_executable_p () { func_ltwrapper_exec_suffix= case $1 in *.exe) ;; *) func_ltwrapper_exec_suffix=.exe ;; esac $GREP "$magic_exe" "$1$func_ltwrapper_exec_suffix" >/dev/null 2>&1 } # func_ltwrapper_scriptname file # Assumes file is an ltwrapper_executable # uses $file to determine the appropriate filename for a # temporary ltwrapper_script. func_ltwrapper_scriptname () { func_ltwrapper_scriptname_result="" if func_ltwrapper_executable_p "$1"; then func_dirname_and_basename "$1" "" "." func_stripname '' '.exe' "$func_basename_result" func_ltwrapper_scriptname_result="$func_dirname_result/$objdir/${func_stripname_result}_ltshwrapper" fi } # func_ltwrapper_p file # True iff FILE is a libtool wrapper script or wrapper executable # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_ltwrapper_p () { func_ltwrapper_script_p "$1" || func_ltwrapper_executable_p "$1" } # func_execute_cmds commands fail_cmd # Execute tilde-delimited COMMANDS. # If FAIL_CMD is given, eval that upon failure. # FAIL_CMD may read-access the current command in variable CMD! func_execute_cmds () { $opt_debug save_ifs=$IFS; IFS='~' for cmd in $1; do IFS=$save_ifs eval cmd=\"$cmd\" func_show_eval "$cmd" "${2-:}" done IFS=$save_ifs } # func_source file # Source FILE, adding directory component if necessary. # Note that it is not necessary on cygwin/mingw to append a dot to # FILE even if both FILE and FILE.exe exist: automatic-append-.exe # behavior happens only for exec(3), not for open(2)! Also, sourcing # `FILE.' does not work on cygwin managed mounts. func_source () { $opt_debug case $1 in */* | *\\*) . "$1" ;; *) . "./$1" ;; esac } # func_infer_tag arg # Infer tagged configuration to use if any are available and # if one wasn't chosen via the "--tag" command line option. # Only attempt this if the compiler in the base compile # command doesn't match the default compiler. # arg is usually of the form 'gcc ...' func_infer_tag () { $opt_debug if test -n "$available_tags" && test -z "$tagname"; then CC_quoted= for arg in $CC; do func_quote_for_eval "$arg" CC_quoted="$CC_quoted $func_quote_for_eval_result" done case $@ in # Blanks in the command may have been stripped by the calling shell, # but not from the CC environment variable when configure was run. " $CC "* | "$CC "* | " `$ECHO $CC` "* | "`$ECHO $CC` "* | " $CC_quoted"* | "$CC_quoted "* | " `$ECHO $CC_quoted` "* | "`$ECHO $CC_quoted` "*) ;; # Blanks at the start of $base_compile will cause this to fail # if we don't check for them as well. *) for z in $available_tags; do if $GREP "^# ### BEGIN LIBTOOL TAG CONFIG: $z$" < "$progpath" > /dev/null; then # Evaluate the configuration. eval "`${SED} -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$z'$/,/^# ### END LIBTOOL TAG CONFIG: '$z'$/p' < $progpath`" CC_quoted= for arg in $CC; do # Double-quote args containing other shell metacharacters. func_quote_for_eval "$arg" CC_quoted="$CC_quoted $func_quote_for_eval_result" done case "$@ " in " $CC "* | "$CC "* | " `$ECHO $CC` "* | "`$ECHO $CC` "* | " $CC_quoted"* | "$CC_quoted "* | " `$ECHO $CC_quoted` "* | "`$ECHO $CC_quoted` "*) # The compiler in the base compile command matches # the one in the tagged configuration. # Assume this is the tagged configuration we want. tagname=$z break ;; esac fi done # If $tagname still isn't set, then no tagged configuration # was found and let the user know that the "--tag" command # line option must be used. if test -z "$tagname"; then func_echo "unable to infer tagged configuration" func_fatal_error "specify a tag with \`--tag'" # else # func_verbose "using $tagname tagged configuration" fi ;; esac fi } # func_write_libtool_object output_name pic_name nonpic_name # Create a libtool object file (analogous to a ".la" file), # but don't create it if we're doing a dry run. func_write_libtool_object () { write_libobj=${1} if test "$build_libtool_libs" = yes; then write_lobj=\'${2}\' else write_lobj=none fi if test "$build_old_libs" = yes; then write_oldobj=\'${3}\' else write_oldobj=none fi $opt_dry_run || { cat >${write_libobj}T <?"'"'"' &()|`$[]' \ && func_warning "libobj name \`$libobj' may not contain shell special characters." func_dirname_and_basename "$obj" "/" "" objname="$func_basename_result" xdir="$func_dirname_result" lobj=${xdir}$objdir/$objname test -z "$base_compile" && \ func_fatal_help "you must specify a compilation command" # Delete any leftover library objects. if test "$build_old_libs" = yes; then removelist="$obj $lobj $libobj ${libobj}T" else removelist="$lobj $libobj ${libobj}T" fi # On Cygwin there's no "real" PIC flag so we must build both object types case $host_os in cygwin* | mingw* | pw32* | os2* | cegcc*) pic_mode=default ;; esac if test "$pic_mode" = no && test "$deplibs_check_method" != pass_all; then # non-PIC code in shared libraries is not supported pic_mode=default fi # Calculate the filename of the output object if compiler does # not support -o with -c if test "$compiler_c_o" = no; then output_obj=`$ECHO "X$srcfile" | $Xsed -e 's%^.*/%%' -e 's%\.[^.]*$%%'`.${objext} lockfile="$output_obj.lock" else output_obj= need_locks=no lockfile= fi # Lock this critical section if it is needed # We use this script file to make the link, it avoids creating a new file if test "$need_locks" = yes; then until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do func_echo "Waiting for $lockfile to be removed" sleep 2 done elif test "$need_locks" = warn; then if test -f "$lockfile"; then $ECHO "\ *** ERROR, $lockfile exists and contains: `cat $lockfile 2>/dev/null` This indicates that another process is trying to use the same temporary object file, and libtool could not work around it because your compiler does not support \`-c' and \`-o' together. If you repeat this compilation, it may succeed, by chance, but you had better avoid parallel builds (make -j) in this platform, or get a better compiler." $opt_dry_run || $RM $removelist exit $EXIT_FAILURE fi removelist="$removelist $output_obj" $ECHO "$srcfile" > "$lockfile" fi $opt_dry_run || $RM $removelist removelist="$removelist $lockfile" trap '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' 1 2 15 if test -n "$fix_srcfile_path"; then eval srcfile=\"$fix_srcfile_path\" fi func_quote_for_eval "$srcfile" qsrcfile=$func_quote_for_eval_result # Only build a PIC object if we are building libtool libraries. if test "$build_libtool_libs" = yes; then # Without this assignment, base_compile gets emptied. fbsd_hideous_sh_bug=$base_compile if test "$pic_mode" != no; then command="$base_compile $qsrcfile $pic_flag" else # Don't build PIC code command="$base_compile $qsrcfile" fi func_mkdir_p "$xdir$objdir" if test -z "$output_obj"; then # Place PIC objects in $objdir command="$command -o $lobj" fi func_show_eval_locale "$command" \ 'test -n "$output_obj" && $RM $removelist; exit $EXIT_FAILURE' if test "$need_locks" = warn && test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then $ECHO "\ *** ERROR, $lockfile contains: `cat $lockfile 2>/dev/null` but it should contain: $srcfile This indicates that another process is trying to use the same temporary object file, and libtool could not work around it because your compiler does not support \`-c' and \`-o' together. If you repeat this compilation, it may succeed, by chance, but you had better avoid parallel builds (make -j) in this platform, or get a better compiler." $opt_dry_run || $RM $removelist exit $EXIT_FAILURE fi # Just move the object if needed, then go on to compile the next one if test -n "$output_obj" && test "X$output_obj" != "X$lobj"; then func_show_eval '$MV "$output_obj" "$lobj"' \ 'error=$?; $opt_dry_run || $RM $removelist; exit $error' fi # Allow error messages only from the first compilation. if test "$suppress_opt" = yes; then suppress_output=' >/dev/null 2>&1' fi fi # Only build a position-dependent object if we build old libraries. if test "$build_old_libs" = yes; then if test "$pic_mode" != yes; then # Don't build PIC code command="$base_compile $qsrcfile$pie_flag" else command="$base_compile $qsrcfile $pic_flag" fi if test "$compiler_c_o" = yes; then command="$command -o $obj" fi # Suppress compiler output if we already did a PIC compilation. command="$command$suppress_output" func_show_eval_locale "$command" \ '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' if test "$need_locks" = warn && test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then $ECHO "\ *** ERROR, $lockfile contains: `cat $lockfile 2>/dev/null` but it should contain: $srcfile This indicates that another process is trying to use the same temporary object file, and libtool could not work around it because your compiler does not support \`-c' and \`-o' together. If you repeat this compilation, it may succeed, by chance, but you had better avoid parallel builds (make -j) in this platform, or get a better compiler." $opt_dry_run || $RM $removelist exit $EXIT_FAILURE fi # Just move the object if needed if test -n "$output_obj" && test "X$output_obj" != "X$obj"; then func_show_eval '$MV "$output_obj" "$obj"' \ 'error=$?; $opt_dry_run || $RM $removelist; exit $error' fi fi $opt_dry_run || { func_write_libtool_object "$libobj" "$objdir/$objname" "$objname" # Unlock the critical section if it was locked if test "$need_locks" != no; then removelist=$lockfile $RM "$lockfile" fi } exit $EXIT_SUCCESS } $opt_help || { test "$mode" = compile && func_mode_compile ${1+"$@"} } func_mode_help () { # We need to display help for each of the modes. case $mode in "") # Generic help is extracted from the usage comments # at the start of this file. func_help ;; clean) $ECHO \ "Usage: $progname [OPTION]... --mode=clean RM [RM-OPTION]... FILE... Remove files from the build directory. RM is the name of the program to use to delete files associated with each FILE (typically \`/bin/rm'). RM-OPTIONS are options (such as \`-f') to be passed to RM. If FILE is a libtool library, object or program, all the files associated with it are deleted. Otherwise, only FILE itself is deleted using RM." ;; compile) $ECHO \ "Usage: $progname [OPTION]... --mode=compile COMPILE-COMMAND... SOURCEFILE Compile a source file into a libtool library object. This mode accepts the following additional options: -o OUTPUT-FILE set the output file name to OUTPUT-FILE -no-suppress do not suppress compiler output for multiple passes -prefer-pic try to building PIC objects only -prefer-non-pic try to building non-PIC objects only -shared do not build a \`.o' file suitable for static linking -static only build a \`.o' file suitable for static linking COMPILE-COMMAND is a command to be used in creating a \`standard' object file from the given SOURCEFILE. The output file name is determined by removing the directory component from SOURCEFILE, then substituting the C source code suffix \`.c' with the library object suffix, \`.lo'." ;; execute) $ECHO \ "Usage: $progname [OPTION]... --mode=execute COMMAND [ARGS]... Automatically set library path, then run a program. This mode accepts the following additional options: -dlopen FILE add the directory containing FILE to the library path This mode sets the library path environment variable according to \`-dlopen' flags. If any of the ARGS are libtool executable wrappers, then they are translated into their corresponding uninstalled binary, and any of their required library directories are added to the library path. Then, COMMAND is executed, with ARGS as arguments." ;; finish) $ECHO \ "Usage: $progname [OPTION]... --mode=finish [LIBDIR]... Complete the installation of libtool libraries. Each LIBDIR is a directory that contains libtool libraries. The commands that this mode executes may require superuser privileges. Use the \`--dry-run' option if you just want to see what would be executed." ;; install) $ECHO \ "Usage: $progname [OPTION]... --mode=install INSTALL-COMMAND... Install executables or libraries. INSTALL-COMMAND is the installation command. The first component should be either the \`install' or \`cp' program. The following components of INSTALL-COMMAND are treated specially: -inst-prefix PREFIX-DIR Use PREFIX-DIR as a staging area for installation The rest of the components are interpreted as arguments to that command (only BSD-compatible install options are recognized)." ;; link) $ECHO \ "Usage: $progname [OPTION]... --mode=link LINK-COMMAND... Link object files or libraries together to form another library, or to create an executable program. LINK-COMMAND is a command using the C compiler that you would use to create a program from several object files. The following components of LINK-COMMAND are treated specially: -all-static do not do any dynamic linking at all -avoid-version do not add a version suffix if possible -dlopen FILE \`-dlpreopen' FILE if it cannot be dlopened at runtime -dlpreopen FILE link in FILE and add its symbols to lt_preloaded_symbols -export-dynamic allow symbols from OUTPUT-FILE to be resolved with dlsym(3) -export-symbols SYMFILE try to export only the symbols listed in SYMFILE -export-symbols-regex REGEX try to export only the symbols matching REGEX -LLIBDIR search LIBDIR for required installed libraries -lNAME OUTPUT-FILE requires the installed library libNAME -module build a library that can dlopened -no-fast-install disable the fast-install mode -no-install link a not-installable executable -no-undefined declare that a library does not refer to external symbols -o OUTPUT-FILE create OUTPUT-FILE from the specified objects -objectlist FILE Use a list of object files found in FILE to specify objects -precious-files-regex REGEX don't remove output files matching REGEX -release RELEASE specify package release information -rpath LIBDIR the created library will eventually be installed in LIBDIR -R[ ]LIBDIR add LIBDIR to the runtime path of programs and libraries -shared only do dynamic linking of libtool libraries -shrext SUFFIX override the standard shared library file extension -static do not do any dynamic linking of uninstalled libtool libraries -static-libtool-libs do not do any dynamic linking of libtool libraries -version-info CURRENT[:REVISION[:AGE]] specify library version info [each variable defaults to 0] -weak LIBNAME declare that the target provides the LIBNAME interface All other options (arguments beginning with \`-') are ignored. Every other argument is treated as a filename. Files ending in \`.la' are treated as uninstalled libtool libraries, other files are standard or library object files. If the OUTPUT-FILE ends in \`.la', then a libtool library is created, only library objects (\`.lo' files) may be specified, and \`-rpath' is required, except when creating a convenience library. If OUTPUT-FILE ends in \`.a' or \`.lib', then a standard library is created using \`ar' and \`ranlib', or on Windows using \`lib'. If OUTPUT-FILE ends in \`.lo' or \`.${objext}', then a reloadable object file is created, otherwise an executable program is created." ;; uninstall) $ECHO \ "Usage: $progname [OPTION]... --mode=uninstall RM [RM-OPTION]... FILE... Remove libraries from an installation directory. RM is the name of the program to use to delete files associated with each FILE (typically \`/bin/rm'). RM-OPTIONS are options (such as \`-f') to be passed to RM. If FILE is a libtool library, all the files associated with it are deleted. Otherwise, only FILE itself is deleted using RM." ;; *) func_fatal_help "invalid operation mode \`$mode'" ;; esac $ECHO $ECHO "Try \`$progname --help' for more information about other modes." exit $? } # Now that we've collected a possible --mode arg, show help if necessary $opt_help && func_mode_help # func_mode_execute arg... func_mode_execute () { $opt_debug # The first argument is the command name. cmd="$nonopt" test -z "$cmd" && \ func_fatal_help "you must specify a COMMAND" # Handle -dlopen flags immediately. for file in $execute_dlfiles; do test -f "$file" \ || func_fatal_help "\`$file' is not a file" dir= case $file in *.la) # Check to see that this really is a libtool archive. func_lalib_unsafe_p "$file" \ || func_fatal_help "\`$lib' is not a valid libtool archive" # Read the libtool library. dlname= library_names= func_source "$file" # Skip this library if it cannot be dlopened. if test -z "$dlname"; then # Warn if it was a shared library. test -n "$library_names" && \ func_warning "\`$file' was not linked with \`-export-dynamic'" continue fi func_dirname "$file" "" "." dir="$func_dirname_result" if test -f "$dir/$objdir/$dlname"; then dir="$dir/$objdir" else if test ! -f "$dir/$dlname"; then func_fatal_error "cannot find \`$dlname' in \`$dir' or \`$dir/$objdir'" fi fi ;; *.lo) # Just add the directory containing the .lo file. func_dirname "$file" "" "." dir="$func_dirname_result" ;; *) func_warning "\`-dlopen' is ignored for non-libtool libraries and objects" continue ;; esac # Get the absolute pathname. absdir=`cd "$dir" && pwd` test -n "$absdir" && dir="$absdir" # Now add the directory to shlibpath_var. if eval "test -z \"\$$shlibpath_var\""; then eval "$shlibpath_var=\"\$dir\"" else eval "$shlibpath_var=\"\$dir:\$$shlibpath_var\"" fi done # This variable tells wrapper scripts just to set shlibpath_var # rather than running their programs. libtool_execute_magic="$magic" # Check if any of the arguments is a wrapper script. args= for file do case $file in -*) ;; *) # Do a test to see if this is really a libtool program. if func_ltwrapper_script_p "$file"; then func_source "$file" # Transform arg to wrapped name. file="$progdir/$program" elif func_ltwrapper_executable_p "$file"; then func_ltwrapper_scriptname "$file" func_source "$func_ltwrapper_scriptname_result" # Transform arg to wrapped name. file="$progdir/$program" fi ;; esac # Quote arguments (to preserve shell metacharacters). func_quote_for_eval "$file" args="$args $func_quote_for_eval_result" done if test "X$opt_dry_run" = Xfalse; then if test -n "$shlibpath_var"; then # Export the shlibpath_var. eval "export $shlibpath_var" fi # Restore saved environment variables for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES do eval "if test \"\${save_$lt_var+set}\" = set; then $lt_var=\$save_$lt_var; export $lt_var else $lt_unset $lt_var fi" done # Now prepare to actually exec the command. exec_cmd="\$cmd$args" else # Display what would be done. if test -n "$shlibpath_var"; then eval "\$ECHO \"\$shlibpath_var=\$$shlibpath_var\"" $ECHO "export $shlibpath_var" fi $ECHO "$cmd$args" exit $EXIT_SUCCESS fi } test "$mode" = execute && func_mode_execute ${1+"$@"} # func_mode_finish arg... func_mode_finish () { $opt_debug libdirs="$nonopt" admincmds= if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then for dir do libdirs="$libdirs $dir" done for libdir in $libdirs; do if test -n "$finish_cmds"; then # Do each command in the finish commands. func_execute_cmds "$finish_cmds" 'admincmds="$admincmds '"$cmd"'"' fi if test -n "$finish_eval"; then # Do the single finish_eval. eval cmds=\"$finish_eval\" $opt_dry_run || eval "$cmds" || admincmds="$admincmds $cmds" fi done fi # Exit here if they wanted silent mode. $opt_silent && exit $EXIT_SUCCESS $ECHO "X----------------------------------------------------------------------" | $Xsed $ECHO "Libraries have been installed in:" for libdir in $libdirs; do $ECHO " $libdir" done $ECHO $ECHO "If you ever happen to want to link against installed libraries" $ECHO "in a given directory, LIBDIR, you must either use libtool, and" $ECHO "specify the full pathname of the library, or use the \`-LLIBDIR'" $ECHO "flag during linking and do at least one of the following:" if test -n "$shlibpath_var"; then $ECHO " - add LIBDIR to the \`$shlibpath_var' environment variable" $ECHO " during execution" fi if test -n "$runpath_var"; then $ECHO " - add LIBDIR to the \`$runpath_var' environment variable" $ECHO " during linking" fi if test -n "$hardcode_libdir_flag_spec"; then libdir=LIBDIR eval flag=\"$hardcode_libdir_flag_spec\" $ECHO " - use the \`$flag' linker flag" fi if test -n "$admincmds"; then $ECHO " - have your system administrator run these commands:$admincmds" fi if test -f /etc/ld.so.conf; then $ECHO " - have your system administrator add LIBDIR to \`/etc/ld.so.conf'" fi $ECHO $ECHO "See any operating system documentation about shared libraries for" case $host in solaris2.[6789]|solaris2.1[0-9]) $ECHO "more information, such as the ld(1), crle(1) and ld.so(8) manual" $ECHO "pages." ;; *) $ECHO "more information, such as the ld(1) and ld.so(8) manual pages." ;; esac $ECHO "X----------------------------------------------------------------------" | $Xsed exit $EXIT_SUCCESS } test "$mode" = finish && func_mode_finish ${1+"$@"} # func_mode_install arg... func_mode_install () { $opt_debug # There may be an optional sh(1) argument at the beginning of # install_prog (especially on Windows NT). if test "$nonopt" = "$SHELL" || test "$nonopt" = /bin/sh || # Allow the use of GNU shtool's install command. $ECHO "X$nonopt" | $GREP shtool >/dev/null; then # Aesthetically quote it. func_quote_for_eval "$nonopt" install_prog="$func_quote_for_eval_result " arg=$1 shift else install_prog= arg=$nonopt fi # The real first argument should be the name of the installation program. # Aesthetically quote it. func_quote_for_eval "$arg" install_prog="$install_prog$func_quote_for_eval_result" # We need to accept at least all the BSD install flags. dest= files= opts= prev= install_type= isdir=no stripme= for arg do if test -n "$dest"; then files="$files $dest" dest=$arg continue fi case $arg in -d) isdir=yes ;; -f) case " $install_prog " in *[\\\ /]cp\ *) ;; *) prev=$arg ;; esac ;; -g | -m | -o) prev=$arg ;; -s) stripme=" -s" continue ;; -*) ;; *) # If the previous option needed an argument, then skip it. if test -n "$prev"; then prev= else dest=$arg continue fi ;; esac # Aesthetically quote the argument. func_quote_for_eval "$arg" install_prog="$install_prog $func_quote_for_eval_result" done test -z "$install_prog" && \ func_fatal_help "you must specify an install program" test -n "$prev" && \ func_fatal_help "the \`$prev' option requires an argument" if test -z "$files"; then if test -z "$dest"; then func_fatal_help "no file or destination specified" else func_fatal_help "you must specify a destination" fi fi # Strip any trailing slash from the destination. func_stripname '' '/' "$dest" dest=$func_stripname_result # Check to see that the destination is a directory. test -d "$dest" && isdir=yes if test "$isdir" = yes; then destdir="$dest" destname= else func_dirname_and_basename "$dest" "" "." destdir="$func_dirname_result" destname="$func_basename_result" # Not a directory, so check to see that there is only one file specified. set dummy $files; shift test "$#" -gt 1 && \ func_fatal_help "\`$dest' is not a directory" fi case $destdir in [\\/]* | [A-Za-z]:[\\/]*) ;; *) for file in $files; do case $file in *.lo) ;; *) func_fatal_help "\`$destdir' must be an absolute directory name" ;; esac done ;; esac # This variable tells wrapper scripts just to set variables rather # than running their programs. libtool_install_magic="$magic" staticlibs= future_libdirs= current_libdirs= for file in $files; do # Do each installation. case $file in *.$libext) # Do the static libraries later. staticlibs="$staticlibs $file" ;; *.la) # Check to see that this really is a libtool archive. func_lalib_unsafe_p "$file" \ || func_fatal_help "\`$file' is not a valid libtool archive" library_names= old_library= relink_command= func_source "$file" # Add the libdir to current_libdirs if it is the destination. if test "X$destdir" = "X$libdir"; then case "$current_libdirs " in *" $libdir "*) ;; *) current_libdirs="$current_libdirs $libdir" ;; esac else # Note the libdir as a future libdir. case "$future_libdirs " in *" $libdir "*) ;; *) future_libdirs="$future_libdirs $libdir" ;; esac fi func_dirname "$file" "/" "" dir="$func_dirname_result" dir="$dir$objdir" if test -n "$relink_command"; then # Determine the prefix the user has applied to our future dir. inst_prefix_dir=`$ECHO "X$destdir" | $Xsed -e "s%$libdir\$%%"` # Don't allow the user to place us outside of our expected # location b/c this prevents finding dependent libraries that # are installed to the same prefix. # At present, this check doesn't affect windows .dll's that # are installed into $libdir/../bin (currently, that works fine) # but it's something to keep an eye on. test "$inst_prefix_dir" = "$destdir" && \ func_fatal_error "error: cannot install \`$file' to a directory not ending in $libdir" if test -n "$inst_prefix_dir"; then # Stick the inst_prefix_dir data into the link command. relink_command=`$ECHO "X$relink_command" | $Xsed -e "s%@inst_prefix_dir@%-inst-prefix-dir $inst_prefix_dir%"` else relink_command=`$ECHO "X$relink_command" | $Xsed -e "s%@inst_prefix_dir@%%"` fi func_warning "relinking \`$file'" func_show_eval "$relink_command" \ 'func_fatal_error "error: relink \`$file'\'' with the above command before installing it"' fi # See the names of the shared library. set dummy $library_names; shift if test -n "$1"; then realname="$1" shift srcname="$realname" test -n "$relink_command" && srcname="$realname"T # Install the shared library and build the symlinks. func_show_eval "$install_prog $dir/$srcname $destdir/$realname" \ 'exit $?' tstripme="$stripme" case $host_os in cygwin* | mingw* | pw32* | cegcc*) case $realname in *.dll.a) tstripme="" ;; esac ;; esac if test -n "$tstripme" && test -n "$striplib"; then func_show_eval "$striplib $destdir/$realname" 'exit $?' fi if test "$#" -gt 0; then # Delete the old symlinks, and create new ones. # Try `ln -sf' first, because the `ln' binary might depend on # the symlink we replace! Solaris /bin/ln does not understand -f, # so we also need to try rm && ln -s. for linkname do test "$linkname" != "$realname" \ && func_show_eval "(cd $destdir && { $LN_S -f $realname $linkname || { $RM $linkname && $LN_S $realname $linkname; }; })" done fi # Do each command in the postinstall commands. lib="$destdir/$realname" func_execute_cmds "$postinstall_cmds" 'exit $?' fi # Install the pseudo-library for information purposes. func_basename "$file" name="$func_basename_result" instname="$dir/$name"i func_show_eval "$install_prog $instname $destdir/$name" 'exit $?' # Maybe install the static library, too. test -n "$old_library" && staticlibs="$staticlibs $dir/$old_library" ;; *.lo) # Install (i.e. copy) a libtool object. # Figure out destination file name, if it wasn't already specified. if test -n "$destname"; then destfile="$destdir/$destname" else func_basename "$file" destfile="$func_basename_result" destfile="$destdir/$destfile" fi # Deduce the name of the destination old-style object file. case $destfile in *.lo) func_lo2o "$destfile" staticdest=$func_lo2o_result ;; *.$objext) staticdest="$destfile" destfile= ;; *) func_fatal_help "cannot copy a libtool object to \`$destfile'" ;; esac # Install the libtool object if requested. test -n "$destfile" && \ func_show_eval "$install_prog $file $destfile" 'exit $?' # Install the old object if enabled. if test "$build_old_libs" = yes; then # Deduce the name of the old-style object file. func_lo2o "$file" staticobj=$func_lo2o_result func_show_eval "$install_prog \$staticobj \$staticdest" 'exit $?' fi exit $EXIT_SUCCESS ;; *) # Figure out destination file name, if it wasn't already specified. if test -n "$destname"; then destfile="$destdir/$destname" else func_basename "$file" destfile="$func_basename_result" destfile="$destdir/$destfile" fi # If the file is missing, and there is a .exe on the end, strip it # because it is most likely a libtool script we actually want to # install stripped_ext="" case $file in *.exe) if test ! -f "$file"; then func_stripname '' '.exe' "$file" file=$func_stripname_result stripped_ext=".exe" fi ;; esac # Do a test to see if this is really a libtool program. case $host in *cygwin* | *mingw*) if func_ltwrapper_executable_p "$file"; then func_ltwrapper_scriptname "$file" wrapper=$func_ltwrapper_scriptname_result else func_stripname '' '.exe' "$file" wrapper=$func_stripname_result fi ;; *) wrapper=$file ;; esac if func_ltwrapper_script_p "$wrapper"; then notinst_deplibs= relink_command= func_source "$wrapper" # Check the variables that should have been set. test -z "$generated_by_libtool_version" && \ func_fatal_error "invalid libtool wrapper script \`$wrapper'" finalize=yes for lib in $notinst_deplibs; do # Check to see that each library is installed. libdir= if test -f "$lib"; then func_source "$lib" fi libfile="$libdir/"`$ECHO "X$lib" | $Xsed -e 's%^.*/%%g'` ### testsuite: skip nested quoting test if test -n "$libdir" && test ! -f "$libfile"; then func_warning "\`$lib' has not been installed in \`$libdir'" finalize=no fi done relink_command= func_source "$wrapper" outputname= if test "$fast_install" = no && test -n "$relink_command"; then $opt_dry_run || { if test "$finalize" = yes; then tmpdir=`func_mktempdir` func_basename "$file$stripped_ext" file="$func_basename_result" outputname="$tmpdir/$file" # Replace the output file specification. relink_command=`$ECHO "X$relink_command" | $Xsed -e 's%@OUTPUT@%'"$outputname"'%g'` $opt_silent || { func_quote_for_expand "$relink_command" eval "func_echo $func_quote_for_expand_result" } if eval "$relink_command"; then : else func_error "error: relink \`$file' with the above command before installing it" $opt_dry_run || ${RM}r "$tmpdir" continue fi file="$outputname" else func_warning "cannot relink \`$file'" fi } else # Install the binary that we compiled earlier. file=`$ECHO "X$file$stripped_ext" | $Xsed -e "s%\([^/]*\)$%$objdir/\1%"` fi fi # remove .exe since cygwin /usr/bin/install will append another # one anyway case $install_prog,$host in */usr/bin/install*,*cygwin*) case $file:$destfile in *.exe:*.exe) # this is ok ;; *.exe:*) destfile=$destfile.exe ;; *:*.exe) func_stripname '' '.exe' "$destfile" destfile=$func_stripname_result ;; esac ;; esac func_show_eval "$install_prog\$stripme \$file \$destfile" 'exit $?' $opt_dry_run || if test -n "$outputname"; then ${RM}r "$tmpdir" fi ;; esac done for file in $staticlibs; do func_basename "$file" name="$func_basename_result" # Set up the ranlib parameters. oldlib="$destdir/$name" func_show_eval "$install_prog \$file \$oldlib" 'exit $?' if test -n "$stripme" && test -n "$old_striplib"; then func_show_eval "$old_striplib $oldlib" 'exit $?' fi # Do each command in the postinstall commands. func_execute_cmds "$old_postinstall_cmds" 'exit $?' done test -n "$future_libdirs" && \ func_warning "remember to run \`$progname --finish$future_libdirs'" if test -n "$current_libdirs"; then # Maybe just do a dry run. $opt_dry_run && current_libdirs=" -n$current_libdirs" exec_cmd='$SHELL $progpath $preserve_args --finish$current_libdirs' else exit $EXIT_SUCCESS fi } test "$mode" = install && func_mode_install ${1+"$@"} # func_generate_dlsyms outputname originator pic_p # Extract symbols from dlprefiles and create ${outputname}S.o with # a dlpreopen symbol table. func_generate_dlsyms () { $opt_debug my_outputname="$1" my_originator="$2" my_pic_p="${3-no}" my_prefix=`$ECHO "$my_originator" | sed 's%[^a-zA-Z0-9]%_%g'` my_dlsyms= if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then if test -n "$NM" && test -n "$global_symbol_pipe"; then my_dlsyms="${my_outputname}S.c" else func_error "not configured to extract global symbols from dlpreopened files" fi fi if test -n "$my_dlsyms"; then case $my_dlsyms in "") ;; *.c) # Discover the nlist of each of the dlfiles. nlist="$output_objdir/${my_outputname}.nm" func_show_eval "$RM $nlist ${nlist}S ${nlist}T" # Parse the name list into a source file. func_verbose "creating $output_objdir/$my_dlsyms" $opt_dry_run || $ECHO > "$output_objdir/$my_dlsyms" "\ /* $my_dlsyms - symbol resolution table for \`$my_outputname' dlsym emulation. */ /* Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION */ #ifdef __cplusplus extern \"C\" { #endif /* External symbol declarations for the compiler. */\ " if test "$dlself" = yes; then func_verbose "generating symbol list for \`$output'" $opt_dry_run || echo ': @PROGRAM@ ' > "$nlist" # Add our own program objects to the symbol list. progfiles=`$ECHO "X$objs$old_deplibs" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP` for progfile in $progfiles; do func_verbose "extracting global C symbols from \`$progfile'" $opt_dry_run || eval "$NM $progfile | $global_symbol_pipe >> '$nlist'" done if test -n "$exclude_expsyms"; then $opt_dry_run || { eval '$EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T' eval '$MV "$nlist"T "$nlist"' } fi if test -n "$export_symbols_regex"; then $opt_dry_run || { eval '$EGREP -e "$export_symbols_regex" "$nlist" > "$nlist"T' eval '$MV "$nlist"T "$nlist"' } fi # Prepare the list of exported symbols if test -z "$export_symbols"; then export_symbols="$output_objdir/$outputname.exp" $opt_dry_run || { $RM $export_symbols eval "${SED} -n -e '/^: @PROGRAM@ $/d' -e 's/^.* \(.*\)$/\1/p' "'< "$nlist" > "$export_symbols"' case $host in *cygwin* | *mingw* | *cegcc* ) eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' eval 'cat "$export_symbols" >> "$output_objdir/$outputname.def"' ;; esac } else $opt_dry_run || { eval "${SED} -e 's/\([].[*^$]\)/\\\\\1/g' -e 's/^/ /' -e 's/$/$/'"' < "$export_symbols" > "$output_objdir/$outputname.exp"' eval '$GREP -f "$output_objdir/$outputname.exp" < "$nlist" > "$nlist"T' eval '$MV "$nlist"T "$nlist"' case $host in *cygwin | *mingw* | *cegcc* ) eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' eval 'cat "$nlist" >> "$output_objdir/$outputname.def"' ;; esac } fi fi for dlprefile in $dlprefiles; do func_verbose "extracting global C symbols from \`$dlprefile'" func_basename "$dlprefile" name="$func_basename_result" $opt_dry_run || { eval '$ECHO ": $name " >> "$nlist"' eval "$NM $dlprefile 2>/dev/null | $global_symbol_pipe >> '$nlist'" } done $opt_dry_run || { # Make sure we have at least an empty file. test -f "$nlist" || : > "$nlist" if test -n "$exclude_expsyms"; then $EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T $MV "$nlist"T "$nlist" fi # Try sorting and uniquifying the output. if $GREP -v "^: " < "$nlist" | if sort -k 3 /dev/null 2>&1; then sort -k 3 else sort +2 fi | uniq > "$nlist"S; then : else $GREP -v "^: " < "$nlist" > "$nlist"S fi if test -f "$nlist"S; then eval "$global_symbol_to_cdecl"' < "$nlist"S >> "$output_objdir/$my_dlsyms"' else $ECHO '/* NONE */' >> "$output_objdir/$my_dlsyms" fi $ECHO >> "$output_objdir/$my_dlsyms" "\ /* The mapping between symbol names and symbols. */ typedef struct { const char *name; void *address; } lt_dlsymlist; " case $host in *cygwin* | *mingw* | *cegcc* ) $ECHO >> "$output_objdir/$my_dlsyms" "\ /* DATA imports from DLLs on WIN32 con't be const, because runtime relocations are performed -- see ld's documentation on pseudo-relocs. */" lt_dlsym_const= ;; *osf5*) echo >> "$output_objdir/$my_dlsyms" "\ /* This system does not cope well with relocations in const data */" lt_dlsym_const= ;; *) lt_dlsym_const=const ;; esac $ECHO >> "$output_objdir/$my_dlsyms" "\ extern $lt_dlsym_const lt_dlsymlist lt_${my_prefix}_LTX_preloaded_symbols[]; $lt_dlsym_const lt_dlsymlist lt_${my_prefix}_LTX_preloaded_symbols[] = {\ { \"$my_originator\", (void *) 0 }," case $need_lib_prefix in no) eval "$global_symbol_to_c_name_address" < "$nlist" >> "$output_objdir/$my_dlsyms" ;; *) eval "$global_symbol_to_c_name_address_lib_prefix" < "$nlist" >> "$output_objdir/$my_dlsyms" ;; esac $ECHO >> "$output_objdir/$my_dlsyms" "\ {0, (void *) 0} }; /* This works around a problem in FreeBSD linker */ #ifdef FREEBSD_WORKAROUND static const void *lt_preloaded_setup() { return lt_${my_prefix}_LTX_preloaded_symbols; } #endif #ifdef __cplusplus } #endif\ " } # !$opt_dry_run pic_flag_for_symtable= case "$compile_command " in *" -static "*) ;; *) case $host in # compiling the symbol table file with pic_flag works around # a FreeBSD bug that causes programs to crash when -lm is # linked before any other PIC object. But we must not use # pic_flag when linking with -static. The problem exists in # FreeBSD 2.2.6 and is fixed in FreeBSD 3.1. *-*-freebsd2*|*-*-freebsd3.0*|*-*-freebsdelf3.0*) pic_flag_for_symtable=" $pic_flag -DFREEBSD_WORKAROUND" ;; *-*-hpux*) pic_flag_for_symtable=" $pic_flag" ;; *) if test "X$my_pic_p" != Xno; then pic_flag_for_symtable=" $pic_flag" fi ;; esac ;; esac symtab_cflags= for arg in $LTCFLAGS; do case $arg in -pie | -fpie | -fPIE) ;; *) symtab_cflags="$symtab_cflags $arg" ;; esac done # Now compile the dynamic symbol file. func_show_eval '(cd $output_objdir && $LTCC$symtab_cflags -c$no_builtin_flag$pic_flag_for_symtable "$my_dlsyms")' 'exit $?' # Clean up the generated files. func_show_eval '$RM "$output_objdir/$my_dlsyms" "$nlist" "${nlist}S" "${nlist}T"' # Transform the symbol file into the correct name. symfileobj="$output_objdir/${my_outputname}S.$objext" case $host in *cygwin* | *mingw* | *cegcc* ) if test -f "$output_objdir/$my_outputname.def"; then compile_command=`$ECHO "X$compile_command" | $Xsed -e "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"` finalize_command=`$ECHO "X$finalize_command" | $Xsed -e "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"` else compile_command=`$ECHO "X$compile_command" | $Xsed -e "s%@SYMFILE@%$symfileobj%"` finalize_command=`$ECHO "X$finalize_command" | $Xsed -e "s%@SYMFILE@%$symfileobj%"` fi ;; *) compile_command=`$ECHO "X$compile_command" | $Xsed -e "s%@SYMFILE@%$symfileobj%"` finalize_command=`$ECHO "X$finalize_command" | $Xsed -e "s%@SYMFILE@%$symfileobj%"` ;; esac ;; *) func_fatal_error "unknown suffix for \`$my_dlsyms'" ;; esac else # We keep going just in case the user didn't refer to # lt_preloaded_symbols. The linker will fail if global_symbol_pipe # really was required. # Nullify the symbol file. compile_command=`$ECHO "X$compile_command" | $Xsed -e "s% @SYMFILE@%%"` finalize_command=`$ECHO "X$finalize_command" | $Xsed -e "s% @SYMFILE@%%"` fi } # func_win32_libid arg # return the library type of file 'arg' # # Need a lot of goo to handle *both* DLLs and import libs # Has to be a shell function in order to 'eat' the argument # that is supplied when $file_magic_command is called. func_win32_libid () { $opt_debug win32_libid_type="unknown" win32_fileres=`file -L $1 2>/dev/null` case $win32_fileres in *ar\ archive\ import\ library*) # definitely import win32_libid_type="x86 archive import" ;; *ar\ archive*) # could be an import, or static if eval $OBJDUMP -f $1 | $SED -e '10q' 2>/dev/null | $EGREP 'file format pe-i386(.*architecture: i386)?' >/dev/null ; then win32_nmres=`eval $NM -f posix -A $1 | $SED -n -e ' 1,100{ / I /{ s,.*,import, p q } }'` case $win32_nmres in import*) win32_libid_type="x86 archive import";; *) win32_libid_type="x86 archive static";; esac fi ;; *DLL*) win32_libid_type="x86 DLL" ;; *executable*) # but shell scripts are "executable" too... case $win32_fileres in *MS\ Windows\ PE\ Intel*) win32_libid_type="x86 DLL" ;; esac ;; esac $ECHO "$win32_libid_type" } # func_extract_an_archive dir oldlib func_extract_an_archive () { $opt_debug f_ex_an_ar_dir="$1"; shift f_ex_an_ar_oldlib="$1" func_show_eval "(cd \$f_ex_an_ar_dir && $AR x \"\$f_ex_an_ar_oldlib\")" 'exit $?' if ($AR t "$f_ex_an_ar_oldlib" | sort | sort -uc >/dev/null 2>&1); then : else func_fatal_error "object name conflicts in archive: $f_ex_an_ar_dir/$f_ex_an_ar_oldlib" fi } # func_extract_archives gentop oldlib ... func_extract_archives () { $opt_debug my_gentop="$1"; shift my_oldlibs=${1+"$@"} my_oldobjs="" my_xlib="" my_xabs="" my_xdir="" for my_xlib in $my_oldlibs; do # Extract the objects. case $my_xlib in [\\/]* | [A-Za-z]:[\\/]*) my_xabs="$my_xlib" ;; *) my_xabs=`pwd`"/$my_xlib" ;; esac func_basename "$my_xlib" my_xlib="$func_basename_result" my_xlib_u=$my_xlib while :; do case " $extracted_archives " in *" $my_xlib_u "*) func_arith $extracted_serial + 1 extracted_serial=$func_arith_result my_xlib_u=lt$extracted_serial-$my_xlib ;; *) break ;; esac done extracted_archives="$extracted_archives $my_xlib_u" my_xdir="$my_gentop/$my_xlib_u" func_mkdir_p "$my_xdir" case $host in *-darwin*) func_verbose "Extracting $my_xabs" # Do not bother doing anything if just a dry run $opt_dry_run || { darwin_orig_dir=`pwd` cd $my_xdir || exit $? darwin_archive=$my_xabs darwin_curdir=`pwd` darwin_base_archive=`basename "$darwin_archive"` darwin_arches=`$LIPO -info "$darwin_archive" 2>/dev/null | $GREP Architectures 2>/dev/null || true` if test -n "$darwin_arches"; then darwin_arches=`$ECHO "$darwin_arches" | $SED -e 's/.*are://'` darwin_arch= func_verbose "$darwin_base_archive has multiple architectures $darwin_arches" for darwin_arch in $darwin_arches ; do func_mkdir_p "unfat-$$/${darwin_base_archive}-${darwin_arch}" $LIPO -thin $darwin_arch -output "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}" "${darwin_archive}" cd "unfat-$$/${darwin_base_archive}-${darwin_arch}" func_extract_an_archive "`pwd`" "${darwin_base_archive}" cd "$darwin_curdir" $RM "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}" done # $darwin_arches ## Okay now we've a bunch of thin objects, gotta fatten them up :) darwin_filelist=`find unfat-$$ -type f -name \*.o -print -o -name \*.lo -print | $SED -e "$basename" | sort -u` darwin_file= darwin_files= for darwin_file in $darwin_filelist; do darwin_files=`find unfat-$$ -name $darwin_file -print | $NL2SP` $LIPO -create -output "$darwin_file" $darwin_files done # $darwin_filelist $RM -rf unfat-$$ cd "$darwin_orig_dir" else cd $darwin_orig_dir func_extract_an_archive "$my_xdir" "$my_xabs" fi # $darwin_arches } # !$opt_dry_run ;; *) func_extract_an_archive "$my_xdir" "$my_xabs" ;; esac my_oldobjs="$my_oldobjs "`find $my_xdir -name \*.$objext -print -o -name \*.lo -print | $NL2SP` done func_extract_archives_result="$my_oldobjs" } # func_emit_wrapper_part1 [arg=no] # # Emit the first part of a libtool wrapper script on stdout. # For more information, see the description associated with # func_emit_wrapper(), below. func_emit_wrapper_part1 () { func_emit_wrapper_part1_arg1=no if test -n "$1" ; then func_emit_wrapper_part1_arg1=$1 fi $ECHO "\ #! $SHELL # $output - temporary wrapper script for $objdir/$outputname # Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION # # The $output program cannot be directly executed until all the libtool # libraries that it depends on are installed. # # This wrapper script should never be moved out of the build directory. # If it is, it will not operate correctly. # Sed substitution that helps us do robust quoting. It backslashifies # metacharacters that are still active within double-quoted strings. Xsed='${SED} -e 1s/^X//' sed_quote_subst='$sed_quote_subst' # Be Bourne compatible if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then emulate sh NULLCMD=: # Zsh 3.x and 4.x performs word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST else case \`(set -o) 2>/dev/null\` in *posix*) set -o posix;; esac fi BIN_SH=xpg4; export BIN_SH # for Tru64 DUALCASE=1; export DUALCASE # for MKS sh # The HP-UX ksh and POSIX shell print the target directory to stdout # if CDPATH is set. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH relink_command=\"$relink_command\" # This environment variable determines our operation mode. if test \"\$libtool_install_magic\" = \"$magic\"; then # install mode needs the following variables: generated_by_libtool_version='$macro_version' notinst_deplibs='$notinst_deplibs' else # When we are sourced in execute mode, \$file and \$ECHO are already set. if test \"\$libtool_execute_magic\" != \"$magic\"; then ECHO=\"$qecho\" file=\"\$0\" # Make sure echo works. if test \"X\$1\" = X--no-reexec; then # Discard the --no-reexec flag, and continue. shift elif test \"X\`{ \$ECHO '\t'; } 2>/dev/null\`\" = 'X\t'; then # Yippee, \$ECHO works! : else # Restart under the correct shell, and then maybe \$ECHO will work. exec $SHELL \"\$0\" --no-reexec \${1+\"\$@\"} fi fi\ " $ECHO "\ # Find the directory that this script lives in. thisdir=\`\$ECHO \"X\$file\" | \$Xsed -e 's%/[^/]*$%%'\` test \"x\$thisdir\" = \"x\$file\" && thisdir=. # Follow symbolic links until we get to the real thisdir. file=\`ls -ld \"\$file\" | ${SED} -n 's/.*-> //p'\` while test -n \"\$file\"; do destdir=\`\$ECHO \"X\$file\" | \$Xsed -e 's%/[^/]*\$%%'\` # If there was a directory component, then change thisdir. if test \"x\$destdir\" != \"x\$file\"; then case \"\$destdir\" in [\\\\/]* | [A-Za-z]:[\\\\/]*) thisdir=\"\$destdir\" ;; *) thisdir=\"\$thisdir/\$destdir\" ;; esac fi file=\`\$ECHO \"X\$file\" | \$Xsed -e 's%^.*/%%'\` file=\`ls -ld \"\$thisdir/\$file\" | ${SED} -n 's/.*-> //p'\` done " } # end: func_emit_wrapper_part1 # func_emit_wrapper_part2 [arg=no] # # Emit the second part of a libtool wrapper script on stdout. # For more information, see the description associated with # func_emit_wrapper(), below. func_emit_wrapper_part2 () { func_emit_wrapper_part2_arg1=no if test -n "$1" ; then func_emit_wrapper_part2_arg1=$1 fi $ECHO "\ # Usually 'no', except on cygwin/mingw when embedded into # the cwrapper. WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=$func_emit_wrapper_part2_arg1 if test \"\$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR\" = \"yes\"; then # special case for '.' if test \"\$thisdir\" = \".\"; then thisdir=\`pwd\` fi # remove .libs from thisdir case \"\$thisdir\" in *[\\\\/]$objdir ) thisdir=\`\$ECHO \"X\$thisdir\" | \$Xsed -e 's%[\\\\/][^\\\\/]*$%%'\` ;; $objdir ) thisdir=. ;; esac fi # Try to get the absolute directory name. absdir=\`cd \"\$thisdir\" && pwd\` test -n \"\$absdir\" && thisdir=\"\$absdir\" " if test "$fast_install" = yes; then $ECHO "\ program=lt-'$outputname'$exeext progdir=\"\$thisdir/$objdir\" if test ! -f \"\$progdir/\$program\" || { file=\`ls -1dt \"\$progdir/\$program\" \"\$progdir/../\$program\" 2>/dev/null | ${SED} 1q\`; \\ test \"X\$file\" != \"X\$progdir/\$program\"; }; then file=\"\$\$-\$program\" if test ! -d \"\$progdir\"; then $MKDIR \"\$progdir\" else $RM \"\$progdir/\$file\" fi" $ECHO "\ # relink executable if necessary if test -n \"\$relink_command\"; then if relink_command_output=\`eval \$relink_command 2>&1\`; then : else $ECHO \"\$relink_command_output\" >&2 $RM \"\$progdir/\$file\" exit 1 fi fi $MV \"\$progdir/\$file\" \"\$progdir/\$program\" 2>/dev/null || { $RM \"\$progdir/\$program\"; $MV \"\$progdir/\$file\" \"\$progdir/\$program\"; } $RM \"\$progdir/\$file\" fi" else $ECHO "\ program='$outputname' progdir=\"\$thisdir/$objdir\" " fi $ECHO "\ if test -f \"\$progdir/\$program\"; then" # Export our shlibpath_var if we have one. if test "$shlibpath_overrides_runpath" = yes && test -n "$shlibpath_var" && test -n "$temp_rpath"; then $ECHO "\ # Add our own library path to $shlibpath_var $shlibpath_var=\"$temp_rpath\$$shlibpath_var\" # Some systems cannot cope with colon-terminated $shlibpath_var # The second colon is a workaround for a bug in BeOS R4 sed $shlibpath_var=\`\$ECHO \"X\$$shlibpath_var\" | \$Xsed -e 's/::*\$//'\` export $shlibpath_var " fi # fixup the dll searchpath if we need to. if test -n "$dllsearchpath"; then $ECHO "\ # Add the dll search path components to the executable PATH PATH=$dllsearchpath:\$PATH " fi $ECHO "\ if test \"\$libtool_execute_magic\" != \"$magic\"; then # Run the actual program with our arguments. " case $host in # Backslashes separate directories on plain windows *-*-mingw | *-*-os2* | *-cegcc*) $ECHO "\ exec \"\$progdir\\\\\$program\" \${1+\"\$@\"} " ;; *) $ECHO "\ exec \"\$progdir/\$program\" \${1+\"\$@\"} " ;; esac $ECHO "\ \$ECHO \"\$0: cannot exec \$program \$*\" 1>&2 exit 1 fi else # The program doesn't exist. \$ECHO \"\$0: error: \\\`\$progdir/\$program' does not exist\" 1>&2 \$ECHO \"This script is just a wrapper for \$program.\" 1>&2 $ECHO \"See the $PACKAGE documentation for more information.\" 1>&2 exit 1 fi fi\ " } # end: func_emit_wrapper_part2 # func_emit_wrapper [arg=no] # # Emit a libtool wrapper script on stdout. # Don't directly open a file because we may want to # incorporate the script contents within a cygwin/mingw # wrapper executable. Must ONLY be called from within # func_mode_link because it depends on a number of variables # set therein. # # ARG is the value that the WRAPPER_SCRIPT_BELONGS_IN_OBJDIR # variable will take. If 'yes', then the emitted script # will assume that the directory in which it is stored is # the $objdir directory. This is a cygwin/mingw-specific # behavior. func_emit_wrapper () { func_emit_wrapper_arg1=no if test -n "$1" ; then func_emit_wrapper_arg1=$1 fi # split this up so that func_emit_cwrapperexe_src # can call each part independently. func_emit_wrapper_part1 "${func_emit_wrapper_arg1}" func_emit_wrapper_part2 "${func_emit_wrapper_arg1}" } # func_to_host_path arg # # Convert paths to host format when used with build tools. # Intended for use with "native" mingw (where libtool itself # is running under the msys shell), or in the following cross- # build environments: # $build $host # mingw (msys) mingw [e.g. native] # cygwin mingw # *nix + wine mingw # where wine is equipped with the `winepath' executable. # In the native mingw case, the (msys) shell automatically # converts paths for any non-msys applications it launches, # but that facility isn't available from inside the cwrapper. # Similar accommodations are necessary for $host mingw and # $build cygwin. Calling this function does no harm for other # $host/$build combinations not listed above. # # ARG is the path (on $build) that should be converted to # the proper representation for $host. The result is stored # in $func_to_host_path_result. func_to_host_path () { func_to_host_path_result="$1" if test -n "$1" ; then case $host in *mingw* ) lt_sed_naive_backslashify='s|\\\\*|\\|g;s|/|\\|g;s|\\|\\\\|g' case $build in *mingw* ) # actually, msys # awkward: cmd appends spaces to result lt_sed_strip_trailing_spaces="s/[ ]*\$//" func_to_host_path_tmp1=`( cmd //c echo "$1" |\ $SED -e "$lt_sed_strip_trailing_spaces" ) 2>/dev/null || echo ""` func_to_host_path_result=`echo "$func_to_host_path_tmp1" |\ $SED -e "$lt_sed_naive_backslashify"` ;; *cygwin* ) func_to_host_path_tmp1=`cygpath -w "$1"` func_to_host_path_result=`echo "$func_to_host_path_tmp1" |\ $SED -e "$lt_sed_naive_backslashify"` ;; * ) # Unfortunately, winepath does not exit with a non-zero # error code, so we are forced to check the contents of # stdout. On the other hand, if the command is not # found, the shell will set an exit code of 127 and print # *an error message* to stdout. So we must check for both # error code of zero AND non-empty stdout, which explains # the odd construction: func_to_host_path_tmp1=`winepath -w "$1" 2>/dev/null` if test "$?" -eq 0 && test -n "${func_to_host_path_tmp1}"; then func_to_host_path_result=`echo "$func_to_host_path_tmp1" |\ $SED -e "$lt_sed_naive_backslashify"` else # Allow warning below. func_to_host_path_result="" fi ;; esac if test -z "$func_to_host_path_result" ; then func_error "Could not determine host path corresponding to" func_error " '$1'" func_error "Continuing, but uninstalled executables may not work." # Fallback: func_to_host_path_result="$1" fi ;; esac fi } # end: func_to_host_path # func_to_host_pathlist arg # # Convert pathlists to host format when used with build tools. # See func_to_host_path(), above. This function supports the # following $build/$host combinations (but does no harm for # combinations not listed here): # $build $host # mingw (msys) mingw [e.g. native] # cygwin mingw # *nix + wine mingw # # Path separators are also converted from $build format to # $host format. If ARG begins or ends with a path separator # character, it is preserved (but converted to $host format) # on output. # # ARG is a pathlist (on $build) that should be converted to # the proper representation on $host. The result is stored # in $func_to_host_pathlist_result. func_to_host_pathlist () { func_to_host_pathlist_result="$1" if test -n "$1" ; then case $host in *mingw* ) lt_sed_naive_backslashify='s|\\\\*|\\|g;s|/|\\|g;s|\\|\\\\|g' # Remove leading and trailing path separator characters from # ARG. msys behavior is inconsistent here, cygpath turns them # into '.;' and ';.', and winepath ignores them completely. func_to_host_pathlist_tmp2="$1" # Once set for this call, this variable should not be # reassigned. It is used in tha fallback case. func_to_host_pathlist_tmp1=`echo "$func_to_host_pathlist_tmp2" |\ $SED -e 's|^:*||' -e 's|:*$||'` case $build in *mingw* ) # Actually, msys. # Awkward: cmd appends spaces to result. lt_sed_strip_trailing_spaces="s/[ ]*\$//" func_to_host_pathlist_tmp2=`( cmd //c echo "$func_to_host_pathlist_tmp1" |\ $SED -e "$lt_sed_strip_trailing_spaces" ) 2>/dev/null || echo ""` func_to_host_pathlist_result=`echo "$func_to_host_pathlist_tmp2" |\ $SED -e "$lt_sed_naive_backslashify"` ;; *cygwin* ) func_to_host_pathlist_tmp2=`cygpath -w -p "$func_to_host_pathlist_tmp1"` func_to_host_pathlist_result=`echo "$func_to_host_pathlist_tmp2" |\ $SED -e "$lt_sed_naive_backslashify"` ;; * ) # unfortunately, winepath doesn't convert pathlists func_to_host_pathlist_result="" func_to_host_pathlist_oldIFS=$IFS IFS=: for func_to_host_pathlist_f in $func_to_host_pathlist_tmp1 ; do IFS=$func_to_host_pathlist_oldIFS if test -n "$func_to_host_pathlist_f" ; then func_to_host_path "$func_to_host_pathlist_f" if test -n "$func_to_host_path_result" ; then if test -z "$func_to_host_pathlist_result" ; then func_to_host_pathlist_result="$func_to_host_path_result" else func_to_host_pathlist_result="$func_to_host_pathlist_result;$func_to_host_path_result" fi fi fi IFS=: done IFS=$func_to_host_pathlist_oldIFS ;; esac if test -z "$func_to_host_pathlist_result" ; then func_error "Could not determine the host path(s) corresponding to" func_error " '$1'" func_error "Continuing, but uninstalled executables may not work." # Fallback. This may break if $1 contains DOS-style drive # specifications. The fix is not to complicate the expression # below, but for the user to provide a working wine installation # with winepath so that path translation in the cross-to-mingw # case works properly. lt_replace_pathsep_nix_to_dos="s|:|;|g" func_to_host_pathlist_result=`echo "$func_to_host_pathlist_tmp1" |\ $SED -e "$lt_replace_pathsep_nix_to_dos"` fi # Now, add the leading and trailing path separators back case "$1" in :* ) func_to_host_pathlist_result=";$func_to_host_pathlist_result" ;; esac case "$1" in *: ) func_to_host_pathlist_result="$func_to_host_pathlist_result;" ;; esac ;; esac fi } # end: func_to_host_pathlist # func_emit_cwrapperexe_src # emit the source code for a wrapper executable on stdout # Must ONLY be called from within func_mode_link because # it depends on a number of variable set therein. func_emit_cwrapperexe_src () { cat < #include #ifdef _MSC_VER # include # include # include # define setmode _setmode #else # include # include # ifdef __CYGWIN__ # include # define HAVE_SETENV # ifdef __STRICT_ANSI__ char *realpath (const char *, char *); int putenv (char *); int setenv (const char *, const char *, int); # endif # endif #endif #include #include #include #include #include #include #include #include #if defined(PATH_MAX) # define LT_PATHMAX PATH_MAX #elif defined(MAXPATHLEN) # define LT_PATHMAX MAXPATHLEN #else # define LT_PATHMAX 1024 #endif #ifndef S_IXOTH # define S_IXOTH 0 #endif #ifndef S_IXGRP # define S_IXGRP 0 #endif #ifdef _MSC_VER # define S_IXUSR _S_IEXEC # define stat _stat # ifndef _INTPTR_T_DEFINED # define intptr_t int # endif #endif #ifndef DIR_SEPARATOR # define DIR_SEPARATOR '/' # define PATH_SEPARATOR ':' #endif #if defined (_WIN32) || defined (__MSDOS__) || defined (__DJGPP__) || \ defined (__OS2__) # define HAVE_DOS_BASED_FILE_SYSTEM # define FOPEN_WB "wb" # ifndef DIR_SEPARATOR_2 # define DIR_SEPARATOR_2 '\\' # endif # ifndef PATH_SEPARATOR_2 # define PATH_SEPARATOR_2 ';' # endif #endif #ifndef DIR_SEPARATOR_2 # define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR) #else /* DIR_SEPARATOR_2 */ # define IS_DIR_SEPARATOR(ch) \ (((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2)) #endif /* DIR_SEPARATOR_2 */ #ifndef PATH_SEPARATOR_2 # define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR) #else /* PATH_SEPARATOR_2 */ # define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR_2) #endif /* PATH_SEPARATOR_2 */ #ifdef __CYGWIN__ # define FOPEN_WB "wb" #endif #ifndef FOPEN_WB # define FOPEN_WB "w" #endif #ifndef _O_BINARY # define _O_BINARY 0 #endif #define XMALLOC(type, num) ((type *) xmalloc ((num) * sizeof(type))) #define XFREE(stale) do { \ if (stale) { free ((void *) stale); stale = 0; } \ } while (0) #undef LTWRAPPER_DEBUGPRINTF #if defined DEBUGWRAPPER # define LTWRAPPER_DEBUGPRINTF(args) ltwrapper_debugprintf args static void ltwrapper_debugprintf (const char *fmt, ...) { va_list args; va_start (args, fmt); (void) vfprintf (stderr, fmt, args); va_end (args); } #else # define LTWRAPPER_DEBUGPRINTF(args) #endif const char *program_name = NULL; void *xmalloc (size_t num); char *xstrdup (const char *string); const char *base_name (const char *name); char *find_executable (const char *wrapper); char *chase_symlinks (const char *pathspec); int make_executable (const char *path); int check_executable (const char *path); char *strendzap (char *str, const char *pat); void lt_fatal (const char *message, ...); void lt_setenv (const char *name, const char *value); char *lt_extend_str (const char *orig_value, const char *add, int to_end); void lt_opt_process_env_set (const char *arg); void lt_opt_process_env_prepend (const char *arg); void lt_opt_process_env_append (const char *arg); int lt_split_name_value (const char *arg, char** name, char** value); void lt_update_exe_path (const char *name, const char *value); void lt_update_lib_path (const char *name, const char *value); static const char *script_text_part1 = EOF func_emit_wrapper_part1 yes | $SED -e 's/\([\\"]\)/\\\1/g' \ -e 's/^/ "/' -e 's/$/\\n"/' echo ";" cat <"))); for (i = 0; i < newargc; i++) { LTWRAPPER_DEBUGPRINTF (("(main) newargz[%d] : %s\n", i, (newargz[i] ? newargz[i] : ""))); } EOF case $host_os in mingw*) cat <<"EOF" /* execv doesn't actually work on mingw as expected on unix */ rval = _spawnv (_P_WAIT, lt_argv_zero, (const char * const *) newargz); if (rval == -1) { /* failed to start process */ LTWRAPPER_DEBUGPRINTF (("(main) failed to launch target \"%s\": errno = %d\n", lt_argv_zero, errno)); return 127; } return rval; EOF ;; *) cat <<"EOF" execv (lt_argv_zero, newargz); return rval; /* =127, but avoids unused variable warning */ EOF ;; esac cat <<"EOF" } void * xmalloc (size_t num) { void *p = (void *) malloc (num); if (!p) lt_fatal ("Memory exhausted"); return p; } char * xstrdup (const char *string) { return string ? strcpy ((char *) xmalloc (strlen (string) + 1), string) : NULL; } const char * base_name (const char *name) { const char *base; #if defined (HAVE_DOS_BASED_FILE_SYSTEM) /* Skip over the disk name in MSDOS pathnames. */ if (isalpha ((unsigned char) name[0]) && name[1] == ':') name += 2; #endif for (base = name; *name; name++) if (IS_DIR_SEPARATOR (*name)) base = name + 1; return base; } int check_executable (const char *path) { struct stat st; LTWRAPPER_DEBUGPRINTF (("(check_executable) : %s\n", path ? (*path ? path : "EMPTY!") : "NULL!")); if ((!path) || (!*path)) return 0; if ((stat (path, &st) >= 0) && (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) return 1; else return 0; } int make_executable (const char *path) { int rval = 0; struct stat st; LTWRAPPER_DEBUGPRINTF (("(make_executable) : %s\n", path ? (*path ? path : "EMPTY!") : "NULL!")); if ((!path) || (!*path)) return 0; if (stat (path, &st) >= 0) { rval = chmod (path, st.st_mode | S_IXOTH | S_IXGRP | S_IXUSR); } return rval; } /* Searches for the full path of the wrapper. Returns newly allocated full path name if found, NULL otherwise Does not chase symlinks, even on platforms that support them. */ char * find_executable (const char *wrapper) { int has_slash = 0; const char *p; const char *p_next; /* static buffer for getcwd */ char tmp[LT_PATHMAX + 1]; int tmp_len; char *concat_name; LTWRAPPER_DEBUGPRINTF (("(find_executable) : %s\n", wrapper ? (*wrapper ? wrapper : "EMPTY!") : "NULL!")); if ((wrapper == NULL) || (*wrapper == '\0')) return NULL; /* Absolute path? */ #if defined (HAVE_DOS_BASED_FILE_SYSTEM) if (isalpha ((unsigned char) wrapper[0]) && wrapper[1] == ':') { concat_name = xstrdup (wrapper); if (check_executable (concat_name)) return concat_name; XFREE (concat_name); } else { #endif if (IS_DIR_SEPARATOR (wrapper[0])) { concat_name = xstrdup (wrapper); if (check_executable (concat_name)) return concat_name; XFREE (concat_name); } #if defined (HAVE_DOS_BASED_FILE_SYSTEM) } #endif for (p = wrapper; *p; p++) if (*p == '/') { has_slash = 1; break; } if (!has_slash) { /* no slashes; search PATH */ const char *path = getenv ("PATH"); if (path != NULL) { for (p = path; *p; p = p_next) { const char *q; size_t p_len; for (q = p; *q; q++) if (IS_PATH_SEPARATOR (*q)) break; p_len = q - p; p_next = (*q == '\0' ? q : q + 1); if (p_len == 0) { /* empty path: current directory */ if (getcwd (tmp, LT_PATHMAX) == NULL) lt_fatal ("getcwd failed"); tmp_len = strlen (tmp); concat_name = XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1); memcpy (concat_name, tmp, tmp_len); concat_name[tmp_len] = '/'; strcpy (concat_name + tmp_len + 1, wrapper); } else { concat_name = XMALLOC (char, p_len + 1 + strlen (wrapper) + 1); memcpy (concat_name, p, p_len); concat_name[p_len] = '/'; strcpy (concat_name + p_len + 1, wrapper); } if (check_executable (concat_name)) return concat_name; XFREE (concat_name); } } /* not found in PATH; assume curdir */ } /* Relative path | not found in path: prepend cwd */ if (getcwd (tmp, LT_PATHMAX) == NULL) lt_fatal ("getcwd failed"); tmp_len = strlen (tmp); concat_name = XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1); memcpy (concat_name, tmp, tmp_len); concat_name[tmp_len] = '/'; strcpy (concat_name + tmp_len + 1, wrapper); if (check_executable (concat_name)) return concat_name; XFREE (concat_name); return NULL; } char * chase_symlinks (const char *pathspec) { #ifndef S_ISLNK return xstrdup (pathspec); #else char buf[LT_PATHMAX]; struct stat s; char *tmp_pathspec = xstrdup (pathspec); char *p; int has_symlinks = 0; while (strlen (tmp_pathspec) && !has_symlinks) { LTWRAPPER_DEBUGPRINTF (("checking path component for symlinks: %s\n", tmp_pathspec)); if (lstat (tmp_pathspec, &s) == 0) { if (S_ISLNK (s.st_mode) != 0) { has_symlinks = 1; break; } /* search backwards for last DIR_SEPARATOR */ p = tmp_pathspec + strlen (tmp_pathspec) - 1; while ((p > tmp_pathspec) && (!IS_DIR_SEPARATOR (*p))) p--; if ((p == tmp_pathspec) && (!IS_DIR_SEPARATOR (*p))) { /* no more DIR_SEPARATORS left */ break; } *p = '\0'; } else { char *errstr = strerror (errno); lt_fatal ("Error accessing file %s (%s)", tmp_pathspec, errstr); } } XFREE (tmp_pathspec); if (!has_symlinks) { return xstrdup (pathspec); } tmp_pathspec = realpath (pathspec, buf); if (tmp_pathspec == 0) { lt_fatal ("Could not follow symlinks for %s", pathspec); } return xstrdup (tmp_pathspec); #endif } char * strendzap (char *str, const char *pat) { size_t len, patlen; assert (str != NULL); assert (pat != NULL); len = strlen (str); patlen = strlen (pat); if (patlen <= len) { str += len - patlen; if (strcmp (str, pat) == 0) *str = '\0'; } return str; } static void lt_error_core (int exit_status, const char *mode, const char *message, va_list ap) { fprintf (stderr, "%s: %s: ", program_name, mode); vfprintf (stderr, message, ap); fprintf (stderr, ".\n"); if (exit_status >= 0) exit (exit_status); } void lt_fatal (const char *message, ...) { va_list ap; va_start (ap, message); lt_error_core (EXIT_FAILURE, "FATAL", message, ap); va_end (ap); } void lt_setenv (const char *name, const char *value) { LTWRAPPER_DEBUGPRINTF (("(lt_setenv) setting '%s' to '%s'\n", (name ? name : ""), (value ? value : ""))); { #ifdef HAVE_SETENV /* always make a copy, for consistency with !HAVE_SETENV */ char *str = xstrdup (value); setenv (name, str, 1); #else int len = strlen (name) + 1 + strlen (value) + 1; char *str = XMALLOC (char, len); sprintf (str, "%s=%s", name, value); if (putenv (str) != EXIT_SUCCESS) { XFREE (str); } #endif } } char * lt_extend_str (const char *orig_value, const char *add, int to_end) { char *new_value; if (orig_value && *orig_value) { int orig_value_len = strlen (orig_value); int add_len = strlen (add); new_value = XMALLOC (char, add_len + orig_value_len + 1); if (to_end) { strcpy (new_value, orig_value); strcpy (new_value + orig_value_len, add); } else { strcpy (new_value, add); strcpy (new_value + add_len, orig_value); } } else { new_value = xstrdup (add); } return new_value; } int lt_split_name_value (const char *arg, char** name, char** value) { const char *p; int len; if (!arg || !*arg) return 1; p = strchr (arg, (int)'='); if (!p) return 1; *value = xstrdup (++p); len = strlen (arg) - strlen (*value); *name = XMALLOC (char, len); strncpy (*name, arg, len-1); (*name)[len - 1] = '\0'; return 0; } void lt_opt_process_env_set (const char *arg) { char *name = NULL; char *value = NULL; if (lt_split_name_value (arg, &name, &value) != 0) { XFREE (name); XFREE (value); lt_fatal ("bad argument for %s: '%s'", env_set_opt, arg); } lt_setenv (name, value); XFREE (name); XFREE (value); } void lt_opt_process_env_prepend (const char *arg) { char *name = NULL; char *value = NULL; char *new_value = NULL; if (lt_split_name_value (arg, &name, &value) != 0) { XFREE (name); XFREE (value); lt_fatal ("bad argument for %s: '%s'", env_prepend_opt, arg); } new_value = lt_extend_str (getenv (name), value, 0); lt_setenv (name, new_value); XFREE (new_value); XFREE (name); XFREE (value); } void lt_opt_process_env_append (const char *arg) { char *name = NULL; char *value = NULL; char *new_value = NULL; if (lt_split_name_value (arg, &name, &value) != 0) { XFREE (name); XFREE (value); lt_fatal ("bad argument for %s: '%s'", env_append_opt, arg); } new_value = lt_extend_str (getenv (name), value, 1); lt_setenv (name, new_value); XFREE (new_value); XFREE (name); XFREE (value); } void lt_update_exe_path (const char *name, const char *value) { LTWRAPPER_DEBUGPRINTF (("(lt_update_exe_path) modifying '%s' by prepending '%s'\n", (name ? name : ""), (value ? value : ""))); if (name && *name && value && *value) { char *new_value = lt_extend_str (getenv (name), value, 0); /* some systems can't cope with a ':'-terminated path #' */ int len = strlen (new_value); while (((len = strlen (new_value)) > 0) && IS_PATH_SEPARATOR (new_value[len-1])) { new_value[len-1] = '\0'; } lt_setenv (name, new_value); XFREE (new_value); } } void lt_update_lib_path (const char *name, const char *value) { LTWRAPPER_DEBUGPRINTF (("(lt_update_lib_path) modifying '%s' by prepending '%s'\n", (name ? name : ""), (value ? value : ""))); if (name && *name && value && *value) { char *new_value = lt_extend_str (getenv (name), value, 0); lt_setenv (name, new_value); XFREE (new_value); } } EOF } # end: func_emit_cwrapperexe_src # func_mode_link arg... func_mode_link () { $opt_debug case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) # It is impossible to link a dll without this setting, and # we shouldn't force the makefile maintainer to figure out # which system we are compiling for in order to pass an extra # flag for every libtool invocation. # allow_undefined=no # FIXME: Unfortunately, there are problems with the above when trying # to make a dll which has undefined symbols, in which case not # even a static library is built. For now, we need to specify # -no-undefined on the libtool link line when we can be certain # that all symbols are satisfied, otherwise we get a static library. allow_undefined=yes ;; *) allow_undefined=yes ;; esac libtool_args=$nonopt base_compile="$nonopt $@" compile_command=$nonopt finalize_command=$nonopt compile_rpath= finalize_rpath= compile_shlibpath= finalize_shlibpath= convenience= old_convenience= deplibs= old_deplibs= compiler_flags= linker_flags= dllsearchpath= lib_search_path=`pwd` inst_prefix_dir= new_inherited_linker_flags= avoid_version=no dlfiles= dlprefiles= dlself=no export_dynamic=no export_symbols= export_symbols_regex= generated= libobjs= ltlibs= module=no no_install=no objs= non_pic_objects= precious_files_regex= prefer_static_libs=no preload=no prev= prevarg= release= rpath= xrpath= perm_rpath= temp_rpath= thread_safe=no vinfo= vinfo_number=no weak_libs= single_module="${wl}-single_module" func_infer_tag $base_compile # We need to know -static, to get the right output filenames. for arg do case $arg in -shared) test "$build_libtool_libs" != yes && \ func_fatal_configuration "can not build a shared library" build_old_libs=no break ;; -all-static | -static | -static-libtool-libs) case $arg in -all-static) if test "$build_libtool_libs" = yes && test -z "$link_static_flag"; then func_warning "complete static linking is impossible in this configuration" fi if test -n "$link_static_flag"; then dlopen_self=$dlopen_self_static fi prefer_static_libs=yes ;; -static) if test -z "$pic_flag" && test -n "$link_static_flag"; then dlopen_self=$dlopen_self_static fi prefer_static_libs=built ;; -static-libtool-libs) if test -z "$pic_flag" && test -n "$link_static_flag"; then dlopen_self=$dlopen_self_static fi prefer_static_libs=yes ;; esac build_libtool_libs=no build_old_libs=yes break ;; esac done # See if our shared archives depend on static archives. test -n "$old_archive_from_new_cmds" && build_old_libs=yes # Go through the arguments, transforming them on the way. while test "$#" -gt 0; do arg="$1" shift func_quote_for_eval "$arg" qarg=$func_quote_for_eval_unquoted_result func_append libtool_args " $func_quote_for_eval_result" # If the previous option needs an argument, assign it. if test -n "$prev"; then case $prev in output) func_append compile_command " @OUTPUT@" func_append finalize_command " @OUTPUT@" ;; esac case $prev in dlfiles|dlprefiles) if test "$preload" = no; then # Add the symbol object into the linking commands. func_append compile_command " @SYMFILE@" func_append finalize_command " @SYMFILE@" preload=yes fi case $arg in *.la | *.lo) ;; # We handle these cases below. force) if test "$dlself" = no; then dlself=needless export_dynamic=yes fi prev= continue ;; self) if test "$prev" = dlprefiles; then dlself=yes elif test "$prev" = dlfiles && test "$dlopen_self" != yes; then dlself=yes else dlself=needless export_dynamic=yes fi prev= continue ;; *) if test "$prev" = dlfiles; then dlfiles="$dlfiles $arg" else dlprefiles="$dlprefiles $arg" fi prev= continue ;; esac ;; expsyms) export_symbols="$arg" test -f "$arg" \ || func_fatal_error "symbol file \`$arg' does not exist" prev= continue ;; expsyms_regex) export_symbols_regex="$arg" prev= continue ;; framework) case $host in *-*-darwin*) case "$deplibs " in *" $qarg.ltframework "*) ;; *) deplibs="$deplibs $qarg.ltframework" # this is fixed later ;; esac ;; esac prev= continue ;; inst_prefix) inst_prefix_dir="$arg" prev= continue ;; objectlist) if test -f "$arg"; then save_arg=$arg moreargs= for fil in `cat "$save_arg"` do # moreargs="$moreargs $fil" arg=$fil # A libtool-controlled object. # Check to see that this really is a libtool object. if func_lalib_unsafe_p "$arg"; then pic_object= non_pic_object= # Read the .lo file func_source "$arg" if test -z "$pic_object" || test -z "$non_pic_object" || test "$pic_object" = none && test "$non_pic_object" = none; then func_fatal_error "cannot find name of object for \`$arg'" fi # Extract subdirectory from the argument. func_dirname "$arg" "/" "" xdir="$func_dirname_result" if test "$pic_object" != none; then # Prepend the subdirectory the object is found in. pic_object="$xdir$pic_object" if test "$prev" = dlfiles; then if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then dlfiles="$dlfiles $pic_object" prev= continue else # If libtool objects are unsupported, then we need to preload. prev=dlprefiles fi fi # CHECK ME: I think I busted this. -Ossama if test "$prev" = dlprefiles; then # Preload the old-style object. dlprefiles="$dlprefiles $pic_object" prev= fi # A PIC object. func_append libobjs " $pic_object" arg="$pic_object" fi # Non-PIC object. if test "$non_pic_object" != none; then # Prepend the subdirectory the object is found in. non_pic_object="$xdir$non_pic_object" # A standard non-PIC object func_append non_pic_objects " $non_pic_object" if test -z "$pic_object" || test "$pic_object" = none ; then arg="$non_pic_object" fi else # If the PIC object exists, use it instead. # $xdir was prepended to $pic_object above. non_pic_object="$pic_object" func_append non_pic_objects " $non_pic_object" fi else # Only an error if not doing a dry-run. if $opt_dry_run; then # Extract subdirectory from the argument. func_dirname "$arg" "/" "" xdir="$func_dirname_result" func_lo2o "$arg" pic_object=$xdir$objdir/$func_lo2o_result non_pic_object=$xdir$func_lo2o_result func_append libobjs " $pic_object" func_append non_pic_objects " $non_pic_object" else func_fatal_error "\`$arg' is not a valid libtool object" fi fi done else func_fatal_error "link input file \`$arg' does not exist" fi arg=$save_arg prev= continue ;; precious_regex) precious_files_regex="$arg" prev= continue ;; release) release="-$arg" prev= continue ;; rpath | xrpath) # We need an absolute path. case $arg in [\\/]* | [A-Za-z]:[\\/]*) ;; *) func_fatal_error "only absolute run-paths are allowed" ;; esac if test "$prev" = rpath; then case "$rpath " in *" $arg "*) ;; *) rpath="$rpath $arg" ;; esac else case "$xrpath " in *" $arg "*) ;; *) xrpath="$xrpath $arg" ;; esac fi prev= continue ;; shrext) shrext_cmds="$arg" prev= continue ;; weak) weak_libs="$weak_libs $arg" prev= continue ;; xcclinker) linker_flags="$linker_flags $qarg" compiler_flags="$compiler_flags $qarg" prev= func_append compile_command " $qarg" func_append finalize_command " $qarg" continue ;; xcompiler) compiler_flags="$compiler_flags $qarg" prev= func_append compile_command " $qarg" func_append finalize_command " $qarg" continue ;; xlinker) linker_flags="$linker_flags $qarg" compiler_flags="$compiler_flags $wl$qarg" prev= func_append compile_command " $wl$qarg" func_append finalize_command " $wl$qarg" continue ;; *) eval "$prev=\"\$arg\"" prev= continue ;; esac fi # test -n "$prev" prevarg="$arg" case $arg in -all-static) if test -n "$link_static_flag"; then # See comment for -static flag below, for more details. func_append compile_command " $link_static_flag" func_append finalize_command " $link_static_flag" fi continue ;; -allow-undefined) # FIXME: remove this flag sometime in the future. func_fatal_error "\`-allow-undefined' must not be used because it is the default" ;; -avoid-version) avoid_version=yes continue ;; -dlopen) prev=dlfiles continue ;; -dlpreopen) prev=dlprefiles continue ;; -export-dynamic) export_dynamic=yes continue ;; -export-symbols | -export-symbols-regex) if test -n "$export_symbols" || test -n "$export_symbols_regex"; then func_fatal_error "more than one -exported-symbols argument is not allowed" fi if test "X$arg" = "X-export-symbols"; then prev=expsyms else prev=expsyms_regex fi continue ;; -framework) prev=framework continue ;; -inst-prefix-dir) prev=inst_prefix continue ;; # The native IRIX linker understands -LANG:*, -LIST:* and -LNO:* # so, if we see these flags be careful not to treat them like -L -L[A-Z][A-Z]*:*) case $with_gcc/$host in no/*-*-irix* | /*-*-irix*) func_append compile_command " $arg" func_append finalize_command " $arg" ;; esac continue ;; -L*) func_stripname '-L' '' "$arg" dir=$func_stripname_result if test -z "$dir"; then if test "$#" -gt 0; then func_fatal_error "require no space between \`-L' and \`$1'" else func_fatal_error "need path for \`-L' option" fi fi # We need an absolute path. case $dir in [\\/]* | [A-Za-z]:[\\/]*) ;; *) absdir=`cd "$dir" && pwd` test -z "$absdir" && \ func_fatal_error "cannot determine absolute directory name of \`$dir'" dir="$absdir" ;; esac case "$deplibs " in *" -L$dir "*) ;; *) deplibs="$deplibs -L$dir" lib_search_path="$lib_search_path $dir" ;; esac case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) testbindir=`$ECHO "X$dir" | $Xsed -e 's*/lib$*/bin*'` case :$dllsearchpath: in *":$dir:"*) ;; ::) dllsearchpath=$dir;; *) dllsearchpath="$dllsearchpath:$dir";; esac case :$dllsearchpath: in *":$testbindir:"*) ;; ::) dllsearchpath=$testbindir;; *) dllsearchpath="$dllsearchpath:$testbindir";; esac ;; esac continue ;; -l*) if test "X$arg" = "X-lc" || test "X$arg" = "X-lm"; then case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-beos* | *-cegcc*) # These systems don't actually have a C or math library (as such) continue ;; *-*-os2*) # These systems don't actually have a C library (as such) test "X$arg" = "X-lc" && continue ;; *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) # Do not include libc due to us having libc/libc_r. test "X$arg" = "X-lc" && continue ;; *-*-rhapsody* | *-*-darwin1.[012]) # Rhapsody C and math libraries are in the System framework deplibs="$deplibs System.ltframework" continue ;; *-*-sco3.2v5* | *-*-sco5v6*) # Causes problems with __ctype test "X$arg" = "X-lc" && continue ;; *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) # Compiler inserts libc in the correct place for threads to work test "X$arg" = "X-lc" && continue ;; esac elif test "X$arg" = "X-lc_r"; then case $host in *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) # Do not include libc_r directly, use -pthread flag. continue ;; esac fi deplibs="$deplibs $arg" continue ;; -module) module=yes continue ;; # Tru64 UNIX uses -model [arg] to determine the layout of C++ # classes, name mangling, and exception handling. # Darwin uses the -arch flag to determine output architecture. -model|-arch|-isysroot) compiler_flags="$compiler_flags $arg" func_append compile_command " $arg" func_append finalize_command " $arg" prev=xcompiler continue ;; -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe|-threads) compiler_flags="$compiler_flags $arg" func_append compile_command " $arg" func_append finalize_command " $arg" case "$new_inherited_linker_flags " in *" $arg "*) ;; * ) new_inherited_linker_flags="$new_inherited_linker_flags $arg" ;; esac continue ;; -multi_module) single_module="${wl}-multi_module" continue ;; -no-fast-install) fast_install=no continue ;; -no-install) case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-darwin* | *-cegcc*) # The PATH hackery in wrapper scripts is required on Windows # and Darwin in order for the loader to find any dlls it needs. func_warning "\`-no-install' is ignored for $host" func_warning "assuming \`-no-fast-install' instead" fast_install=no ;; *) no_install=yes ;; esac continue ;; -no-undefined) allow_undefined=no continue ;; -objectlist) prev=objectlist continue ;; -o) prev=output ;; -precious-files-regex) prev=precious_regex continue ;; -release) prev=release continue ;; -rpath) prev=rpath continue ;; -R) prev=xrpath continue ;; -R*) func_stripname '-R' '' "$arg" dir=$func_stripname_result # We need an absolute path. case $dir in [\\/]* | [A-Za-z]:[\\/]*) ;; *) func_fatal_error "only absolute run-paths are allowed" ;; esac case "$xrpath " in *" $dir "*) ;; *) xrpath="$xrpath $dir" ;; esac continue ;; -shared) # The effects of -shared are defined in a previous loop. continue ;; -shrext) prev=shrext continue ;; -static | -static-libtool-libs) # The effects of -static are defined in a previous loop. # We used to do the same as -all-static on platforms that # didn't have a PIC flag, but the assumption that the effects # would be equivalent was wrong. It would break on at least # Digital Unix and AIX. continue ;; -thread-safe) thread_safe=yes continue ;; -version-info) prev=vinfo continue ;; -version-number) prev=vinfo vinfo_number=yes continue ;; -weak) prev=weak continue ;; -Wc,*) func_stripname '-Wc,' '' "$arg" args=$func_stripname_result arg= save_ifs="$IFS"; IFS=',' for flag in $args; do IFS="$save_ifs" func_quote_for_eval "$flag" arg="$arg $wl$func_quote_for_eval_result" compiler_flags="$compiler_flags $func_quote_for_eval_result" done IFS="$save_ifs" func_stripname ' ' '' "$arg" arg=$func_stripname_result ;; -Wl,*) func_stripname '-Wl,' '' "$arg" args=$func_stripname_result arg= save_ifs="$IFS"; IFS=',' for flag in $args; do IFS="$save_ifs" func_quote_for_eval "$flag" arg="$arg $wl$func_quote_for_eval_result" compiler_flags="$compiler_flags $wl$func_quote_for_eval_result" linker_flags="$linker_flags $func_quote_for_eval_result" done IFS="$save_ifs" func_stripname ' ' '' "$arg" arg=$func_stripname_result ;; -Xcompiler) prev=xcompiler continue ;; -Xlinker) prev=xlinker continue ;; -XCClinker) prev=xcclinker continue ;; # -msg_* for osf cc -msg_*) func_quote_for_eval "$arg" arg="$func_quote_for_eval_result" ;; # -64, -mips[0-9] enable 64-bit mode on the SGI compiler # -r[0-9][0-9]* specifies the processor on the SGI compiler # -xarch=*, -xtarget=* enable 64-bit mode on the Sun compiler # +DA*, +DD* enable 64-bit mode on the HP compiler # -q* pass through compiler args for the IBM compiler # -m*, -t[45]*, -txscale* pass through architecture-specific # compiler args for GCC # -F/path gives path to uninstalled frameworks, gcc on darwin # -p, -pg, --coverage, -fprofile-* pass through profiling flag for GCC # @file GCC response files -64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*| \ -t[45]*|-txscale*|-p|-pg|--coverage|-fprofile-*|-F*|@*) func_quote_for_eval "$arg" arg="$func_quote_for_eval_result" func_append compile_command " $arg" func_append finalize_command " $arg" compiler_flags="$compiler_flags $arg" continue ;; # Some other compiler flag. -* | +*) func_quote_for_eval "$arg" arg="$func_quote_for_eval_result" ;; *.$objext) # A standard object. objs="$objs $arg" ;; *.lo) # A libtool-controlled object. # Check to see that this really is a libtool object. if func_lalib_unsafe_p "$arg"; then pic_object= non_pic_object= # Read the .lo file func_source "$arg" if test -z "$pic_object" || test -z "$non_pic_object" || test "$pic_object" = none && test "$non_pic_object" = none; then func_fatal_error "cannot find name of object for \`$arg'" fi # Extract subdirectory from the argument. func_dirname "$arg" "/" "" xdir="$func_dirname_result" if test "$pic_object" != none; then # Prepend the subdirectory the object is found in. pic_object="$xdir$pic_object" if test "$prev" = dlfiles; then if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then dlfiles="$dlfiles $pic_object" prev= continue else # If libtool objects are unsupported, then we need to preload. prev=dlprefiles fi fi # CHECK ME: I think I busted this. -Ossama if test "$prev" = dlprefiles; then # Preload the old-style object. dlprefiles="$dlprefiles $pic_object" prev= fi # A PIC object. func_append libobjs " $pic_object" arg="$pic_object" fi # Non-PIC object. if test "$non_pic_object" != none; then # Prepend the subdirectory the object is found in. non_pic_object="$xdir$non_pic_object" # A standard non-PIC object func_append non_pic_objects " $non_pic_object" if test -z "$pic_object" || test "$pic_object" = none ; then arg="$non_pic_object" fi else # If the PIC object exists, use it instead. # $xdir was prepended to $pic_object above. non_pic_object="$pic_object" func_append non_pic_objects " $non_pic_object" fi else # Only an error if not doing a dry-run. if $opt_dry_run; then # Extract subdirectory from the argument. func_dirname "$arg" "/" "" xdir="$func_dirname_result" func_lo2o "$arg" pic_object=$xdir$objdir/$func_lo2o_result non_pic_object=$xdir$func_lo2o_result func_append libobjs " $pic_object" func_append non_pic_objects " $non_pic_object" else func_fatal_error "\`$arg' is not a valid libtool object" fi fi ;; *.$libext) # An archive. deplibs="$deplibs $arg" old_deplibs="$old_deplibs $arg" continue ;; *.la) # A libtool-controlled library. if test "$prev" = dlfiles; then # This library was specified with -dlopen. dlfiles="$dlfiles $arg" prev= elif test "$prev" = dlprefiles; then # The library was specified with -dlpreopen. dlprefiles="$dlprefiles $arg" prev= else deplibs="$deplibs $arg" fi continue ;; # Some other compiler argument. *) # Unknown arguments in both finalize_command and compile_command need # to be aesthetically quoted because they are evaled later. func_quote_for_eval "$arg" arg="$func_quote_for_eval_result" ;; esac # arg # Now actually substitute the argument into the commands. if test -n "$arg"; then func_append compile_command " $arg" func_append finalize_command " $arg" fi done # argument parsing loop test -n "$prev" && \ func_fatal_help "the \`$prevarg' option requires an argument" if test "$export_dynamic" = yes && test -n "$export_dynamic_flag_spec"; then eval arg=\"$export_dynamic_flag_spec\" func_append compile_command " $arg" func_append finalize_command " $arg" fi oldlibs= # calculate the name of the file, without its directory func_basename "$output" outputname="$func_basename_result" libobjs_save="$libobjs" if test -n "$shlibpath_var"; then # get the directories listed in $shlibpath_var eval shlib_search_path=\`\$ECHO \"X\${$shlibpath_var}\" \| \$Xsed -e \'s/:/ /g\'\` else shlib_search_path= fi eval sys_lib_search_path=\"$sys_lib_search_path_spec\" eval sys_lib_dlsearch_path=\"$sys_lib_dlsearch_path_spec\" func_dirname "$output" "/" "" output_objdir="$func_dirname_result$objdir" # Create the object directory. func_mkdir_p "$output_objdir" # Determine the type of output case $output in "") func_fatal_help "you must specify an output file" ;; *.$libext) linkmode=oldlib ;; *.lo | *.$objext) linkmode=obj ;; *.la) linkmode=lib ;; *) linkmode=prog ;; # Anything else should be a program. esac specialdeplibs= libs= # Find all interdependent deplibs by searching for libraries # that are linked more than once (e.g. -la -lb -la) for deplib in $deplibs; do if $opt_duplicate_deps ; then case "$libs " in *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;; esac fi libs="$libs $deplib" done if test "$linkmode" = lib; then libs="$predeps $libs $compiler_lib_search_path $postdeps" # Compute libraries that are listed more than once in $predeps # $postdeps and mark them as special (i.e., whose duplicates are # not to be eliminated). pre_post_deps= if $opt_duplicate_compiler_generated_deps; then for pre_post_dep in $predeps $postdeps; do case "$pre_post_deps " in *" $pre_post_dep "*) specialdeplibs="$specialdeplibs $pre_post_deps" ;; esac pre_post_deps="$pre_post_deps $pre_post_dep" done fi pre_post_deps= fi deplibs= newdependency_libs= newlib_search_path= need_relink=no # whether we're linking any uninstalled libtool libraries notinst_deplibs= # not-installed libtool libraries notinst_path= # paths that contain not-installed libtool libraries case $linkmode in lib) passes="conv dlpreopen link" for file in $dlfiles $dlprefiles; do case $file in *.la) ;; *) func_fatal_help "libraries can \`-dlopen' only libtool libraries: $file" ;; esac done ;; prog) compile_deplibs= finalize_deplibs= alldeplibs=no newdlfiles= newdlprefiles= passes="conv scan dlopen dlpreopen link" ;; *) passes="conv" ;; esac for pass in $passes; do # The preopen pass in lib mode reverses $deplibs; put it back here # so that -L comes before libs that need it for instance... if test "$linkmode,$pass" = "lib,link"; then ## FIXME: Find the place where the list is rebuilt in the wrong ## order, and fix it there properly tmp_deplibs= for deplib in $deplibs; do tmp_deplibs="$deplib $tmp_deplibs" done deplibs="$tmp_deplibs" fi if test "$linkmode,$pass" = "lib,link" || test "$linkmode,$pass" = "prog,scan"; then libs="$deplibs" deplibs= fi if test "$linkmode" = prog; then case $pass in dlopen) libs="$dlfiles" ;; dlpreopen) libs="$dlprefiles" ;; link) libs="$deplibs %DEPLIBS%" test "X$link_all_deplibs" != Xno && libs="$libs $dependency_libs" ;; esac fi if test "$linkmode,$pass" = "lib,dlpreopen"; then # Collect and forward deplibs of preopened libtool libs for lib in $dlprefiles; do # Ignore non-libtool-libs dependency_libs= case $lib in *.la) func_source "$lib" ;; esac # Collect preopened libtool deplibs, except any this library # has declared as weak libs for deplib in $dependency_libs; do deplib_base=`$ECHO "X$deplib" | $Xsed -e "$basename"` case " $weak_libs " in *" $deplib_base "*) ;; *) deplibs="$deplibs $deplib" ;; esac done done libs="$dlprefiles" fi if test "$pass" = dlopen; then # Collect dlpreopened libraries save_deplibs="$deplibs" deplibs= fi for deplib in $libs; do lib= found=no case $deplib in -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe|-threads) if test "$linkmode,$pass" = "prog,link"; then compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else compiler_flags="$compiler_flags $deplib" if test "$linkmode" = lib ; then case "$new_inherited_linker_flags " in *" $deplib "*) ;; * ) new_inherited_linker_flags="$new_inherited_linker_flags $deplib" ;; esac fi fi continue ;; -l*) if test "$linkmode" != lib && test "$linkmode" != prog; then func_warning "\`-l' is ignored for archives/objects" continue fi func_stripname '-l' '' "$deplib" name=$func_stripname_result if test "$linkmode" = lib; then searchdirs="$newlib_search_path $lib_search_path $compiler_lib_search_dirs $sys_lib_search_path $shlib_search_path" else searchdirs="$newlib_search_path $lib_search_path $sys_lib_search_path $shlib_search_path" fi for searchdir in $searchdirs; do for search_ext in .la $std_shrext .so .a; do # Search the libtool library lib="$searchdir/lib${name}${search_ext}" if test -f "$lib"; then if test "$search_ext" = ".la"; then found=yes else found=no fi break 2 fi done done if test "$found" != yes; then # deplib doesn't seem to be a libtool library if test "$linkmode,$pass" = "prog,link"; then compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else deplibs="$deplib $deplibs" test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs" fi continue else # deplib is a libtool library # If $allow_libtool_libs_with_static_runtimes && $deplib is a stdlib, # We need to do some special things here, and not later. if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then case " $predeps $postdeps " in *" $deplib "*) if func_lalib_p "$lib"; then library_names= old_library= func_source "$lib" for l in $old_library $library_names; do ll="$l" done if test "X$ll" = "X$old_library" ; then # only static version available found=no func_dirname "$lib" "" "." ladir="$func_dirname_result" lib=$ladir/$old_library if test "$linkmode,$pass" = "prog,link"; then compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else deplibs="$deplib $deplibs" test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs" fi continue fi fi ;; *) ;; esac fi fi ;; # -l *.ltframework) if test "$linkmode,$pass" = "prog,link"; then compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else deplibs="$deplib $deplibs" if test "$linkmode" = lib ; then case "$new_inherited_linker_flags " in *" $deplib "*) ;; * ) new_inherited_linker_flags="$new_inherited_linker_flags $deplib" ;; esac fi fi continue ;; -L*) case $linkmode in lib) deplibs="$deplib $deplibs" test "$pass" = conv && continue newdependency_libs="$deplib $newdependency_libs" func_stripname '-L' '' "$deplib" newlib_search_path="$newlib_search_path $func_stripname_result" ;; prog) if test "$pass" = conv; then deplibs="$deplib $deplibs" continue fi if test "$pass" = scan; then deplibs="$deplib $deplibs" else compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" fi func_stripname '-L' '' "$deplib" newlib_search_path="$newlib_search_path $func_stripname_result" ;; *) func_warning "\`-L' is ignored for archives/objects" ;; esac # linkmode continue ;; # -L -R*) if test "$pass" = link; then func_stripname '-R' '' "$deplib" dir=$func_stripname_result # Make sure the xrpath contains only unique directories. case "$xrpath " in *" $dir "*) ;; *) xrpath="$xrpath $dir" ;; esac fi deplibs="$deplib $deplibs" continue ;; *.la) lib="$deplib" ;; *.$libext) if test "$pass" = conv; then deplibs="$deplib $deplibs" continue fi case $linkmode in lib) # Linking convenience modules into shared libraries is allowed, # but linking other static libraries is non-portable. case " $dlpreconveniencelibs " in *" $deplib "*) ;; *) valid_a_lib=no case $deplibs_check_method in match_pattern*) set dummy $deplibs_check_method; shift match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"` if eval "\$ECHO \"X$deplib\"" 2>/dev/null | $Xsed -e 10q \ | $EGREP "$match_pattern_regex" > /dev/null; then valid_a_lib=yes fi ;; pass_all) valid_a_lib=yes ;; esac if test "$valid_a_lib" != yes; then $ECHO $ECHO "*** Warning: Trying to link with static lib archive $deplib." $ECHO "*** I have the capability to make that library automatically link in when" $ECHO "*** you link to this library. But I can only do this if you have a" $ECHO "*** shared version of the library, which you do not appear to have" $ECHO "*** because the file extensions .$libext of this argument makes me believe" $ECHO "*** that it is just a static archive that I should not use here." else $ECHO $ECHO "*** Warning: Linking the shared library $output against the" $ECHO "*** static library $deplib is not portable!" deplibs="$deplib $deplibs" fi ;; esac continue ;; prog) if test "$pass" != link; then deplibs="$deplib $deplibs" else compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" fi continue ;; esac # linkmode ;; # *.$libext *.lo | *.$objext) if test "$pass" = conv; then deplibs="$deplib $deplibs" elif test "$linkmode" = prog; then if test "$pass" = dlpreopen || test "$dlopen_support" != yes || test "$build_libtool_libs" = no; then # If there is no dlopen support or we're linking statically, # we need to preload. newdlprefiles="$newdlprefiles $deplib" compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else newdlfiles="$newdlfiles $deplib" fi fi continue ;; %DEPLIBS%) alldeplibs=yes continue ;; esac # case $deplib if test "$found" = yes || test -f "$lib"; then : else func_fatal_error "cannot find the library \`$lib' or unhandled argument \`$deplib'" fi # Check to see that this really is a libtool archive. func_lalib_unsafe_p "$lib" \ || func_fatal_error "\`$lib' is not a valid libtool archive" func_dirname "$lib" "" "." ladir="$func_dirname_result" dlname= dlopen= dlpreopen= libdir= library_names= old_library= inherited_linker_flags= # If the library was installed with an old release of libtool, # it will not redefine variables installed, or shouldnotlink installed=yes shouldnotlink=no avoidtemprpath= # Read the .la file func_source "$lib" # Convert "-framework foo" to "foo.ltframework" if test -n "$inherited_linker_flags"; then tmp_inherited_linker_flags=`$ECHO "X$inherited_linker_flags" | $Xsed -e 's/-framework \([^ $]*\)/\1.ltframework/g'` for tmp_inherited_linker_flag in $tmp_inherited_linker_flags; do case " $new_inherited_linker_flags " in *" $tmp_inherited_linker_flag "*) ;; *) new_inherited_linker_flags="$new_inherited_linker_flags $tmp_inherited_linker_flag";; esac done fi dependency_libs=`$ECHO "X $dependency_libs" | $Xsed -e 's% \([^ $]*\).ltframework% -framework \1%g'` if test "$linkmode,$pass" = "lib,link" || test "$linkmode,$pass" = "prog,scan" || { test "$linkmode" != prog && test "$linkmode" != lib; }; then test -n "$dlopen" && dlfiles="$dlfiles $dlopen" test -n "$dlpreopen" && dlprefiles="$dlprefiles $dlpreopen" fi if test "$pass" = conv; then # Only check for convenience libraries deplibs="$lib $deplibs" if test -z "$libdir"; then if test -z "$old_library"; then func_fatal_error "cannot find name of link library for \`$lib'" fi # It is a libtool convenience library, so add in its objects. convenience="$convenience $ladir/$objdir/$old_library" old_convenience="$old_convenience $ladir/$objdir/$old_library" tmp_libs= for deplib in $dependency_libs; do deplibs="$deplib $deplibs" if $opt_duplicate_deps ; then case "$tmp_libs " in *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;; esac fi tmp_libs="$tmp_libs $deplib" done elif test "$linkmode" != prog && test "$linkmode" != lib; then func_fatal_error "\`$lib' is not a convenience library" fi continue fi # $pass = conv # Get the name of the library we link against. linklib= for l in $old_library $library_names; do linklib="$l" done if test -z "$linklib"; then func_fatal_error "cannot find name of link library for \`$lib'" fi # This library was specified with -dlopen. if test "$pass" = dlopen; then if test -z "$libdir"; then func_fatal_error "cannot -dlopen a convenience library: \`$lib'" fi if test -z "$dlname" || test "$dlopen_support" != yes || test "$build_libtool_libs" = no; then # If there is no dlname, no dlopen support or we're linking # statically, we need to preload. We also need to preload any # dependent libraries so libltdl's deplib preloader doesn't # bomb out in the load deplibs phase. dlprefiles="$dlprefiles $lib $dependency_libs" else newdlfiles="$newdlfiles $lib" fi continue fi # $pass = dlopen # We need an absolute path. case $ladir in [\\/]* | [A-Za-z]:[\\/]*) abs_ladir="$ladir" ;; *) abs_ladir=`cd "$ladir" && pwd` if test -z "$abs_ladir"; then func_warning "cannot determine absolute directory name of \`$ladir'" func_warning "passing it literally to the linker, although it might fail" abs_ladir="$ladir" fi ;; esac func_basename "$lib" laname="$func_basename_result" # Find the relevant object directory and library name. if test "X$installed" = Xyes; then if test ! -f "$libdir/$linklib" && test -f "$abs_ladir/$linklib"; then func_warning "library \`$lib' was moved." dir="$ladir" absdir="$abs_ladir" libdir="$abs_ladir" else dir="$libdir" absdir="$libdir" fi test "X$hardcode_automatic" = Xyes && avoidtemprpath=yes else if test ! -f "$ladir/$objdir/$linklib" && test -f "$abs_ladir/$linklib"; then dir="$ladir" absdir="$abs_ladir" # Remove this search path later notinst_path="$notinst_path $abs_ladir" else dir="$ladir/$objdir" absdir="$abs_ladir/$objdir" # Remove this search path later notinst_path="$notinst_path $abs_ladir" fi fi # $installed = yes func_stripname 'lib' '.la' "$laname" name=$func_stripname_result # This library was specified with -dlpreopen. if test "$pass" = dlpreopen; then if test -z "$libdir" && test "$linkmode" = prog; then func_fatal_error "only libraries may -dlpreopen a convenience library: \`$lib'" fi # Prefer using a static library (so that no silly _DYNAMIC symbols # are required to link). if test -n "$old_library"; then newdlprefiles="$newdlprefiles $dir/$old_library" # Keep a list of preopened convenience libraries to check # that they are being used correctly in the link pass. test -z "$libdir" && \ dlpreconveniencelibs="$dlpreconveniencelibs $dir/$old_library" # Otherwise, use the dlname, so that lt_dlopen finds it. elif test -n "$dlname"; then newdlprefiles="$newdlprefiles $dir/$dlname" else newdlprefiles="$newdlprefiles $dir/$linklib" fi fi # $pass = dlpreopen if test -z "$libdir"; then # Link the convenience library if test "$linkmode" = lib; then deplibs="$dir/$old_library $deplibs" elif test "$linkmode,$pass" = "prog,link"; then compile_deplibs="$dir/$old_library $compile_deplibs" finalize_deplibs="$dir/$old_library $finalize_deplibs" else deplibs="$lib $deplibs" # used for prog,scan pass fi continue fi if test "$linkmode" = prog && test "$pass" != link; then newlib_search_path="$newlib_search_path $ladir" deplibs="$lib $deplibs" linkalldeplibs=no if test "$link_all_deplibs" != no || test -z "$library_names" || test "$build_libtool_libs" = no; then linkalldeplibs=yes fi tmp_libs= for deplib in $dependency_libs; do case $deplib in -L*) func_stripname '-L' '' "$deplib" newlib_search_path="$newlib_search_path $func_stripname_result" ;; esac # Need to link against all dependency_libs? if test "$linkalldeplibs" = yes; then deplibs="$deplib $deplibs" else # Need to hardcode shared library paths # or/and link against static libraries newdependency_libs="$deplib $newdependency_libs" fi if $opt_duplicate_deps ; then case "$tmp_libs " in *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;; esac fi tmp_libs="$tmp_libs $deplib" done # for deplib continue fi # $linkmode = prog... if test "$linkmode,$pass" = "prog,link"; then if test -n "$library_names" && { { test "$prefer_static_libs" = no || test "$prefer_static_libs,$installed" = "built,yes"; } || test -z "$old_library"; }; then # We need to hardcode the library path if test -n "$shlibpath_var" && test -z "$avoidtemprpath" ; then # Make sure the rpath contains only unique directories. case "$temp_rpath:" in *"$absdir:"*) ;; *) temp_rpath="$temp_rpath$absdir:" ;; esac fi # Hardcode the library path. # Skip directories that are in the system default run-time # search path. case " $sys_lib_dlsearch_path " in *" $absdir "*) ;; *) case "$compile_rpath " in *" $absdir "*) ;; *) compile_rpath="$compile_rpath $absdir" esac ;; esac case " $sys_lib_dlsearch_path " in *" $libdir "*) ;; *) case "$finalize_rpath " in *" $libdir "*) ;; *) finalize_rpath="$finalize_rpath $libdir" esac ;; esac fi # $linkmode,$pass = prog,link... if test "$alldeplibs" = yes && { test "$deplibs_check_method" = pass_all || { test "$build_libtool_libs" = yes && test -n "$library_names"; }; }; then # We only need to search for static libraries continue fi fi link_static=no # Whether the deplib will be linked statically use_static_libs=$prefer_static_libs if test "$use_static_libs" = built && test "$installed" = yes; then use_static_libs=no fi if test -n "$library_names" && { test "$use_static_libs" = no || test -z "$old_library"; }; then case $host in *cygwin* | *mingw* | *cegcc*) # No point in relinking DLLs because paths are not encoded notinst_deplibs="$notinst_deplibs $lib" need_relink=no ;; *) if test "$installed" = no; then notinst_deplibs="$notinst_deplibs $lib" need_relink=yes fi ;; esac # This is a shared library # Warn about portability, can't link against -module's on some # systems (darwin). Don't bleat about dlopened modules though! dlopenmodule="" for dlpremoduletest in $dlprefiles; do if test "X$dlpremoduletest" = "X$lib"; then dlopenmodule="$dlpremoduletest" break fi done if test -z "$dlopenmodule" && test "$shouldnotlink" = yes && test "$pass" = link; then $ECHO if test "$linkmode" = prog; then $ECHO "*** Warning: Linking the executable $output against the loadable module" else $ECHO "*** Warning: Linking the shared library $output against the loadable module" fi $ECHO "*** $linklib is not portable!" fi if test "$linkmode" = lib && test "$hardcode_into_libs" = yes; then # Hardcode the library path. # Skip directories that are in the system default run-time # search path. case " $sys_lib_dlsearch_path " in *" $absdir "*) ;; *) case "$compile_rpath " in *" $absdir "*) ;; *) compile_rpath="$compile_rpath $absdir" esac ;; esac case " $sys_lib_dlsearch_path " in *" $libdir "*) ;; *) case "$finalize_rpath " in *" $libdir "*) ;; *) finalize_rpath="$finalize_rpath $libdir" esac ;; esac fi if test -n "$old_archive_from_expsyms_cmds"; then # figure out the soname set dummy $library_names shift realname="$1" shift libname=`eval "\\$ECHO \"$libname_spec\""` # use dlname if we got it. it's perfectly good, no? if test -n "$dlname"; then soname="$dlname" elif test -n "$soname_spec"; then # bleh windows case $host in *cygwin* | mingw* | *cegcc*) func_arith $current - $age major=$func_arith_result versuffix="-$major" ;; esac eval soname=\"$soname_spec\" else soname="$realname" fi # Make a new name for the extract_expsyms_cmds to use soroot="$soname" func_basename "$soroot" soname="$func_basename_result" func_stripname 'lib' '.dll' "$soname" newlib=libimp-$func_stripname_result.a # If the library has no export list, then create one now if test -f "$output_objdir/$soname-def"; then : else func_verbose "extracting exported symbol list from \`$soname'" func_execute_cmds "$extract_expsyms_cmds" 'exit $?' fi # Create $newlib if test -f "$output_objdir/$newlib"; then :; else func_verbose "generating import library for \`$soname'" func_execute_cmds "$old_archive_from_expsyms_cmds" 'exit $?' fi # make sure the library variables are pointing to the new library dir=$output_objdir linklib=$newlib fi # test -n "$old_archive_from_expsyms_cmds" if test "$linkmode" = prog || test "$mode" != relink; then add_shlibpath= add_dir= add= lib_linked=yes case $hardcode_action in immediate | unsupported) if test "$hardcode_direct" = no; then add="$dir/$linklib" case $host in *-*-sco3.2v5.0.[024]*) add_dir="-L$dir" ;; *-*-sysv4*uw2*) add_dir="-L$dir" ;; *-*-sysv5OpenUNIX* | *-*-sysv5UnixWare7.[01].[10]* | \ *-*-unixware7*) add_dir="-L$dir" ;; *-*-darwin* ) # if the lib is a (non-dlopened) module then we can not # link against it, someone is ignoring the earlier warnings if /usr/bin/file -L $add 2> /dev/null | $GREP ": [^:]* bundle" >/dev/null ; then if test "X$dlopenmodule" != "X$lib"; then $ECHO "*** Warning: lib $linklib is a module, not a shared library" if test -z "$old_library" ; then $ECHO $ECHO "*** And there doesn't seem to be a static archive available" $ECHO "*** The link will probably fail, sorry" else add="$dir/$old_library" fi elif test -n "$old_library"; then add="$dir/$old_library" fi fi esac elif test "$hardcode_minus_L" = no; then case $host in *-*-sunos*) add_shlibpath="$dir" ;; esac add_dir="-L$dir" add="-l$name" elif test "$hardcode_shlibpath_var" = no; then add_shlibpath="$dir" add="-l$name" else lib_linked=no fi ;; relink) if test "$hardcode_direct" = yes && test "$hardcode_direct_absolute" = no; then add="$dir/$linklib" elif test "$hardcode_minus_L" = yes; then add_dir="-L$dir" # Try looking first in the location we're being installed to. if test -n "$inst_prefix_dir"; then case $libdir in [\\/]*) add_dir="$add_dir -L$inst_prefix_dir$libdir" ;; esac fi add="-l$name" elif test "$hardcode_shlibpath_var" = yes; then add_shlibpath="$dir" add="-l$name" else lib_linked=no fi ;; *) lib_linked=no ;; esac if test "$lib_linked" != yes; then func_fatal_configuration "unsupported hardcode properties" fi if test -n "$add_shlibpath"; then case :$compile_shlibpath: in *":$add_shlibpath:"*) ;; *) compile_shlibpath="$compile_shlibpath$add_shlibpath:" ;; esac fi if test "$linkmode" = prog; then test -n "$add_dir" && compile_deplibs="$add_dir $compile_deplibs" test -n "$add" && compile_deplibs="$add $compile_deplibs" else test -n "$add_dir" && deplibs="$add_dir $deplibs" test -n "$add" && deplibs="$add $deplibs" if test "$hardcode_direct" != yes && test "$hardcode_minus_L" != yes && test "$hardcode_shlibpath_var" = yes; then case :$finalize_shlibpath: in *":$libdir:"*) ;; *) finalize_shlibpath="$finalize_shlibpath$libdir:" ;; esac fi fi fi if test "$linkmode" = prog || test "$mode" = relink; then add_shlibpath= add_dir= add= # Finalize command for both is simple: just hardcode it. if test "$hardcode_direct" = yes && test "$hardcode_direct_absolute" = no; then add="$libdir/$linklib" elif test "$hardcode_minus_L" = yes; then add_dir="-L$libdir" add="-l$name" elif test "$hardcode_shlibpath_var" = yes; then case :$finalize_shlibpath: in *":$libdir:"*) ;; *) finalize_shlibpath="$finalize_shlibpath$libdir:" ;; esac add="-l$name" elif test "$hardcode_automatic" = yes; then if test -n "$inst_prefix_dir" && test -f "$inst_prefix_dir$libdir/$linklib" ; then add="$inst_prefix_dir$libdir/$linklib" else add="$libdir/$linklib" fi else # We cannot seem to hardcode it, guess we'll fake it. add_dir="-L$libdir" # Try looking first in the location we're being installed to. if test -n "$inst_prefix_dir"; then case $libdir in [\\/]*) add_dir="$add_dir -L$inst_prefix_dir$libdir" ;; esac fi add="-l$name" fi if test "$linkmode" = prog; then test -n "$add_dir" && finalize_deplibs="$add_dir $finalize_deplibs" test -n "$add" && finalize_deplibs="$add $finalize_deplibs" else test -n "$add_dir" && deplibs="$add_dir $deplibs" test -n "$add" && deplibs="$add $deplibs" fi fi elif test "$linkmode" = prog; then # Here we assume that one of hardcode_direct or hardcode_minus_L # is not unsupported. This is valid on all known static and # shared platforms. if test "$hardcode_direct" != unsupported; then test -n "$old_library" && linklib="$old_library" compile_deplibs="$dir/$linklib $compile_deplibs" finalize_deplibs="$dir/$linklib $finalize_deplibs" else compile_deplibs="-l$name -L$dir $compile_deplibs" finalize_deplibs="-l$name -L$dir $finalize_deplibs" fi elif test "$build_libtool_libs" = yes; then # Not a shared library if test "$deplibs_check_method" != pass_all; then # We're trying link a shared library against a static one # but the system doesn't support it. # Just print a warning and add the library to dependency_libs so # that the program can be linked against the static library. $ECHO $ECHO "*** Warning: This system can not link to static lib archive $lib." $ECHO "*** I have the capability to make that library automatically link in when" $ECHO "*** you link to this library. But I can only do this if you have a" $ECHO "*** shared version of the library, which you do not appear to have." if test "$module" = yes; then $ECHO "*** But as you try to build a module library, libtool will still create " $ECHO "*** a static module, that should work as long as the dlopening application" $ECHO "*** is linked with the -dlopen flag to resolve symbols at runtime." if test -z "$global_symbol_pipe"; then $ECHO $ECHO "*** However, this would only work if libtool was able to extract symbol" $ECHO "*** lists from a program, using \`nm' or equivalent, but libtool could" $ECHO "*** not find such a program. So, this module is probably useless." $ECHO "*** \`nm' from GNU binutils and a full rebuild may help." fi if test "$build_old_libs" = no; then build_libtool_libs=module build_old_libs=yes else build_libtool_libs=no fi fi else deplibs="$dir/$old_library $deplibs" link_static=yes fi fi # link shared/static library? if test "$linkmode" = lib; then if test -n "$dependency_libs" && { test "$hardcode_into_libs" != yes || test "$build_old_libs" = yes || test "$link_static" = yes; }; then # Extract -R from dependency_libs temp_deplibs= for libdir in $dependency_libs; do case $libdir in -R*) func_stripname '-R' '' "$libdir" temp_xrpath=$func_stripname_result case " $xrpath " in *" $temp_xrpath "*) ;; *) xrpath="$xrpath $temp_xrpath";; esac;; *) temp_deplibs="$temp_deplibs $libdir";; esac done dependency_libs="$temp_deplibs" fi newlib_search_path="$newlib_search_path $absdir" # Link against this library test "$link_static" = no && newdependency_libs="$abs_ladir/$laname $newdependency_libs" # ... and its dependency_libs tmp_libs= for deplib in $dependency_libs; do newdependency_libs="$deplib $newdependency_libs" if $opt_duplicate_deps ; then case "$tmp_libs " in *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;; esac fi tmp_libs="$tmp_libs $deplib" done if test "$link_all_deplibs" != no; then # Add the search paths of all dependency libraries for deplib in $dependency_libs; do path= case $deplib in -L*) path="$deplib" ;; *.la) func_dirname "$deplib" "" "." dir="$func_dirname_result" # We need an absolute path. case $dir in [\\/]* | [A-Za-z]:[\\/]*) absdir="$dir" ;; *) absdir=`cd "$dir" && pwd` if test -z "$absdir"; then func_warning "cannot determine absolute directory name of \`$dir'" absdir="$dir" fi ;; esac if $GREP "^installed=no" $deplib > /dev/null; then case $host in *-*-darwin*) depdepl= eval deplibrary_names=`${SED} -n -e 's/^library_names=\(.*\)$/\1/p' $deplib` if test -n "$deplibrary_names" ; then for tmp in $deplibrary_names ; do depdepl=$tmp done if test -f "$absdir/$objdir/$depdepl" ; then depdepl="$absdir/$objdir/$depdepl" darwin_install_name=`${OTOOL} -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'` if test -z "$darwin_install_name"; then darwin_install_name=`${OTOOL64} -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'` fi compiler_flags="$compiler_flags ${wl}-dylib_file ${wl}${darwin_install_name}:${depdepl}" linker_flags="$linker_flags -dylib_file ${darwin_install_name}:${depdepl}" path= fi fi ;; *) path="-L$absdir/$objdir" ;; esac else eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $deplib` test -z "$libdir" && \ func_fatal_error "\`$deplib' is not a valid libtool archive" test "$absdir" != "$libdir" && \ func_warning "\`$deplib' seems to be moved" path="-L$absdir" fi ;; esac case " $deplibs " in *" $path "*) ;; *) deplibs="$path $deplibs" ;; esac done fi # link_all_deplibs != no fi # linkmode = lib done # for deplib in $libs if test "$pass" = link; then if test "$linkmode" = "prog"; then compile_deplibs="$new_inherited_linker_flags $compile_deplibs" finalize_deplibs="$new_inherited_linker_flags $finalize_deplibs" else compiler_flags="$compiler_flags "`$ECHO "X $new_inherited_linker_flags" | $Xsed -e 's% \([^ $]*\).ltframework% -framework \1%g'` fi fi dependency_libs="$newdependency_libs" if test "$pass" = dlpreopen; then # Link the dlpreopened libraries before other libraries for deplib in $save_deplibs; do deplibs="$deplib $deplibs" done fi if test "$pass" != dlopen; then if test "$pass" != conv; then # Make sure lib_search_path contains only unique directories. lib_search_path= for dir in $newlib_search_path; do case "$lib_search_path " in *" $dir "*) ;; *) lib_search_path="$lib_search_path $dir" ;; esac done newlib_search_path= fi if test "$linkmode,$pass" != "prog,link"; then vars="deplibs" else vars="compile_deplibs finalize_deplibs" fi for var in $vars dependency_libs; do # Add libraries to $var in reverse order eval tmp_libs=\"\$$var\" new_libs= for deplib in $tmp_libs; do # FIXME: Pedantically, this is the right thing to do, so # that some nasty dependency loop isn't accidentally # broken: #new_libs="$deplib $new_libs" # Pragmatically, this seems to cause very few problems in # practice: case $deplib in -L*) new_libs="$deplib $new_libs" ;; -R*) ;; *) # And here is the reason: when a library appears more # than once as an explicit dependence of a library, or # is implicitly linked in more than once by the # compiler, it is considered special, and multiple # occurrences thereof are not removed. Compare this # with having the same library being listed as a # dependency of multiple other libraries: in this case, # we know (pedantically, we assume) the library does not # need to be listed more than once, so we keep only the # last copy. This is not always right, but it is rare # enough that we require users that really mean to play # such unportable linking tricks to link the library # using -Wl,-lname, so that libtool does not consider it # for duplicate removal. case " $specialdeplibs " in *" $deplib "*) new_libs="$deplib $new_libs" ;; *) case " $new_libs " in *" $deplib "*) ;; *) new_libs="$deplib $new_libs" ;; esac ;; esac ;; esac done tmp_libs= for deplib in $new_libs; do case $deplib in -L*) case " $tmp_libs " in *" $deplib "*) ;; *) tmp_libs="$tmp_libs $deplib" ;; esac ;; *) tmp_libs="$tmp_libs $deplib" ;; esac done eval $var=\"$tmp_libs\" done # for var fi # Last step: remove runtime libs from dependency_libs # (they stay in deplibs) tmp_libs= for i in $dependency_libs ; do case " $predeps $postdeps $compiler_lib_search_path " in *" $i "*) i="" ;; esac if test -n "$i" ; then tmp_libs="$tmp_libs $i" fi done dependency_libs=$tmp_libs done # for pass if test "$linkmode" = prog; then dlfiles="$newdlfiles" fi if test "$linkmode" = prog || test "$linkmode" = lib; then dlprefiles="$newdlprefiles" fi case $linkmode in oldlib) if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then func_warning "\`-dlopen' is ignored for archives" fi case " $deplibs" in *\ -l* | *\ -L*) func_warning "\`-l' and \`-L' are ignored for archives" ;; esac test -n "$rpath" && \ func_warning "\`-rpath' is ignored for archives" test -n "$xrpath" && \ func_warning "\`-R' is ignored for archives" test -n "$vinfo" && \ func_warning "\`-version-info/-version-number' is ignored for archives" test -n "$release" && \ func_warning "\`-release' is ignored for archives" test -n "$export_symbols$export_symbols_regex" && \ func_warning "\`-export-symbols' is ignored for archives" # Now set the variables for building old libraries. build_libtool_libs=no oldlibs="$output" objs="$objs$old_deplibs" ;; lib) # Make sure we only generate libraries of the form `libNAME.la'. case $outputname in lib*) func_stripname 'lib' '.la' "$outputname" name=$func_stripname_result eval shared_ext=\"$shrext_cmds\" eval libname=\"$libname_spec\" ;; *) test "$module" = no && \ func_fatal_help "libtool library \`$output' must begin with \`lib'" if test "$need_lib_prefix" != no; then # Add the "lib" prefix for modules if required func_stripname '' '.la' "$outputname" name=$func_stripname_result eval shared_ext=\"$shrext_cmds\" eval libname=\"$libname_spec\" else func_stripname '' '.la' "$outputname" libname=$func_stripname_result fi ;; esac if test -n "$objs"; then if test "$deplibs_check_method" != pass_all; then func_fatal_error "cannot build libtool library \`$output' from non-libtool objects on this host:$objs" else $ECHO $ECHO "*** Warning: Linking the shared library $output against the non-libtool" $ECHO "*** objects $objs is not portable!" libobjs="$libobjs $objs" fi fi test "$dlself" != no && \ func_warning "\`-dlopen self' is ignored for libtool libraries" set dummy $rpath shift test "$#" -gt 1 && \ func_warning "ignoring multiple \`-rpath's for a libtool library" install_libdir="$1" oldlibs= if test -z "$rpath"; then if test "$build_libtool_libs" = yes; then # Building a libtool convenience library. # Some compilers have problems with a `.al' extension so # convenience libraries should have the same extension an # archive normally would. oldlibs="$output_objdir/$libname.$libext $oldlibs" build_libtool_libs=convenience build_old_libs=yes fi test -n "$vinfo" && \ func_warning "\`-version-info/-version-number' is ignored for convenience libraries" test -n "$release" && \ func_warning "\`-release' is ignored for convenience libraries" else # Parse the version information argument. save_ifs="$IFS"; IFS=':' set dummy $vinfo 0 0 0 shift IFS="$save_ifs" test -n "$7" && \ func_fatal_help "too many parameters to \`-version-info'" # convert absolute version numbers to libtool ages # this retains compatibility with .la files and attempts # to make the code below a bit more comprehensible case $vinfo_number in yes) number_major="$1" number_minor="$2" number_revision="$3" # # There are really only two kinds -- those that # use the current revision as the major version # and those that subtract age and use age as # a minor version. But, then there is irix # which has an extra 1 added just for fun # case $version_type in darwin|linux|osf|windows|none) func_arith $number_major + $number_minor current=$func_arith_result age="$number_minor" revision="$number_revision" ;; freebsd-aout|freebsd-elf|sunos) current="$number_major" revision="$number_minor" age="0" ;; irix|nonstopux) func_arith $number_major + $number_minor current=$func_arith_result age="$number_minor" revision="$number_minor" lt_irix_increment=no ;; *) func_fatal_configuration "$modename: unknown library version type \`$version_type'" ;; esac ;; no) current="$1" revision="$2" age="$3" ;; esac # Check that each of the things are valid numbers. case $current in 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; *) func_error "CURRENT \`$current' must be a nonnegative integer" func_fatal_error "\`$vinfo' is not valid version information" ;; esac case $revision in 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; *) func_error "REVISION \`$revision' must be a nonnegative integer" func_fatal_error "\`$vinfo' is not valid version information" ;; esac case $age in 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; *) func_error "AGE \`$age' must be a nonnegative integer" func_fatal_error "\`$vinfo' is not valid version information" ;; esac if test "$age" -gt "$current"; then func_error "AGE \`$age' is greater than the current interface number \`$current'" func_fatal_error "\`$vinfo' is not valid version information" fi # Calculate the version variables. major= versuffix= verstring= case $version_type in none) ;; darwin) # Like Linux, but with the current version available in # verstring for coding it into the library header func_arith $current - $age major=.$func_arith_result versuffix="$major.$age.$revision" # Darwin ld doesn't like 0 for these options... func_arith $current + 1 minor_current=$func_arith_result xlcverstring="${wl}-compatibility_version ${wl}$minor_current ${wl}-current_version ${wl}$minor_current.$revision" verstring="-compatibility_version $minor_current -current_version $minor_current.$revision" ;; freebsd-aout) major=".$current" versuffix=".$current.$revision"; ;; freebsd-elf) major=".$current" versuffix=".$current" ;; irix | nonstopux) if test "X$lt_irix_increment" = "Xno"; then func_arith $current - $age else func_arith $current - $age + 1 fi major=$func_arith_result case $version_type in nonstopux) verstring_prefix=nonstopux ;; *) verstring_prefix=sgi ;; esac verstring="$verstring_prefix$major.$revision" # Add in all the interfaces that we are compatible with. loop=$revision while test "$loop" -ne 0; do func_arith $revision - $loop iface=$func_arith_result func_arith $loop - 1 loop=$func_arith_result verstring="$verstring_prefix$major.$iface:$verstring" done # Before this point, $major must not contain `.'. major=.$major versuffix="$major.$revision" ;; linux) func_arith $current - $age major=.$func_arith_result versuffix="$major.$age.$revision" ;; osf) func_arith $current - $age major=.$func_arith_result versuffix=".$current.$age.$revision" verstring="$current.$age.$revision" # Add in all the interfaces that we are compatible with. loop=$age while test "$loop" -ne 0; do func_arith $current - $loop iface=$func_arith_result func_arith $loop - 1 loop=$func_arith_result verstring="$verstring:${iface}.0" done # Make executables depend on our current version. verstring="$verstring:${current}.0" ;; qnx) major=".$current" versuffix=".$current" ;; sunos) major=".$current" versuffix=".$current.$revision" ;; windows) # Use '-' rather than '.', since we only want one # extension on DOS 8.3 filesystems. func_arith $current - $age major=$func_arith_result versuffix="-$major" ;; *) func_fatal_configuration "unknown library version type \`$version_type'" ;; esac # Clear the version info if we defaulted, and they specified a release. if test -z "$vinfo" && test -n "$release"; then major= case $version_type in darwin) # we can't check for "0.0" in archive_cmds due to quoting # problems, so we reset it completely verstring= ;; *) verstring="0.0" ;; esac if test "$need_version" = no; then versuffix= else versuffix=".0.0" fi fi # Remove version info from name if versioning should be avoided if test "$avoid_version" = yes && test "$need_version" = no; then major= versuffix= verstring="" fi # Check to see if the archive will have undefined symbols. if test "$allow_undefined" = yes; then if test "$allow_undefined_flag" = unsupported; then func_warning "undefined symbols not allowed in $host shared libraries" build_libtool_libs=no build_old_libs=yes fi else # Don't allow undefined symbols. allow_undefined_flag="$no_undefined_flag" fi fi func_generate_dlsyms "$libname" "$libname" "yes" libobjs="$libobjs $symfileobj" test "X$libobjs" = "X " && libobjs= if test "$mode" != relink; then # Remove our outputs, but don't remove object files since they # may have been created when compiling PIC objects. removelist= tempremovelist=`$ECHO "$output_objdir/*"` for p in $tempremovelist; do case $p in *.$objext | *.gcno) ;; $output_objdir/$outputname | $output_objdir/$libname.* | $output_objdir/${libname}${release}.*) if test "X$precious_files_regex" != "X"; then if $ECHO "$p" | $EGREP -e "$precious_files_regex" >/dev/null 2>&1 then continue fi fi removelist="$removelist $p" ;; *) ;; esac done test -n "$removelist" && \ func_show_eval "${RM}r \$removelist" fi # Now set the variables for building old libraries. if test "$build_old_libs" = yes && test "$build_libtool_libs" != convenience ; then oldlibs="$oldlibs $output_objdir/$libname.$libext" # Transform .lo files to .o files. oldobjs="$objs "`$ECHO "X$libobjs" | $SP2NL | $Xsed -e '/\.'${libext}'$/d' -e "$lo2o" | $NL2SP` fi # Eliminate all temporary directories. #for path in $notinst_path; do # lib_search_path=`$ECHO "X$lib_search_path " | $Xsed -e "s% $path % %g"` # deplibs=`$ECHO "X$deplibs " | $Xsed -e "s% -L$path % %g"` # dependency_libs=`$ECHO "X$dependency_libs " | $Xsed -e "s% -L$path % %g"` #done if test -n "$xrpath"; then # If the user specified any rpath flags, then add them. temp_xrpath= for libdir in $xrpath; do temp_xrpath="$temp_xrpath -R$libdir" case "$finalize_rpath " in *" $libdir "*) ;; *) finalize_rpath="$finalize_rpath $libdir" ;; esac done if test "$hardcode_into_libs" != yes || test "$build_old_libs" = yes; then dependency_libs="$temp_xrpath $dependency_libs" fi fi # Make sure dlfiles contains only unique files that won't be dlpreopened old_dlfiles="$dlfiles" dlfiles= for lib in $old_dlfiles; do case " $dlprefiles $dlfiles " in *" $lib "*) ;; *) dlfiles="$dlfiles $lib" ;; esac done # Make sure dlprefiles contains only unique files old_dlprefiles="$dlprefiles" dlprefiles= for lib in $old_dlprefiles; do case "$dlprefiles " in *" $lib "*) ;; *) dlprefiles="$dlprefiles $lib" ;; esac done if test "$build_libtool_libs" = yes; then if test -n "$rpath"; then case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-beos* | *-cegcc*) # these systems don't actually have a c library (as such)! ;; *-*-rhapsody* | *-*-darwin1.[012]) # Rhapsody C library is in the System framework deplibs="$deplibs System.ltframework" ;; *-*-netbsd*) # Don't link with libc until the a.out ld.so is fixed. ;; *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) # Do not include libc due to us having libc/libc_r. ;; *-*-sco3.2v5* | *-*-sco5v6*) # Causes problems with __ctype ;; *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) # Compiler inserts libc in the correct place for threads to work ;; *) # Add libc to deplibs on all other systems if necessary. if test "$build_libtool_need_lc" = "yes"; then deplibs="$deplibs -lc" fi ;; esac fi # Transform deplibs into only deplibs that can be linked in shared. name_save=$name libname_save=$libname release_save=$release versuffix_save=$versuffix major_save=$major # I'm not sure if I'm treating the release correctly. I think # release should show up in the -l (ie -lgmp5) so we don't want to # add it in twice. Is that correct? release="" versuffix="" major="" newdeplibs= droppeddeps=no case $deplibs_check_method in pass_all) # Don't check for shared/static. Everything works. # This might be a little naive. We might want to check # whether the library exists or not. But this is on # osf3 & osf4 and I'm not really sure... Just # implementing what was already the behavior. newdeplibs=$deplibs ;; test_compile) # This code stresses the "libraries are programs" paradigm to its # limits. Maybe even breaks it. We compile a program, linking it # against the deplibs as a proxy for the library. Then we can check # whether they linked in statically or dynamically with ldd. $opt_dry_run || $RM conftest.c cat > conftest.c </dev/null` for potent_lib in $potential_libs; do # Follow soft links. if ls -lLd "$potent_lib" 2>/dev/null | $GREP " -> " >/dev/null; then continue fi # The statement above tries to avoid entering an # endless loop below, in case of cyclic links. # We might still enter an endless loop, since a link # loop can be closed while we follow links, # but so what? potlib="$potent_lib" while test -h "$potlib" 2>/dev/null; do potliblink=`ls -ld $potlib | ${SED} 's/.* -> //'` case $potliblink in [\\/]* | [A-Za-z]:[\\/]*) potlib="$potliblink";; *) potlib=`$ECHO "X$potlib" | $Xsed -e 's,[^/]*$,,'`"$potliblink";; esac done if eval $file_magic_cmd \"\$potlib\" 2>/dev/null | $SED -e 10q | $EGREP "$file_magic_regex" > /dev/null; then newdeplibs="$newdeplibs $a_deplib" a_deplib="" break 2 fi done done fi if test -n "$a_deplib" ; then droppeddeps=yes $ECHO $ECHO "*** Warning: linker path does not have real file for library $a_deplib." $ECHO "*** I have the capability to make that library automatically link in when" $ECHO "*** you link to this library. But I can only do this if you have a" $ECHO "*** shared version of the library, which you do not appear to have" $ECHO "*** because I did check the linker path looking for a file starting" if test -z "$potlib" ; then $ECHO "*** with $libname but no candidates were found. (...for file magic test)" else $ECHO "*** with $libname and none of the candidates passed a file format test" $ECHO "*** using a file magic. Last file checked: $potlib" fi fi ;; *) # Add a -L argument. newdeplibs="$newdeplibs $a_deplib" ;; esac done # Gone through all deplibs. ;; match_pattern*) set dummy $deplibs_check_method; shift match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"` for a_deplib in $deplibs; do case $a_deplib in -l*) func_stripname -l '' "$a_deplib" name=$func_stripname_result if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then case " $predeps $postdeps " in *" $a_deplib "*) newdeplibs="$newdeplibs $a_deplib" a_deplib="" ;; esac fi if test -n "$a_deplib" ; then libname=`eval "\\$ECHO \"$libname_spec\""` for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do potential_libs=`ls $i/$libname[.-]* 2>/dev/null` for potent_lib in $potential_libs; do potlib="$potent_lib" # see symlink-check above in file_magic test if eval "\$ECHO \"X$potent_lib\"" 2>/dev/null | $Xsed -e 10q | \ $EGREP "$match_pattern_regex" > /dev/null; then newdeplibs="$newdeplibs $a_deplib" a_deplib="" break 2 fi done done fi if test -n "$a_deplib" ; then droppeddeps=yes $ECHO $ECHO "*** Warning: linker path does not have real file for library $a_deplib." $ECHO "*** I have the capability to make that library automatically link in when" $ECHO "*** you link to this library. But I can only do this if you have a" $ECHO "*** shared version of the library, which you do not appear to have" $ECHO "*** because I did check the linker path looking for a file starting" if test -z "$potlib" ; then $ECHO "*** with $libname but no candidates were found. (...for regex pattern test)" else $ECHO "*** with $libname and none of the candidates passed a file format test" $ECHO "*** using a regex pattern. Last file checked: $potlib" fi fi ;; *) # Add a -L argument. newdeplibs="$newdeplibs $a_deplib" ;; esac done # Gone through all deplibs. ;; none | unknown | *) newdeplibs="" tmp_deplibs=`$ECHO "X $deplibs" | $Xsed \ -e 's/ -lc$//' -e 's/ -[LR][^ ]*//g'` if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then for i in $predeps $postdeps ; do # can't use Xsed below, because $i might contain '/' tmp_deplibs=`$ECHO "X $tmp_deplibs" | $Xsed -e "s,$i,,"` done fi if $ECHO "X $tmp_deplibs" | $Xsed -e 's/[ ]//g' | $GREP . >/dev/null; then $ECHO if test "X$deplibs_check_method" = "Xnone"; then $ECHO "*** Warning: inter-library dependencies are not supported in this platform." else $ECHO "*** Warning: inter-library dependencies are not known to be supported." fi $ECHO "*** All declared inter-library dependencies are being dropped." droppeddeps=yes fi ;; esac versuffix=$versuffix_save major=$major_save release=$release_save libname=$libname_save name=$name_save case $host in *-*-rhapsody* | *-*-darwin1.[012]) # On Rhapsody replace the C library with the System framework newdeplibs=`$ECHO "X $newdeplibs" | $Xsed -e 's/ -lc / System.ltframework /'` ;; esac if test "$droppeddeps" = yes; then if test "$module" = yes; then $ECHO $ECHO "*** Warning: libtool could not satisfy all declared inter-library" $ECHO "*** dependencies of module $libname. Therefore, libtool will create" $ECHO "*** a static module, that should work as long as the dlopening" $ECHO "*** application is linked with the -dlopen flag." if test -z "$global_symbol_pipe"; then $ECHO $ECHO "*** However, this would only work if libtool was able to extract symbol" $ECHO "*** lists from a program, using \`nm' or equivalent, but libtool could" $ECHO "*** not find such a program. So, this module is probably useless." $ECHO "*** \`nm' from GNU binutils and a full rebuild may help." fi if test "$build_old_libs" = no; then oldlibs="$output_objdir/$libname.$libext" build_libtool_libs=module build_old_libs=yes else build_libtool_libs=no fi else $ECHO "*** The inter-library dependencies that have been dropped here will be" $ECHO "*** automatically added whenever a program is linked with this library" $ECHO "*** or is declared to -dlopen it." if test "$allow_undefined" = no; then $ECHO $ECHO "*** Since this library must not contain undefined symbols," $ECHO "*** because either the platform does not support them or" $ECHO "*** it was explicitly requested with -no-undefined," $ECHO "*** libtool will only create a static version of it." if test "$build_old_libs" = no; then oldlibs="$output_objdir/$libname.$libext" build_libtool_libs=module build_old_libs=yes else build_libtool_libs=no fi fi fi fi # Done checking deplibs! deplibs=$newdeplibs fi # Time to change all our "foo.ltframework" stuff back to "-framework foo" case $host in *-*-darwin*) newdeplibs=`$ECHO "X $newdeplibs" | $Xsed -e 's% \([^ $]*\).ltframework% -framework \1%g'` new_inherited_linker_flags=`$ECHO "X $new_inherited_linker_flags" | $Xsed -e 's% \([^ $]*\).ltframework% -framework \1%g'` deplibs=`$ECHO "X $deplibs" | $Xsed -e 's% \([^ $]*\).ltframework% -framework \1%g'` ;; esac # move library search paths that coincide with paths to not yet # installed libraries to the beginning of the library search list new_libs= for path in $notinst_path; do case " $new_libs " in *" -L$path/$objdir "*) ;; *) case " $deplibs " in *" -L$path/$objdir "*) new_libs="$new_libs -L$path/$objdir" ;; esac ;; esac done for deplib in $deplibs; do case $deplib in -L*) case " $new_libs " in *" $deplib "*) ;; *) new_libs="$new_libs $deplib" ;; esac ;; *) new_libs="$new_libs $deplib" ;; esac done deplibs="$new_libs" # All the library-specific variables (install_libdir is set above). library_names= old_library= dlname= # Test again, we may have decided not to build it any more if test "$build_libtool_libs" = yes; then if test "$hardcode_into_libs" = yes; then # Hardcode the library paths hardcode_libdirs= dep_rpath= rpath="$finalize_rpath" test "$mode" != relink && rpath="$compile_rpath$rpath" for libdir in $rpath; do if test -n "$hardcode_libdir_flag_spec"; then if test -n "$hardcode_libdir_separator"; then if test -z "$hardcode_libdirs"; then hardcode_libdirs="$libdir" else # Just accumulate the unique libdirs. case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) ;; *) hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir" ;; esac fi else eval flag=\"$hardcode_libdir_flag_spec\" dep_rpath="$dep_rpath $flag" fi elif test -n "$runpath_var"; then case "$perm_rpath " in *" $libdir "*) ;; *) perm_rpath="$perm_rpath $libdir" ;; esac fi done # Substitute the hardcoded libdirs into the rpath. if test -n "$hardcode_libdir_separator" && test -n "$hardcode_libdirs"; then libdir="$hardcode_libdirs" if test -n "$hardcode_libdir_flag_spec_ld"; then eval dep_rpath=\"$hardcode_libdir_flag_spec_ld\" else eval dep_rpath=\"$hardcode_libdir_flag_spec\" fi fi if test -n "$runpath_var" && test -n "$perm_rpath"; then # We should set the runpath_var. rpath= for dir in $perm_rpath; do rpath="$rpath$dir:" done eval "$runpath_var='$rpath\$$runpath_var'; export $runpath_var" fi test -n "$dep_rpath" && deplibs="$dep_rpath $deplibs" fi shlibpath="$finalize_shlibpath" test "$mode" != relink && shlibpath="$compile_shlibpath$shlibpath" if test -n "$shlibpath"; then eval "$shlibpath_var='$shlibpath\$$shlibpath_var'; export $shlibpath_var" fi # Get the real and link names of the library. eval shared_ext=\"$shrext_cmds\" eval library_names=\"$library_names_spec\" set dummy $library_names shift realname="$1" shift if test -n "$soname_spec"; then eval soname=\"$soname_spec\" else soname="$realname" fi if test -z "$dlname"; then dlname=$soname fi lib="$output_objdir/$realname" linknames= for link do linknames="$linknames $link" done # Use standard objects if they are pic test -z "$pic_flag" && libobjs=`$ECHO "X$libobjs" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP` test "X$libobjs" = "X " && libobjs= delfiles= if test -n "$export_symbols" && test -n "$include_expsyms"; then $opt_dry_run || cp "$export_symbols" "$output_objdir/$libname.uexp" export_symbols="$output_objdir/$libname.uexp" delfiles="$delfiles $export_symbols" fi orig_export_symbols= case $host_os in cygwin* | mingw* | cegcc*) if test -n "$export_symbols" && test -z "$export_symbols_regex"; then # exporting using user supplied symfile if test "x`$SED 1q $export_symbols`" != xEXPORTS; then # and it's NOT already a .def file. Must figure out # which of the given symbols are data symbols and tag # them as such. So, trigger use of export_symbols_cmds. # export_symbols gets reassigned inside the "prepare # the list of exported symbols" if statement, so the # include_expsyms logic still works. orig_export_symbols="$export_symbols" export_symbols= always_export_symbols=yes fi fi ;; esac # Prepare the list of exported symbols if test -z "$export_symbols"; then if test "$always_export_symbols" = yes || test -n "$export_symbols_regex"; then func_verbose "generating symbol list for \`$libname.la'" export_symbols="$output_objdir/$libname.exp" $opt_dry_run || $RM $export_symbols cmds=$export_symbols_cmds save_ifs="$IFS"; IFS='~' for cmd in $cmds; do IFS="$save_ifs" eval cmd=\"$cmd\" func_len " $cmd" len=$func_len_result if test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then func_show_eval "$cmd" 'exit $?' skipped_export=false else # The command line is too long to execute in one step. func_verbose "using reloadable object file for export list..." skipped_export=: # Break out early, otherwise skipped_export may be # set to false by a later but shorter cmd. break fi done IFS="$save_ifs" if test -n "$export_symbols_regex" && test "X$skipped_export" != "X:"; then func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' func_show_eval '$MV "${export_symbols}T" "$export_symbols"' fi fi fi if test -n "$export_symbols" && test -n "$include_expsyms"; then tmp_export_symbols="$export_symbols" test -n "$orig_export_symbols" && tmp_export_symbols="$orig_export_symbols" $opt_dry_run || eval '$ECHO "X$include_expsyms" | $Xsed | $SP2NL >> "$tmp_export_symbols"' fi if test "X$skipped_export" != "X:" && test -n "$orig_export_symbols"; then # The given exports_symbols file has to be filtered, so filter it. func_verbose "filter symbol list for \`$libname.la' to tag DATA exports" # FIXME: $output_objdir/$libname.filter potentially contains lots of # 's' commands which not all seds can handle. GNU sed should be fine # though. Also, the filter scales superlinearly with the number of # global variables. join(1) would be nice here, but unfortunately # isn't a blessed tool. $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter delfiles="$delfiles $export_symbols $output_objdir/$libname.filter" export_symbols=$output_objdir/$libname.def $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols fi tmp_deplibs= for test_deplib in $deplibs; do case " $convenience " in *" $test_deplib "*) ;; *) tmp_deplibs="$tmp_deplibs $test_deplib" ;; esac done deplibs="$tmp_deplibs" if test -n "$convenience"; then if test -n "$whole_archive_flag_spec" && test "$compiler_needs_object" = yes && test -z "$libobjs"; then # extract the archives, so we have objects to list. # TODO: could optimize this to just extract one archive. whole_archive_flag_spec= fi if test -n "$whole_archive_flag_spec"; then save_libobjs=$libobjs eval libobjs=\"\$libobjs $whole_archive_flag_spec\" test "X$libobjs" = "X " && libobjs= else gentop="$output_objdir/${outputname}x" generated="$generated $gentop" func_extract_archives $gentop $convenience libobjs="$libobjs $func_extract_archives_result" test "X$libobjs" = "X " && libobjs= fi fi if test "$thread_safe" = yes && test -n "$thread_safe_flag_spec"; then eval flag=\"$thread_safe_flag_spec\" linker_flags="$linker_flags $flag" fi # Make a backup of the uninstalled library when relinking if test "$mode" = relink; then $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}U && $MV $realname ${realname}U)' || exit $? fi # Do each of the archive commands. if test "$module" = yes && test -n "$module_cmds" ; then if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then eval test_cmds=\"$module_expsym_cmds\" cmds=$module_expsym_cmds else eval test_cmds=\"$module_cmds\" cmds=$module_cmds fi else if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then eval test_cmds=\"$archive_expsym_cmds\" cmds=$archive_expsym_cmds else eval test_cmds=\"$archive_cmds\" cmds=$archive_cmds fi fi if test "X$skipped_export" != "X:" && func_len " $test_cmds" && len=$func_len_result && test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then : else # The command line is too long to link in one step, link piecewise # or, if using GNU ld and skipped_export is not :, use a linker # script. # Save the value of $output and $libobjs because we want to # use them later. If we have whole_archive_flag_spec, we # want to use save_libobjs as it was before # whole_archive_flag_spec was expanded, because we can't # assume the linker understands whole_archive_flag_spec. # This may have to be revisited, in case too many # convenience libraries get linked in and end up exceeding # the spec. if test -z "$convenience" || test -z "$whole_archive_flag_spec"; then save_libobjs=$libobjs fi save_output=$output output_la=`$ECHO "X$output" | $Xsed -e "$basename"` # Clear the reloadable object creation command queue and # initialize k to one. test_cmds= concat_cmds= objlist= last_robj= k=1 if test -n "$save_libobjs" && test "X$skipped_export" != "X:" && test "$with_gnu_ld" = yes; then output=${output_objdir}/${output_la}.lnkscript func_verbose "creating GNU ld script: $output" $ECHO 'INPUT (' > $output for obj in $save_libobjs do $ECHO "$obj" >> $output done $ECHO ')' >> $output delfiles="$delfiles $output" elif test -n "$save_libobjs" && test "X$skipped_export" != "X:" && test "X$file_list_spec" != X; then output=${output_objdir}/${output_la}.lnk func_verbose "creating linker input file list: $output" : > $output set x $save_libobjs shift firstobj= if test "$compiler_needs_object" = yes; then firstobj="$1 " shift fi for obj do $ECHO "$obj" >> $output done delfiles="$delfiles $output" output=$firstobj\"$file_list_spec$output\" else if test -n "$save_libobjs"; then func_verbose "creating reloadable object files..." output=$output_objdir/$output_la-${k}.$objext eval test_cmds=\"$reload_cmds\" func_len " $test_cmds" len0=$func_len_result len=$len0 # Loop over the list of objects to be linked. for obj in $save_libobjs do func_len " $obj" func_arith $len + $func_len_result len=$func_arith_result if test "X$objlist" = X || test "$len" -lt "$max_cmd_len"; then func_append objlist " $obj" else # The command $test_cmds is almost too long, add a # command to the queue. if test "$k" -eq 1 ; then # The first file doesn't have a previous command to add. eval concat_cmds=\"$reload_cmds $objlist $last_robj\" else # All subsequent reloadable object files will link in # the last one created. eval concat_cmds=\"\$concat_cmds~$reload_cmds $objlist $last_robj~\$RM $last_robj\" fi last_robj=$output_objdir/$output_la-${k}.$objext func_arith $k + 1 k=$func_arith_result output=$output_objdir/$output_la-${k}.$objext objlist=$obj func_len " $last_robj" func_arith $len0 + $func_len_result len=$func_arith_result fi done # Handle the remaining objects by creating one last # reloadable object file. All subsequent reloadable object # files will link in the last one created. test -z "$concat_cmds" || concat_cmds=$concat_cmds~ eval concat_cmds=\"\${concat_cmds}$reload_cmds $objlist $last_robj\" if test -n "$last_robj"; then eval concat_cmds=\"\${concat_cmds}~\$RM $last_robj\" fi delfiles="$delfiles $output" else output= fi if ${skipped_export-false}; then func_verbose "generating symbol list for \`$libname.la'" export_symbols="$output_objdir/$libname.exp" $opt_dry_run || $RM $export_symbols libobjs=$output # Append the command to create the export file. test -z "$concat_cmds" || concat_cmds=$concat_cmds~ eval concat_cmds=\"\$concat_cmds$export_symbols_cmds\" if test -n "$last_robj"; then eval concat_cmds=\"\$concat_cmds~\$RM $last_robj\" fi fi test -n "$save_libobjs" && func_verbose "creating a temporary reloadable object file: $output" # Loop through the commands generated above and execute them. save_ifs="$IFS"; IFS='~' for cmd in $concat_cmds; do IFS="$save_ifs" $opt_silent || { func_quote_for_expand "$cmd" eval "func_echo $func_quote_for_expand_result" } $opt_dry_run || eval "$cmd" || { lt_exit=$? # Restore the uninstalled library and exit if test "$mode" = relink; then ( cd "$output_objdir" && \ $RM "${realname}T" && \ $MV "${realname}U" "$realname" ) fi exit $lt_exit } done IFS="$save_ifs" if test -n "$export_symbols_regex" && ${skipped_export-false}; then func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' func_show_eval '$MV "${export_symbols}T" "$export_symbols"' fi fi if ${skipped_export-false}; then if test -n "$export_symbols" && test -n "$include_expsyms"; then tmp_export_symbols="$export_symbols" test -n "$orig_export_symbols" && tmp_export_symbols="$orig_export_symbols" $opt_dry_run || eval '$ECHO "X$include_expsyms" | $Xsed | $SP2NL >> "$tmp_export_symbols"' fi if test -n "$orig_export_symbols"; then # The given exports_symbols file has to be filtered, so filter it. func_verbose "filter symbol list for \`$libname.la' to tag DATA exports" # FIXME: $output_objdir/$libname.filter potentially contains lots of # 's' commands which not all seds can handle. GNU sed should be fine # though. Also, the filter scales superlinearly with the number of # global variables. join(1) would be nice here, but unfortunately # isn't a blessed tool. $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter delfiles="$delfiles $export_symbols $output_objdir/$libname.filter" export_symbols=$output_objdir/$libname.def $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols fi fi libobjs=$output # Restore the value of output. output=$save_output if test -n "$convenience" && test -n "$whole_archive_flag_spec"; then eval libobjs=\"\$libobjs $whole_archive_flag_spec\" test "X$libobjs" = "X " && libobjs= fi # Expand the library linking commands again to reset the # value of $libobjs for piecewise linking. # Do each of the archive commands. if test "$module" = yes && test -n "$module_cmds" ; then if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then cmds=$module_expsym_cmds else cmds=$module_cmds fi else if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then cmds=$archive_expsym_cmds else cmds=$archive_cmds fi fi fi if test -n "$delfiles"; then # Append the command to remove temporary files to $cmds. eval cmds=\"\$cmds~\$RM $delfiles\" fi # Add any objects from preloaded convenience libraries if test -n "$dlprefiles"; then gentop="$output_objdir/${outputname}x" generated="$generated $gentop" func_extract_archives $gentop $dlprefiles libobjs="$libobjs $func_extract_archives_result" test "X$libobjs" = "X " && libobjs= fi save_ifs="$IFS"; IFS='~' for cmd in $cmds; do IFS="$save_ifs" eval cmd=\"$cmd\" $opt_silent || { func_quote_for_expand "$cmd" eval "func_echo $func_quote_for_expand_result" } $opt_dry_run || eval "$cmd" || { lt_exit=$? # Restore the uninstalled library and exit if test "$mode" = relink; then ( cd "$output_objdir" && \ $RM "${realname}T" && \ $MV "${realname}U" "$realname" ) fi exit $lt_exit } done IFS="$save_ifs" # Restore the uninstalled library and exit if test "$mode" = relink; then $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}T && $MV $realname ${realname}T && $MV ${realname}U $realname)' || exit $? if test -n "$convenience"; then if test -z "$whole_archive_flag_spec"; then func_show_eval '${RM}r "$gentop"' fi fi exit $EXIT_SUCCESS fi # Create links to the real library. for linkname in $linknames; do if test "$realname" != "$linkname"; then func_show_eval '(cd "$output_objdir" && $RM "$linkname" && $LN_S "$realname" "$linkname")' 'exit $?' fi done # If -module or -export-dynamic was specified, set the dlname. if test "$module" = yes || test "$export_dynamic" = yes; then # On all known operating systems, these are identical. dlname="$soname" fi fi ;; obj) if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then func_warning "\`-dlopen' is ignored for objects" fi case " $deplibs" in *\ -l* | *\ -L*) func_warning "\`-l' and \`-L' are ignored for objects" ;; esac test -n "$rpath" && \ func_warning "\`-rpath' is ignored for objects" test -n "$xrpath" && \ func_warning "\`-R' is ignored for objects" test -n "$vinfo" && \ func_warning "\`-version-info' is ignored for objects" test -n "$release" && \ func_warning "\`-release' is ignored for objects" case $output in *.lo) test -n "$objs$old_deplibs" && \ func_fatal_error "cannot build library object \`$output' from non-libtool objects" libobj=$output func_lo2o "$libobj" obj=$func_lo2o_result ;; *) libobj= obj="$output" ;; esac # Delete the old objects. $opt_dry_run || $RM $obj $libobj # Objects from convenience libraries. This assumes # single-version convenience libraries. Whenever we create # different ones for PIC/non-PIC, this we'll have to duplicate # the extraction. reload_conv_objs= gentop= # reload_cmds runs $LD directly, so let us get rid of # -Wl from whole_archive_flag_spec and hope we can get by with # turning comma into space.. wl= if test -n "$convenience"; then if test -n "$whole_archive_flag_spec"; then eval tmp_whole_archive_flags=\"$whole_archive_flag_spec\" reload_conv_objs=$reload_objs\ `$ECHO "X$tmp_whole_archive_flags" | $Xsed -e 's|,| |g'` else gentop="$output_objdir/${obj}x" generated="$generated $gentop" func_extract_archives $gentop $convenience reload_conv_objs="$reload_objs $func_extract_archives_result" fi fi # Create the old-style object. reload_objs="$objs$old_deplibs "`$ECHO "X$libobjs" | $SP2NL | $Xsed -e '/\.'${libext}$'/d' -e '/\.lib$/d' -e "$lo2o" | $NL2SP`" $reload_conv_objs" ### testsuite: skip nested quoting test output="$obj" func_execute_cmds "$reload_cmds" 'exit $?' # Exit if we aren't doing a library object file. if test -z "$libobj"; then if test -n "$gentop"; then func_show_eval '${RM}r "$gentop"' fi exit $EXIT_SUCCESS fi if test "$build_libtool_libs" != yes; then if test -n "$gentop"; then func_show_eval '${RM}r "$gentop"' fi # Create an invalid libtool object if no PIC, so that we don't # accidentally link it into a program. # $show "echo timestamp > $libobj" # $opt_dry_run || eval "echo timestamp > $libobj" || exit $? exit $EXIT_SUCCESS fi if test -n "$pic_flag" || test "$pic_mode" != default; then # Only do commands if we really have different PIC objects. reload_objs="$libobjs $reload_conv_objs" output="$libobj" func_execute_cmds "$reload_cmds" 'exit $?' fi if test -n "$gentop"; then func_show_eval '${RM}r "$gentop"' fi exit $EXIT_SUCCESS ;; prog) case $host in *cygwin*) func_stripname '' '.exe' "$output" output=$func_stripname_result.exe;; esac test -n "$vinfo" && \ func_warning "\`-version-info' is ignored for programs" test -n "$release" && \ func_warning "\`-release' is ignored for programs" test "$preload" = yes \ && test "$dlopen_support" = unknown \ && test "$dlopen_self" = unknown \ && test "$dlopen_self_static" = unknown && \ func_warning "\`LT_INIT([dlopen])' not used. Assuming no dlopen support." case $host in *-*-rhapsody* | *-*-darwin1.[012]) # On Rhapsody replace the C library is the System framework compile_deplibs=`$ECHO "X $compile_deplibs" | $Xsed -e 's/ -lc / System.ltframework /'` finalize_deplibs=`$ECHO "X $finalize_deplibs" | $Xsed -e 's/ -lc / System.ltframework /'` ;; esac case $host in *-*-darwin*) # Don't allow lazy linking, it breaks C++ global constructors # But is supposedly fixed on 10.4 or later (yay!). if test "$tagname" = CXX ; then case ${MACOSX_DEPLOYMENT_TARGET-10.0} in 10.[0123]) compile_command="$compile_command ${wl}-bind_at_load" finalize_command="$finalize_command ${wl}-bind_at_load" ;; esac fi # Time to change all our "foo.ltframework" stuff back to "-framework foo" compile_deplibs=`$ECHO "X $compile_deplibs" | $Xsed -e 's% \([^ $]*\).ltframework% -framework \1%g'` finalize_deplibs=`$ECHO "X $finalize_deplibs" | $Xsed -e 's% \([^ $]*\).ltframework% -framework \1%g'` ;; esac # move library search paths that coincide with paths to not yet # installed libraries to the beginning of the library search list new_libs= for path in $notinst_path; do case " $new_libs " in *" -L$path/$objdir "*) ;; *) case " $compile_deplibs " in *" -L$path/$objdir "*) new_libs="$new_libs -L$path/$objdir" ;; esac ;; esac done for deplib in $compile_deplibs; do case $deplib in -L*) case " $new_libs " in *" $deplib "*) ;; *) new_libs="$new_libs $deplib" ;; esac ;; *) new_libs="$new_libs $deplib" ;; esac done compile_deplibs="$new_libs" compile_command="$compile_command $compile_deplibs" finalize_command="$finalize_command $finalize_deplibs" if test -n "$rpath$xrpath"; then # If the user specified any rpath flags, then add them. for libdir in $rpath $xrpath; do # This is the magic to use -rpath. case "$finalize_rpath " in *" $libdir "*) ;; *) finalize_rpath="$finalize_rpath $libdir" ;; esac done fi # Now hardcode the library paths rpath= hardcode_libdirs= for libdir in $compile_rpath $finalize_rpath; do if test -n "$hardcode_libdir_flag_spec"; then if test -n "$hardcode_libdir_separator"; then if test -z "$hardcode_libdirs"; then hardcode_libdirs="$libdir" else # Just accumulate the unique libdirs. case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) ;; *) hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir" ;; esac fi else eval flag=\"$hardcode_libdir_flag_spec\" rpath="$rpath $flag" fi elif test -n "$runpath_var"; then case "$perm_rpath " in *" $libdir "*) ;; *) perm_rpath="$perm_rpath $libdir" ;; esac fi case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) testbindir=`${ECHO} "$libdir" | ${SED} -e 's*/lib$*/bin*'` case :$dllsearchpath: in *":$libdir:"*) ;; ::) dllsearchpath=$libdir;; *) dllsearchpath="$dllsearchpath:$libdir";; esac case :$dllsearchpath: in *":$testbindir:"*) ;; ::) dllsearchpath=$testbindir;; *) dllsearchpath="$dllsearchpath:$testbindir";; esac ;; esac done # Substitute the hardcoded libdirs into the rpath. if test -n "$hardcode_libdir_separator" && test -n "$hardcode_libdirs"; then libdir="$hardcode_libdirs" eval rpath=\" $hardcode_libdir_flag_spec\" fi compile_rpath="$rpath" rpath= hardcode_libdirs= for libdir in $finalize_rpath; do if test -n "$hardcode_libdir_flag_spec"; then if test -n "$hardcode_libdir_separator"; then if test -z "$hardcode_libdirs"; then hardcode_libdirs="$libdir" else # Just accumulate the unique libdirs. case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) ;; *) hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir" ;; esac fi else eval flag=\"$hardcode_libdir_flag_spec\" rpath="$rpath $flag" fi elif test -n "$runpath_var"; then case "$finalize_perm_rpath " in *" $libdir "*) ;; *) finalize_perm_rpath="$finalize_perm_rpath $libdir" ;; esac fi done # Substitute the hardcoded libdirs into the rpath. if test -n "$hardcode_libdir_separator" && test -n "$hardcode_libdirs"; then libdir="$hardcode_libdirs" eval rpath=\" $hardcode_libdir_flag_spec\" fi finalize_rpath="$rpath" if test -n "$libobjs" && test "$build_old_libs" = yes; then # Transform all the library objects into standard objects. compile_command=`$ECHO "X$compile_command" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP` finalize_command=`$ECHO "X$finalize_command" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP` fi func_generate_dlsyms "$outputname" "@PROGRAM@" "no" # template prelinking step if test -n "$prelink_cmds"; then func_execute_cmds "$prelink_cmds" 'exit $?' fi wrappers_required=yes case $host in *cygwin* | *mingw* ) if test "$build_libtool_libs" != yes; then wrappers_required=no fi ;; *cegcc) # Disable wrappers for cegcc, we are cross compiling anyway. wrappers_required=no ;; *) if test "$need_relink" = no || test "$build_libtool_libs" != yes; then wrappers_required=no fi ;; esac if test "$wrappers_required" = no; then # Replace the output file specification. compile_command=`$ECHO "X$compile_command" | $Xsed -e 's%@OUTPUT@%'"$output"'%g'` link_command="$compile_command$compile_rpath" # We have no uninstalled library dependencies, so finalize right now. exit_status=0 func_show_eval "$link_command" 'exit_status=$?' # Delete the generated files. if test -f "$output_objdir/${outputname}S.${objext}"; then func_show_eval '$RM "$output_objdir/${outputname}S.${objext}"' fi exit $exit_status fi if test -n "$compile_shlibpath$finalize_shlibpath"; then compile_command="$shlibpath_var=\"$compile_shlibpath$finalize_shlibpath\$$shlibpath_var\" $compile_command" fi if test -n "$finalize_shlibpath"; then finalize_command="$shlibpath_var=\"$finalize_shlibpath\$$shlibpath_var\" $finalize_command" fi compile_var= finalize_var= if test -n "$runpath_var"; then if test -n "$perm_rpath"; then # We should set the runpath_var. rpath= for dir in $perm_rpath; do rpath="$rpath$dir:" done compile_var="$runpath_var=\"$rpath\$$runpath_var\" " fi if test -n "$finalize_perm_rpath"; then # We should set the runpath_var. rpath= for dir in $finalize_perm_rpath; do rpath="$rpath$dir:" done finalize_var="$runpath_var=\"$rpath\$$runpath_var\" " fi fi if test "$no_install" = yes; then # We don't need to create a wrapper script. link_command="$compile_var$compile_command$compile_rpath" # Replace the output file specification. link_command=`$ECHO "X$link_command" | $Xsed -e 's%@OUTPUT@%'"$output"'%g'` # Delete the old output file. $opt_dry_run || $RM $output # Link the executable and exit func_show_eval "$link_command" 'exit $?' exit $EXIT_SUCCESS fi if test "$hardcode_action" = relink; then # Fast installation is not supported link_command="$compile_var$compile_command$compile_rpath" relink_command="$finalize_var$finalize_command$finalize_rpath" func_warning "this platform does not like uninstalled shared libraries" func_warning "\`$output' will be relinked during installation" else if test "$fast_install" != no; then link_command="$finalize_var$compile_command$finalize_rpath" if test "$fast_install" = yes; then relink_command=`$ECHO "X$compile_var$compile_command$compile_rpath" | $Xsed -e 's%@OUTPUT@%\$progdir/\$file%g'` else # fast_install is set to needless relink_command= fi else link_command="$compile_var$compile_command$compile_rpath" relink_command="$finalize_var$finalize_command$finalize_rpath" fi fi # Replace the output file specification. link_command=`$ECHO "X$link_command" | $Xsed -e 's%@OUTPUT@%'"$output_objdir/$outputname"'%g'` # Delete the old output files. $opt_dry_run || $RM $output $output_objdir/$outputname $output_objdir/lt-$outputname func_show_eval "$link_command" 'exit $?' # Now create the wrapper script. func_verbose "creating $output" # Quote the relink command for shipping. if test -n "$relink_command"; then # Preserve any variables that may affect compiler behavior for var in $variables_saved_for_relink; do if eval test -z \"\${$var+set}\"; then relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command" elif eval var_value=\$$var; test -z "$var_value"; then relink_command="$var=; export $var; $relink_command" else func_quote_for_eval "$var_value" relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command" fi done relink_command="(cd `pwd`; $relink_command)" relink_command=`$ECHO "X$relink_command" | $Xsed -e "$sed_quote_subst"` fi # Quote $ECHO for shipping. if test "X$ECHO" = "X$SHELL $progpath --fallback-echo"; then case $progpath in [\\/]* | [A-Za-z]:[\\/]*) qecho="$SHELL $progpath --fallback-echo";; *) qecho="$SHELL `pwd`/$progpath --fallback-echo";; esac qecho=`$ECHO "X$qecho" | $Xsed -e "$sed_quote_subst"` else qecho=`$ECHO "X$ECHO" | $Xsed -e "$sed_quote_subst"` fi # Only actually do things if not in dry run mode. $opt_dry_run || { # win32 will think the script is a binary if it has # a .exe suffix, so we strip it off here. case $output in *.exe) func_stripname '' '.exe' "$output" output=$func_stripname_result ;; esac # test for cygwin because mv fails w/o .exe extensions case $host in *cygwin*) exeext=.exe func_stripname '' '.exe' "$outputname" outputname=$func_stripname_result ;; *) exeext= ;; esac case $host in *cygwin* | *mingw* ) func_dirname_and_basename "$output" "" "." output_name=$func_basename_result output_path=$func_dirname_result cwrappersource="$output_path/$objdir/lt-$output_name.c" cwrapper="$output_path/$output_name.exe" $RM $cwrappersource $cwrapper trap "$RM $cwrappersource $cwrapper; exit $EXIT_FAILURE" 1 2 15 func_emit_cwrapperexe_src > $cwrappersource # The wrapper executable is built using the $host compiler, # because it contains $host paths and files. If cross- # compiling, it, like the target executable, must be # executed on the $host or under an emulation environment. $opt_dry_run || { $LTCC $LTCFLAGS -o $cwrapper $cwrappersource $STRIP $cwrapper } # Now, create the wrapper script for func_source use: func_ltwrapper_scriptname $cwrapper $RM $func_ltwrapper_scriptname_result trap "$RM $func_ltwrapper_scriptname_result; exit $EXIT_FAILURE" 1 2 15 $opt_dry_run || { # note: this script will not be executed, so do not chmod. if test "x$build" = "x$host" ; then $cwrapper --lt-dump-script > $func_ltwrapper_scriptname_result else func_emit_wrapper no > $func_ltwrapper_scriptname_result fi } ;; * ) $RM $output trap "$RM $output; exit $EXIT_FAILURE" 1 2 15 func_emit_wrapper no > $output chmod +x $output ;; esac } exit $EXIT_SUCCESS ;; esac # See if we need to build an old-fashioned archive. for oldlib in $oldlibs; do if test "$build_libtool_libs" = convenience; then oldobjs="$libobjs_save $symfileobj" addlibs="$convenience" build_libtool_libs=no else if test "$build_libtool_libs" = module; then oldobjs="$libobjs_save" build_libtool_libs=no else oldobjs="$old_deplibs $non_pic_objects" if test "$preload" = yes && test -f "$symfileobj"; then oldobjs="$oldobjs $symfileobj" fi fi addlibs="$old_convenience" fi if test -n "$addlibs"; then gentop="$output_objdir/${outputname}x" generated="$generated $gentop" func_extract_archives $gentop $addlibs oldobjs="$oldobjs $func_extract_archives_result" fi # Do each command in the archive commands. if test -n "$old_archive_from_new_cmds" && test "$build_libtool_libs" = yes; then cmds=$old_archive_from_new_cmds else # Add any objects from preloaded convenience libraries if test -n "$dlprefiles"; then gentop="$output_objdir/${outputname}x" generated="$generated $gentop" func_extract_archives $gentop $dlprefiles oldobjs="$oldobjs $func_extract_archives_result" fi # POSIX demands no paths to be encoded in archives. We have # to avoid creating archives with duplicate basenames if we # might have to extract them afterwards, e.g., when creating a # static archive out of a convenience library, or when linking # the entirety of a libtool archive into another (currently # not supported by libtool). if (for obj in $oldobjs do func_basename "$obj" $ECHO "$func_basename_result" done | sort | sort -uc >/dev/null 2>&1); then : else $ECHO "copying selected object files to avoid basename conflicts..." gentop="$output_objdir/${outputname}x" generated="$generated $gentop" func_mkdir_p "$gentop" save_oldobjs=$oldobjs oldobjs= counter=1 for obj in $save_oldobjs do func_basename "$obj" objbase="$func_basename_result" case " $oldobjs " in " ") oldobjs=$obj ;; *[\ /]"$objbase "*) while :; do # Make sure we don't pick an alternate name that also # overlaps. newobj=lt$counter-$objbase func_arith $counter + 1 counter=$func_arith_result case " $oldobjs " in *[\ /]"$newobj "*) ;; *) if test ! -f "$gentop/$newobj"; then break; fi ;; esac done func_show_eval "ln $obj $gentop/$newobj || cp $obj $gentop/$newobj" oldobjs="$oldobjs $gentop/$newobj" ;; *) oldobjs="$oldobjs $obj" ;; esac done fi eval cmds=\"$old_archive_cmds\" func_len " $cmds" len=$func_len_result if test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then cmds=$old_archive_cmds else # the command line is too long to link in one step, link in parts func_verbose "using piecewise archive linking..." save_RANLIB=$RANLIB RANLIB=: objlist= concat_cmds= save_oldobjs=$oldobjs oldobjs= # Is there a better way of finding the last object in the list? for obj in $save_oldobjs do last_oldobj=$obj done eval test_cmds=\"$old_archive_cmds\" func_len " $test_cmds" len0=$func_len_result len=$len0 for obj in $save_oldobjs do func_len " $obj" func_arith $len + $func_len_result len=$func_arith_result func_append objlist " $obj" if test "$len" -lt "$max_cmd_len"; then : else # the above command should be used before it gets too long oldobjs=$objlist if test "$obj" = "$last_oldobj" ; then RANLIB=$save_RANLIB fi test -z "$concat_cmds" || concat_cmds=$concat_cmds~ eval concat_cmds=\"\${concat_cmds}$old_archive_cmds\" objlist= len=$len0 fi done RANLIB=$save_RANLIB oldobjs=$objlist if test "X$oldobjs" = "X" ; then eval cmds=\"\$concat_cmds\" else eval cmds=\"\$concat_cmds~\$old_archive_cmds\" fi fi fi func_execute_cmds "$cmds" 'exit $?' done test -n "$generated" && \ func_show_eval "${RM}r$generated" # Now create the libtool archive. case $output in *.la) old_library= test "$build_old_libs" = yes && old_library="$libname.$libext" func_verbose "creating $output" # Preserve any variables that may affect compiler behavior for var in $variables_saved_for_relink; do if eval test -z \"\${$var+set}\"; then relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command" elif eval var_value=\$$var; test -z "$var_value"; then relink_command="$var=; export $var; $relink_command" else func_quote_for_eval "$var_value" relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command" fi done # Quote the link command for shipping. relink_command="(cd `pwd`; $SHELL $progpath $preserve_args --mode=relink $libtool_args @inst_prefix_dir@)" relink_command=`$ECHO "X$relink_command" | $Xsed -e "$sed_quote_subst"` if test "$hardcode_automatic" = yes ; then relink_command= fi # Only create the output if not a dry run. $opt_dry_run || { for installed in no yes; do if test "$installed" = yes; then if test -z "$install_libdir"; then break fi output="$output_objdir/$outputname"i # Replace all uninstalled libtool libraries with the installed ones newdependency_libs= for deplib in $dependency_libs; do case $deplib in *.la) func_basename "$deplib" name="$func_basename_result" eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $deplib` test -z "$libdir" && \ func_fatal_error "\`$deplib' is not a valid libtool archive" newdependency_libs="$newdependency_libs $libdir/$name" ;; *) newdependency_libs="$newdependency_libs $deplib" ;; esac done dependency_libs="$newdependency_libs" newdlfiles= for lib in $dlfiles; do case $lib in *.la) func_basename "$lib" name="$func_basename_result" eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib` test -z "$libdir" && \ func_fatal_error "\`$lib' is not a valid libtool archive" newdlfiles="$newdlfiles $libdir/$name" ;; *) newdlfiles="$newdlfiles $lib" ;; esac done dlfiles="$newdlfiles" newdlprefiles= for lib in $dlprefiles; do case $lib in *.la) # Only pass preopened files to the pseudo-archive (for # eventual linking with the app. that links it) if we # didn't already link the preopened objects directly into # the library: func_basename "$lib" name="$func_basename_result" eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib` test -z "$libdir" && \ func_fatal_error "\`$lib' is not a valid libtool archive" newdlprefiles="$newdlprefiles $libdir/$name" ;; esac done dlprefiles="$newdlprefiles" else newdlfiles= for lib in $dlfiles; do case $lib in [\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;; *) abs=`pwd`"/$lib" ;; esac newdlfiles="$newdlfiles $abs" done dlfiles="$newdlfiles" newdlprefiles= for lib in $dlprefiles; do case $lib in [\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;; *) abs=`pwd`"/$lib" ;; esac newdlprefiles="$newdlprefiles $abs" done dlprefiles="$newdlprefiles" fi $RM $output # place dlname in correct position for cygwin tdlname=$dlname case $host,$output,$installed,$module,$dlname in *cygwin*,*lai,yes,no,*.dll | *mingw*,*lai,yes,no,*.dll | *cegcc*,*lai,yes,no,*.dll) tdlname=../bin/$dlname ;; esac $ECHO > $output "\ # $outputname - a libtool library file # Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION # # Please DO NOT delete this file! # It is necessary for linking the library. # The name that we can dlopen(3). dlname='$tdlname' # Names of this library. library_names='$library_names' # The name of the static archive. old_library='$old_library' # Linker flags that can not go in dependency_libs. inherited_linker_flags='$new_inherited_linker_flags' # Libraries that this one depends upon. dependency_libs='$dependency_libs' # Names of additional weak libraries provided by this library weak_library_names='$weak_libs' # Version information for $libname. current=$current age=$age revision=$revision # Is this an already installed library? installed=$installed # Should we warn about portability when linking against -modules? shouldnotlink=$module # Files to dlopen/dlpreopen dlopen='$dlfiles' dlpreopen='$dlprefiles' # Directory that this library needs to be installed in: libdir='$install_libdir'" if test "$installed" = no && test "$need_relink" = yes; then $ECHO >> $output "\ relink_command=\"$relink_command\"" fi done } # Do a symbolic link so that the libtool archive can be found in # LD_LIBRARY_PATH before the program is installed. func_show_eval '( cd "$output_objdir" && $RM "$outputname" && $LN_S "../$outputname" "$outputname" )' 'exit $?' ;; esac exit $EXIT_SUCCESS } { test "$mode" = link || test "$mode" = relink; } && func_mode_link ${1+"$@"} # func_mode_uninstall arg... func_mode_uninstall () { $opt_debug RM="$nonopt" files= rmforce= exit_status=0 # This variable tells wrapper scripts just to set variables rather # than running their programs. libtool_install_magic="$magic" for arg do case $arg in -f) RM="$RM $arg"; rmforce=yes ;; -*) RM="$RM $arg" ;; *) files="$files $arg" ;; esac done test -z "$RM" && \ func_fatal_help "you must specify an RM program" rmdirs= origobjdir="$objdir" for file in $files; do func_dirname "$file" "" "." dir="$func_dirname_result" if test "X$dir" = X.; then objdir="$origobjdir" else objdir="$dir/$origobjdir" fi func_basename "$file" name="$func_basename_result" test "$mode" = uninstall && objdir="$dir" # Remember objdir for removal later, being careful to avoid duplicates if test "$mode" = clean; then case " $rmdirs " in *" $objdir "*) ;; *) rmdirs="$rmdirs $objdir" ;; esac fi # Don't error if the file doesn't exist and rm -f was used. if { test -L "$file"; } >/dev/null 2>&1 || { test -h "$file"; } >/dev/null 2>&1 || test -f "$file"; then : elif test -d "$file"; then exit_status=1 continue elif test "$rmforce" = yes; then continue fi rmfiles="$file" case $name in *.la) # Possibly a libtool archive, so verify it. if func_lalib_p "$file"; then func_source $dir/$name # Delete the libtool libraries and symlinks. for n in $library_names; do rmfiles="$rmfiles $objdir/$n" done test -n "$old_library" && rmfiles="$rmfiles $objdir/$old_library" case "$mode" in clean) case " $library_names " in # " " in the beginning catches empty $dlname *" $dlname "*) ;; *) rmfiles="$rmfiles $objdir/$dlname" ;; esac test -n "$libdir" && rmfiles="$rmfiles $objdir/$name $objdir/${name}i" ;; uninstall) if test -n "$library_names"; then # Do each command in the postuninstall commands. func_execute_cmds "$postuninstall_cmds" 'test "$rmforce" = yes || exit_status=1' fi if test -n "$old_library"; then # Do each command in the old_postuninstall commands. func_execute_cmds "$old_postuninstall_cmds" 'test "$rmforce" = yes || exit_status=1' fi # FIXME: should reinstall the best remaining shared library. ;; esac fi ;; *.lo) # Possibly a libtool object, so verify it. if func_lalib_p "$file"; then # Read the .lo file func_source $dir/$name # Add PIC object to the list of files to remove. if test -n "$pic_object" && test "$pic_object" != none; then rmfiles="$rmfiles $dir/$pic_object" fi # Add non-PIC object to the list of files to remove. if test -n "$non_pic_object" && test "$non_pic_object" != none; then rmfiles="$rmfiles $dir/$non_pic_object" fi fi ;; *) if test "$mode" = clean ; then noexename=$name case $file in *.exe) func_stripname '' '.exe' "$file" file=$func_stripname_result func_stripname '' '.exe' "$name" noexename=$func_stripname_result # $file with .exe has already been added to rmfiles, # add $file without .exe rmfiles="$rmfiles $file" ;; esac # Do a test to see if this is a libtool program. if func_ltwrapper_p "$file"; then if func_ltwrapper_executable_p "$file"; then func_ltwrapper_scriptname "$file" relink_command= func_source $func_ltwrapper_scriptname_result rmfiles="$rmfiles $func_ltwrapper_scriptname_result" else relink_command= func_source $dir/$noexename fi # note $name still contains .exe if it was in $file originally # as does the version of $file that was added into $rmfiles rmfiles="$rmfiles $objdir/$name $objdir/${name}S.${objext}" if test "$fast_install" = yes && test -n "$relink_command"; then rmfiles="$rmfiles $objdir/lt-$name" fi if test "X$noexename" != "X$name" ; then rmfiles="$rmfiles $objdir/lt-${noexename}.c" fi fi fi ;; esac func_show_eval "$RM $rmfiles" 'exit_status=1' done objdir="$origobjdir" # Try to remove the ${objdir}s in the directories where we deleted files for dir in $rmdirs; do if test -d "$dir"; then func_show_eval "rmdir $dir >/dev/null 2>&1" fi done exit $exit_status } { test "$mode" = uninstall || test "$mode" = clean; } && func_mode_uninstall ${1+"$@"} test -z "$mode" && { help="$generic_help" func_fatal_help "you must specify a MODE" } test -z "$exec_cmd" && \ func_fatal_help "invalid operation mode \`$mode'" if test -n "$exec_cmd"; then eval exec "$exec_cmd" exit $EXIT_FAILURE fi exit $exit_status # The TAGs below are defined such that we never get into a situation # in which we disable both kinds of libraries. Given conflicting # choices, we go for a static library, that is the most portable, # since we can't tell whether shared libraries were disabled because # the user asked for that or because the platform doesn't support # them. This is particularly important on AIX, because we don't # support having both static and shared libraries enabled at the same # time on that platform, so we default to a shared-only configuration. # If a disable-shared tag is given, we'll fallback to a static-only # configuration. But we'll never go from static-only to shared-only. # ### BEGIN LIBTOOL TAG CONFIG: disable-shared build_libtool_libs=no build_old_libs=yes # ### END LIBTOOL TAG CONFIG: disable-shared # ### BEGIN LIBTOOL TAG CONFIG: disable-static build_old_libs=`case $build_libtool_libs in yes) echo no;; *) echo yes;; esac` # ### END LIBTOOL TAG CONFIG: disable-static # Local Variables: # mode:shell-script # sh-indentation:2 # End: # vi:sw=2 haildb-2.3.2/config/config.guess0000755000175000017500000012763711513177417017445 0ustar00pcrewspcrews00000000000000#! /bin/sh # Attempt to guess a canonical system name. # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, # 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 # Free Software Foundation, Inc. timestamp='2009-12-30' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA # 02110-1301, USA. # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # Originally written by Per Bothner. Please send patches (context # diff format) to and include a ChangeLog # entry. # # This script attempts to guess a canonical system name similar to # config.sub. If it succeeds, it prints the system name on stdout, and # exits with 0. Otherwise, it exits with 1. # # You can get the latest version of this script from: # http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] Output the configuration name of the system \`$me' is run on. Operation modes: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.guess ($timestamp) Originally written by Per Bothner. Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" >&2 exit 1 ;; * ) break ;; esac done if test $# != 0; then echo "$me: too many arguments$help" >&2 exit 1 fi trap 'exit 1' 1 2 15 # CC_FOR_BUILD -- compiler used by this script. Note that the use of a # compiler to aid in system detection is discouraged as it requires # temporary files to be created and, as you can see below, it is a # headache to deal with in a portable fashion. # Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still # use `HOST_CC' if defined, but it is deprecated. # Portable tmp directory creation inspired by the Autoconf team. set_cc_for_build=' trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; : ${TMPDIR=/tmp} ; { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; dummy=$tmp/dummy ; tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; case $CC_FOR_BUILD,$HOST_CC,$CC in ,,) echo "int x;" > $dummy.c ; for c in cc gcc c89 c99 ; do if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then CC_FOR_BUILD="$c"; break ; fi ; done ; if test x"$CC_FOR_BUILD" = x ; then CC_FOR_BUILD=no_compiler_found ; fi ;; ,,*) CC_FOR_BUILD=$CC ;; ,*,*) CC_FOR_BUILD=$HOST_CC ;; esac ; set_cc_for_build= ;' # This is needed to find uname on a Pyramid OSx when run in the BSD universe. # (ghazi@noc.rutgers.edu 1994-08-24) if (test -f /.attbin/uname) >/dev/null 2>&1 ; then PATH=$PATH:/.attbin ; export PATH fi UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown # Note: order is significant - the case branches are not exclusive. case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in *:NetBSD:*:*) # NetBSD (nbsd) targets should (where applicable) match one or # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*, # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently # switched to ELF, *-*-netbsd* would select the old # object file format. This provides both forward # compatibility and a consistent mechanism for selecting the # object file format. # # Note: NetBSD doesn't particularly care about the vendor # portion of the name. We always set it to "unknown". sysctl="sysctl -n hw.machine_arch" UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \ /usr/sbin/$sysctl 2>/dev/null || echo unknown)` case "${UNAME_MACHINE_ARCH}" in armeb) machine=armeb-unknown ;; arm*) machine=arm-unknown ;; sh3el) machine=shl-unknown ;; sh3eb) machine=sh-unknown ;; sh5el) machine=sh5le-unknown ;; *) machine=${UNAME_MACHINE_ARCH}-unknown ;; esac # The Operating System including object format, if it has switched # to ELF recently, or will in the future. case "${UNAME_MACHINE_ARCH}" in arm*|i386|m68k|ns32k|sh3*|sparc|vax) eval $set_cc_for_build if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ELF__ then # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). # Return netbsd for either. FIX? os=netbsd else os=netbsdelf fi ;; *) os=netbsd ;; esac # The OS release # Debian GNU/NetBSD machines have a different userland, and # thus, need a distinct triplet. However, they do not need # kernel version information, so it can be replaced with a # suitable tag, in the style of linux-gnu. case "${UNAME_VERSION}" in Debian*) release='-gnu' ;; *) release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` ;; esac # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: # contains redundant information, the shorter form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. echo "${machine}-${os}${release}" exit ;; *:OpenBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} exit ;; *:ekkoBSD:*:*) echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} exit ;; *:SolidBSD:*:*) echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE} exit ;; macppc:MirBSD:*:*) echo powerpc-unknown-mirbsd${UNAME_RELEASE} exit ;; *:MirBSD:*:*) echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} exit ;; alpha:OSF1:*:*) case $UNAME_RELEASE in *4.0) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` ;; *5.*) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` ;; esac # According to Compaq, /usr/sbin/psrinfo has been available on # OSF/1 and Tru64 systems produced since 1995. I hope that # covers most systems running today. This code pipes the CPU # types through head -n 1, so we only detect the type of CPU 0. ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` case "$ALPHA_CPU_TYPE" in "EV4 (21064)") UNAME_MACHINE="alpha" ;; "EV4.5 (21064)") UNAME_MACHINE="alpha" ;; "LCA4 (21066/21068)") UNAME_MACHINE="alpha" ;; "EV5 (21164)") UNAME_MACHINE="alphaev5" ;; "EV5.6 (21164A)") UNAME_MACHINE="alphaev56" ;; "EV5.6 (21164PC)") UNAME_MACHINE="alphapca56" ;; "EV5.7 (21164PC)") UNAME_MACHINE="alphapca57" ;; "EV6 (21264)") UNAME_MACHINE="alphaev6" ;; "EV6.7 (21264A)") UNAME_MACHINE="alphaev67" ;; "EV6.8CB (21264C)") UNAME_MACHINE="alphaev68" ;; "EV6.8AL (21264B)") UNAME_MACHINE="alphaev68" ;; "EV6.8CX (21264D)") UNAME_MACHINE="alphaev68" ;; "EV6.9A (21264/EV69A)") UNAME_MACHINE="alphaev69" ;; "EV7 (21364)") UNAME_MACHINE="alphaev7" ;; "EV7.9 (21364A)") UNAME_MACHINE="alphaev79" ;; esac # A Pn.n version is a patched version. # A Vn.n version is a released version. # A Tn.n version is a released field test version. # A Xn.n version is an unreleased experimental baselevel. # 1.2 uses "1.2" for uname -r. echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` exit ;; Alpha\ *:Windows_NT*:*) # How do we know it's Interix rather than the generic POSIX subsystem? # Should we change UNAME_MACHINE based on the output of uname instead # of the specific Alpha model? echo alpha-pc-interix exit ;; 21064:Windows_NT:50:3) echo alpha-dec-winnt3.5 exit ;; Amiga*:UNIX_System_V:4.0:*) echo m68k-unknown-sysv4 exit ;; *:[Aa]miga[Oo][Ss]:*:*) echo ${UNAME_MACHINE}-unknown-amigaos exit ;; *:[Mm]orph[Oo][Ss]:*:*) echo ${UNAME_MACHINE}-unknown-morphos exit ;; *:OS/390:*:*) echo i370-ibm-openedition exit ;; *:z/VM:*:*) echo s390-ibm-zvmoe exit ;; *:OS400:*:*) echo powerpc-ibm-os400 exit ;; arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) echo arm-acorn-riscix${UNAME_RELEASE} exit ;; arm:riscos:*:*|arm:RISCOS:*:*) echo arm-unknown-riscos exit ;; SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) echo hppa1.1-hitachi-hiuxmpp exit ;; Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. if test "`(/bin/universe) 2>/dev/null`" = att ; then echo pyramid-pyramid-sysv3 else echo pyramid-pyramid-bsd fi exit ;; NILE*:*:*:dcosx) echo pyramid-pyramid-svr4 exit ;; DRS?6000:unix:4.0:6*) echo sparc-icl-nx6 exit ;; DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) case `/usr/bin/uname -p` in sparc) echo sparc-icl-nx7; exit ;; esac ;; s390x:SunOS:*:*) echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4H:SunOS:5.*:*) echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) echo i386-pc-auroraux${UNAME_RELEASE} exit ;; i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) eval $set_cc_for_build SUN_ARCH="i386" # If there is a compiler, see if it is configured for 64-bit objects. # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. # This test works for both compilers. if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then SUN_ARCH="x86_64" fi fi echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:6*:*) # According to config.sub, this is the proper way to canonicalize # SunOS6. Hard to guess exactly what SunOS6 will be like, but # it's likely to be more like Solaris than SunOS4. echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:*:*) case "`/usr/bin/arch -k`" in Series*|S4*) UNAME_RELEASE=`uname -v` ;; esac # Japanese Language versions have a version number like `4.1.3-JL'. echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` exit ;; sun3*:SunOS:*:*) echo m68k-sun-sunos${UNAME_RELEASE} exit ;; sun*:*:4.2BSD:*) UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 case "`/bin/arch`" in sun3) echo m68k-sun-sunos${UNAME_RELEASE} ;; sun4) echo sparc-sun-sunos${UNAME_RELEASE} ;; esac exit ;; aushp:SunOS:*:*) echo sparc-auspex-sunos${UNAME_RELEASE} exit ;; # The situation for MiNT is a little confusing. The machine name # can be virtually everything (everything which is not # "atarist" or "atariste" at least should have a processor # > m68000). The system name ranges from "MiNT" over "FreeMiNT" # to the lowercase version "mint" (or "freemint"). Finally # the system name "TOS" denotes a system which is actually not # MiNT. But MiNT is downward compatible to TOS, so this should # be no problem. atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) echo m68k-milan-mint${UNAME_RELEASE} exit ;; hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) echo m68k-hades-mint${UNAME_RELEASE} exit ;; *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) echo m68k-unknown-mint${UNAME_RELEASE} exit ;; m68k:machten:*:*) echo m68k-apple-machten${UNAME_RELEASE} exit ;; powerpc:machten:*:*) echo powerpc-apple-machten${UNAME_RELEASE} exit ;; RISC*:Mach:*:*) echo mips-dec-mach_bsd4.3 exit ;; RISC*:ULTRIX:*:*) echo mips-dec-ultrix${UNAME_RELEASE} exit ;; VAX*:ULTRIX*:*:*) echo vax-dec-ultrix${UNAME_RELEASE} exit ;; 2020:CLIX:*:* | 2430:CLIX:*:*) echo clipper-intergraph-clix${UNAME_RELEASE} exit ;; mips:*:*:UMIPS | mips:*:*:RISCos) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #ifdef __cplusplus #include /* for printf() prototype */ int main (int argc, char *argv[]) { #else int main (argc, argv) int argc; char *argv[]; { #endif #if defined (host_mips) && defined (MIPSEB) #if defined (SYSTYPE_SYSV) printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_SVR4) printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); #endif #endif exit (-1); } EOF $CC_FOR_BUILD -o $dummy $dummy.c && dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` && SYSTEM_NAME=`$dummy $dummyarg` && { echo "$SYSTEM_NAME"; exit; } echo mips-mips-riscos${UNAME_RELEASE} exit ;; Motorola:PowerMAX_OS:*:*) echo powerpc-motorola-powermax exit ;; Motorola:*:4.3:PL8-*) echo powerpc-harris-powermax exit ;; Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) echo powerpc-harris-powermax exit ;; Night_Hawk:Power_UNIX:*:*) echo powerpc-harris-powerunix exit ;; m88k:CX/UX:7*:*) echo m88k-harris-cxux7 exit ;; m88k:*:4*:R4*) echo m88k-motorola-sysv4 exit ;; m88k:*:3*:R3*) echo m88k-motorola-sysv3 exit ;; AViiON:dgux:*:*) # DG/UX returns AViiON for all architectures UNAME_PROCESSOR=`/usr/bin/uname -p` if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] then if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ [ ${TARGET_BINARY_INTERFACE}x = x ] then echo m88k-dg-dgux${UNAME_RELEASE} else echo m88k-dg-dguxbcs${UNAME_RELEASE} fi else echo i586-dg-dgux${UNAME_RELEASE} fi exit ;; M88*:DolphinOS:*:*) # DolphinOS (SVR3) echo m88k-dolphin-sysv3 exit ;; M88*:*:R3*:*) # Delta 88k system running SVR3 echo m88k-motorola-sysv3 exit ;; XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) echo m88k-tektronix-sysv3 exit ;; Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) echo m68k-tektronix-bsd exit ;; *:IRIX*:*:*) echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` exit ;; ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' i*86:AIX:*:*) echo i386-ibm-aix exit ;; ia64:AIX:*:*) if [ -x /usr/bin/oslevel ] ; then IBM_REV=`/usr/bin/oslevel` else IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} fi echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} exit ;; *:AIX:2:3) if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #include main() { if (!__power_pc()) exit(1); puts("powerpc-ibm-aix3.2.5"); exit(0); } EOF if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` then echo "$SYSTEM_NAME" else echo rs6000-ibm-aix3.2.5 fi elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then echo rs6000-ibm-aix3.2.4 else echo rs6000-ibm-aix3.2 fi exit ;; *:AIX:*:[456]) IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then IBM_ARCH=rs6000 else IBM_ARCH=powerpc fi if [ -x /usr/bin/oslevel ] ; then IBM_REV=`/usr/bin/oslevel` else IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} fi echo ${IBM_ARCH}-ibm-aix${IBM_REV} exit ;; *:AIX:*:*) echo rs6000-ibm-aix exit ;; ibmrt:4.4BSD:*|romp-ibm:BSD:*) echo romp-ibm-bsd4.4 exit ;; ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to exit ;; # report: romp-ibm BSD 4.3 *:BOSX:*:*) echo rs6000-bull-bosx exit ;; DPX/2?00:B.O.S.:*:*) echo m68k-bull-sysv3 exit ;; 9000/[34]??:4.3bsd:1.*:*) echo m68k-hp-bsd exit ;; hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) echo m68k-hp-bsd4.4 exit ;; 9000/[34678]??:HP-UX:*:*) HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` case "${UNAME_MACHINE}" in 9000/31? ) HP_ARCH=m68000 ;; 9000/[34]?? ) HP_ARCH=m68k ;; 9000/[678][0-9][0-9]) if [ -x /usr/bin/getconf ]; then sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` case "${sc_cpu_version}" in 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 532) # CPU_PA_RISC2_0 case "${sc_kernel_bits}" in 32) HP_ARCH="hppa2.0n" ;; 64) HP_ARCH="hppa2.0w" ;; '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 esac ;; esac fi if [ "${HP_ARCH}" = "" ]; then eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #define _HPUX_SOURCE #include #include int main () { #if defined(_SC_KERNEL_BITS) long bits = sysconf(_SC_KERNEL_BITS); #endif long cpu = sysconf (_SC_CPU_VERSION); switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0"); break; case CPU_PA_RISC1_1: puts ("hppa1.1"); break; case CPU_PA_RISC2_0: #if defined(_SC_KERNEL_BITS) switch (bits) { case 64: puts ("hppa2.0w"); break; case 32: puts ("hppa2.0n"); break; default: puts ("hppa2.0"); break; } break; #else /* !defined(_SC_KERNEL_BITS) */ puts ("hppa2.0"); break; #endif default: puts ("hppa1.0"); break; } exit (0); } EOF (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` test -z "$HP_ARCH" && HP_ARCH=hppa fi ;; esac if [ ${HP_ARCH} = "hppa2.0w" ] then eval $set_cc_for_build # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler # generating 64-bit code. GNU and HP use different nomenclature: # # $ CC_FOR_BUILD=cc ./config.guess # => hppa2.0w-hp-hpux11.23 # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess # => hppa64-hp-hpux11.23 if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | grep -q __LP64__ then HP_ARCH="hppa2.0w" else HP_ARCH="hppa64" fi fi echo ${HP_ARCH}-hp-hpux${HPUX_REV} exit ;; ia64:HP-UX:*:*) HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` echo ia64-hp-hpux${HPUX_REV} exit ;; 3050*:HI-UX:*:*) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #include int main () { long cpu = sysconf (_SC_CPU_VERSION); /* The order matters, because CPU_IS_HP_MC68K erroneously returns true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct results, however. */ if (CPU_IS_PA_RISC (cpu)) { switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; default: puts ("hppa-hitachi-hiuxwe2"); break; } } else if (CPU_IS_HP_MC68K (cpu)) puts ("m68k-hitachi-hiuxwe2"); else puts ("unknown-hitachi-hiuxwe2"); exit (0); } EOF $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` && { echo "$SYSTEM_NAME"; exit; } echo unknown-hitachi-hiuxwe2 exit ;; 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) echo hppa1.1-hp-bsd exit ;; 9000/8??:4.3bsd:*:*) echo hppa1.0-hp-bsd exit ;; *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) echo hppa1.0-hp-mpeix exit ;; hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) echo hppa1.1-hp-osf exit ;; hp8??:OSF1:*:*) echo hppa1.0-hp-osf exit ;; i*86:OSF1:*:*) if [ -x /usr/sbin/sysversion ] ; then echo ${UNAME_MACHINE}-unknown-osf1mk else echo ${UNAME_MACHINE}-unknown-osf1 fi exit ;; parisc*:Lites*:*:*) echo hppa1.1-hp-lites exit ;; C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) echo c1-convex-bsd exit ;; C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) if getsysinfo -f scalar_acc then echo c32-convex-bsd else echo c2-convex-bsd fi exit ;; C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) echo c34-convex-bsd exit ;; C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) echo c38-convex-bsd exit ;; C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) echo c4-convex-bsd exit ;; CRAY*Y-MP:*:*:*) echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*[A-Z]90:*:*:*) echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ -e 's/\.[^.]*$/.X/' exit ;; CRAY*TS:*:*:*) echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*T3E:*:*:*) echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*SV1:*:*:*) echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; *:UNICOS/mp:*:*) echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; 5000:UNIX_System_V:4.*:*) FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'` echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} exit ;; sparc*:BSD/OS:*:*) echo sparc-unknown-bsdi${UNAME_RELEASE} exit ;; *:BSD/OS:*:*) echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} exit ;; *:FreeBSD:*:*) case ${UNAME_MACHINE} in pc98) echo i386-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; amd64) echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; *) echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; esac exit ;; i*:CYGWIN*:*) echo ${UNAME_MACHINE}-pc-cygwin exit ;; *:MINGW*:*) echo ${UNAME_MACHINE}-pc-mingw32 exit ;; i*:windows32*:*) # uname -m includes "-pc" on this system. echo ${UNAME_MACHINE}-mingw32 exit ;; i*:PW*:*) echo ${UNAME_MACHINE}-pc-pw32 exit ;; *:Interix*:*) case ${UNAME_MACHINE} in x86) echo i586-pc-interix${UNAME_RELEASE} exit ;; authenticamd | genuineintel | EM64T) echo x86_64-unknown-interix${UNAME_RELEASE} exit ;; IA64) echo ia64-unknown-interix${UNAME_RELEASE} exit ;; esac ;; [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) echo i${UNAME_MACHINE}-pc-mks exit ;; 8664:Windows_NT:*) echo x86_64-pc-mks exit ;; i*:Windows_NT*:* | Pentium*:Windows_NT*:*) # How do we know it's Interix rather than the generic POSIX subsystem? # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we # UNAME_MACHINE based on the output of uname instead of i386? echo i586-pc-interix exit ;; i*:UWIN*:*) echo ${UNAME_MACHINE}-pc-uwin exit ;; amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) echo x86_64-unknown-cygwin exit ;; p*:CYGWIN*:*) echo powerpcle-unknown-cygwin exit ;; prep*:SunOS:5.*:*) echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; *:GNU:*:*) # the GNU system echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` exit ;; *:GNU/*:*:*) # other systems with GNU libc and userland echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu exit ;; i*86:Minix:*:*) echo ${UNAME_MACHINE}-pc-minix exit ;; alpha:Linux:*:*) case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in EV5) UNAME_MACHINE=alphaev5 ;; EV56) UNAME_MACHINE=alphaev56 ;; PCA56) UNAME_MACHINE=alphapca56 ;; PCA57) UNAME_MACHINE=alphapca56 ;; EV6) UNAME_MACHINE=alphaev6 ;; EV67) UNAME_MACHINE=alphaev67 ;; EV68*) UNAME_MACHINE=alphaev68 ;; esac objdump --private-headers /bin/sh | grep -q ld.so.1 if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} exit ;; arm*:Linux:*:*) eval $set_cc_for_build if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_EABI__ then echo ${UNAME_MACHINE}-unknown-linux-gnu else echo ${UNAME_MACHINE}-unknown-linux-gnueabi fi exit ;; avr32*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; cris:Linux:*:*) echo cris-axis-linux-gnu exit ;; crisv32:Linux:*:*) echo crisv32-axis-linux-gnu exit ;; frv:Linux:*:*) echo frv-unknown-linux-gnu exit ;; i*86:Linux:*:*) LIBC=gnu eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #ifdef __dietlibc__ LIBC=dietlibc #endif EOF eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'` echo "${UNAME_MACHINE}-pc-linux-${LIBC}" exit ;; ia64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; m32r*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; m68*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; mips:Linux:*:* | mips64:Linux:*:*) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #undef CPU #undef ${UNAME_MACHINE} #undef ${UNAME_MACHINE}el #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) CPU=${UNAME_MACHINE}el #else #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) CPU=${UNAME_MACHINE} #else CPU= #endif #endif EOF eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'` test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; } ;; or32:Linux:*:*) echo or32-unknown-linux-gnu exit ;; padre:Linux:*:*) echo sparc-unknown-linux-gnu exit ;; parisc64:Linux:*:* | hppa64:Linux:*:*) echo hppa64-unknown-linux-gnu exit ;; parisc:Linux:*:* | hppa:Linux:*:*) # Look for CPU level case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in PA7*) echo hppa1.1-unknown-linux-gnu ;; PA8*) echo hppa2.0-unknown-linux-gnu ;; *) echo hppa-unknown-linux-gnu ;; esac exit ;; ppc64:Linux:*:*) echo powerpc64-unknown-linux-gnu exit ;; ppc:Linux:*:*) echo powerpc-unknown-linux-gnu exit ;; s390:Linux:*:* | s390x:Linux:*:*) echo ${UNAME_MACHINE}-ibm-linux exit ;; sh64*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; sh*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; sparc:Linux:*:* | sparc64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; vax:Linux:*:*) echo ${UNAME_MACHINE}-dec-linux-gnu exit ;; x86_64:Linux:*:*) echo x86_64-unknown-linux-gnu exit ;; xtensa*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; i*86:DYNIX/ptx:4*:*) # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. # earlier versions are messed up and put the nodename in both # sysname and nodename. echo i386-sequent-sysv4 exit ;; i*86:UNIX_SV:4.2MP:2.*) # Unixware is an offshoot of SVR4, but it has its own version # number series starting with 2... # I am not positive that other SVR4 systems won't match this, # I just have to hope. -- rms. # Use sysv4.2uw... so that sysv4* matches it. echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} exit ;; i*86:OS/2:*:*) # If we were able to find `uname', then EMX Unix compatibility # is probably installed. echo ${UNAME_MACHINE}-pc-os2-emx exit ;; i*86:XTS-300:*:STOP) echo ${UNAME_MACHINE}-unknown-stop exit ;; i*86:atheos:*:*) echo ${UNAME_MACHINE}-unknown-atheos exit ;; i*86:syllable:*:*) echo ${UNAME_MACHINE}-pc-syllable exit ;; i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) echo i386-unknown-lynxos${UNAME_RELEASE} exit ;; i*86:*DOS:*:*) echo ${UNAME_MACHINE}-pc-msdosdjgpp exit ;; i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} else echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} fi exit ;; i*86:*:5:[678]*) # UnixWare 7.x, OpenUNIX and OpenServer 6. case `/bin/uname -X | grep "^Machine"` in *486*) UNAME_MACHINE=i486 ;; *Pentium) UNAME_MACHINE=i586 ;; *Pent*|*Celeron) UNAME_MACHINE=i686 ;; esac echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} exit ;; i*86:*:3.2:*) if test -f /usr/options/cb.name; then UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ && UNAME_MACHINE=i586 (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ && UNAME_MACHINE=i686 (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ && UNAME_MACHINE=i686 echo ${UNAME_MACHINE}-pc-sco$UNAME_REL else echo ${UNAME_MACHINE}-pc-sysv32 fi exit ;; pc:*:*:*) # Left here for compatibility: # uname -m prints for DJGPP always 'pc', but it prints nothing about # the processor, so we play safe by assuming i586. # Note: whatever this is, it MUST be the same as what config.sub # prints for the "djgpp" host, or else GDB configury will decide that # this is a cross-build. echo i586-pc-msdosdjgpp exit ;; Intel:Mach:3*:*) echo i386-pc-mach3 exit ;; paragon:*:*:*) echo i860-intel-osf1 exit ;; i860:*:4.*:*) # i860-SVR4 if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 else # Add other i860-SVR4 vendors below as they are discovered. echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 fi exit ;; mini*:CTIX:SYS*5:*) # "miniframe" echo m68010-convergent-sysv exit ;; mc68k:UNIX:SYSTEM5:3.51m) echo m68k-convergent-sysv exit ;; M680?0:D-NIX:5.3:*) echo m68k-diab-dnix exit ;; M68*:*:R3V[5678]*:*) test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) OS_REL='' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3${OS_REL}; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4; exit; } ;; NCR*:*:4.2:* | MPRAS*:*:4.2:*) OS_REL='.3' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3${OS_REL}; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) echo m68k-unknown-lynxos${UNAME_RELEASE} exit ;; mc68030:UNIX_System_V:4.*:*) echo m68k-atari-sysv4 exit ;; TSUNAMI:LynxOS:2.*:*) echo sparc-unknown-lynxos${UNAME_RELEASE} exit ;; rs6000:LynxOS:2.*:*) echo rs6000-unknown-lynxos${UNAME_RELEASE} exit ;; PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) echo powerpc-unknown-lynxos${UNAME_RELEASE} exit ;; SM[BE]S:UNIX_SV:*:*) echo mips-dde-sysv${UNAME_RELEASE} exit ;; RM*:ReliantUNIX-*:*:*) echo mips-sni-sysv4 exit ;; RM*:SINIX-*:*:*) echo mips-sni-sysv4 exit ;; *:SINIX-*:*:*) if uname -p 2>/dev/null >/dev/null ; then UNAME_MACHINE=`(uname -p) 2>/dev/null` echo ${UNAME_MACHINE}-sni-sysv4 else echo ns32k-sni-sysv fi exit ;; PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort # says echo i586-unisys-sysv4 exit ;; *:UNIX_System_V:4*:FTX*) # From Gerald Hewes . # How about differentiating between stratus architectures? -djm echo hppa1.1-stratus-sysv4 exit ;; *:*:*:FTX*) # From seanf@swdc.stratus.com. echo i860-stratus-sysv4 exit ;; i*86:VOS:*:*) # From Paul.Green@stratus.com. echo ${UNAME_MACHINE}-stratus-vos exit ;; *:VOS:*:*) # From Paul.Green@stratus.com. echo hppa1.1-stratus-vos exit ;; mc68*:A/UX:*:*) echo m68k-apple-aux${UNAME_RELEASE} exit ;; news*:NEWS-OS:6*:*) echo mips-sony-newsos6 exit ;; R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) if [ -d /usr/nec ]; then echo mips-nec-sysv${UNAME_RELEASE} else echo mips-unknown-sysv${UNAME_RELEASE} fi exit ;; BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. echo powerpc-be-beos exit ;; BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. echo powerpc-apple-beos exit ;; BePC:BeOS:*:*) # BeOS running on Intel PC compatible. echo i586-pc-beos exit ;; BePC:Haiku:*:*) # Haiku running on Intel PC compatible. echo i586-pc-haiku exit ;; SX-4:SUPER-UX:*:*) echo sx4-nec-superux${UNAME_RELEASE} exit ;; SX-5:SUPER-UX:*:*) echo sx5-nec-superux${UNAME_RELEASE} exit ;; SX-6:SUPER-UX:*:*) echo sx6-nec-superux${UNAME_RELEASE} exit ;; SX-7:SUPER-UX:*:*) echo sx7-nec-superux${UNAME_RELEASE} exit ;; SX-8:SUPER-UX:*:*) echo sx8-nec-superux${UNAME_RELEASE} exit ;; SX-8R:SUPER-UX:*:*) echo sx8r-nec-superux${UNAME_RELEASE} exit ;; Power*:Rhapsody:*:*) echo powerpc-apple-rhapsody${UNAME_RELEASE} exit ;; *:Rhapsody:*:*) echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} exit ;; *:Darwin:*:*) UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown case $UNAME_PROCESSOR in i386) eval $set_cc_for_build if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then UNAME_PROCESSOR="x86_64" fi fi ;; unknown) UNAME_PROCESSOR=powerpc ;; esac echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} exit ;; *:procnto*:*:* | *:QNX:[0123456789]*:*) UNAME_PROCESSOR=`uname -p` if test "$UNAME_PROCESSOR" = "x86"; then UNAME_PROCESSOR=i386 UNAME_MACHINE=pc fi echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} exit ;; *:QNX:*:4*) echo i386-pc-qnx exit ;; NSE-?:NONSTOP_KERNEL:*:*) echo nse-tandem-nsk${UNAME_RELEASE} exit ;; NSR-?:NONSTOP_KERNEL:*:*) echo nsr-tandem-nsk${UNAME_RELEASE} exit ;; *:NonStop-UX:*:*) echo mips-compaq-nonstopux exit ;; BS2000:POSIX*:*:*) echo bs2000-siemens-sysv exit ;; DS/*:UNIX_System_V:*:*) echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} exit ;; *:Plan9:*:*) # "uname -m" is not consistent, so use $cputype instead. 386 # is converted to i386 for consistency with other x86 # operating systems. if test "$cputype" = "386"; then UNAME_MACHINE=i386 else UNAME_MACHINE="$cputype" fi echo ${UNAME_MACHINE}-unknown-plan9 exit ;; *:TOPS-10:*:*) echo pdp10-unknown-tops10 exit ;; *:TENEX:*:*) echo pdp10-unknown-tenex exit ;; KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) echo pdp10-dec-tops20 exit ;; XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) echo pdp10-xkl-tops20 exit ;; *:TOPS-20:*:*) echo pdp10-unknown-tops20 exit ;; *:ITS:*:*) echo pdp10-unknown-its exit ;; SEI:*:*:SEIUX) echo mips-sei-seiux${UNAME_RELEASE} exit ;; *:DragonFly:*:*) echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` exit ;; *:*VMS:*:*) UNAME_MACHINE=`(uname -p) 2>/dev/null` case "${UNAME_MACHINE}" in A*) echo alpha-dec-vms ; exit ;; I*) echo ia64-dec-vms ; exit ;; V*) echo vax-dec-vms ; exit ;; esac ;; *:XENIX:*:SysV) echo i386-pc-xenix exit ;; i*86:skyos:*:*) echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//' exit ;; i*86:rdos:*:*) echo ${UNAME_MACHINE}-pc-rdos exit ;; i*86:AROS:*:*) echo ${UNAME_MACHINE}-pc-aros exit ;; esac #echo '(No uname command or uname output not recognized.)' 1>&2 #echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2 eval $set_cc_for_build cat >$dummy.c < # include #endif main () { #if defined (sony) #if defined (MIPSEB) /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, I don't know.... */ printf ("mips-sony-bsd\n"); exit (0); #else #include printf ("m68k-sony-newsos%s\n", #ifdef NEWSOS4 "4" #else "" #endif ); exit (0); #endif #endif #if defined (__arm) && defined (__acorn) && defined (__unix) printf ("arm-acorn-riscix\n"); exit (0); #endif #if defined (hp300) && !defined (hpux) printf ("m68k-hp-bsd\n"); exit (0); #endif #if defined (NeXT) #if !defined (__ARCHITECTURE__) #define __ARCHITECTURE__ "m68k" #endif int version; version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; if (version < 4) printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); else printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); exit (0); #endif #if defined (MULTIMAX) || defined (n16) #if defined (UMAXV) printf ("ns32k-encore-sysv\n"); exit (0); #else #if defined (CMU) printf ("ns32k-encore-mach\n"); exit (0); #else printf ("ns32k-encore-bsd\n"); exit (0); #endif #endif #endif #if defined (__386BSD__) printf ("i386-pc-bsd\n"); exit (0); #endif #if defined (sequent) #if defined (i386) printf ("i386-sequent-dynix\n"); exit (0); #endif #if defined (ns32000) printf ("ns32k-sequent-dynix\n"); exit (0); #endif #endif #if defined (_SEQUENT_) struct utsname un; uname(&un); if (strncmp(un.version, "V2", 2) == 0) { printf ("i386-sequent-ptx2\n"); exit (0); } if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ printf ("i386-sequent-ptx1\n"); exit (0); } printf ("i386-sequent-ptx\n"); exit (0); #endif #if defined (vax) # if !defined (ultrix) # include # if defined (BSD) # if BSD == 43 printf ("vax-dec-bsd4.3\n"); exit (0); # else # if BSD == 199006 printf ("vax-dec-bsd4.3reno\n"); exit (0); # else printf ("vax-dec-bsd\n"); exit (0); # endif # endif # else printf ("vax-dec-bsd\n"); exit (0); # endif # else printf ("vax-dec-ultrix\n"); exit (0); # endif #endif #if defined (alliant) && defined (i860) printf ("i860-alliant-bsd\n"); exit (0); #endif exit (1); } EOF $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` && { echo "$SYSTEM_NAME"; exit; } # Apollos put the system type in the environment. test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; } # Convex versions that predate uname can use getsysinfo(1) if [ -x /usr/convex/getsysinfo ] then case `getsysinfo -f cpu_type` in c1*) echo c1-convex-bsd exit ;; c2*) if getsysinfo -f scalar_acc then echo c32-convex-bsd else echo c2-convex-bsd fi exit ;; c34*) echo c34-convex-bsd exit ;; c38*) echo c38-convex-bsd exit ;; c4*) echo c4-convex-bsd exit ;; esac fi cat >&2 < in order to provide the needed information to handle your system. config.guess timestamp = $timestamp uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` /bin/uname -X = `(/bin/uname -X) 2>/dev/null` hostinfo = `(hostinfo) 2>/dev/null` /bin/universe = `(/bin/universe) 2>/dev/null` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` /bin/arch = `(/bin/arch) 2>/dev/null` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` UNAME_MACHINE = ${UNAME_MACHINE} UNAME_RELEASE = ${UNAME_RELEASE} UNAME_SYSTEM = ${UNAME_SYSTEM} UNAME_VERSION = ${UNAME_VERSION} EOF exit 1 # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: haildb-2.3.2/config/uncrustify.cfg0000644000175000017500000013560311513177357020014 0ustar00pcrewspcrews00000000000000# # Uncrustify options (with comments) for Pandora Projects # nl_after_brace_close = TRUE # The type of line endings newlines = lf # auto/lf/crlf/cr # The original size of tabs in the input input_tab_size = 8 # number # The size of tabs in the output (only used if align_with_tabs=true) output_tab_size = 3 # number # The ascii value of the string escape char, usually 92 (\) or 94 (^). (Pawn) string_escape_char = 92 # number # Alternate string escape char for Pawn. Only works right before the quote char. string_escape_char2 = 0 # number # # Indenting # # The number of columns to indent per level. # Usually 2, 3, 4, or 8. indent_columns = 2 # number # How to use tabs when indenting code # 0=spaces only # 1=indent with tabs, align with spaces # 2=indent and align with tabs indent_with_tabs = 0 # number # Whether to indent strings broken by '\' so that they line up indent_align_string = true # false/true # The number of spaces to indent multi-line XML strings. # Requires indent_align_string=True indent_xml_string = 0 # number # Spaces to indent '{' from level indent_brace = 0 # number # Whether braces are indented to the body level indent_braces = false # false/true # Disabled indenting function braces if indent_braces is true indent_braces_no_func = false # false/true # Indent based on the size of the brace parent, ie 'if' => 3 spaces, 'for' => 4 spaces, etc. indent_brace_parent = false # false/true # Whether the 'namespace' body is indented indent_namespace = false # false/true # Whether the 'extern "C"' body is indented indent_extern = false # false/true # Whether the 'class' body is indented indent_class = true # false/true # Whether to indent the stuff after a leading class colon indent_class_colon = true # false/true # False=treat 'else\nif' as 'else if' for indenting purposes # True=indent the 'if' one level indent_else_if = false # false/true # True: indent continued function call parameters one indent level # False: align parameters under the open paren indent_func_call_param = false # false/true # Same as indent_func_call_param, but for function defs indent_func_def_param = false # false/true # Same as indent_func_call_param, but for function protos indent_func_proto_param = false # false/true # Same as indent_func_call_param, but for class declarations indent_func_class_param = false # false/true # Same as indent_func_call_param, but for class variable constructors indent_func_ctor_var_param = false # false/true # Same as indent_func_call_param, but for templates indent_template_param = false # false/true # Double the indent for indent_func_xxx_param options indent_func_param_double = false # false/true # Indentation column for standalone 'const' function decl/proto qualifier indent_func_const = 0 # number # Indentation column for standalone 'throw' function decl/proto qualifier indent_func_throw = 0 # number # The number of spaces to indent a continued '->' or '.' # Usually set to 0, 1, or indent_columns. indent_member = 3 # number # Spaces to indent single line ('//') comments on lines before code indent_sing_line_comments = 0 # number # If set, will indent trailing single line ('//') comments relative # to the code instead of trying to keep the same absolute column indent_relative_single_line_comments = false # false/true # Spaces to indent 'case' from 'switch' # Usually 0 or indent_columns. indent_switch_case = 0 # number # Spaces to shift the 'case' line, without affecting any other lines # Usually 0. indent_case_shift = 0 # number # Spaces to indent '{' from 'case'. # By default, the brace will appear under the 'c' in case. # Usually set to 0 or indent_columns. indent_case_brace = 0 # number # Whether to indent comments found in first column indent_col1_comment = false # false/true # How to indent goto labels # >0 : absolute column where 1 is the leftmost column # <=0 : subtract from brace indent indent_label = 1 # number # Same as indent_label, but for access specifiers that are followed by a colon indent_access_spec = 1 # number # Indent the code after an access specifier by one level. # If set, this option forces 'indent_access_spec=0' indent_access_spec_body = false # false/true # If an open paren is followed by a newline, indent the next line so that it lines up after the open paren (not recommended) indent_paren_nl = false # false/true # Controls the indent of a close paren after a newline. # 0: Indent to body level # 1: Align under the open paren # 2: Indent to the brace level indent_paren_close = 0 # number # Controls the indent of a comma when inside a paren.If TRUE, aligns under the open paren indent_comma_paren = false # false/true # Controls the indent of a BOOL operator when inside a paren.If TRUE, aligns under the open paren indent_bool_paren = false # false/true # If an open square is followed by a newline, indent the next line so that it lines up after the open square (not recommended) indent_square_nl = false # false/true # Don't change the relative indent of ESQL/C 'EXEC SQL' bodies indent_preserve_sql = false # false/true # # Spacing options # # Add or remove space around arithmetic operator '+', '-', '/', '*', etc sp_arith = force # ignore/add/remove/force # Add or remove space before assignment operator '=', '+=', etc sp_before_assign = remove # ignore/add/remove/force # Add or remove space after assignment operator '=', '+=', etc sp_after_assign = force # ignore/add/remove/force # Add or remove space around assignment '=' in enum sp_enum_assign = ignore # ignore/add/remove/force # Add or remove space around boolean operators '&&' and '||' sp_bool = force # ignore/add/remove/force # Add or remove space around compare operator '<', '>', '==', etc sp_compare = force # ignore/add/remove/force # Add or remove space inside '(' and ')' sp_inside_paren = remove # ignore/add/remove/force # Add or remove space between nested parens sp_paren_paren = remove # ignore/add/remove/force # Whether to balance spaces inside nested parens sp_balance_nested_parens = false # false/true # Add or remove space between ')' and '{' sp_paren_brace = ignore # ignore/add/remove/force # Add or remove space before pointer star '*' sp_before_ptr_star = force # ignore/add/remove/force # Add or remove space before pointer star '*' that isn't followed by a variable name # If set to 'ignore', sp_before_ptr_star is used instead. sp_before_unnamed_ptr_star = ignore # ignore/add/remove/force # Add or remove space between pointer stars '*' sp_between_ptr_star = remove # ignore/add/remove/force # Add or remove space after pointer star '*', if followed by a word. sp_after_ptr_star = remove # ignore/add/remove/force # Add or remove space after a pointer star '*', if followed by a func proto/def. sp_after_ptr_star_func = ignore # ignore/add/remove/force # Add or remove space before a pointer star '*', if followed by a func proto/def. sp_before_ptr_star_func = ignore # ignore/add/remove/force # Add or remove space before a reference sign '&' sp_before_byref = remove # ignore/add/remove/force # Add or remove space before a reference sign '&' that isn't followed by a variable name # If set to 'ignore', sp_before_byref is used instead. sp_before_unnamed_byref = ignore # ignore/add/remove/force # Add or remove space after reference sign '&', if followed by a word. sp_after_byref = force # ignore/add/remove/force # Add or remove space after a reference sign '&', if followed by a func proto/def. sp_after_byref_func = ignore # ignore/add/remove/force # Add or remove space before a reference sign '&', if followed by a func proto/def. sp_before_byref_func = ignore # ignore/add/remove/force # Add or remove space between type and word sp_after_type = force # ignore/add/remove/force # Add or remove space in 'template <' vs 'template<'. # If set to ignore, sp_before_angle is used. sp_template_angle = ignore # ignore/add/remove/force # Add or remove space before '<>' sp_before_angle = remove # ignore/add/remove/force # Add or remove space inside '<' and '>' sp_inside_angle = remove # ignore/add/remove/force # Add or remove space after '<>' sp_after_angle = force # ignore/add/remove/force # Add or remove space between '<>' and '(' as found in 'new List();' sp_angle_paren = ignore # ignore/add/remove/force # Add or remove space between '<>' and a word as in 'List m;' sp_angle_word = ignore # ignore/add/remove/force # Add or remove space before '(' of 'if', 'for', 'switch', and 'while' sp_before_sparen = force # ignore/add/remove/force # Add or remove space inside if-condition '(' and ')' sp_inside_sparen = remove # ignore/add/remove/force # Add or remove space after ')' of 'if', 'for', 'switch', and 'while' sp_after_sparen = force # ignore/add/remove/force # Add or remove space between ')' and '{' of 'if', 'for', 'switch', and 'while' sp_sparen_brace = add # ignore/add/remove/force # Add or remove space before empty statement ';' on 'if', 'for' and 'while' sp_special_semi = ignore # ignore/add/remove/force # Add or remove space before ';' sp_before_semi = remove # ignore/add/remove/force # Add or remove space before ';' in non-empty 'for' statements sp_before_semi_for = ignore # ignore/add/remove/force # Add or remove space before a semicolon of an empty part of a for statment. sp_before_semi_for_empty = force # ignore/add/remove/force # Add or remove space after the final semicolon of an empty part of a for statment: for ( ; ; ). sp_after_semi_for_empty = ignore # ignore/add/remove/force # Add or remove space before '[' (except '[]') sp_before_square = ignore # ignore/add/remove/force # Add or remove space before '[]' sp_before_squares = ignore # ignore/add/remove/force # Add or remove space inside '[' and ']' sp_inside_square = remove # ignore/add/remove/force # Add or remove space after ',' sp_after_comma = force # ignore/add/remove/force # Add or remove space before ',' sp_before_comma = remove # ignore/add/remove/force # Add or remove space after class ':' sp_after_class_colon = ignore # ignore/add/remove/force # Add or remove space before class ':' sp_before_class_colon = ignore # ignore/add/remove/force # Add or remove space between 'operator' and operator sign sp_after_operator = ignore # ignore/add/remove/force # Add or remove space between the operator symbol and the open paren, as in 'operator ++(' sp_after_operator_sym = ignore # ignore/add/remove/force # Add or remove space after C/D cast, ie 'cast(int)a' vs 'cast(int) a' or '(int)a' vs '(int) a' sp_after_cast = remove # ignore/add/remove/force # Add or remove spaces inside cast parens sp_inside_paren_cast = remove # ignore/add/remove/force # Add or remove space between the type and open paren in a C++ cast, ie 'int(exp)' vs 'int (exp)' sp_cpp_cast_paren = remove # ignore/add/remove/force # Add or remove space between 'sizeof' and '(' sp_sizeof_paren = remove # ignore/add/remove/force # Add or remove space after the tag keyword (Pawn) sp_after_tag = ignore # ignore/add/remove/force # Add or remove space inside enum '{' and '}' sp_inside_braces_enum = force # ignore/add/remove/force # Add or remove space inside struct/union '{' and '}' sp_inside_braces_struct = force # ignore/add/remove/force # Add or remove space inside '{' and '}' sp_inside_braces = force # ignore/add/remove/force # Add or remove space inside '{}' sp_inside_braces_empty = ignore # ignore/add/remove/force # Add or remove space between return type and function name # A minimum of 1 is forced except for pointer return types. sp_type_func = force # ignore/add/remove/force # Add or remove space between function name and '(' on function declaration sp_func_proto_paren = remove # ignore/add/remove/force # Add or remove space between function name and '(' on function definition sp_func_def_paren = remove # ignore/add/remove/force # Add or remove space inside empty function '()' sp_inside_fparens = ignore # ignore/add/remove/force # Add or remove space inside function '(' and ')' sp_inside_fparen = remove # ignore/add/remove/force # Add or remove space between ']' and '(' when part of a function call. sp_square_fparen = ignore # ignore/add/remove/force # Add or remove space between ')' and '{' of function sp_fparen_brace = add # ignore/add/remove/force # Add or remove space between function name and '(' on function calls sp_func_call_paren = remove # ignore/add/remove/force # Add or remove space between the user function name and '(' on function calls # You need to set a keyword to be a user function, like this: 'set func_call_user _' in the config file. sp_func_call_user_paren = ignore # ignore/add/remove/force # Add or remove space between a constructor/destructor and the open paren sp_func_class_paren = remove # ignore/add/remove/force # Add or remove space between 'return' and '(' sp_return_paren = remove # ignore/add/remove/force # Add or remove space between '__attribute__' and '(' sp_attribute_paren = remove # ignore/add/remove/force # Add or remove space between 'defined' and '(' in '#if defined (FOO)' sp_defined_paren = remove # ignore/add/remove/force # Add or remove space between 'throw' and '(' in 'throw (something)' sp_throw_paren = ignore # ignore/add/remove/force # Add or remove space between macro and value sp_macro = ignore # ignore/add/remove/force # Add or remove space between macro function ')' and value sp_macro_func = ignore # ignore/add/remove/force # Add or remove space between 'else' and '{' if on the same line sp_else_brace = ignore # ignore/add/remove/force # Add or remove space between '}' and 'else' if on the same line sp_brace_else = ignore # ignore/add/remove/force # Add or remove space between '}' and the name of a typedef on the same line sp_brace_typedef = force # ignore/add/remove/force # Add or remove space between 'catch' and '{' if on the same line sp_catch_brace = ignore # ignore/add/remove/force # Add or remove space between '}' and 'catch' if on the same line sp_brace_catch = ignore # ignore/add/remove/force # Add or remove space between 'finally' and '{' if on the same line sp_finally_brace = ignore # ignore/add/remove/force # Add or remove space between '}' and 'finally' if on the same line sp_brace_finally = ignore # ignore/add/remove/force # Add or remove space between 'try' and '{' if on the same line sp_try_brace = ignore # ignore/add/remove/force # Add or remove space between get/set and '{' if on the same line sp_getset_brace = ignore # ignore/add/remove/force # Add or remove space before the '::' operator sp_before_dc = remove # ignore/add/remove/force # Add or remove space after the '::' operator sp_after_dc = remove # ignore/add/remove/force # Add or remove around the D named array initializer ':' operator sp_d_array_colon = ignore # ignore/add/remove/force # Add or remove space after the '!' (not) operator. sp_not = force # ignore/add/remove/force # Add or remove space after the '~' (invert) operator. sp_inv = remove # ignore/add/remove/force # Add or remove space after the '&' (address-of) operator. # This does not affect the spacing after a '&' that is part of a type. sp_addr = remove # ignore/add/remove/force # Add or remove space around the '.' or '->' operators sp_member = remove # ignore/add/remove/force # Add or remove space after the '*' (dereference) operator. # This does not affect the spacing after a '*' that is part of a type. sp_deref = remove # ignore/add/remove/force # Add or remove space after '+' or '-', as in 'x = -5' or 'y = +7' sp_sign = remove # ignore/add/remove/force # Add or remove space before or after '++' and '--', as in '(--x)' or 'y++;' sp_incdec = remove # ignore/add/remove/force # Add or remove space before a backslash-newline at the end of a line sp_before_nl_cont = add # ignore/add/remove/force # Add or remove space after the scope '+' or '-', as in '-(void) foo;' or '+(int) bar;' sp_after_oc_scope = ignore # ignore/add/remove/force # Add or remove space after the colon in message specs # '-(int) f:(int) x;' vs '-(int) f: (int) x;' sp_after_oc_colon = ignore # ignore/add/remove/force # Add or remove space before the colon in message specs # '-(int) f: (int) x;' vs '-(int) f : (int) x;' sp_before_oc_colon = ignore # ignore/add/remove/force # Add or remove space after the colon in message specs # '[object setValue:1];' vs '[object setValue: 1];' sp_after_send_oc_colon = ignore # ignore/add/remove/force # Add or remove space before the colon in message specs # '[object setValue:1];' vs '[object setValue :1];' sp_before_send_oc_colon = ignore # ignore/add/remove/force # Add or remove space after the (type) in message specs # '-(int) f: (int) x;' vs '-(int) f: (int)x;' sp_after_oc_type = ignore # ignore/add/remove/force # Add or remove space around the ':' in 'b ? t : f' sp_cond_colon = ignore # ignore/add/remove/force # Add or remove space around the '?' in 'b ? t : f' sp_cond_question = ignore # ignore/add/remove/force # Fix the spacing between 'case' and the label. Only 'ignore' and 'force' make sense here. sp_case_label = remove # ignore/add/remove/force # # Code alignment (not left column spaces/tabs) # # Whether to keep non-indenting tabs align_keep_tabs = false # false/true # Whether to use tabs for alinging align_with_tabs = false # false/true # Whether to bump out to the next tab when aligning align_on_tabstop = false # false/true # Whether to left-align numbers align_number_left = false # false/true # Align variable definitions in prototypes and functions align_func_params = false # false/true # Align parameters in single-line functions that have the same name. # The function names must already be aligned with each other. align_same_func_call_params = false # false/true # The span for aligning variable definitions (0=don't align) align_var_def_span = 1 # number # How to align the star in variable definitions. # 0=Part of the type 'void * foo;' # 1=Part of the variable 'void *foo;' # 2=Dangling 'void *foo;' align_var_def_star_style = 1 # number # How to align the '&' in variable definitions. # 0=Part of the type # 1=Part of the variable # 2=Dangling align_var_def_amp_style = 0 # number # The threshold for aligning variable definitions (0=no limit) align_var_def_thresh = 1 # number # The gap for aligning variable definitions align_var_def_gap = 0 # number # Whether to align the colon in struct bit fields align_var_def_colon = false # false/true # Whether to align inline struct/enum/union variable definitions align_var_def_inline = false # false/true # The span for aligning on '=' in assignments (0=don't align) align_assign_span = 0 # number # The threshold for aligning on '=' in assignments (0=no limit) align_assign_thresh = 12 # number # The span for aligning on '=' in enums (0=don't align) align_enum_equ_span = 0 # number # The threshold for aligning on '=' in enums (0=no limit) align_enum_equ_thresh = 0 # number # The span for aligning struct/union (0=don't align) align_var_struct_span = 0 # number # The threshold for aligning struct/union member definitions (0=no limit) align_var_struct_thresh = 0 # number # The gap for aligning struct/union member definitions align_var_struct_gap = 0 # number # The span for aligning struct initializer values (0=don't align) align_struct_init_span = 3 # number # The minimum space between the type and the synonym of a typedef align_typedef_gap = 3 # number # The span for aligning single-line typedefs (0=don't align) align_typedef_span = 5 # number # How to align typedef'd functions with other typedefs # 0: Don't mix them at all # 1: align the open paren with the types # 2: align the function type name with the other type names align_typedef_func = 0 # number # Controls the positioning of the '*' in typedefs. Just try it. # 0: Align on typdef type, ignore '*' # 1: The '*' is part of type name: typedef int *pint; # 2: The '*' is part of the type, but dangling: typedef int *pint; align_typedef_star_style = 0 # number # Controls the positioning of the '&' in typedefs. Just try it. # 0: Align on typdef type, ignore '&' # 1: The '&' is part of type name: typedef int &pint; # 2: The '&' is part of the type, but dangling: typedef int &pint; align_typedef_amp_style = 0 # number # The span for aligning comments that end lines (0=don't align) align_right_cmt_span = 3 # number # If aligning comments, mix with comments after '}' and #endif with less than 3 spaces before the comment align_right_cmt_mix = false # false/true # If a trailing comment is more than this number of columns away from the text it follows, # it will qualify for being aligned. align_right_cmt_gap = 0 # number # The span for aligning function prototypes (0=don't align) align_func_proto_span = 0 # number # Minimum gap between the return type and the function name. align_func_proto_gap = 0 # number # Whether to mix aligning prototype and variable declarations. # If true, align_var_def_XXX options are used instead of align_func_proto_XXX options. align_mix_var_proto = false # false/true # Align single-line functions with function prototypes, uses align_func_proto_span align_single_line_func = false # false/true # Aligning the open brace of single-line functions. # Requires align_single_line_func=true, uses align_func_proto_span align_single_line_brace = false # false/true # Gap for align_single_line_brace. align_single_line_brace_gap = 0 # number # The span for aligning ObjC msg spec (0=don't align) align_oc_msg_spec_span = 0 # number # Whether to align macros wrapped with a backslash and a newline. # This will not work right if the macro contains a multi-line comment. align_nl_cont = true # false/true # The minimum space between label and value of a preprocessor define align_pp_define_gap = 4 # number # The span for aligning on '#define' bodies (0=don't align) align_pp_define_span = 3 # number # Align lines that start with '<<' with previous '<<'. Default=true align_left_shift = true # false/true # # Newline adding and removing options # # Whether to collapse empty blocks between '{' and '}' nl_collapse_empty_body = true # false/true # Don't split one-line braced assignments - 'foo_t f = { 1, 2 };' nl_assign_leave_one_liners = false # false/true # Don't split one-line braced statements inside a class xx { } body nl_class_leave_one_liners = false # false/true # Don't split one-line enums: 'enum foo { BAR = 15 };' nl_enum_leave_one_liners = false # false/true # Don't split one-line get or set functions nl_getset_leave_one_liners = false # false/true # Don't split one-line function definitions - 'int foo() { return 0; }' nl_func_leave_one_liners = false # false/true # Don't split one-line if/else statements - 'if(a) b++;' nl_if_leave_one_liners = false # false/true # Add or remove newlines at the start of the file nl_start_of_file = remove # ignore/add/remove/force # The number of newlines at the start of the file (only used if nl_start_of_file is 'add' or 'force' nl_start_of_file_min = 0 # number # Add or remove newline at the end of the file nl_end_of_file = force # ignore/add/remove/force # The number of newlines at the end of the file (only used if nl_end_of_file is 'add' or 'force') nl_end_of_file_min = 1 # number # Add or remove newline between '=' and '{' nl_assign_brace = force # ignore/add/remove/force # Add or remove newline between '=' and '[' (D only) nl_assign_square = force # ignore/add/remove/force # Add or remove newline after '= [' (D only). Will also affect the newline before the ']' nl_after_square_assign = force # ignore/add/remove/force # The number of newlines after a block of variable definitions nl_func_var_def_blk = 1 # number # Add or remove newline between a function call's ')' and '{', as in: # list_for_each(item, &list) { } nl_fcall_brace = force # ignore/add/remove/force # Add or remove newline between 'enum' and '{' nl_enum_brace = force # ignore/add/remove/force # Add or remove newline between 'struct and '{' nl_struct_brace = force # ignore/add/remove/force # Add or remove newline between 'union' and '{' nl_union_brace = force # ignore/add/remove/force # Add or remove newline between 'if' and '{' nl_if_brace = force # ignore/add/remove/force # Add or remove newline between '}' and 'else' nl_brace_else = force # ignore/add/remove/force # Add or remove newline between 'else if' and '{' # If set to ignore, nl_if_brace is used instead nl_elseif_brace = force # ignore/add/remove/force # Add or remove newline between 'else' and '{' nl_else_brace = force # ignore/add/remove/force # Add or remove newline between 'else' and 'if' nl_else_if = remove # ignore/add/remove/force # Add or remove newline between '}' and 'finally' nl_brace_finally = force # ignore/add/remove/force # Add or remove newline between 'finally' and '{' nl_finally_brace = force # ignore/add/remove/force # Add or remove newline between 'try' and '{' nl_try_brace = force # ignore/add/remove/force # Add or remove newline between get/set and '{' nl_getset_brace = force # ignore/add/remove/force # Add or remove newline between 'for' and '{' nl_for_brace = force # ignore/add/remove/force # Add or remove newline between 'catch' and '{' nl_catch_brace = force # ignore/add/remove/force # Add or remove newline between '}' and 'catch' nl_brace_catch = force # ignore/add/remove/force # Add or remove newline between 'while' and '{' nl_while_brace = force # ignore/add/remove/force # Add or remove newline between 'do' and '{' nl_do_brace = force # ignore/add/remove/force # Add or remove newline between '}' and 'while' of 'do' statement nl_brace_while = force # ignore/add/remove/force # Add or remove newline between 'switch' and '{' nl_switch_brace = force # ignore/add/remove/force # Add or remove newline when condition spans two or more lines nl_multi_line_cond = false # false/true # Force a newline in a define after the macro name for multi-line defines. nl_multi_line_define = true # false/true # Whether to put a newline before 'case' statement nl_before_case = true # false/true # Add or remove newline between ')' and 'throw' nl_before_throw = ignore # ignore/add/remove/force # Whether to put a newline after 'case' statement nl_after_case = true # false/true # Newline between namespace and { nl_namespace_brace = add # ignore/add/remove/force # Add or remove newline between 'template<>' and whatever follows. nl_template_class = add # ignore/add/remove/force # Add or remove newline between 'class' and '{' nl_class_brace = add # ignore/add/remove/force # Add or remove newline after each ',' in the constructor member initialization nl_class_init_args = force # ignore/add/remove/force # Add or remove newline between return type and function name in definition nl_func_type_name = ignore # ignore/add/remove/force # Add or remove newline between return type and function name in a prototype nl_func_proto_type_name = remove # ignore/add/remove/force # Add or remove newline between a function name and the opening '(' nl_func_paren = remove # ignore/add/remove/force # Add or remove newline after '(' in a function declaration nl_func_decl_start = remove # ignore/add/remove/force # Add or remove newline after each ',' in a function declaration nl_func_decl_args = remove # ignore/add/remove/force # Add or remove newline before the ')' in a function declaration nl_func_decl_end = remove # ignore/add/remove/force # Add or remove newline between function signature and '{' nl_fdef_brace = add # ignore/add/remove/force # Whether to put a newline after 'return' statement nl_after_return = true # false/true # Add or remove a newline between the return keyword and return expression. nl_return_expr = remove # ignore/add/remove/force # Whether to put a newline after semicolons, except in 'for' statements nl_after_semicolon = true # false/true # Whether to put a newline after brace open. # This also adds a newline before the matching brace close. nl_after_brace_open = true # false/true # If nl_after_brace_open and nl_after_brace_open_cmt are true, a newline is # placed between the open brace and a trailing single-line comment. nl_after_brace_open_cmt = false # false/true # Whether to put a newline after a virtual brace open. # These occur in un-braced if/while/do/for statement bodies. nl_after_vbrace_open = true # false/true # Whether to alter newlines in '#define' macros nl_define_macro = true # false/true # Whether to not put blanks after '#ifxx', '#elxx', or before '#endif' nl_squeeze_ifdef = false # false/true # Add or remove newline before 'if' nl_before_if = ignore # ignore/add/remove/force # Add or remove newline after 'if' nl_after_if = ignore # ignore/add/remove/force # Add or remove newline before 'for' nl_before_for = ignore # ignore/add/remove/force # Add or remove newline after 'for' nl_after_for = ignore # ignore/add/remove/force # Add or remove newline before 'while' nl_before_while = ignore # ignore/add/remove/force # Add or remove newline after 'while' nl_after_while = ignore # ignore/add/remove/force # Add or remove newline before 'switch' nl_before_switch = force # ignore/add/remove/force # Add or remove newline after 'switch' nl_after_switch = force # ignore/add/remove/force # Add or remove newline before 'do' nl_before_do = ignore # ignore/add/remove/force # Add or remove newline after 'do' nl_after_do = ignore # ignore/add/remove/force # Whether to double-space commented-entries in struct/enum nl_ds_struct_enum_cmt = false # false/true # Whether to double-space before the close brace of a struct/union/enum nl_ds_struct_enum_close_brace = false # false/true # Add or remove a newline around a class colon. # Related to pos_class_colon, nl_class_init_args, and pos_comma. nl_class_colon = ignore # ignore/add/remove/force # Change simple unbraced if statements into a one-liner # 'if(b)\n i++;' => 'if(b) i++;' nl_create_if_one_liner = false # false/true # Change simple unbraced for statements into a one-liner # 'for (i=0;i<5;i++)\n foo(i);' => 'for (i=0;i<5;i++) foo(i);' nl_create_for_one_liner = false # false/true # Change simple unbraced while statements into a one-liner # 'while (i<5)\n foo(i++);' => 'while (i<5) foo(i++);' nl_create_while_one_liner = false # false/true # # Positioning options # # The position of arithmetic operators in wrapped expressions pos_arith = lead # ignore/lead/trail # The position of assignment in wrapped expressions pos_assign = trail # ignore/lead/trail # The position of boolean operators in wrapped expressions pos_bool = lead # ignore/lead/trail # The position of the comma in wrapped expressions pos_comma = trail # ignore/lead/trail # The position of the comma in the constructor initialization list pos_class_comma = trail # ignore/lead/trail # The position of colons between constructor and member initialization pos_class_colon = lead # ignore/lead/trail # # Line Splitting options # # Try to limit code width to N number of columns code_width = 76 # number # Whether to fully split long 'for' statements at semi-colons ls_for_split_full = true # false/true # Whether to fully split long function protos/calls at commas ls_func_split_full = true # false/true # # Blank line options # # The maximum consecutive newlines nl_max = 4 # number # The number of newlines after a function prototype, if followed by another function prototype nl_after_func_proto = 0 # number # The number of newlines after a function prototype, if not followed by another function prototype nl_after_func_proto_group = 3 # number # The number of newlines after '}' of a multi-line function body nl_after_func_body = 3 # number # The number of newlines after '}' of a single line function body nl_after_func_body_one_liner = 1 # number # The minimum number of newlines before a multi-line comment. # Doesn't apply if after a brace open or another multi-line comment. nl_before_block_comment = 2 # number # The minimum number of newlines before a single-line C comment. # Doesn't apply if after a brace open or other single-line C comments. nl_before_c_comment = 1 # number # The minimum number of newlines before a CPP comment. # Doesn't apply if after a brace open or other CPP comments. nl_before_cpp_comment = 0 # number # Whether to force a newline after a mulit-line comment. nl_after_multiline_comment = false # false/true # The number of newlines before a 'private:', 'public:', 'protected:', 'signals:', or 'slots:' label. # Will not change the newline count if after a brace open. # 0 = No change. nl_before_access_spec = 1 # number # The number of newlines after a 'private:', 'public:', 'protected:', 'signals:', or 'slots:' label. # 0 = No change. nl_after_access_spec = 1 # number # The number of newlines between a function def and the function comment. # 0 = No change. nl_comment_func_def = 0 # number # Whether to remove blank lines after '{' eat_blanks_after_open_brace = true # false/true # Whether to remove blank lines before '}' eat_blanks_before_close_brace = true # false/true # # Code modifying options (non-whitespace) # # Add or remove braces on single-line 'do' statement mod_full_brace_do = add # ignore/add/remove/force # Add or remove braces on single-line 'for' statement mod_full_brace_for = add # ignore/add/remove/force # Add or remove braces on single-line function defintions. (Pawn) mod_full_brace_function = ignore # ignore/add/remove/force # Add or remove braces on single-line 'if' statement. Will not remove the braces if they contain an 'else'. mod_full_brace_if = ignore # ignore/add/remove/force # Don't remove braces around statements that span N newlines mod_full_brace_nl = 0 # number # Add or remove braces on single-line 'while' statement mod_full_brace_while = add # ignore/add/remove/force # Add or remove unnecessary paren on 'return' statement mod_paren_on_return = remove # ignore/add/remove/force # Whether to change optional semicolons to real semicolons mod_pawn_semicolon = false # false/true # Add parens on 'while' and 'if' statement around bools mod_full_paren_if_bool = true # false/true # Whether to remove superfluous semicolons mod_remove_extra_semicolon = true # false/true # If a function body exceeds the specified number of newlines and doesn't have a comment after # the close brace, a comment will be added. mod_add_long_function_closebrace_comment = 5 # number # If a switch body exceeds the specified number of newlines and doesn't have a comment after # the close brace, a comment will be added. mod_add_long_switch_closebrace_comment = 5 # number # If TRUE, will sort consecutive single-line 'import' statements [Java, D] mod_sort_import = false # false/true # If TRUE, will sort consecutive single-line 'using' statements [C#] mod_sort_using = false # false/true # If TRUE, will sort consecutive single-line '#include' statements [C/C++] and '#import' statements [Obj-C] # This is generally a bad idea, as it may break your code. mod_sort_include = false # false/true # If TRUE, it will move a 'break' that appears after a fully braced 'case' before the close brace. mod_move_case_break = false # false/true # If TRUE, it will remove a void 'return;' that appears as the last statement in a function. mod_remove_empty_return = true # false/true # # Comment modifications # # Try to wrap comments at cmt_width columns cmt_width = 0 # number # If false, disable all multi-line comment changes, including cmt_width and leading chars. # Default is true. cmt_indent_multi = true # false/true # Whether to group c-comments that look like they are in a block cmt_c_group = false # false/true # Whether to put an empty '/*' on the first line of the combined c-comment cmt_c_nl_start = false # false/true # Whether to put a newline before the closing '*/' of the combined c-comment cmt_c_nl_end = false # false/true # Whether to group cpp-comments that look like they are in a block cmt_cpp_group = false # false/true # Whether to put an empty '/*' on the first line of the combined cpp-comment cmt_cpp_nl_start = false # false/true # Whether to put a newline before the closing '*/' of the combined cpp-comment cmt_cpp_nl_end = false # false/true # Whether to change cpp-comments into c-comments cmt_cpp_to_c = false # false/true # Whether to put a star on subsequent comment lines cmt_star_cont = true # false/true # The number of spaces to insert at the start of subsequent comment lines cmt_sp_before_star_cont = 0 # number # The number of spaces to insert after the star on subsequent comment lines cmt_sp_after_star_cont = 1 # number # For multi-line comments with a '*' lead, remove leading spaces if the first and last lines of # the comment are the same length. Default=True cmt_multi_check_last = true # false/true # The filename that contains text to insert at the head of a file if the file doesn't start with a C/C++ comment. # Will substitue $(filename) with the current file's name. cmt_insert_file_header = "" # string # The filename that contains text to insert before a function implementation if the function isn't preceeded with a C/C++ comment. # Will substitue $(function) with the function name and $(javaparam) with the javadoc @param and @return stuff. # Will also substitute $(fclass) with the class name: void CFoo::Bar() { ... } cmt_insert_func_header = "" # string # The filename that contains text to insert before a class if the class isn't preceeded with a C/C++ comment. # Will substitue $(class) with the class name. cmt_insert_class_header = "" # string # If a preprocessor is encountered when stepping backwards from a function name, then # this option decides whether the comment should be inserted. # Affects cmt_insert_func_header and cmt_insert_class_header. cmt_insert_before_preproc = false # false/true # # Preprocessor options # # Control indent of preprocessors inside #if blocks at brace level 0 pp_indent = remove # ignore/add/remove/force # Whether to indent #if/#else/#endif at the brace level (true) or from column 1 (false) pp_indent_at_level = false # false/true # If pp_indent_at_level=false, specifies the number of columns to indent per level. Default=1. pp_indent_count = 1 # number # Add or remove space after # based on pp_level of #if blocks pp_space = add # ignore/add/remove/force # Sets the number of spaces added with pp_space pp_space_count = 1 # number # The indent for #region and #endregion in C# and '#pragma region' in C/C++ pp_indent_region = 0 # number # Whether to indent the code between #region and #endregion pp_region_indent_code = false # false/true # If pp_indent_at_level=true, sets the indent for #if, #else, and #endif when not at file-level pp_indent_if = 0 # number # Control whether to indent the code between #if, #else and #endif when not at file-level pp_if_indent_code = false # false/true # Whether to indent '#define' at the brace level (true) or from column 1 (false) pp_define_at_level = false # false/true # You can force a token to be a type with the 'type' option. # Example: # type myfoo1 myfoo2 # # You can create custom macro-based indentation using macro-open, # macro-else and macro-close. # Example: # macro-open BEGIN_TEMPLATE_MESSAGE_MAP # macro-open BEGIN_MESSAGE_MAP # macro-close END_MESSAGE_MAP # # You can assign any keyword to any type with the set option. # set func_call_user _ N_ haildb-2.3.2/config/missing0000755000175000017500000002623311513177417016512 0ustar00pcrewspcrews00000000000000#! /bin/sh # Common stub for a few missing GNU programs while installing. scriptversion=2009-04-28.21; # UTC # Copyright (C) 1996, 1997, 1999, 2000, 2002, 2003, 2004, 2005, 2006, # 2008, 2009 Free Software Foundation, Inc. # Originally by Fran,cois Pinard , 1996. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # This program 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 General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. if test $# -eq 0; then echo 1>&2 "Try \`$0 --help' for more information" exit 1 fi run=: sed_output='s/.* --output[ =]\([^ ]*\).*/\1/p' sed_minuso='s/.* -o \([^ ]*\).*/\1/p' # In the cases where this matters, `missing' is being run in the # srcdir already. if test -f configure.ac; then configure_ac=configure.ac else configure_ac=configure.in fi msg="missing on your system" case $1 in --run) # Try to run requested program, and just exit if it succeeds. run= shift "$@" && exit 0 # Exit code 63 means version mismatch. This often happens # when the user try to use an ancient version of a tool on # a file that requires a minimum version. In this case we # we should proceed has if the program had been absent, or # if --run hadn't been passed. if test $? = 63; then run=: msg="probably too old" fi ;; -h|--h|--he|--hel|--help) echo "\ $0 [OPTION]... PROGRAM [ARGUMENT]... Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an error status if there is no known handling for PROGRAM. Options: -h, --help display this help and exit -v, --version output version information and exit --run try to run the given command, and emulate it if it fails Supported PROGRAM values: aclocal touch file \`aclocal.m4' autoconf touch file \`configure' autoheader touch file \`config.h.in' autom4te touch the output file, or create a stub one automake touch all \`Makefile.in' files bison create \`y.tab.[ch]', if possible, from existing .[ch] flex create \`lex.yy.c', if possible, from existing .c help2man touch the output file lex create \`lex.yy.c', if possible, from existing .c makeinfo touch the output file tar try tar, gnutar, gtar, then tar without non-portable flags yacc create \`y.tab.[ch]', if possible, from existing .[ch] Version suffixes to PROGRAM as well as the prefixes \`gnu-', \`gnu', and \`g' are ignored when checking the name. Send bug reports to ." exit $? ;; -v|--v|--ve|--ver|--vers|--versi|--versio|--version) echo "missing $scriptversion (GNU Automake)" exit $? ;; -*) echo 1>&2 "$0: Unknown \`$1' option" echo 1>&2 "Try \`$0 --help' for more information" exit 1 ;; esac # normalize program name to check for. program=`echo "$1" | sed ' s/^gnu-//; t s/^gnu//; t s/^g//; t'` # Now exit if we have it, but it failed. Also exit now if we # don't have it and --version was passed (most likely to detect # the program). This is about non-GNU programs, so use $1 not # $program. case $1 in lex*|yacc*) # Not GNU programs, they don't have --version. ;; tar*) if test -n "$run"; then echo 1>&2 "ERROR: \`tar' requires --run" exit 1 elif test "x$2" = "x--version" || test "x$2" = "x--help"; then exit 1 fi ;; *) if test -z "$run" && ($1 --version) > /dev/null 2>&1; then # We have it, but it failed. exit 1 elif test "x$2" = "x--version" || test "x$2" = "x--help"; then # Could not run --version or --help. This is probably someone # running `$TOOL --version' or `$TOOL --help' to check whether # $TOOL exists and not knowing $TOOL uses missing. exit 1 fi ;; esac # If it does not exist, or fails to run (possibly an outdated version), # try to emulate it. case $program in aclocal*) echo 1>&2 "\ WARNING: \`$1' is $msg. You should only need it if you modified \`acinclude.m4' or \`${configure_ac}'. You might want to install the \`Automake' and \`Perl' packages. Grab them from any GNU archive site." touch aclocal.m4 ;; autoconf*) echo 1>&2 "\ WARNING: \`$1' is $msg. You should only need it if you modified \`${configure_ac}'. You might want to install the \`Autoconf' and \`GNU m4' packages. Grab them from any GNU archive site." touch configure ;; autoheader*) echo 1>&2 "\ WARNING: \`$1' is $msg. You should only need it if you modified \`acconfig.h' or \`${configure_ac}'. You might want to install the \`Autoconf' and \`GNU m4' packages. Grab them from any GNU archive site." files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}` test -z "$files" && files="config.h" touch_files= for f in $files; do case $f in *:*) touch_files="$touch_files "`echo "$f" | sed -e 's/^[^:]*://' -e 's/:.*//'`;; *) touch_files="$touch_files $f.in";; esac done touch $touch_files ;; automake*) echo 1>&2 "\ WARNING: \`$1' is $msg. You should only need it if you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'. You might want to install the \`Automake' and \`Perl' packages. Grab them from any GNU archive site." find . -type f -name Makefile.am -print | sed 's/\.am$/.in/' | while read f; do touch "$f"; done ;; autom4te*) echo 1>&2 "\ WARNING: \`$1' is needed, but is $msg. You might have modified some files without having the proper tools for further handling them. You can get \`$1' as part of \`Autoconf' from any GNU archive site." file=`echo "$*" | sed -n "$sed_output"` test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"` if test -f "$file"; then touch $file else test -z "$file" || exec >$file echo "#! /bin/sh" echo "# Created by GNU Automake missing as a replacement of" echo "# $ $@" echo "exit 0" chmod +x $file exit 1 fi ;; bison*|yacc*) echo 1>&2 "\ WARNING: \`$1' $msg. You should only need it if you modified a \`.y' file. You may need the \`Bison' package in order for those modifications to take effect. You can get \`Bison' from any GNU archive site." rm -f y.tab.c y.tab.h if test $# -ne 1; then eval LASTARG="\${$#}" case $LASTARG in *.y) SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'` if test -f "$SRCFILE"; then cp "$SRCFILE" y.tab.c fi SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'` if test -f "$SRCFILE"; then cp "$SRCFILE" y.tab.h fi ;; esac fi if test ! -f y.tab.h; then echo >y.tab.h fi if test ! -f y.tab.c; then echo 'main() { return 0; }' >y.tab.c fi ;; lex*|flex*) echo 1>&2 "\ WARNING: \`$1' is $msg. You should only need it if you modified a \`.l' file. You may need the \`Flex' package in order for those modifications to take effect. You can get \`Flex' from any GNU archive site." rm -f lex.yy.c if test $# -ne 1; then eval LASTARG="\${$#}" case $LASTARG in *.l) SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'` if test -f "$SRCFILE"; then cp "$SRCFILE" lex.yy.c fi ;; esac fi if test ! -f lex.yy.c; then echo 'main() { return 0; }' >lex.yy.c fi ;; help2man*) echo 1>&2 "\ WARNING: \`$1' is $msg. You should only need it if you modified a dependency of a manual page. You may need the \`Help2man' package in order for those modifications to take effect. You can get \`Help2man' from any GNU archive site." file=`echo "$*" | sed -n "$sed_output"` test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"` if test -f "$file"; then touch $file else test -z "$file" || exec >$file echo ".ab help2man is required to generate this page" exit $? fi ;; makeinfo*) echo 1>&2 "\ WARNING: \`$1' is $msg. You should only need it if you modified a \`.texi' or \`.texinfo' file, or any other file indirectly affecting the aspect of the manual. The spurious call might also be the consequence of using a buggy \`make' (AIX, DU, IRIX). You might want to install the \`Texinfo' package or the \`GNU make' package. Grab either from any GNU archive site." # The file to touch is that specified with -o ... file=`echo "$*" | sed -n "$sed_output"` test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"` if test -z "$file"; then # ... or it is the one specified with @setfilename ... infile=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'` file=`sed -n ' /^@setfilename/{ s/.* \([^ ]*\) *$/\1/ p q }' $infile` # ... or it is derived from the source name (dir/f.texi becomes f.info) test -z "$file" && file=`echo "$infile" | sed 's,.*/,,;s,.[^.]*$,,'`.info fi # If the file does not exist, the user really needs makeinfo; # let's fail without touching anything. test -f $file || exit 1 touch $file ;; tar*) shift # We have already tried tar in the generic part. # Look for gnutar/gtar before invocation to avoid ugly error # messages. if (gnutar --version > /dev/null 2>&1); then gnutar "$@" && exit 0 fi if (gtar --version > /dev/null 2>&1); then gtar "$@" && exit 0 fi firstarg="$1" if shift; then case $firstarg in *o*) firstarg=`echo "$firstarg" | sed s/o//` tar "$firstarg" "$@" && exit 0 ;; esac case $firstarg in *h*) firstarg=`echo "$firstarg" | sed s/h//` tar "$firstarg" "$@" && exit 0 ;; esac fi echo 1>&2 "\ WARNING: I can't seem to be able to run \`tar' with the given arguments. You may want to install GNU tar or Free paxutils, or check the command line arguments." exit 1 ;; *) echo 1>&2 "\ WARNING: \`$1' is needed, and is $msg. You might have modified some files without having the proper tools for further handling them. Check the \`README' file, it often tells you about the needed prerequisites for installing this package. You may also peek at any GNU archive site, in case some other package would contain this missing \`$1' program." exit 1 ;; esac exit 0 # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC" # time-stamp-end: "; # UTC" # End: haildb-2.3.2/config/pandora-plugin0000755000175000017500000006563211513177357017772 0ustar00pcrewspcrews00000000000000#!/usr/bin/python # Copyright (C) 2009 Sun Microsystems, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; version 2 of the License. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA pandora_plugin_file = 'config/pandora-plugin.ini' # Find plugins in the tree and add them to the build system import ConfigParser, os, sys import datetime, time import subprocess plugin_am_file=None plugin_ac_file=None plugin_doc_index=None class ChangeProtectedFile(object): def __init__(self, fname): self.bogus_file= False self.real_fname= fname self.new_fname= "%s.new" % fname try: self.new_file= open(self.new_fname,'w+') except IOError: self.bogus_file= True def write(self, text): if not self.bogus_file: self.new_file.write(text) # We've written all of this out into .new files, now we only copy them # over the old ones if they are different, so that we don't cause # unnecessary recompiles def close(self): """Return True if the file had changed.""" if self.bogus_file: return self.new_file.seek(0) new_content = self.new_file.read() self.new_file.close() try: old_file = file(self.real_fname, 'r') old_content = old_file.read() old_file.close() except IOError: old_content = None if new_content != old_content: if old_content != None: os.unlink(self.real_fname) os.rename(self.new_fname, self.real_fname) return True else: try: os.unlink(self.new_fname) except: pass def write_external_configure(plugin, plugin_file): """Write the initial bits of the configure.ac file""" if not os.path.exists('m4'): os.mkdir('m4') plugin_file.write(""" AC_PREREQ(2.59)dnl Minimum Autoconf version required. AC_INIT([%(name)s],[%(version)s],[%(url)s]) AC_CONFIG_SRCDIR([%(main_source)s]) AC_CONFIG_AUX_DIR(config) PANDORA_CANONICAL_TARGET(less-warnings, warnings-always-on, require-cxx, force-gcc42,skip-visibility) PANDORA_REQUIRE_LIBPROTOBUF PANDORA_PROTOBUF_REQUIRE_VERSION([2.1.0]) PANDORA_REQUIRE_PROTOC AC_LANG_PUSH(C++) PANDORA_REQUIRE_PTHREAD PANDORA_REQUIRE_LIBDL AC_LANG_POP PANDORA_USE_BETTER_MALLOC PANDORA_DRIZZLE_BUILD """ % plugin) write_plugin_ac(plugin, plugin_file) plugin_file.write(""" AC_CONFIG_FILES(Makefile) AC_OUTPUT echo "---" echo "Configuration summary for $PACKAGE_NAME version $VERSION $PANDORA_RELEASE_COMMENT" echo "" echo " * Installation prefix: $prefix" echo " * System type: $host_vendor-$host_os" echo " * Host CPU: $host_cpu" echo " * C Compiler: $CC_VERSION" echo " * C++ Compiler: $CXX_VERSION" echo " * Debug enabled: $with_debug" echo " * Warnings as failure: $ac_cv_warnings_as_errors" echo " * C++ cstdint location: $ac_cv_cxx_cstdint" echo " * C++ hash_map location: $ac_cv_cxx_hash_map" echo " * C++ hash namespace: $ac_cv_cxx_hash_namespace" echo " * C++ shared_ptr namespace: $ac_cv_shared_ptr_namespace" echo "" echo "---" """ % plugin) def write_external_makefile(plugin, plugin_file): plugin_file.write(""" ACLOCAL_AMFLAGS = -I m4 --force VERSION=$(PANDORA_RELEASE_VERSION) pkgplugindir=%(pkgplugindir)s EXTRA_DIST = plugin.ini noinst_HEADERS= nobase_include_HEADERS= check_PROGRAMS= noinst_LTLIBRARIES= bin_PROGRAMS= """ % plugin) if plugin['headers'] != "": plugin_file.write("noinst_HEADERS += %(headers)s\n" % plugin) if plugin['install_headers'] != "": plugin_file.write("nobase_include_HEADERS += %(install_headers)s\n" % plugin) if plugin['testsuite']: if plugin.has_key('testsuitedir') and plugin['testsuitedir'] != "": plugin_file.write("EXTRA_DIST += %(testsuitedir)s\n" % plugin) plugin_file.write(""" pkgplugin_LTLIBRARIES=%(libname)s.la %(libname)s_la_LDFLAGS=-avoid-version -rpath $(pkgplugindir) $(AM_LDFLAGS) %(ldflags)s $(GCOV_LIBS) %(libname)s_la_LIBADD=%(libs)s %(libname)s_la_DEPENDENCIES=%(libs)s %(libname)s_la_CPPFLAGS=$(AM_CPPFLAGS) -DPANDORA_DYNAMIC_PLUGIN -DPANDORA_MODULE_NAME=%(module_name)s -DPANDORA_MODULE_AUTHOR='"%(author)s"' -DPANDORA_MODULE_TITLE='"%(title)s"' -DPANDORA_MODULE_VERSION='"%(version)s"' -DPANDORA_MODULE_LICENSE=%(license)s %(cppflags)s %(libname)s_la_CXXFLAGS=$(AM_CXXFLAGS) %(cxxflags)s %(libname)s_la_CFLAGS=$(AM_CFLAGS) %(cflags)s %(libname)s_la_SOURCES=%(sources)s check_PROGRAMS += %(tests)s """ % plugin) plugin_am_file=os.path.join(plugin['rel_path'],'plugin.am') if os.path.exists(plugin_am_file): plugin_file.write('include %s\n' % plugin_am_file) def write_external_plugin(): """Return True if the plugin had changed.""" plugin = read_plugin_ini('.') expand_plugin_ini(plugin) plugin_file = ChangeProtectedFile('configure.ac') write_external_configure(plugin, plugin_file) result = plugin_file.close() plugin_file = ChangeProtectedFile('Makefile.am') write_external_makefile(plugin, plugin_file) # Write some stub configure.ac and Makefile.am files that include the above result = plugin_file.close() or result return result def write_plugin(plugin, plugin_ini_list): # Since this function is recursive, make sure we're not already in it. if plugin.has_key('writing_status'): if plugin['writing_status'] == 'done': return else: print "Dependency loop detected with %s" % plugin['name'] exit(1) plugin['writing_status'] = 'dependencies' # Write all dependencies first to get around annoying automake bug for dependency in plugin['dependency_list']: found = False for find_plugin in plugin_ini_list: if find_plugin['module_name'] == dependency: found = True write_plugin(find_plugin, plugin_ini_list) break if found is False: print "Could not find dependency %s: %s" % (plugin['name'], dependency) exit(1) write_plugin_ac(plugin, plugin_ac_file) write_plugin_am(plugin, plugin_am_file) write_plugin_docs(plugin, plugin_doc_index) plugin['writing_status'] = 'done' def write_plugin_docs(plugin, doc_index): if plugin['docs'] is not None and os.path.isdir("docs/plugins"): if not os.path.exists(os.path.join("docs/plugins",plugin["name"])): os.symlink(os.path.abspath(plugin["docs"]), os.path.join("docs/plugins",plugin["name"])) doc_index.write(""" %(name)s/index""" % plugin) def write_plugin_ac(plugin, plugin_ac): # # Write plugin config instructions into plugin.ac file. # plugin_ac_file=os.path.join(plugin['rel_path'],'plugin.ac') plugin_m4_dir=os.path.join(plugin['rel_path'],'m4') plugin_m4_files=[] if os.path.exists(plugin_m4_dir) and os.path.isdir(plugin_m4_dir): for m4_file in os.listdir(plugin_m4_dir): if os.path.splitext(m4_file)[-1] == '.m4': plugin_m4_files.append(os.path.join(plugin['rel_path'], m4_file)) plugin_ac.write(""" dnl Config for %(title)s """ % plugin) for m4_file in plugin_m4_files: plugin_ac.write('m4_sinclude([%s])\n' % m4_file) plugin['plugin_dep_libs']=" ".join(["\${top_builddir}/%s" % f for f in plugin['libs'].split()]) plugin_ac.write(""" AC_ARG_WITH([%(name_with_dashes)s-plugin],[ dnl indented wierd to make the help output correct AS_HELP_STRING([--with-%(name_with_dashes)s-plugin],[Build %(title)s. @<:@default=%(enabled)s@:>@]) AS_HELP_STRING([--without-%(name_with_dashes)s-plugin],[Disable building %(title)s]) ],[ with_%(name)s_plugin="$withval" AS_IF([test "x$with_%(name)s_plugin" = "xyes"],[ requested_%(name)s_plugin="yes" ],[ requested_%(name)s_plugin="no" ]) ],[ with_%(name)s_plugin="%(enabled)s" requested_%(name)s_plugin="no" ]) AC_ARG_ENABLE([%(name_with_dashes)s-plugin],[ dnl indented wierd to make the help output correct AS_HELP_STRING([--enable-%(name_with_dashes)s-plugin],[Enable loading %(title)s by default. @<:@default=%(default_yesno)s@:>@]) AS_HELP_STRING([--disable-%(name_with_dashes)s-plugin],[Disable loading %(title)s by default.]) ], [enable_%(name)s_plugin="$enableval"], [enable_%(name)s_plugin=%(default_yesno)s]) """ % plugin) if os.path.exists(plugin_ac_file): plugin_ac.write('m4_sinclude([%s])\n' % plugin_ac_file) # The plugin author has specified some check to make to determine # if the plugin can be built. If the plugin is turned on and this # check fails, then configure should error out. If the plugin is not # turned on, then the normal conditional build stuff should just let # it silently not build if plugin['has_build_conditional']: plugin_ac.write(""" AS_IF([test %(build_conditional)s], [], dnl build_conditional can only negate [ AS_IF([test "x${requested_%(name)s_plugin}" = "xyes"], [AC_MSG_ERROR([Plugin %(name)s was explicitly requested, yet failed build dependency checks. Aborting!])]) with_%(name)s_plugin=no ]) """ % plugin) if not plugin['unconditional']: plugin_ac.write(""" AM_CONDITIONAL([%(build_conditional_tag)s], [test %(build_conditional)s]) """ % plugin) plugin_ac.write(""" AS_IF([test "x$with_%(name)s_plugin" = "xyes"], [ """ % plugin) if plugin['testsuite']: plugin_ac.write(""" pandora_plugin_test_list="%(name)s,${pandora_plugin_test_list}" """ % plugin) if plugin['static']: plugin_ac.write(""" AS_IF([test "x$enable_%(name)s_plugin" = "xyes"],[ pandora_builtin_load_list="%(module_name)s,${pandora_builtin_load_list}" pandora_builtin_load_symbols_list="_drizzled_%(module_name)s_plugin_,${pandora_builtin_load_symbols_list}" PANDORA_PLUGIN_DEP_LIBS="${PANDORA_PLUGIN_DEP_LIBS} %(plugin_dep_libs)s" ]) pandora_builtin_list="%(module_name)s,${pandora_builtin_list}" pandora_builtin_symbols_list="_drizzled_%(module_name)s_plugin_,${pandora_builtin_symbols_list}" pandora_plugin_libs="${pandora_plugin_libs} \${top_builddir}/%(root_plugin_dir)s/%(libname)s.la" """ % plugin) else: plugin_ac.write(""" AS_IF([test "x$enable_%(name)s_plugin" = "xyes"],[ pandora_default_plugin_list="%(name)s,${pandora_default_plugin_list}" ]) """ % plugin) plugin_ac.write(" ])\n") def fix_file_paths(plugin, files): # TODO: determine path to plugin dir relative to top_srcdir... append it to # source files if they don't already have it new_files="" if plugin['plugin_dir'] != ".": for file in files.split(): if not file.startswith(plugin['rel_path']): file= os.path.join(plugin['rel_path'], file) new_files= "%s %s" % (new_files, file) else: new_files= " ".join(plugin['sources'].split()) if new_files != "": return new_files return files def expand_plugin_ini(plugin): if plugin['name'] == "**OUT-OF-TREE**": print "Out of tree plugins require the name field to be specified in plugin.ini" sys.exit(1) if plugin['plugin_dir'] == ".": plugin['rel_path']= plugin['plugin_dir'] plugin['unconditional']=True else: plugin['rel_path']= plugin['plugin_dir'][len(config['top_srcdir'])+len(os.path.sep):] plugin['unconditional']=False plugin['sources']= fix_file_paths(plugin, plugin['sources']) plugin['main_source']= plugin['sources'].split()[0] plugin['headers']= fix_file_paths(plugin, plugin['headers']) plugin['install_headers']= fix_file_paths(plugin, plugin['install_headers']) plugin['tests']= fix_file_paths(plugin, plugin['tests']) # Make a yes/no version for autoconf help messages if plugin['load_by_default']: plugin['default_yesno']="yes" else: plugin['default_yesno']="no" if plugin.has_key('extra_dist'): plugin['extra_dist']=" ".join([os.path.join(plugin['rel_path'],f) for f in plugin['extra_dist'].split()]) plugin['build_conditional_tag']= "BUILD_%s_PLUGIN" % plugin['name'].upper() plugin['name_with_dashes']= plugin['name'].replace('_','-') if plugin.has_key('build_conditional'): plugin['has_build_conditional']=True plugin['build_conditional']='"x${with_%(name)s_plugin}" = "xyes" -a %(build_conditional)s' % plugin else: plugin['has_build_conditional']=False plugin['build_conditional']='"x${with_%(name)s_plugin}" = "xyes"' %plugin if plugin['install']: plugin['library_type']= 'pkgplugin' else: plugin['library_type']= 'noinst' def find_testsuite(plugin_dir): for testdir in ['drizzle-tests','tests']: if os.path.isdir(os.path.join(plugin_dir,testdir)): return testdir if os.path.isdir(os.path.join('tests','suite',os.path.basename(plugin_dir))): return "" return None def find_docs(plugin_dir): if os.path.isfile(os.path.join(plugin_dir, "docs", "index.rst")): return os.path.join(plugin_dir, "docs") def read_plugin_ini(plugin_dir): sources_default="" if plugin_dir == ".": plugin_name="**OUT-OF-TREE**" module_name="**OUT-OF-TREE**" else: sources_default="%s.cc" % os.path.basename(plugin_dir) plugin_name = plugin_dir[plugin_dir.index(config['root_plugin_dir']) + len(config['root_plugin_dir']) + 1:] module_name = plugin_name.replace("/", config['module_name_separator']).replace("\\", config['module_name_separator']) plugin_name = plugin_name.replace("/", config['plugin_name_separator']).replace("\\", config['plugin_name_separator']) plugin_file= os.path.join(plugin_dir,config['plugin_ini_fname']) plugin_defaults= dict(sources=sources_default, headers="", install_headers="", cflags="", cppflags="", cxxflags="", libs="", ldflags="", author="", title="", description="", license="PLUGIN_LICENSE_GPL", name=plugin_name, module_name=module_name, load_by_default=config['default_load_by_default'], disabled="False", static="False", dependencies="", dependency_aliases="", tests="", install=config['default_install']) parser=ConfigParser.ConfigParser(defaults= plugin_defaults) parser.read(plugin_file) plugin=dict(parser.items('plugin')) plugin['plugin_dir'] = plugin_dir if plugin_dir == '.': if not plugin.has_key('url'): print "External Plugins are required to specifiy a url" plugin['url']= 'http://launchpad.net/%(name)s' % plugin sys.exit(1) if plugin_dir == '.' and not plugin.has_key('version'): print "External Plugins are required to specifiy a version" sys.exit(1) if not plugin.has_key('version'): plugin['version'] = config['default_plugin_version'] if plugin.has_key('load_by_default'): plugin['load_by_default']=parser.getboolean('plugin','load_by_default') if plugin.has_key('disabled'): plugin['disabled']=parser.getboolean('plugin','disabled') if plugin['disabled']: plugin['enabled']="no" else: plugin['enabled']="yes" if plugin.has_key('static'): try: plugin['static']= parser.getboolean('plugin','static') except: if plugin['static'][:5] == os.sys.platform[:5]: plugin['static']= True else: plugin['static']= False if plugin.has_key('install'): plugin['install']= parser.getboolean('plugin','install') if plugin.has_key('testsuite'): if plugin['testsuite'] == 'disable': plugin['testsuite']= False plugin['dist_testsuite']= find_testsuite(plugin_dir) else: plugin_testsuite= find_testsuite(plugin_dir) plugin['testsuitedir']=plugin_testsuite if plugin_testsuite is not None: plugin['testsuite']=True else: plugin['testsuite']=False plugin['docs']= find_docs(plugin_dir) plugin['cflags']+= ' ' + config['extra_cflags'] plugin['cppflags']+= ' ' + config['extra_cppflags'] plugin['cxxflags']+= ' ' + config['extra_cxxflags'] plugin['libname']= "lib%s%s%s" % (config['plugin_prefix'], plugin['name'], config['plugin_suffix']) if config['force_lowercase_libname']: plugin['libname']= plugin['libname'].lower() plugin['root_plugin_dir']= config['root_plugin_dir'] plugin['plugin_prefix']= config['plugin_prefix'] plugin['plugin_suffix']= config['plugin_suffix'] plugin['pkgplugindir']= config['pkgplugindir'] # Dependencies must have a module but dependency aliases are simply added # to the variable passed during compile. plugin['dependency_list'] = plugin['dependencies'].split() dependency_aliases = plugin['dependency_aliases'].split() plugin['dependencies'] = ','.join(plugin['dependency_list'] + plugin['dependency_aliases'].split()) dependency_libs = ["%s/lib%s%s.la" % (config['root_plugin_dir'], dependency.lower().replace('::', '_'), config['plugin_suffix']) for dependency in plugin['dependency_list']] plugin['libs'] = " ".join(plugin['libs'].split() + dependency_libs); # Libtool is going to expand: # -DPANDORA_MODULE_AUTHOR='"Padraig O'"'"'Sullivan"' # to: # "-DPANDORA_MODULE_AUTHOR=\"Padraig O'Sullivan\"" # So we have to replace internal ''s to '"'"' for key in ('author','title','description','version'): plugin[key]=plugin[key].replace('"','\\"') plugin[key]=plugin[key].replace("'","'\"'\"'") return plugin def write_plugin_am(plugin, plugin_am): """Write an automake fragment for this plugin. :param plugin: The plugin dict. :param plugin_am: The file to write to. """ # The .plugin.ini.stamp avoids changing the datestamp on plugin.ini which can # confuse VCS systems. plugin_am.write(""" EXTRA_DIST += %(rel_path)s/plugin.ini # Prevent errors when a plugin dir is removed %(rel_path)s/plugin.ini: """ % plugin) if plugin.has_key('extra_dist') and plugin['extra_dist'] != "": plugin_am.write("EXTRA_DIST += %(extra_dist)s\n" % plugin) if plugin['headers'] != "": plugin_am.write("noinst_HEADERS += %(headers)s\n" % plugin) if plugin['install_headers'] != "": plugin_am.write("nobase_include_HEADERS += %(install_headers)s\n" % plugin) if plugin['testsuite']: if plugin.has_key('testsuitedir') and plugin['testsuitedir'] != "": plugin_am.write("EXTRA_DIST += %(rel_path)s/%(testsuitedir)s\n" % plugin) if plugin.has_key('dist_testsuite') and plugin['dist_testsuite'] != "": plugin_am.write("EXTRA_DIST += %(rel_path)s/%(dist_testsuite)s\n" % plugin) if plugin['docs'] is not None: plugin_am.write("EXTRA_DIST += ${top_srcdir}/%(rel_path)s/docs/*.rst\n" % plugin) if plugin['static']: plugin_am.write(""" %(root_plugin_dir)s_%(plugin_prefix)s%(name)s_dir=${top_srcdir}/%(rel_path)s EXTRA_DIST += %(rel_path)s/plugin.ini if %(build_conditional_tag)s noinst_LTLIBRARIES+=%(root_plugin_dir)s/%(libname)s.la %(root_plugin_dir)s_%(libname)s_la_LIBADD=%(libs)s %(root_plugin_dir)s_%(libname)s_la_DEPENDENCIES=%(libs)s %(root_plugin_dir)s_%(libname)s_la_LDFLAGS=$(AM_LDFLAGS) %(ldflags)s $(GCOV_LIBS) %(root_plugin_dir)s_%(libname)s_la_CPPFLAGS=$(AM_CPPFLAGS) -DPANDORA_MODULE_NAME=%(module_name)s -DPANDORA_MODULE_AUTHOR='"%(author)s"' -DPANDORA_MODULE_TITLE='"%(title)s"' -DPANDORA_MODULE_VERSION='"%(version)s"' -DPANDORA_MODULE_LICENSE=%(license)s -DPANDORA_MODULE_DEPENDENCIES='"%(dependencies)s"' %(cppflags)s %(root_plugin_dir)s_%(libname)s_la_CXXFLAGS=$(AM_CXXFLAGS) %(cxxflags)s %(root_plugin_dir)s_%(libname)s_la_CFLAGS=$(AM_CFLAGS) %(cflags)s %(root_plugin_dir)s_%(libname)s_la_SOURCES=%(sources)s check_PROGRAMS += %(tests)s PANDORA_DYNAMIC_LDADDS+=${top_builddir}/%(root_plugin_dir)s/%(libname)s.la endif """ % plugin) else: plugin_am.write(""" %(root_plugin_dir)s_%(plugin_prefix)s%(name)s_dir=${top_srcdir}/%(rel_path)s EXTRA_DIST += %(rel_path)s/plugin.ini if %(build_conditional_tag)s %(library_type)s_LTLIBRARIES+=%(root_plugin_dir)s/%(libname)s.la %(root_plugin_dir)s_%(libname)s_la_LDFLAGS=-avoid-version -rpath $(pkgplugindir) $(AM_LDFLAGS) %(ldflags)s $(GCOV_LIBS) %(root_plugin_dir)s_%(libname)s_la_LIBADD=%(libs)s %(root_plugin_dir)s_%(libname)s_la_DEPENDENCIES=%(libs)s %(root_plugin_dir)s_%(libname)s_la_CPPFLAGS=$(AM_CPPFLAGS) -DPANDORA_DYNAMIC_PLUGIN -DPANDORA_MODULE_NAME=%(module_name)s -DPANDORA_MODULE_AUTHOR='"%(author)s"' -DPANDORA_MODULE_TITLE='"%(title)s"' -DPANDORA_MODULE_VERSION='"%(version)s"' -DPANDORA_MODULE_LICENSE=%(license)s -DPANDORA_MODULE_DEPENDENCIES='"%(dependencies)s"' %(cppflags)s %(root_plugin_dir)s_%(libname)s_la_CXXFLAGS=$(AM_CXXFLAGS) %(cxxflags)s %(root_plugin_dir)s_%(libname)s_la_CFLAGS=$(AM_CFLAGS) %(cflags)s %(root_plugin_dir)s_%(libname)s_la_SOURCES=%(sources)s check_PROGRAMS += %(tests)s endif """ % plugin) plugin_am_file=os.path.join(plugin['rel_path'],'plugin.am') if os.path.exists(plugin_am_file): plugin_am.write('include %s\n' % plugin_am_file) # # MAIN STARTS HERE: # # Parse the pandora-plugin config file config_defaults= dict( top_srcdir='.', top_builddir='.', plugin_ini_fname='plugin.ini', plugin_prefix='', plugin_suffix='', extra_cflags='', extra_cppflags='', extra_cxxflags='', root_plugin_dir='', pkgplugindir='', default_install='True', default_plugin_version='', default_load_by_default='False', force_lowercase_libname='True', plugin_name_separator='_', module_name_separator='::' ) config_parser = ConfigParser.ConfigParser(defaults=config_defaults) config_parser.read(pandora_plugin_file) config = dict(config_parser.items('pandora-plugin')) config['force_lowercase_libname']=config_parser.getboolean('pandora-plugin','force_lowercase_libname') # I'm 3 seconds away from writing a comprehensive build solution if not os.path.exists('config/pandora_vc_revinfo'): if os.path.exists('.bzr'): bzr_revno= subprocess.Popen(["bzr", "revno"], stdout=subprocess.PIPE).communicate()[0].strip() rev_date= datetime.date.fromtimestamp(time.time()) config['default_plugin_version'] = "%d.%02d.%s" % (rev_date.year, rev_date.month, bzr_revno) else: config['default_plugin_version']=datetime.date.fromtimestamp(time.time()).isoformat() else: # need to read config/pandora_vc_revno pandora_vc_revno=open('config/pandora_vc_revinfo','r').read().split() rev_date="" bzr_revno="" for revno_line in pandora_vc_revno: (revno_key,revno_val)= revno_line.split("=") if revno_key == 'PANDORA_VC_REVNO': bzr_revno=revno_val.strip() elif revno_key == 'PANDORA_RELEASE_DATE': rev_date=revno_val.strip() config['default_plugin_version'] = "%s.%s" % (rev_date, bzr_revno) actions=[] for arg in sys.argv: if arg.startswith('--top_srcdir='): config['top_srcdir']=arg[12:] elif arg.startswith('--top_builddir='): config['top_builddir']=arg[14:] elif arg == "--force-all": actions=['plugin-list','pandora-plugin.am','write'] break else: actions.append(arg) if len(actions) == 0: actions.append('write') plugin_list=[] def accumulate_plugins(arg, dirname, fnames): # plugin_ini_fname is a name in dirname indicating dirname is a plugin. if config['plugin_ini_fname'] in fnames: arg.append(dirname) os.path.walk(os.path.join(config['top_srcdir'], config['root_plugin_dir']), accumulate_plugins, plugin_list) if not os.path.exists("config/pandora-plugin.am") or "write" in actions: plugin_am_file = ChangeProtectedFile(os.path.join('config', 'pandora-plugin.am')) plugin_am_file.write(""" # always the current list, generated every build so keep this lean. # pandora-plugin.list: datestamp preserved list ${srcdir}/config/pandora-plugin.list: .plugin.scan .plugin.scan: @cd ${top_srcdir} && python config/pandora-plugin plugin-list # Plugins affect configure; so to prevent configure running twice in a tarball # build (once up front, once with the right list of plugins, we ship the # generated list of plugins and the housekeeping material for that list so it # is likewise not updated. EXTRA_DIST += \ config/pandora-plugin.am \ config/pandora-plugin.ac \ config/pandora-plugin \ config/pandora-plugin.ini # Seed the list of plugin LDADDS which plugins may extend. PANDORA_DYNAMIC_LDADDS= # plugin.stamp: graph dominator for creating all per pandora-plugin.ac/am # files. This is invoked when the code to generate such files has altered.""") if not os.path.exists("config/pandora-plugin.ac") or "write" in actions: plugin_ac_file = ChangeProtectedFile(os.path.join('config', 'pandora-plugin.ac')) plugin_ac_file.write("dnl Generated file, run make to rebuild\n") if os.path.exists("docs/plugins"): if not os.path.exists("docs/plugins/list.rst") or "write" in actions: plugin_doc_index = ChangeProtectedFile("docs/plugins/list.rst") plugin_doc_index.write(""" Plugin Documentation ==================== .. toctree:: :maxdepth: 2 """) if os.path.exists('plugin.ini'): # Are we in a plugin dir which wants to have a self-sufficient build system? plugin_list=['.'] write_external_plugin() else: plugin_list_file = ChangeProtectedFile(os.path.join('config', 'pandora-plugin.list')) for p in plugin_list: plugin_list_file.write(p) plugin_list_file.write("\n") plugin_list.sort() plugin_list_file.close() if not os.path.exists("config/pandora-plugin.am") or 'write' in actions: plugin_am_file.write("\n${top_srcdir}/config/pandora-plugin.am: ${top_srcdir}/config/pandora-plugin.list ${top_srcdir}/config/pandora-plugin ") for plugin_dir in plugin_list: plugin_am_file.write("\\\n\t%s/plugin.ini " % plugin_dir) plugin_am_file.write("\n\tcd ${top_srcdir} && python config/pandora-plugin write\n") plugin_ini_list=[] # Load all plugin.ini files first so we can do dependency tracking. for plugin_dir in plugin_list: plugin = read_plugin_ini(plugin_dir) expand_plugin_ini(plugin) plugin_ini_list.append(plugin) # Check for duplicates plugin_name_list = [plugin['libname'] for plugin in plugin_ini_list] for plugin in plugin_ini_list: if plugin_name_list.count(plugin['libname']) != 1: print "Duplicate module name %s" % plugin['libname'] exit(1) for plugin in plugin_ini_list: write_plugin(plugin, plugin_ini_list) if plugin_am_file is not None: plugin_am_file.close() if plugin_ac_file is not None: plugin_ac_file.close() if plugin_doc_index is not None: plugin_doc_index.close() haildb-2.3.2/CMakeLists.txt0000644000175000017500000002551611513177357016414 0ustar00pcrewspcrews00000000000000# Copyright (C) 2009 Oracle/Innobase Oy # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; version 2 of the License. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # This is the CMakeLists for Embedded InnoDB CMAKE_MINIMUM_REQUIRED(VERSION 2.6 FATAL_ERROR) PROJECT (EMBEDDED_INNODB) # Set both LIBRARY_OUTPUT_PATH and EXECUTABLE_OUTPUT_PATH SET (LIBRARY_OUTPUT_PATH ${CMAKE_SOURCE_DIR}/bin) SET (EXECUTABLE_OUTPUT_PATH ${CMAKE_SOURCE_DIR}/bin) IF(NOT DEFINED DISABLE-COMPRESSION) INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/zlib) ENDIF(NOT DEFINED DISABLE-COMPRESSION) # Print out CMake info MESSAGE(STATUS "CMAKE_GENERATOR: " ${CMAKE_GENERATOR}) MESSAGE(STATUS "CMAKE_SOURCE_DIR: " ${CMAKE_SOURCE_DIR}) # Print out system information MESSAGE(STATUS "CMAKE_SYSTEM: " ${CMAKE_SYSTEM}) MESSAGE(STATUS "CMAKE_SYSTEM_PROCESSOR: " ${CMAKE_SYSTEM_PROCESSOR}) MESSAGE(STATUS "UNIX: " ${UNIX}) MESSAGE(STATUS "WIN32: " ${WIN32}) MESSAGE(STATUS "WIN64: " ${WIN64}) MESSAGE(STATUS "MSVC: " ${MSVC}) SET(IB_LANG "english") # Check for programs required to build InnoDB e.g., Bison and Flex FIND_PROGRAM(FLEX flex) FIND_PROGRAM(BISON bison) include(CheckTypeSize) CHECK_TYPE_SIZE(char SIZEOF_CHAR) CHECK_TYPE_SIZE("unsigned char" SIZEOF_UCHAR) CHECK_TYPE_SIZE(short SIZEOF_SHORT) CHECK_TYPE_SIZE("unsigned short" SIZEOF_USHORT) CHECK_TYPE_SIZE(int SIZEOF_INT) CHECK_TYPE_SIZE("unsigned int" SIZEOF_UINT) CHECK_TYPE_SIZE(long SIZEOF_LONG) CHECK_TYPE_SIZE("unsigned long" SIZEOF_ULONG) CHECK_TYPE_SIZE("long long int" SIZEOF_LONG_LONG) CHECK_TYPE_SIZE("unsigned long long int" SIZEOF_ULONG_LONG) CHECK_TYPE_SIZE(char* SIZEOF_CHARP) CHECK_TYPE_SIZE(void* SIZEOF_VOIDP) CHECK_TYPE_SIZE(off_t SIZEOF_OFF_T) CHECK_TYPE_SIZE(int8_t HAVE_INT8_T) CHECK_TYPE_SIZE(uint8_t HAVE_UINT8_T) CHECK_TYPE_SIZE(int16_t HAVE_INT16_T) CHECK_TYPE_SIZE(uint16_t HAVE_UINT16_T) CHECK_TYPE_SIZE(int32_t HAVE_INT32_T) CHECK_TYPE_SIZE(uint32_t HAVE_UINT32_T) CHECK_TYPE_SIZE(int64_t HAVE_INT64_T) CHECK_TYPE_SIZE(uint64_t HAVE_UINT64_T) include(CheckIncludeFiles) CHECK_INCLUDE_FILES(alloca.h HAVE_ALLOCA_H) CHECK_INCLUDE_FILES(assert.h HAVE_ASSERT_H) CHECK_INCLUDE_FILES(atomic.h HAVE_ATOMIC_H) CHECK_INCLUDE_FILES(ctype.h HAVE_CTYPE_H) CHECK_INCLUDE_FILES(dirent.h HAVE_DIRENT_H) CHECK_INCLUDE_FILES(errno.h HAVE_ERRNO_H) CHECK_INCLUDE_FILES(fcntl.h HAVE_FCNTL_H) CHECK_INCLUDE_FILES(inttypes.h HAVE_INTTYPES_H) CHECK_INCLUDE_FILES(libintl.h HAVE_LIBINTL_H) CHECK_INCLUDE_FILES(malloc.h HAVE_MALLOC_H) CHECK_INCLUDE_FILES(math.h HAVE_MATH_H) CHECK_INCLUDE_FILES(pthread.h HAVE_PTHREAD_H) CHECK_INCLUDE_FILES(sched.h HAVE_SCHED_H) CHECK_INCLUDE_FILES(stdarg.h HAVE_STDARG_H) CHECK_INCLUDE_FILES(stddef.h HAVE_STDDEF_H) CHECK_INCLUDE_FILES(stdint.h HAVE_STDINT_H) CHECK_INCLUDE_FILES(stdio.h HAVE_STDIO_H) CHECK_INCLUDE_FILES(stdlib.h HAVE_STDLIB_H) CHECK_INCLUDE_FILES(string.h HAVE_STRING_H) CHECK_INCLUDE_FILES(strings.h HAVE_STRINGS_H) CHECK_INCLUDE_FILES(sys/ipc.h sys/HAVE_IPC_H) CHECK_INCLUDE_FILES(sys/mman.h sys/HAVE_MMAN_H) CHECK_INCLUDE_FILES(sys/resource.h sys/HAVE_RESOURCE_H) CHECK_INCLUDE_FILES(sys/shm.h sys/HAVE_SHM_H) CHECK_INCLUDE_FILES(sys/stat.h sys/HAVE_STAT_H) CHECK_INCLUDE_FILES(sys/time.h sys/HAVE_TIME_H) CHECK_INCLUDE_FILES(sys/types.h sys/HAVE_TYPES_H) CHECK_INCLUDE_FILES(sys/utsname.h sys/HAVE_UTSNAME_H) CHECK_INCLUDE_FILES(time.h HAVE_TIME_H) CHECK_INCLUDE_FILES(unistd.h HAVE_UNISTD_H) CHECK_INCLUDE_FILES(valgrind/memcheck.h HAVE_MEMCHECK_H) CHECK_INCLUDE_FILES(zlib.h HAVE_ZLIB_H) include(CheckLibraryExists) CHECK_LIBRARY_EXISTS(m floor "" HAVE_MATH) CHECK_LIBRARY_EXISTS(z inflate "" HAVE_ZLIB) CHECK_LIBRARY_EXISTS(pthread pthread_create "" HAVE_PTHREAD) Include(CheckFunctionExists) CHECK_FUNCTION_EXISTS(bcmp HAVE_BCMP) CHECK_FUNCTION_EXISTS(fcntl HAVE_FCNTL) CHECK_FUNCTION_EXISTS(finite HAVE_FINITE) CHECK_FUNCTION_EXISTS(fsync HAVE_FSYNC) CHECK_FUNCTION_EXISTS(ftruncate HAVE_FTRUNCATE) CHECK_FUNCTION_EXISTS(getcwd HAVE_GETCWD) CHECK_FUNCTION_EXISTS(getrusage HAVE_GETRUSAGE) CHECK_FUNCTION_EXISTS(index HAVE_INDEX) CHECK_FUNCTION_EXISTS(localtime_r HAVE_LOCALTIME_R) CHECK_FUNCTION_EXISTS(locking HAVE_LOCKING) CHECK_FUNCTION_EXISTS(memcpy HAVE_MEMCPY) CHECK_FUNCTION_EXISTS(memmove HAVE_MEMMOVE) CHECK_FUNCTION_EXISTS(perror HAVE_PERROR) CHECK_FUNCTION_EXISTS(pread HAVE_PREAD) CHECK_FUNCTION_EXISTS(mmap HAVE_MMAP) CHECK_FUNCTION_EXISTS(getpagesize HAVE_GETPAGESIZE) CHECK_FUNCTION_EXISTS(pthread_attr_setstacksize HAVE_PTHREAD_ATTR_SETSTACKSIZE) CHECK_FUNCTION_EXISTS(pthread_setprio HAVE_PTHREAD_SETPRIO) CHECK_FUNCTION_EXISTS(rename HAVE_RENAME) CHECK_FUNCTION_EXISTS(rint HAVE_RINT) CHECK_FUNCTION_EXISTS(shmget HAVE_SHMGET) CHECK_FUNCTION_EXISTS(shmat HAVE_SHMAT) CHECK_FUNCTION_EXISTS(shmdt HAVE_SHMDT) CHECK_FUNCTION_EXISTS(shmctl HAVE_SHMCTL) CHECK_FUNCTION_EXISTS(sleep HAVE_SLEEP) CHECK_FUNCTION_EXISTS(snprintf HAVE_SNPRINTF) CHECK_FUNCTION_EXISTS(stpcpy HAVE_STPCPY) CHECK_FUNCTION_EXISTS(strcasecmp HAVE_STRCASECMP) CHECK_FUNCTION_EXISTS(strerror HAVE_STRERROR) CHECK_FUNCTION_EXISTS(strstr HAVE_STRSTR) CHECK_FUNCTION_EXISTS(strtoul HAVE_STRTOUL) CHECK_FUNCTION_EXISTS(tell HAVE_TELL) # Checks for Solaris 10+ atomic functions IF(CMAKE_SYSTEM_NAME MATCHES "Solaris") CHECK_FUNCTION_EXISTS(atomic_cas_ulong HAVE_ATOMIC_CAS_ULONG) CHECK_FUNCTION_EXISTS(atomic_cas_32 HAVE_ATOMIC_CAS_32) CHECK_FUNCTION_EXISTS(atomic_cas_64 HAVE_ATOMIC_CAS_64) CHECK_FUNCTION_EXISTS(atomic_add_long HAVE_ATOMIC_ADD_LONG) IF(DEFINED HAVE_ATOMIC_CAS_ULONG AND DEFINED HAVE_ATOMIC_CAS_32 AND DEFINED HAVE_ATOMIC_CAS_64 AND DEFINED HAVE_ATOMIC_ADD_LONG) SET(HAVE_SOLARIS_ATOMICS TRUE) ENDIF(DEFINED HAVE_ATOMIC_CAS_ULONG AND DEFINED HAVE_ATOMIC_CAS_32 AND DEFINED HAVE_ATOMIC_CAS_64 AND DEFINED HAVE_ATOMIC_ADD_LONG) ENDIF(CMAKE_SYSTEM_NAME MATCHES "Solaris") SET(CMAKE_EXTRA_INCLUDE_FILES pthread.h) CHECK_TYPE_SIZE(pthread_t SIZEOF_PTHREAD_T) SET(CMAKE_EXTRA_INCLUDE_FILES) # Include directories under innobase INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/include) SET(INNODB_SOURCES btr/btr0btr.c btr/btr0cur.c btr/btr0pcur.c btr/btr0sea.c buf/buf0buf.c buf/buf0flu.c buf/buf0lru.c buf/buf0rea.c data/data0data.c data/data0type.c dict/dict0boot.c dict/dict0crea.c dict/dict0dict.c dict/dict0load.c dict/dict0mem.c dyn/dyn0dyn.c eval/eval0eval.c eval/eval0proc.c fil/fil0fil.c fsp/fsp0fsp.c fut/fut0fut.c fut/fut0lst.c ha/ha0ha.c ha/hash0hash.c ha/ha0storage.c ibuf/ibuf0ibuf.c pars/lexyy.c pars/pars0grm.c pars/pars0opt.c pars/pars0pars.c pars/pars0sym.c lock/lock0lock.c lock/lock0iter.c log/log0log.c log/log0recv.c mach/mach0data.c mem/mem0mem.c mtr/mtr0log.c mtr/mtr0mtr.c os/os0file.c os/os0proc.c os/os0sync.c os/os0thread.c page/page0cur.c page/page0page.c que/que0que.c read/read0read.c rem/rem0cmp.c rem/rem0rec.c row/row0ext.c row/row0ins.c row/row0merge.c row/row0purge.c row/row0row.c row/row0prebuilt.c row/row0sel.c row/row0uins.c row/row0umod.c row/row0undo.c row/row0upd.c row/row0vers.c srv/srv0que.c srv/srv0srv.c srv/srv0start.c sync/sync0arr.c sync/sync0rw.c sync/sync0sync.c thr/thr0loc.c trx/trx0purge.c trx/trx0rec.c trx/trx0roll.c trx/trx0rseg.c trx/trx0sys.c trx/trx0trx.c trx/trx0undo.c usr/usr0sess.c ut/ut0byte.c ut/ut0dbg.c ut/ut0mem.c ut/ut0rnd.c ut/ut0ut.c ut/ut0vec.c ut/ut0list.c ddl/ddl0ddl.c api/api0api.c api/api0misc.c api/api0ucode.c api/api0cfg.c api/api0status.c api/api0sql.c) IF(NOT DEFINED DISABLE-COMPRESSION) SET(INNODB_SOURCES ${INNODB_SOURCES} buf/buf0buddy.c page/page0zip.c) ENDIF(NOT DEFINED DISABLE-COMPRESSION) IF(DEFINED WIN32) ADD_DEFINITIONS("-D__WIN__ -D_WINDOWS -D_CRT_SECURE_NO_DEPRECATE") # Check for 64 bit platform IF(CMAKE_SIZEOF_VOID_P MATCHES 8) SET(ARCH "amd64") ADD_DEFINITIONS("-D_WIN64") ELSE(CMAKE_SIZEOF_VOID_P MATCHES 8) SET(ARCH "x86") ADD_DEFINITIONS("-D_WIN32") # Enable Windows InterLocked functions on 32-bit Windows ADD_DEFINITIONS(-DHAVE_WINDOWS_ATOMICS) ENDIF(CMAKE_SIZEOF_VOID_P MATCHES 8) SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /MAP /MAPINFO:EXPORTS") SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /STACK:1048576") # Removing compiler optimizations for innodb/mem/* files # on 64-bit Windows due to 64-bit compiler error, See MySQL # Bug #19424, #36366, #34297 IF(ARCH MATCHES "amd64") SET_SOURCE_FILES_PROPERTIES(mem/mem0mem.c PROPERTIES COMPILE_FLAGS -Od) ENDIF(ARCH MATCHES "amd64") # Windows we always build a DLL ADD_LIBRARY(innodb SHARED ${INNODB_SOURCES} win/innodb.def) IF(NOT DEFINED DISABLE-COMPRESSION) ADD_DEPENDENCIES(innodb zlib) ENDIF(NOT DEFINED DISABLE-COMPRESSION) ELSEIF(DEFINED UNIX) IF(CMAKE_BUILD_TYPE MATCHES debug) ADD_DEFINITIONS(-g -Wall -DUNIV_DEBUG) ELSEIF(CMAKE_BUILD_TYPE MATCHES release) ADD_DEFINITIONS(-O2) ELSE(CMAKE_BUILD_TYPE MATCHES debug) ADD_DEFINITIONS(-g -Wall -Werror) ENDIF(CMAKE_BUILD_TYPE MATCHES debug) IF(CMAKE_SYSTEM_NAME MATCHES "Linux") ADD_DEFINITIONS(-DUNIV_LINUX) ELSEIF(CMAKE_SYSTEM_NAME MATCHES "Solaris" OR CMAKE_SYSTEM_NAME MATCHES "SunOS") ADD_DEFINITIONS(-DUNIV_SOLARIS) ELSEIF(CMAKE_SYSTEM_NAME MATCHES "aix") ADD_DEFINITIONS(-DUNIV_AIX) ENDIF(CMAKE_SYSTEM_NAME MATCHES "Linux") # Static build for now ADD_LIBRARY(innodb STATIC ${INNODB_SOURCES}) ENDIF(DEFINED WIN32) IF(NOT DEFINED DISABLE-XA) ADD_DEFINITIONS("-DWITH_XOPEN") ENDIF(NOT DEFINED DISABLE-XA) IF(NOT DEFINED DISABLE-COMPRESSION) ADD_DEFINITIONS("-DWITH_ZIP") SUBDIRS(zlib) ENDIF(NOT DEFINED DISABLE-COMPRESSION) SUBDIRS(tests) SUBDIRS(c++) CONFIGURE_FILE( ${CMAKE_CURRENT_SOURCE_DIR}/config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/include/ib0config.h) # List all install files and use CPack for packaging and installation SET(CMAKE_INSTALL_PREFIX package) INSTALL(FILES COPYING COPYING.Google COPYING.Percona COPYING.Sun_Microsystems DESTINATION .) INSTALL(FILES innodb.h DESTINATION include) INSTALL(FILES tests/ib_cfg.c tests/ib_compressed.c tests/ib_cursor.c tests/ib_index.c tests/ib_logger.c tests/ib_search.c tests/ib_status.c tests/ib_test1.c tests/ib_test2.c tests/ib_test3.c tests/ib_test5.c tests/ib_types.c tests/ib_update.c tests/test0aux.c tests/test0aux.h tests/README DESTINATION examples) INSTALL(FILES tests/CMakeLists.examples DESTINATION examples RENAME CMakeLists.txt) INSTALL(TARGETS innodb RUNTIME DESTINATION bin LIBRARY DESTINATION lib ARCHIVE DESTINATION lib )